kappelas

package module
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 15 Imported by: 0

README

kappelas-sdk-go

Go Reference Go version GitHub

Official Go SDK for the Kappela messaging platform.
Build bots and personal automations — send messages, handle events, manage chats.


Table of contents


Prerequisites

You need a bot token from BotMother, the official Kappela bot manager.

  1. Open Kappela and start a conversation with BotMother
  2. Follow the instructions to create a bot
  3. BotMother gives you a token — keep it secret, it gives full control over your bot

For personal automation (sending messages as yourself), generate an API key from your Kappela account settings (sk_...).


Install

go get github.com/Arnel7/kappelas-sdk-go

Requires Go 1.21+.


Quick start

Bot
package main

import (
    "context"

    "github.com/Arnel7/kappelas-sdk-go"
)

func main() {
    ctx := context.Background()
    bot := kappelas.NewBot("YOUR_BOT_TOKEN")

    bot.OnMessage(func(msg *kappelas.Message) {
        bot.Reply(ctx, msg, "Echo: "+msg.GetText())
    })

    bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) {
        bot.Reply(ctx, cb, "Tu as cliqué : "+cb.CallbackData)
    })

    bot.Start()
    select {} // keep alive
}
Personal automation
me := kappelas.NewUser("sk_...")

me.OnMessage(func(msg *kappelas.Message) {
    fmt.Printf("[%d] %s\n", msg.ChatID, msg.GetText())
})

me.Start()
select {}

User exposes the same resources as Botme.Messages, me.Chats (including member management and invite links), me.Communities, me.Webhooks, me.Profile, and me.Reply() — acting as yourself. In addition, User has me.Stories (user-only). Collections and Hugging Face credentials are bot-only.

ctx := context.Background()
me.Reply(ctx, msg, "Got it! 👋")
me.Communities.Create(ctx, kappelas.CreateCommunityParams{Name: "Devs", RequiresApproval: true})

// User-only: stories
me.Stories.Create(ctx, kappelas.CreateStoryParams{Type: kappelas.StoryText, Caption: "Hello 👋"})
Pausing automations

Pausing your personal automation makes your account stop receiving incoming messages over /v1/me, so an AI auto-responder is never triggered, and any send is rejected with AUTOMATIONS_PAUSED — until you resume. Pausing a bot makes it stop receiving incoming messages (no WebSocket push, no webhook) and rejects sends with BOT_PAUSED until resumed. This is useful when the human owner wants to take over and stop the AI.

ctx := context.Background()

// Personal automation — *kappelas.AutomationStatus{AutomationsPaused bool}
me.PauseAutomations(ctx)     // → AutomationsPaused: true
me.ResumeAutomations(ctx)    // → AutomationsPaused: false
me.GetAutomationStatus(ctx)  // → AutomationsPaused: bool

// Bot — *kappelas.BotPauseStatus{Paused bool}
bot.Pause(ctx)               // → Paused: true
bot.Resume(ctx)              // → Paused: false
bot.GetStatus(ctx)           // → Paused: bool

To pause only in ONE conversation (e.g. you take over a single chat while the AI keeps handling the rest):

// Personal automation, scoped to one chat (chatID int64) — → Done: true
me.PauseAutomationInChat(ctx, chatID)
me.ResumeAutomationInChat(ctx, chatID)

// Bot, scoped to one chat
bot.PauseInChat(ctx, chatID)
bot.ResumeInChat(ctx, chatID)

Events — WebSocket vs Webhook

Mode Method Best for
WebSocket bot.Start() Development, local scripts
Webhook bot.Webhooks.Set() + bot.HandleWebhook() Production servers

The same OnMessage and OnCallbackQuery handlers work in both modes — no code change needed when switching.

WebSocket (development)
bot := kappelas.NewBot("YOUR_BOT_TOKEN")

bot.OnMessage(func(msg *kappelas.Message) { /* … */ })
bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) { /* … */ })

bot.Start()   // non-blocking, auto-reconnects on disconnect
select {}
Webhook (production)
import (
    "io"
    "net/http"

    "github.com/Arnel7/kappelas-sdk-go"
)

bot := kappelas.NewBot("YOUR_BOT_TOKEN")

// register once
ctx := context.Background()
bot.Webhooks.Set(ctx, kappelas.SetWebhookParams{
    URL: "https://your-server.com/kappela-webhook",
})

bot.OnMessage(func(msg *kappelas.Message) { /* … */ })
bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) { /* … */ })

http.HandleFunc("/kappela-webhook", func(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    bot.HandleWebhook(body)
    w.WriteHeader(http.StatusOK)
})

http.ListenAndServe(":8080", nil)

Do not call bot.Start() in webhook mode.

Event reference
Method Signature Description
OnMessage func(*Message) Incoming message of any type
OnCallbackQuery func(*CallbackQuery) Inline button clicked by a user
OnConnected func() WebSocket connected or reconnected
OnDisconnected func(code int, reason string) WebSocket disconnected
OnError func(error) Connection or transport error

WebSocket reconnection — by default the SDK retries up to 12 times with exponential back-off. Override with WithWSMaxRetries(n). When retries are exhausted OnDisconnected fires with code 1006 and reason "max retries reached".


bot.Reply() — convenience shorthand

Reply sends a text reply without having to repeat ChatID and ReplyToID manually.

  • Called with a *Message — sets ReplyToID automatically (shows a quote banner in the chat).
  • Called with a *CallbackQuery — sends to the same chat, no quote banner (callback queries have no message ID).
bot.OnMessage(func(msg *kappelas.Message) {
    ctx := context.Background()

    // Simple reply
    bot.Reply(ctx, msg, "Reçu 👍")

    // With an inline keyboard
    bot.Reply(ctx, msg, "Choisis une option :", kappelas.SendMessageParams{
        ReplyMarkup: kappelas.InlineKeyboard{
            InlineKeyboard: [][]kappelas.InlineKeyboardButton{{
                {Text: "✅ Oui", CallbackData: ptr("yes")},
                {Text: "❌ Non", CallbackData: ptr("no")},
            }},
        },
    })
})

bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) {
    ctx := context.Background()
    bot.Reply(ctx, cb, "Tu as cliqué : "+cb.CallbackData)
})

ChatID, ReplyToID, and Text in the optional SendMessageParams are filled automatically — you only need to set extra fields like ReplyMarkup or DeletePrevious.


Message fields
bot.OnMessage(func(msg *kappelas.Message) {
    msg.ID               // int64         — unique message ID
    msg.ChatID           // int64         — conversation ID
    msg.ChatType         // *ChatType     — "private" | "group" | "channel" (may be nil for history)
    msg.SenderID         // *string       — UUID of the sender (nil for system messages)
    msg.Type             // MessageType   — "text" | "image" | "video" | "audio" | "document" | …
    msg.Text             // *string       — text content (nil for media-only messages); use msg.GetText() to avoid nil deref
    msg.MediaID          // *string       — server-side media ID
    msg.ExtraData        // json.RawMessage — inline keyboard payload (when attached)
    msg.Status           // MessageStatus — "sent" | "delivered" | "read"
    msg.CreatedAt        // int64         — Unix timestamp (seconds)
    msg.EditedAt         // *int64        — last edit time, or nil
    msg.DeletedAt        // *int64        — deletion time, or nil
    msg.ReplyToID        // *int64        — ID of the message being replied to
    msg.ReplyToSnapshot  // *ReplySnapshot — snapshot of the replied-to message
    msg.Mentions         // []string      — UUIDs of mentioned users
    msg.SenderName       // *string       — display name in groups/channels (nil in private)
    msg.SenderAvatarURL  // *string       — avatar URL of the sender
    msg.ExpiresAt        // *int64        — expiry time for ephemeral messages
})

MessageType values

Value Description
"text" Plain text message
"image" Photo
"video" Video
"audio" Audio file
"document" Generic file
"sticker" Sticker
"poll" Poll
"location" Location pin
"contact" Contact card
"system" System notification (member joined, etc.)

CallbackQuery fields
bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) {
    cb.ChatID          // int64   — chat where the button was clicked
    cb.SenderID        // string  — UUID of the user who clicked
    cb.SenderName      // *string — display name (e.g. "Arnel LAWSON")  use cb.GetSenderName() to avoid nil deref
    cb.SenderUsername  // *string — username (e.g. "arnell")
    cb.CallbackData    // string  — value set on the button
    cb.SentAt          // int64   — Unix timestamp (seconds)
})

Clicks are deduplicated server-side — your handler fires exactly once per click.



API reference

Constructor options
kappelas.NewBot(token string, opts ...BotOption) *Bot
Option Default Description
WithBaseURL(url) https://api.kappelas.com Override API base URL
WithMaxRetries(n) 2 HTTP retry count on 429 / 5xx
WithTimeout(d) 30s Per-request timeout
WithWSMaxRetries(n) 12 Max WebSocket reconnect attempts
kappelas.NewUser(apiKey string, opts ...UserOption) *User
Option Default Description
WithUserBaseURL(url) https://api.kappelas.com Override API base URL
WithUserMaxRetries(n) 2 HTTP retry count on 429 / 5xx
WithUserTimeout(d) 30s Per-request timeout
WithUserWSMaxRetries(n) 12 Max WebSocket reconnect attempts
bot := kappelas.NewBot("token",
    kappelas.WithTimeout(10 * time.Second),
    kappelas.WithMaxRetries(3),
)

messages

Recipient — ChatID or UserID. Every send / edit / delete / typing method takes either ChatID (int64) or UserID (string UUID). With UserID the message is routed to your 1-to-1 private chat with that user — a Bot requires the conversation to already exist (FORBIDDEN otherwise); a User creates it automatically (find-or-create). Applies to Send, SendPhoto/SendVideo/SendDocument/SendAudio, SendCarousel, SendTyping, Edit and Delete (for Edit/Delete the conversation must already exist).

bot.Messages.Send(ctx, kappelas.SendMessageParams{UserID: "f19f2127-…", Text: "Hi"})
me.Messages.SendPhoto(ctx, kappelas.SendMediaParams{UserID: cb.SenderID, File: f})
Messages.Send(ctx, params)(*SendResult, error)
yes, no := "yes", "no"
replyTo := int64(123)

result, err := bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID:    42,
    Text:      "Bonjour !",
    ReplyToID: &replyTo,          // optional — shows a quote banner
    ReplyMarkup: kappelas.InlineKeyboard{
        InlineKeyboard: [][]kappelas.InlineKeyboardButton{{
            {Text: "Oui", CallbackData: &yes},
            {Text: "Non", CallbackData: &no},
        }},
    },
})
// → &SendResult{MessageID: …, CreatedAt: …}

Pass DeletePrevious: true to automatically remove the previous message from this bot in the same chat before sending.

Action button

ActionButton renders a single button at the foot of the bubble (WhatsApp-style), distinct from inline keyboards — it performs a client-side action instead of firing a CallbackQuery. Set it via SendMessageParams.ActionButton (text messages only). When both ActionButton and ReplyMarkup are set, ActionButton wins.

// One-time code the user copies with a tap
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   "Your code is 837192",
    ActionButton: &kappelas.ActionButton{Label: "Copy code", Type: "copy_text", Value: "837192"},
})
Type What a tap does Value is…
copy_text Copies Value to the clipboard The text to copy (e.g. an OTP)
external_link Opens Value in the in-app browser An external URL
internal_link Opens Value as an in-app deep link An internal Kappela link
join Joins the chat directly (no landing screen) An invite link (group/channel/community)

Label is 1–100 characters, Value 1–2048.

Messages.SendPhoto(ctx, params)(*SendMediaResult, error)
data, _ := os.ReadFile("banner.png")
result, err := bot.Messages.SendPhoto(ctx, kappelas.SendMediaParams{
    ChatID:  42,
    File:    kappelas.FileInput{Data: data, Filename: "banner.png", ContentType: "image/png"},
    Caption: "Voici notre bannière !",
})
// → SendMediaResult{MessageID, CreatedAt, MediaID}
Messages.SendVideo / SendDocument / SendAudio(*SendMediaResult, error)

Same shape — pass the appropriate FileInput.

Messages.SendCarousel(ctx, params)(*SendCarouselResult, error)
btn := "Voir"
bot.Messages.SendCarousel(ctx, kappelas.SendCarouselParams{
    ChatID: 42,
    Text:   "Choisissez un produit :",
    Carousel: []kappelas.CarouselCard{
        {ID: "p1", Title: "Produit A", Subtitle: ptr("9 900 FCFA"), ButtonText: &btn},
        {ID: "p2", Title: "Produit B", Subtitle: ptr("19 900 FCFA"), ButtonText: &btn},
    },
    QuickReplyButtons: []kappelas.ScrollKeyboardButton{
        {Text: "Voir plus"}, {Text: "Annuler"},
    },
})

When a user clicks a carousel card button, a CallbackQuery fires with CallbackData set to the card's ID ("p1", "p2", …).

Messages.Edit(ctx, params)(*EditMessageResult, error)
// Edit text only
bot.Messages.Edit(ctx, kappelas.EditMessageParams{
    ChatID: 42, MessageID: 123, NewText: "Mis à jour !",
})

// Edit inline keyboard only (keep existing text)
import "encoding/json"
done := "done"
kb, _ := json.Marshal(kappelas.InlineKeyboard{
    InlineKeyboard: [][]kappelas.InlineKeyboardButton{
        {{Text: "Terminé ✅", CallbackData: &done}},
    },
})
bot.Messages.Edit(ctx, kappelas.EditMessageParams{
    ChatID: 42, MessageID: 123, NewExtraData: kb,
})

// Remove the keyboard entirely
bot.Messages.Edit(ctx, kappelas.EditMessageParams{
    ChatID: 42, MessageID: 123, NewExtraData: json.RawMessage("null"),
})
// → EditMessageResult{Edited: true, MessageID: 123}
Messages.SendTyping(ctx, params)(*TypingResult, error)
bot.Messages.SendTyping(ctx, kappelas.SendTypingParams{ChatID: 42})        // show
f := false
bot.Messages.SendTyping(ctx, kappelas.SendTypingParams{ChatID: 42, IsTyping: &f}) // hide
Messages.Delete(ctx, params)(*DeleteResult, error)
bot.Messages.Delete(ctx, kappelas.DeleteMessageParams{ChatID: 42, MessageID: 123})
// → DeleteResult{Deleted: true}

chats
Chats.List(ctx, params)(ChatsResult, error)
result, err := bot.Chats.List(ctx, kappelas.GetChatsParams{Limit: 20, Offset: 0})
fmt.Println(result.Chats, result.HasMore)
Chats.Iterate(ctx, pageSize, fn)error
err := bot.Chats.Iterate(ctx, 50, func(chat *kappelas.Chat) bool {
    fmt.Println(chat.ChatID, chat.Type)
    return true // return false to stop early
})

Chat fields

chat.ChatID              // int64         — conversation ID
chat.Type                // ChatType      — "private" | "group" | "channel"
chat.Title               // *string       — group/channel name (nil for private)
chat.Participants        // []Participant — members (private only; empty for large groups)
chat.LastMessageAt       // *string       — ISO 8601 timestamp of last message
chat.CreatedAt           // string        — ISO 8601 creation timestamp
chat.IsPublic            // bool          — public group or channel
chat.OnlyAdminsCanWrite  // bool          — only admins can post
chat.Description         // *string       — group/channel description
chat.AvatarURL           // *string       — avatar image URL

Groups & channels

Bots work identically in private chats, groups, and channels — same API, same events. The only requirement is that the bot must be a member of the conversation.

Receiving group messages

When a bot is added to a group or channel, it automatically receives every message posted there via the same OnMessage handler used for DMs.

bot.OnMessage(func(msg *kappelas.Message) {
    // msg.ChatID    — the group's id
    // msg.ChatType  — pointer to "private" | "group" | "channel"
    // msg.SenderID  — UUID of the user who sent the message
    // msg.Text      — message content (nil for media-only)
})

The ChatType field lets you distinguish where a message came from without an extra API call.

Replying in a group

ReplyToID attaches a quote banner to your message. It works identically in private chats, groups, and channels. In groups, always quote the user you're responding to — it makes the context clear to all members.

bot.OnMessage(func(msg *kappelas.Message) {
    ctx := context.Background()
    name := msg.GetSenderName() // "" in private chats, display name in groups/channels
    if name == "" {
        name = "ami"
    }
    bot.Messages.Send(ctx, kappelas.SendMessageParams{
        ChatID:    msg.ChatID,
        Text:      "Reçu, " + name + " 👋",
        ReplyToID: &msg.ID, // quotes the original message
    })
})

Quoting any historical messageReplyToID accepts any MessageID, not just the one that triggered the event:

historyID := int64(456)
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID:    msg.ChatID,
    Text:      "Voici la réponse à ta question précédente :",
    ReplyToID: &historyID,
})

Works on all Send methods* — SendPhoto, SendVideo, SendDocument, SendAudio, and SendCarousel all accept ReplyToID:

replyTo := msg.ID
bot.Messages.SendCarousel(ctx, kappelas.SendCarouselParams{
    ChatID:    msg.ChatID,
    Text:      "Voici nos produits :",
    Carousel:  []kappelas.CarouselCard{{ID: "p1", Title: "Produit A"}},
    ReplyToID: &replyTo, // banner shows above the carousel
})
Getting member IDs

There are three ways to obtain the UserID of members in a group or channel:

1. From incoming messages — the simplest. msg.SenderID is always set on every message event:

bot.OnMessage(func(msg *kappelas.Message) {
    if msg.ChatType != nil && *msg.ChatType == kappelas.ChatTypeGroup {
        fmt.Println(*msg.SenderID)   // UUID of the sender
        fmt.Println(*msg.SenderName) // display name (nil in private chats)
    }
})

2. From the participants listChats.List() returns the full member list for each chat:

result, err := bot.Chats.List(ctx, kappelas.GetChatsParams{Limit: 50})
for _, chat := range result.Chats {
    for _, member := range chat.Participants {
        fmt.Println(member.ID)     // UUID — use as UserID in member calls
        fmt.Println(member.Nom)    // display name
        fmt.Println(member.IsBot)  // true if this participant is a bot
        if member.Role != nil {
            fmt.Println(*member.Role) // "admin" | "member" (nil on private chats)
        }
    }
}

3. From Chats.GetAdministrators() — when you only need admin IDs:

result, err := bot.Chats.GetAdministrators(ctx, kappelas.GetChatAdministratorsParams{ChatID: 42})
for _, admin := range result.Admins {
    fmt.Println(admin.UserID) // UUID
}

Chats.GetMember() lets you check whether a specific user is still in the group and what their current role is — useful after a BanMember or PromoteMember call to confirm the change.

Detecting conversation type

msg.ChatType is available on every incoming message. Use it to adapt bot behaviour per context:

bot.OnMessage(func(msg *kappelas.Message) {
    ctx := context.Background()
    if msg.ChatType == nil {
        return
    }
    switch *msg.ChatType {
    case kappelas.ChatTypePrivate:
        // 1-on-1 chat — show full keyboard, personalise replies
        bot.Messages.Send(ctx, kappelas.SendMessageParams{
            ChatID: msg.ChatID,
            Text:   "De quoi as-tu besoin ?",
            ReplyMarkup: kappelas.ScrollKeyboard{
                ScrollKeyboard: []kappelas.ScrollKeyboardButton{
                    {Text: "📦 Commandes"}, {Text: "❓ Aide"}, {Text: "⚙️ Réglages"},
                },
            },
        })

    case kappelas.ChatTypeGroup:
        // Multi-user — reply with a quote so context is clear
        bot.Messages.Send(ctx, kappelas.SendMessageParams{
            ChatID:    msg.ChatID,
            Text:      "✅ Noté !",
            ReplyToID: &msg.ID,
        })

    case kappelas.ChatTypeChannel:
        // Bot-only posting — no user interaction expected
    }
})
Full group bot example

A bot that works across private chats, groups, and channels:

package main

import (
    "context"
    "fmt"

    "github.com/Arnel7/kappelas-sdk-go"
)

func main() {
    ctx := context.Background()
    bot := kappelas.NewBot("YOUR_BOT_TOKEN")

    bot.OnMessage(func(msg *kappelas.Message) {
        text := msg.GetText()
        if text == "" {
            return
        }

        isGroup   := msg.ChatType != nil && *msg.ChatType == kappelas.ChatTypeGroup
        isPrivate := msg.ChatType != nil && *msg.ChatType == kappelas.ChatTypePrivate

        // /status command — works anywhere
        if text == "/status" {
            p := kappelas.SendMessageParams{
                ChatID: msg.ChatID,
                Text:   "🟢 Bot en ligne",
            }
            if isGroup {
                p.ReplyToID = &msg.ID // quote in groups
            }
            bot.Messages.Send(ctx, p)
            return
        }

        // /invite command — admin-only, group/channel only
        if text == "/invite" && !isPrivate {
            link, err := bot.Chats.CreateInviteLink(ctx, kappelas.CreateChatInviteLinkParams{
                ChatID: msg.ChatID,
            })
            if err != nil {
                bot.Messages.Send(ctx, kappelas.SendMessageParams{
                    ChatID: msg.ChatID,
                    Text:   "❌ J'ai besoin des droits admin pour créer des liens d'invitation.",
                })
                return
            }
            p := kappelas.SendMessageParams{
                ChatID: msg.ChatID,
                Text:   fmt.Sprintf("🔗 Lien d'invitation : %s", link.URL),
            }
            if isGroup {
                p.ReplyToID = &msg.ID
            }
            bot.Messages.Send(ctx, p)
            return
        }

        // Private only — interactive keyboard
        if isPrivate {
            yes, help := "orders", "help"
            bot.Messages.Send(ctx, kappelas.SendMessageParams{
                ChatID: msg.ChatID,
                Text:   "De quoi as-tu besoin ?",
                ReplyMarkup: kappelas.InlineKeyboard{
                    InlineKeyboard: [][]kappelas.InlineKeyboardButton{{
                        {Text: "📦 Commandes", CallbackData: &yes},
                        {Text: "❓ Aide",      CallbackData: &help},
                    }},
                },
            })
        }
    })

    bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) {
        bot.Messages.Send(ctx, kappelas.SendMessageParams{
            ChatID: cb.ChatID,
            Text:   "Tu as choisi : " + cb.CallbackData,
        })
    })

    bot.Start()
    select {}
}
Chats.GetMyGroups(ctx)(*GetMyGroupsResult, error)

Returns every group and channel the bot belongs to, with the bot's role in each.

result, err := bot.Chats.GetMyGroups(ctx)
for _, g := range result.Groups {
    title := "(sans titre)"
    if g.Title != nil {
        title = *g.Title
    }
    fmt.Printf("%d (%s) %q → %s\n", g.ChatID, g.Type, title, g.BotRole)
}

// Filter to groups where the bot is admin
for _, g := range result.Groups {
    if g.BotRole == kappelas.ParticipantRoleAdmin {
        // can create invite links, manage members…
    }
}

BotGroupEntry fields:

Field Type Description
ChatID int64 Conversation ID
Type ChatType "group" or "channel" (never "private")
Title *string Group or channel name
ParticipantCount int Total members (including the bot)
BotRole ParticipantRole "member" or "admin"

Chat member management

All methods below require the bot to be a member of the conversation.
Methods that modify membership (AddMember, BanMember, PromoteMember) additionally require admin rights.

Chats.GetAdministrators(ctx, params)(*GetChatAdministratorsResult, error)
result, err := bot.Chats.GetAdministrators(ctx, kappelas.GetChatAdministratorsParams{
    ChatID: 42,
})
for _, admin := range result.Admins {
    fmt.Println(admin.UserID, admin.Role) // role is always "admin"
}
Chats.GetMember(ctx, params)(*ChatMemberInfo, error)

Returns the role of a specific member. Returns ErrCodeNotFound if the user is not in the conversation.

member, err := bot.Chats.GetMember(ctx, kappelas.GetChatMemberParams{
    ChatID: 42,
    UserID: "user-uuid",
})
fmt.Println(member.Role) // "admin" | "member"
Chats.AddMember(ctx, params)(*AddChatMemberResult, error)
bot.Chats.AddMember(ctx, kappelas.AddChatMemberParams{
    ChatID: 42,
    UserID: "user-uuid",
})
Chats.BanMember(ctx, params)(*BanChatMemberResult, error)

Removes (kicks) a user. To remove the bot itself, use LeaveChat instead.

bot.Chats.BanMember(ctx, kappelas.BanChatMemberParams{
    ChatID: 42,
    UserID: "user-uuid",
})
Chats.PromoteMember(ctx, params)(*PromoteChatMemberResult, error)
// Promote to admin
bot.Chats.PromoteMember(ctx, kappelas.PromoteChatMemberParams{
    ChatID: 42,
    UserID: "user-uuid",
    Role:   kappelas.ParticipantRoleAdmin,
})

// Demote back to member
bot.Chats.PromoteMember(ctx, kappelas.PromoteChatMemberParams{
    ChatID: 42,
    UserID: "user-uuid",
    Role:   kappelas.ParticipantRoleMember,
})
Chats.LeaveChat(ctx, params)(*LeaveChatResult, error)
bot.Chats.LeaveChat(ctx, kappelas.LeaveChatParams{ChatID: 42})

All invite link methods require admin rights.

// Permanent link, unlimited uses
link, err := bot.Chats.CreateInviteLink(ctx, kappelas.CreateChatInviteLinkParams{
    ChatID: 42,
})
fmt.Println(link.URL) // "https://kappelas.com/invite/aBcD123xyz"

// Single-use, expires in 24 h
link, err := bot.Chats.CreateInviteLink(ctx, kappelas.CreateChatInviteLinkParams{
    ChatID:    42,
    MaxUses:   1,
    ExpiresIn: "24h",
})

ExpiresIn values: "1h" · "24h" · "7d" · "30d" · "never" (default)

Shorthand for CreateInviteLink with MaxUses: 1.

link, err := bot.Chats.CreateSingleUseInviteLink(ctx, kappelas.CreateChatInviteLinkParams{
    ChatID: 42,
})
result, err := bot.Chats.GetInviteLinks(ctx, kappelas.GetChatInviteLinksParams{ChatID: 42})
for _, link := range result.InviteLinks {
    max := "∞"
    if link.MaxUses > 0 {
        max = strconv.Itoa(link.MaxUses)
    }
    fmt.Printf("%s — %d/%s uses\n", link.URL, link.UseCount, max)
}
result, err := bot.Chats.RevokeInviteLink(ctx, kappelas.RevokeChatInviteLinkParams{
    ChatID: 42,
    Code:   "aBcD123xyz", // link.Code from CreateInviteLink
})
fmt.Println(result.Revoked) // true

communities

Manage communities with a bot (same rights as a community admin). A bot administers a community only if it is admin of that community.

⚠️ Distinct scopes. Being admin of a group attached to a community does not make you admin of the community. Community.Role is the role in the community.

To make someone (a person OR a bot) a community admin, it's two steps — add as member, then promote:

ctx := context.Background()

// 1) add as member   2) promote to community admin (same flow for a user or a bot)
bot.Communities.AddMember(ctx, kappelas.AddCommunityMemberParams{
    CommunityID: 7, UserID: "<uuid or bot_user_id>", Role: kappelas.ParticipantRoleMember,
})
bot.Communities.PromoteMember(ctx, kappelas.PromoteCommunityMemberParams{
    CommunityID: 7, UserID: "<uuid>", Role: kappelas.ParticipantRoleAdmin,
})
Listing
// Communities the bot is a member of (each carries the bot's Role).
res, _ := bot.Communities.List(ctx)
for _, c := range res.Communities {
    fmt.Println(c.ID, c.Name, "→", c.Role) // "member" | "admin"
}

// Only the ones where the bot is community admin.
admins, _ := bot.Communities.ListAdmin(ctx)
fmt.Printf("Community admin in %d community(ies)\n", len(admins))

// Full detail (infos + groups + members, members enriched with Name/AvatarURL).
detail, _ := bot.Communities.Get(ctx, kappelas.GetCommunityParams{CommunityID: 7})
Other methods
// CRUD
bot.Communities.Create(ctx, kappelas.CreateCommunityParams{Name: "Devs", RequiresApproval: true})
desc := "New"
bot.Communities.Update(ctx, kappelas.UpdateCommunityParams{CommunityID: 7, Description: &desc}) // admin
bot.Communities.Delete(ctx, kappelas.GetCommunityParams{CommunityID: 7})                        // admin
r, _ := bot.Communities.Join(ctx, kappelas.GetCommunityParams{CommunityID: 7})                  // r.Pending if approval-required

// Members (admin)
bot.Communities.BanMember(ctx, kappelas.BanCommunityMemberParams{CommunityID: 7, UserID: "u"})
bot.Communities.Leave(ctx, kappelas.GetCommunityParams{CommunityID: 7})

// Invite links (admin)
inv, _ := bot.Communities.CreateInviteLink(ctx, kappelas.CreateCommunityInviteLinkParams{CommunityID: 7, MaxUses: 1, ExpiresIn: "24h"})
bot.Communities.GetInviteLinks(ctx, kappelas.GetCommunityParams{CommunityID: 7})
bot.Communities.RevokeInviteLink(ctx, kappelas.RevokeCommunityInviteLinkParams{CommunityID: 7, Code: inv.Code})
bot.Communities.PreviewInvite(ctx, kappelas.CommunityInviteCodeParams{Code: "aBcD123"})
bot.Communities.AcceptInvite(ctx, kappelas.CommunityInviteCodeParams{Code: "aBcD123"}) // bot joins via link

// Join requests — user → community (admin, when RequiresApproval)
reqs, _ := bot.Communities.GetJoinRequests(ctx, kappelas.GetCommunityParams{CommunityID: 7})
bot.Communities.ApproveJoinRequest(ctx, kappelas.CommunityRequestActionParams{CommunityID: 7, RequestID: 3})
bot.Communities.RejectJoinRequest(ctx, kappelas.CommunityRequestActionParams{CommunityID: 7, RequestID: 3})

// Group requests + linking groups (admin)
bot.Communities.GetGroupRequests(ctx, kappelas.GetCommunityParams{CommunityID: 7})
bot.Communities.ApproveGroupRequest(ctx, kappelas.CommunityRequestActionParams{CommunityID: 7, RequestID: 3})
bot.Communities.RejectGroupRequest(ctx, kappelas.CommunityRequestActionParams{CommunityID: 7, RequestID: 3})
bot.Communities.AddGroup(ctx, kappelas.AddCommunityGroupParams{CommunityID: 7, ConversationID: 42})    // admin commu + admin group
bot.Communities.RemoveGroup(ctx, kappelas.RemoveCommunityGroupParams{CommunityID: 7, ConversationID: 42})

webhooks
Webhooks.Set(ctx, params)(*WebhookSetResult, error)
bot.Webhooks.Set(ctx, kappelas.SetWebhookParams{
    URL: "https://your-server.com/kappela-webhook",
})
Webhooks.GetInfo(ctx)(*WebhookInfo, error)
info, err := bot.Webhooks.GetInfo(ctx)
// → WebhookInfo{Active: true, URL: &"https://…", CreatedAt: &1234567890}
Webhooks.Delete(ctx)(*WebhookDeleteResult, error)
bot.Webhooks.Delete(ctx)
// → WebhookDeleteResult{Active: false}

profile
Profile.Get(ctx)(*BotProfile, error) / (*UserProfile, error)
// Bot
profile, err := bot.Profile.Get(ctx)
// → BotProfile{UserID, Username, IsBot: true, About, Description, AvatarURL}

// User
profile, err := me.Profile.Get(ctx)
// → UserProfile{ID, Username, Nom, IsBot: false, IsPremium, AvatarURL, …}

stories (User only)

Create and manage stories (ephemeral, 24 h) via me.Stories. Available on User only — their audience is based on your private-conversation contacts.

For image/video stories, set Media (a *FileInput) — the SDK uploads it automatically and uses the resulting media id. For text/poll stories, no upload is needed. You can also pass a pre-uploaded MediaID.

ctx := context.Background()

// Image story — SDK uploads the file, then creates the story
img := kappelas.FileInput{Data: imgBytes, Filename: "photo.jpg", ContentType: "image/jpeg"}
story, err := me.Stories.Create(ctx, kappelas.CreateStoryParams{
    Type:    kappelas.StoryImage,
    Media:   &img,
    Caption: "Sunset 🌇",
    Audience: kappelas.StoryAudienceAll, // "all" (default) | "selected" | "excluded"
})

// Text story — no media
me.Stories.Create(ctx, kappelas.CreateStoryParams{Type: kappelas.StoryText, Caption: "Good morning ☀️"})

// Restricted audience
me.Stories.Create(ctx, kappelas.CreateStoryParams{
    Type: kappelas.StoryText, Caption: "Privé",
    Audience: kappelas.StoryAudienceSelected, AudienceUserIDs: []string{"<uuid>"},
})

// Clickable CTA link over the story (text or image)
me.Stories.Create(ctx, kappelas.CreateStoryParams{Type: kappelas.StoryText, Caption: "New drop", Link: "https://shop.example.com", LinkLabel: "Shop now"})

Link (CTA)Link (+ optional LinkLabel) adds a clickable link shown over the story in the Kappela apps. The SDK carries it inside the caption as a JSON envelope ({text, link, linkLabel}); without a link the caption stays plain text.

Method Returns Description
Stories.Create(ctx, params) (*Story, error) Create a story. Media (uploaded automatically) or MediaID for image/video; Caption, Audience, AudienceUserIDs optional.
Stories.UploadMedia(ctx, file) (*StoryMediaUpload, error) Upload story media manually and get a media id (usually unnecessary).
Stories.List(ctx) ([]Story, error) Feed of your contacts' active stories.
Stories.ListMine(ctx) ([]Story, error) Your own stories.
Stories.Get(ctx, storyID) (*Story, error) A single story (audience-checked server-side).
Stories.Delete(ctx, storyID) (*StoryActionResult, error) Delete one of your stories.
Stories.View(ctx, storyID) (*StoryActionResult, error) Mark a story as viewed.
Stories.GetViewers(ctx, storyID) ([]StoryView, error) Who viewed your story (owner only).
Stories.GetPreferences(ctx) (*StoryPreferences, error) Your default audience preference.
Stories.SetPreferences(ctx, prefs) (*StoryActionResult, error) Set your default audience preference.

Pause — while automations are paused, story reads still work but creating/deleting/viewing stories is rejected with AUTOMATIONS_PAUSED.


Keyboards

Three keyboard types can be passed as ReplyMarkup on any Send* call.

Comparison
Inline Reply Scroll
Position Attached to the message Below the input bar Horizontal chips above input
Stays after tap ✅ Yes ❌ Dismissed ✅ Yes
Separate CallbackData ✅ Always ✅ Yes (long form) ✅ Yes (long form)
URL button ✅ Yes ❌ No ❌ No
Layout 2-D grid [][] 2-D grid [][] 1-D list []

Inline keyboard — attached to the message

Buttons stay visible after being tapped. Each button fires a CallbackQuery (CallbackData) or opens a URL (URL).

yes, no := "yes", "no"
url := "https://kappelas.com"
inline := kappelas.InlineKeyboard{
    InlineKeyboard: [][]kappelas.InlineKeyboardButton{
        {
            {Text: "✅ Confirmer", CallbackData: &yes},
            {Text: "❌ Annuler",   CallbackData: &no},
        },
        {
            {Text: "🌐 Site web", URL: &url},
        },
    },
}
Reply keyboard — shown below the input bar

Dismissed after the user taps a button. Buttons trigger a CallbackQuery.

Short form — label and callback value are the same:

reply := kappelas.ReplyKeyboard{
    Keyboard: [][]kappelas.ReplyKeyboardButton{
        {{Text: "📦 Mes commandes"}, {Text: "❓ Aide"}},
        {{Text: "🔙 Retour"}},
    },
}

Long form — separate label and callback value:

reply := kappelas.ReplyKeyboard{
    Keyboard: [][]kappelas.ReplyKeyboardButton{
        {
            {Text: "✅ Confirmer", CallbackData: "confirm_yes"},
            {Text: "❌ Annuler",   CallbackData: "confirm_no"},
        },
        {
            {Text: "↩ Retour", CallbackData: "cancel"},
        },
    },
}

You can mix short and long buttons in the same grid:

reply := kappelas.ReplyKeyboard{
    Keyboard: [][]kappelas.ReplyKeyboardButton{
        {{Text: "✅ Confirmer", CallbackData: "confirm"}, {Text: "❓ Aide"}},
    },
}
Scroll keyboard — horizontal scrollable chips

A single row of chips, always visible above the input bar.

// Short form
scroll := kappelas.ScrollKeyboard{
    ScrollKeyboard: []kappelas.ScrollKeyboardButton{
        {Text: "Petit"}, {Text: "Moyen"}, {Text: "Grand"},
    },
}

// Long form — emoji label, clean callback value
scroll := kappelas.ScrollKeyboard{
    ScrollKeyboard: []kappelas.ScrollKeyboardButton{
        {Text: "📦 Commandes", CallbackData: "menu_orders"},
        {Text: "❓ Aide",      CallbackData: "menu_help"},
        {Text: "⚙️ Réglages",  CallbackData: "menu_settings"},
    },
}
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID:      42,
    Text:        "Choisis une option :",
    ReplyMarkup: inline, // or reply, or scroll
})
Full example — all three in one bot
package main

import (
    "context"
    "github.com/Arnel7/kappelas-sdk-go"
)

func main() {
    ctx := context.Background()
    bot := kappelas.NewBot("YOUR_BOT_TOKEN")

    bot.OnMessage(func(msg *kappelas.Message) {
        if msg.GetText() != "/start" {
            return
        }
        // Persistent navigation chips
        orders, help := "menu_orders", "menu_help"
        bot.Messages.Send(ctx, kappelas.SendMessageParams{
            ChatID: msg.ChatID,
            Text:   "Bienvenue ! De quoi as-tu besoin ?",
            ReplyMarkup: kappelas.ScrollKeyboard{
                ScrollKeyboard: []kappelas.ScrollKeyboardButton{
                    {Text: "📦 Commandes", CallbackData: orders},
                    {Text: "❓ Aide",      CallbackData: help},
                },
            },
        })
    })

    bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) {
        switch cb.CallbackData {
        case "menu_orders":
            // Inline confirm/cancel buttons
            confirm, cancel := "order_confirm", "order_cancel"
            bot.Messages.Send(ctx, kappelas.SendMessageParams{
                ChatID: cb.ChatID,
                Text:   "Confirmer ta dernière commande ?",
                ReplyMarkup: kappelas.InlineKeyboard{
                    InlineKeyboard: [][]kappelas.InlineKeyboardButton{{
                        {Text: "✅ Confirmer", CallbackData: &confirm},
                        {Text: "❌ Annuler",   CallbackData: &cancel},
                    }},
                },
            })

        case "menu_help":
            // Reply keyboard for topic selection
            bot.Messages.Send(ctx, kappelas.SendMessageParams{
                ChatID: cb.ChatID,
                Text:   "Quel sujet ?",
                ReplyMarkup: kappelas.ReplyKeyboard{
                    Keyboard: [][]kappelas.ReplyKeyboardButton{
                        {
                            {Text: "💳 Facturation", CallbackData: "help_billing"},
                            {Text: "🚚 Livraison",   CallbackData: "help_delivery"},
                        },
                        {{Text: "↩ Retour au menu", CallbackData: "menu_back"}},
                    },
                },
            })
        }
    })

    bot.Start()
    select {}
}

Text formatting

Kappela renders a WhatsApp/Telegram-style subset of Markdown in every message bubble — bot messages, group messages, and private chat messages. All formatting is applied client-side by the Android app; you only need to send the correct markup in the Text or Caption field.

Inline styles
Syntax Result
**bold** or *bold* Bold
__italic__ or _italic_ Italic
~strikethrough~ Strikethrough
`inline code` Monospace with a tinted background
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   "Commande *confirmée* ✅\nTotal : **24 990 FCFA**\nRef : `ORD-2024-001`",
})
Block code

Triple backticks render as a block code card — only when placed on their own line.

Position Rendu
`code` en cours de phrase Monospace inline avec fond teinté
```code``` sur sa propre ligne Carte pleine largeur + bouton copier
// Inline — reste dans le flux de texte
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   "Ta ref est `ORD-2024-001` — garde-la précieusement.",
})

// Block — doit être sur sa propre ligne pour s'afficher en carte
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   "Ta clé API :\n```\nsk_live_abc123xyz\n```",
})

The code card collapses to a single line with an ellipsis if the content is too long. Tapping anywhere on the card copies the content to the clipboard.

Blockquote / citation

Prefix a line with > to render it as a citation banner (a bar on the left, italic, slightly faded):

bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   "> Question originale ici\n\nVoici ta réponse.",
})

You can combine blockquotes with ReplyToID — use ReplyToID when you want to quote a specific existing message (the app shows a reply banner); use > when you want to render a quote inline within the text itself.

Mentions and commands

@username and /command are auto-detected and rendered as tappable blue links:

// Mention a user by their username
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   "Merci @arnell, ta commande est prête !",
})

// Send a command hint
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   "Tape /help pour voir toutes les commandes disponibles.",
})

Protection rule: @ and / inside URLs are never formatted. @buy_something_bot is treated as a mention, not as buy + _something_bot (italic).

The renderer automatically makes the following clickable without any markup:

Pattern Behaviour
https://… or http://… Opens in the in-app browser
domain.com, domain.io, domain.fr Prefixed with https:// and opened
email@example.com Opens the mail app
+229 01 62 86 15 71, (229) 0162-861571 Opens the dialler
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   "Visitez kappelas.com ou contactez-nous à support@kappelas.com",
})

Supported domain extensions — only the following TLDs are auto-linked: com org net fr io dev co me app tech info biz xyz eu uk de ru tv cc gg ai be ch ca

Country codes like .bj, .sn, .ci are not auto-detected — use a full https:// URL instead: https://kappelas.bj.

Phone format — any sequence of 8+ digits is detected, with spaces, dashes, and parentheses allowed: +229 01 62 86 15 71, +22901628​61571, (229) 0162-861571 all open the dialler.

Combining formats

All inline styles can be combined freely:

lines := []string{
    "🛒 *Récapitulatif de commande*",
    "",
    "> Widget A × 2",
    "",
    "Total : **49 980 FCFA**",
    "Statut : `CONFIRMÉ`",
    "",
    "Des questions ? Contactez support@kappelas.com ou tapez /help",
}
bot.Messages.Send(ctx, kappelas.SendMessageParams{
    ChatID: 42,
    Text:   strings.Join(lines, "\n"),
})

Renders as:

🛒 Récapitulatif de commande   ← bold

┃ Widget A × 2                 ← blockquote (italic, faded)

Total : 49 980 FCFA            ← bold amount
Statut : CONFIRMÉ              ← monospace badge

Des questions ? Contactez support@kappelas.com ou tapez /help
                               ← email and /help are tappable

Error handling

All API errors return a *KappelaError with structured fields:

import "errors"

_, err := bot.Messages.Send(ctx, kappelas.SendMessageParams{ChatID: 999, Text: "Hi"})
if err != nil {
    var e *kappelas.KappelaError
    if errors.As(err, &e) {
        e.Code      // kappelas.ErrCodeForbidden
        e.Status    // 403
        e.Message   // server error message
        e.RequestID // mention this when contacting support
        fmt.Println(e) // formatted block with hints
    }
}
Error codes
Code HTTP Meaning
ErrCodeUnauthorized 401 Token or API key invalid / expired
ErrCodeForbidden 403 Missing permission or role (bot not in chat, not admin…)
ErrCodeNotFound 404 Resource does not exist
ErrCodeMissingField 400 Required parameter missing
ErrCodeInvalidField 400 Parameter has wrong type or format
ErrCodeConflict 409 Resource already exists
ErrCodeMethodNotAllowed 405 Wrong HTTP method
ErrCodeInvalidPath 404 API path does not exist
ErrCodeInternalError 500 Unexpected server error
ErrCodeServiceUnavailable 503 Service temporarily down
ErrCodeUpstreamError 502 Upstream service error

ErrCodeForbidden (not ErrCodeNotFound) is returned when the bot tries to send a message to a chat it has never joined.


File input

Media methods accept a FileInput struct:

type FileInput struct {
    Data        []byte
    Filename    string
    ContentType string
}
// From disk
data, _ := os.ReadFile("photo.jpg")
bot.Messages.SendPhoto(ctx, kappelas.SendMediaParams{
    ChatID: 42,
    File:   kappelas.FileInput{Data: data, Filename: "photo.jpg", ContentType: "image/jpeg"},
})

// From memory
bot.Messages.SendDocument(ctx, kappelas.SendMediaParams{
    ChatID: 42,
    File:   kappelas.FileInput{Data: pdfBytes, Filename: "rapport.pdf", ContentType: "application/pdf"},
})

The Go SDK accepts raw bytes only. To send a file from an HTTPS URL, fetch it first with http.Get and pass the response body as Data.


License

MIT © Arnel LAWSON

Documentation

Index

Constants

View Source
const (
	StoryImage = "image"
	StoryVideo = "video"
	StoryText  = "text"
	StoryPoll  = "poll"
)

Story media types.

View Source
const (
	StoryAudienceAll      = "all"
	StoryAudienceSelected = "selected"
	StoryAudienceExcluded = "excluded"
)

Story audiences.

Variables

This section is empty.

Functions

This section is empty.

Types

type AcceptCommunityInviteResult added in v0.3.0

type AcceptCommunityInviteResult struct {
	CommunityID int64 `json:"community_id"`
}

AcceptCommunityInviteResult is returned by AcceptInvite.

type ActionButton added in v0.7.0

type ActionButton struct {
	Label string           `json:"label"`
	Type  ActionButtonType `json:"type"` // copy_text | external_link | internal_link | join
	Value string           `json:"value"`
}

ActionButton is a single button rendered at the foot of the message bubble (WhatsApp-style), distinct from inline keyboards. It performs a client-side action (copy / open / join) instead of firing a callback_query.

Set it via SendMessageParams.ActionButton. When both ActionButton and ReplyMarkup are set, ActionButton takes precedence. Label: 1–100 chars, Value: 1–2048 chars.

type ActionButtonType added in v0.7.0

type ActionButtonType = string

SendMessageParams holds the parameters for sending a text message. ActionButtonType is what tapping an ActionButton does. The meaning of Value follows it:

  • "copy_text" — copies Value to the clipboard (e.g. a one-time code / OTP).
  • "external_link" — opens Value (an external URL) in the in-app browser.
  • "internal_link" — opens Value as an in-app deep link.
  • "join" — Value is an invite link (group/channel/community); tap joins directly.

type AddChatMemberParams added in v0.2.0

type AddChatMemberParams struct {
	ChatID int64  `json:"chat_id"`
	UserID string `json:"user_id"`
}

AddChatMemberParams holds the parameters for adding a user to a group or channel. The bot must be admin of the conversation.

type AddChatMemberResult added in v0.2.0

type AddChatMemberResult struct {
	Description string `json:"description"`
}

AddChatMemberResult is returned after successfully adding a user.

type AddCommunityGroupParams added in v0.3.0

type AddCommunityGroupParams struct {
	CommunityID    int64 `json:"community_id"`
	ConversationID int64 `json:"conversation_id"`
}

type AddCommunityMemberParams added in v0.3.0

type AddCommunityMemberParams struct {
	CommunityID int64           `json:"community_id"`
	UserID      string          `json:"user_id"`
	Role        ParticipantRole `json:"role,omitempty"` // default "member"
}

type AutomationStatus added in v0.4.0

type AutomationStatus struct {
	AutomationsPaused bool `json:"automations_paused"`
}

AutomationStatus reports whether an account's personal automations are paused.

type BanChatMemberParams added in v0.2.0

type BanChatMemberParams struct {
	ChatID int64  `json:"chat_id"`
	UserID string `json:"user_id"`
}

BanChatMemberParams holds the parameters for removing (kicking) a user. The bot must be admin. To remove itself, use LeaveChatParams instead.

type BanChatMemberResult added in v0.2.0

type BanChatMemberResult struct {
	Description string `json:"description"`
}

BanChatMemberResult is returned after successfully removing a user.

type BanCommunityMemberParams added in v0.3.0

type BanCommunityMemberParams struct {
	CommunityID int64  `json:"community_id"`
	UserID      string `json:"user_id"`
}

type Bot

type Bot struct {
	// Messages provides methods to send and manage messages.
	Messages *MessagesResource
	// Chats provides methods to list and iterate over chats.
	Chats *ChatsResource
	// Webhooks provides methods to manage webhooks.
	Webhooks *WebhooksResource
	// Profile provides access to the bot's own profile.
	Profile *BotProfileResource
	// Communities provides methods to manage communities (members, roles, invites, requests).
	Communities *CommunitiesResource
	// contains filtered or unexported fields
}

Bot is the Kappela bot client. Authenticate with a token from BotMother.

Example:

bot := kappelas.NewBot("YOUR_BOT_TOKEN")

bot.OnMessage(func(msg *kappelas.Message) {
    bot.Messages.Send(ctx, kappelas.SendMessageParams{
        ChatID: msg.ChatID,
        Text:   "Echo: " + *msg.Text,
    })
})

bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) {
    bot.Messages.Send(ctx, kappelas.SendMessageParams{
        ChatID: cb.ChatID,
        Text:   "Button clicked: " + cb.CallbackData,
    })
})

bot.Start()
select {} // keep alive

func NewBot

func NewBot(token string, opts ...BotOption) *Bot

NewBot creates a Bot authenticated with the given token.

func (*Bot) Connected

func (b *Bot) Connected() bool

Connected reports whether the WebSocket is currently open.

func (*Bot) GetStatus added in v0.4.0

func (b *Bot) GetStatus(ctx context.Context) (*BotPauseStatus, error)

GetStatus reports whether this bot is currently paused.

func (*Bot) HandleWebhook

func (b *Bot) HandleWebhook(body []byte)

HandleWebhook processes a webhook payload sent by Kappela to your server. Call this inside your HTTP handler and respond 200 immediately. The same OnMessage and OnCallbackQuery handlers fire for both WS and webhook events.

Example (net/http):

http.HandleFunc("/kappela-webhook", func(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    bot.HandleWebhook(body)
    w.WriteHeader(http.StatusOK)
})

func (*Bot) OnCallbackQuery

func (b *Bot) OnCallbackQuery(h func(*CallbackQuery))

OnCallbackQuery registers a handler called when a user clicks an inline button.

func (*Bot) OnConnected

func (b *Bot) OnConnected(h func())

OnConnected registers a handler called when the WebSocket connects (or reconnects).

func (*Bot) OnDisconnected

func (b *Bot) OnDisconnected(h func(code int, reason string))

OnDisconnected registers a handler called when the WebSocket disconnects.

func (*Bot) OnError

func (b *Bot) OnError(h func(error))

OnError registers a handler called on WebSocket or connection errors.

func (*Bot) OnMessage

func (b *Bot) OnMessage(h func(*Message))

OnMessage registers a handler called for every incoming message.

func (*Bot) Pause added in v0.4.0

func (b *Bot) Pause(ctx context.Context) (*BotPauseStatus, error)

Pause pauses this bot. While paused, the bot stops receiving incoming messages (no WS push, no webhook) and any send call is rejected with BOT_PAUSED, until Resume is called. Lets an owner stop an AI bot on demand.

func (*Bot) PauseInChat added in v0.5.0

func (b *Bot) PauseInChat(ctx context.Context, chatID int64) (*ChatAutomationResult, error)

PauseInChat pauses this bot in ONE conversation only (the bot must be a participant). The bot stops receiving messages from that conversation while it keeps working in all its other chats. Unlike Pause, this is scoped to a single conversation.

func (*Bot) Reply added in v0.2.0

func (b *Bot) Reply(ctx context.Context, event any, text string, opts ...SendMessageParams) (*SendResult, error)

Reply sends a text reply to a Message or CallbackQuery event.

  • When called with a *Message — sets ChatID and ReplyToID automatically (shows a quote banner).
  • When called with a *CallbackQuery — sets ChatID only (callback queries have no message ID).

Pass an optional SendMessageParams to attach a keyboard or set other options. ChatID, ReplyToID, and Text in opts are overwritten automatically.

Example:

bot.OnMessage(func(msg *kappelas.Message) {
    ctx := context.Background()
    bot.Reply(ctx, msg, "Got it! 👋")

    // With an inline keyboard
    bot.Reply(ctx, msg, "Pick one:", kappelas.SendMessageParams{
        ReplyMarkup: kappelas.InlineKeyboard{
            InlineKeyboard: [][]kappelas.InlineKeyboardButton{{
                {Text: "✅ Oui", CallbackData: ptr("yes")},
                {Text: "❌ Non", CallbackData: ptr("no")},
            }},
        },
    })
})

bot.OnCallbackQuery(func(cb *kappelas.CallbackQuery) {
    ctx := context.Background()
    bot.Reply(ctx, cb, "Tu as cliqué : "+cb.CallbackData)
})

func (*Bot) Resume added in v0.4.0

func (b *Bot) Resume(ctx context.Context) (*BotPauseStatus, error)

Resume resumes this bot after Pause.

func (*Bot) ResumeInChat added in v0.5.0

func (b *Bot) ResumeInChat(ctx context.Context, chatID int64) (*ChatAutomationResult, error)

ResumeInChat resumes this bot in a conversation after PauseInChat.

func (*Bot) Start

func (b *Bot) Start()

Start connects via WebSocket and begins receiving events in the background. Your OnMessage and OnCallbackQuery handlers will be called for each event.

func (*Bot) Stop

func (b *Bot) Stop()

Stop closes the WebSocket connection.

type BotGroupEntry added in v0.2.0

type BotGroupEntry struct {
	// ChatID is the conversation ID — use this as ChatID in all API calls.
	ChatID int64 `json:"chat_id"`
	// Type is "group" or "channel". Never "private".
	Type ChatType `json:"type"`
	// Title is the group or channel name.
	Title *string `json:"title"`
	// ParticipantCount is the total number of members (including the bot).
	ParticipantCount int `json:"participant_count"`
	// BotRole is the bot's role in this conversation.
	BotRole ParticipantRole `json:"bot_role"`
}

BotGroupEntry is a group or channel the bot belongs to, enriched with its role.

type BotOption

type BotOption func(*botConfig)

BotOption configures a Bot.

func WithBaseURL

func WithBaseURL(u string) BotOption

WithBaseURL overrides the API base URL (default: https://api.kappelas.com).

func WithMaxRetries

func WithMaxRetries(n int) BotOption

WithMaxRetries sets the maximum number of HTTP retry attempts (default: 2).

func WithTimeout

func WithTimeout(d time.Duration) BotOption

WithTimeout sets the HTTP request timeout (default: 30s).

func WithWSMaxRetries

func WithWSMaxRetries(n int) BotOption

WithWSMaxRetries sets the maximum WebSocket reconnect attempts (default: 12).

type BotPauseStatus added in v0.4.0

type BotPauseStatus struct {
	Paused bool `json:"paused"`
}

BotPauseStatus reports whether a bot is paused.

type BotProfile

type BotProfile struct {
	UserID      string  `json:"user_id"`
	Username    string  `json:"username"`
	IsBot       bool    `json:"is_bot"`
	About       string  `json:"about"`
	Description string  `json:"description"`
	AvatarURL   *string `json:"avatar_url"`
}

BotProfile is the profile returned for a bot account.

type BotProfileResource

type BotProfileResource struct {
	// contains filtered or unexported fields
}

BotProfileResource provides access to the bot's own profile.

func (*BotProfileResource) Get

Get returns the bot's own profile.

type CallbackQuery

type CallbackQuery struct {
	ChatID   int64  `json:"chat_id"`
	SenderID string `json:"sender_id"`
	// SenderName is the display name of the user who clicked (e.g. "Arnel LAWSON").
	SenderName     *string `json:"sender_name"`
	SenderUsername *string `json:"sender_username"`
	CallbackData   string  `json:"callback_data"`
	SentAt         int64   `json:"sent_at"`
}

CallbackQuery is fired when a user clicks an inline button.

func (*CallbackQuery) GetSenderName added in v0.2.2

func (cb *CallbackQuery) GetSenderName() string

GetSenderName returns the sender display name, or "" if absent. Use this instead of dereferencing SenderName directly to avoid nil-pointer panics.

fmt.Sprintf("Hello %s!", cb.GetSenderName())  // safe

type CarouselCard

type CarouselCard struct {
	ID         string  `json:"id"`
	Title      string  `json:"title"`
	Subtitle   *string `json:"subtitle,omitempty"`
	ImageURL   *string `json:"image_url,omitempty"`
	ButtonText *string `json:"button_text,omitempty"`
}

CarouselCard is a single card inside a carousel message.

type Chat

type Chat struct {
	ChatID             int64         `json:"chat_id"`
	ID                 int64         `json:"id"`
	Type               ChatType      `json:"type"`
	Title              *string       `json:"title"`
	Participants       []Participant `json:"participants"`
	LastMessageAt      *string       `json:"last_message_at"`
	CreatedAt          string        `json:"created_at"`
	CreatedBy          string        `json:"created_by"`
	IsPinned           bool          `json:"is_pinned"`
	IsPremium          bool          `json:"is_premium"`
	IsPublic           bool          `json:"is_public"`
	OnlyAdminsCanWrite bool          `json:"only_admins_can_write"`
	Labels             []string      `json:"labels"`
	Description        *string       `json:"description"`
	AvatarURL          *string       `json:"avatar_url"`
}

Chat represents a Kappela conversation.

type ChatAutomationResult added in v0.5.0

type ChatAutomationResult struct {
	Done bool `json:"done"`
}

ChatAutomationResult is returned by the per-conversation pause methods.

type ChatInviteLink struct {
	// Code is the short identifier used in the URL (e.g. "aBcD123xyz").
	Code string `json:"code"`
	// URL is the full invite URL (e.g. "https://kappelas.com/invite/aBcD123xyz").
	URL string `json:"url"`
	// MaxUses is the maximum number of allowed uses (0 = unlimited).
	MaxUses int `json:"max_uses"`
	// UseCount is the current number of uses.
	UseCount int `json:"use_count"`
	// ExpiresAt is the expiry as Unix timestamp (seconds), or nil if permanent.
	ExpiresAt *int64 `json:"expires_at"`
	// CreatedAt is the creation time as Unix timestamp (seconds).
	CreatedAt int64 `json:"created_at"`
}

ChatInviteLink describes an active invite link for a group or channel.

type ChatMemberInfo added in v0.2.0

type ChatMemberInfo struct {
	UserID string          `json:"user_id"`
	Role   ParticipantRole `json:"role"`
}

ChatMemberInfo is a minimal member record returned by GetMember and GetAdministrators.

type ChatType

type ChatType string

ChatType is the type of a conversation.

const (
	ChatTypePrivate ChatType = "private"
	ChatTypeGroup   ChatType = "group"
	ChatTypeChannel ChatType = "channel"
)

type ChatsResource

type ChatsResource struct {
	// contains filtered or unexported fields
}

ChatsResource provides methods to access and manage chats.

func (*ChatsResource) AddMember added in v0.2.0

AddMember adds a user to a group or channel. The bot must be admin of the conversation.

Example:

result, err := bot.Chats.AddMember(ctx, kappelas.AddChatMemberParams{
    ChatID: 42, UserID: "user-uuid",
})

func (*ChatsResource) BanMember added in v0.2.0

BanMember removes (kicks) a user from a group or channel. The bot must be admin. To remove itself, use LeaveChat instead.

Example:

result, err := bot.Chats.BanMember(ctx, kappelas.BanChatMemberParams{
    ChatID: 42, UserID: "user-uuid",
})
func (r *ChatsResource) CreateInviteLink(ctx context.Context, params CreateChatInviteLinkParams) (*ChatInviteLink, error)

CreateInviteLink creates an invite link for a group or channel. The bot must be admin of the conversation.

Example:

// Permanent link, unlimited uses
link, err := bot.Chats.CreateInviteLink(ctx, kappelas.CreateChatInviteLinkParams{
    ChatID: 42,
})
fmt.Println(link.URL) // "https://kappelas.com/invite/aBcD123xyz"

// Single-use, expires in 24 h
link, err := bot.Chats.CreateInviteLink(ctx, kappelas.CreateChatInviteLinkParams{
    ChatID: 42, MaxUses: 1, ExpiresIn: "24h",
})
func (r *ChatsResource) CreateSingleUseInviteLink(ctx context.Context, params CreateChatInviteLinkParams) (*ChatInviteLink, error)

CreateSingleUseInviteLink is a shorthand to create a single-use invite link. Equivalent to CreateInviteLink with MaxUses: 1. The bot must be admin.

Example:

link, err := bot.Chats.CreateSingleUseInviteLink(ctx, kappelas.CreateChatInviteLinkParams{
    ChatID: 42,
})

func (*ChatsResource) GetAdministrators added in v0.2.0

GetAdministrators returns all admins of a group or channel. The bot must be a member of the conversation.

Example:

result, err := bot.Chats.GetAdministrators(ctx, kappelas.GetChatAdministratorsParams{ChatID: 42})
for _, admin := range result.Admins {
    fmt.Println(admin.UserID, admin.Role)
}

GetInviteLinks returns all active invite links for a group or channel. The bot must be admin.

Example:

result, err := bot.Chats.GetInviteLinks(ctx, kappelas.GetChatInviteLinksParams{ChatID: 42})
for _, link := range result.InviteLinks {
    fmt.Printf("%s — %d/%d uses\n", link.URL, link.UseCount, link.MaxUses)
}

func (*ChatsResource) GetMember added in v0.2.0

func (r *ChatsResource) GetMember(ctx context.Context, params GetChatMemberParams) (*ChatMemberInfo, error)

GetMember returns info for a specific member (UserID + Role). The bot must be a member of the conversation. Returns ErrCodeNotFound if the user is not in the conversation.

Example:

member, err := bot.Chats.GetMember(ctx, kappelas.GetChatMemberParams{
    ChatID: 42, UserID: "user-uuid",
})
fmt.Println(member.Role) // "admin" | "member"

func (*ChatsResource) GetMyGroups added in v0.2.0

func (r *ChatsResource) GetMyGroups(ctx context.Context) (*GetMyGroupsResult, error)

GetMyGroups returns every group and channel the bot is a member of, together with the bot's own role in each.

Useful to discover which groups the bot can manage (e.g. create invite links).

Example:

result, err := bot.Chats.GetMyGroups(ctx)
for _, g := range result.Groups {
    fmt.Printf("%d (%s) %q → %s\n", g.ChatID, g.Type, g.Title, g.BotRole)
    if g.BotRole == kappelas.ParticipantRoleAdmin {
        // bot can create invite links, manage members…
    }
}

func (*ChatsResource) Iterate

func (r *ChatsResource) Iterate(ctx context.Context, pageSize int, fn func(*Chat) bool) error

Iterate calls fn for every chat, handling pagination automatically. Return false from fn to stop iteration early.

Example:

err := bot.Chats.Iterate(ctx, 50, func(chat *kappelas.Chat) bool {
    fmt.Println(chat.ChatID, chat.Type)
    return true // continue
})

func (*ChatsResource) LeaveChat added in v0.2.0

func (r *ChatsResource) LeaveChat(ctx context.Context, params LeaveChatParams) (*LeaveChatResult, error)

LeaveChat makes the bot leave a group or channel.

Example:

result, err := bot.Chats.LeaveChat(ctx, kappelas.LeaveChatParams{ChatID: 42})

func (*ChatsResource) List

func (r *ChatsResource) List(ctx context.Context, params GetChatsParams) (ChatsResult, error)

List returns a paginated list of chats accessible to this bot or user.

func (*ChatsResource) PromoteMember added in v0.2.0

PromoteMember promotes or demotes a member. The bot must be admin.

  • Role: ParticipantRoleAdmin — grants admin rights
  • Role: ParticipantRoleMember — revokes admin rights

Example:

// Promote to admin
bot.Chats.PromoteMember(ctx, kappelas.PromoteChatMemberParams{
    ChatID: 42,
    UserID: "user-uuid",
    Role:   kappelas.ParticipantRoleAdmin,
})

RevokeInviteLink revokes an active invite link so it can no longer be used. The bot must be admin.

Example:

result, err := bot.Chats.RevokeInviteLink(ctx, kappelas.RevokeChatInviteLinkParams{
    ChatID: 42, Code: "aBcD123xyz",
})

type ChatsResult

type ChatsResult struct {
	Chats   []Chat `json:"chats"`
	HasMore bool   `json:"has_more"`
}

ChatsResult is the paginated response from the list chats endpoint.

type CommunitiesResource added in v0.3.0

type CommunitiesResource struct {
	// contains filtered or unexported fields
}

CommunitiesResource provides methods to manage communities (members, roles, invites, join/group requests). A bot can administer a community only if it is admin of that community.

To make someone (a person OR a bot) a community admin, the flow is two steps — add as member first, then promote:

bot.Communities.AddMember(ctx, kappelas.AddCommunityMemberParams{CommunityID: 7, UserID: "uuid", Role: kappelas.ParticipantRoleMember})
bot.Communities.PromoteMember(ctx, kappelas.PromoteCommunityMemberParams{CommunityID: 7, UserID: "uuid", Role: kappelas.ParticipantRoleAdmin})

func (*CommunitiesResource) AcceptInvite added in v0.3.0

AcceptInvite makes the bot join a community via an invite code.

func (*CommunitiesResource) AddGroup added in v0.3.0

AddGroup links a group to the community (the bot must be community admin AND group admin).

func (*CommunitiesResource) AddMember added in v0.3.0

AddMember adds a member (person or bot). The bot must be community admin.

func (*CommunitiesResource) ApproveGroupRequest added in v0.3.0

ApproveGroupRequest approves a group request (links the group). Admin.

func (*CommunitiesResource) ApproveJoinRequest added in v0.3.0

ApproveJoinRequest approves a join request. Admin.

func (*CommunitiesResource) BanMember added in v0.3.0

BanMember removes a member. The bot must be community admin (or removes itself).

func (*CommunitiesResource) Create added in v0.3.0

Create creates a community (the bot becomes admin).

CreateInviteLink creates an invite link. The bot must be community admin.

func (*CommunitiesResource) Delete added in v0.3.0

Delete deletes a community (admin).

func (*CommunitiesResource) Get added in v0.3.0

Get returns the full detail of a community (infos + groups + members).

func (*CommunitiesResource) GetGroupRequests added in v0.3.0

func (r *CommunitiesResource) GetGroupRequests(ctx context.Context, params GetCommunityParams) ([]CommunityGroupRequest, error)

GetGroupRequests returns pending requests from groups to join the community. Admin.

GetInviteLinks returns the active invite links. The bot must be community admin.

func (*CommunitiesResource) GetJoinRequests added in v0.3.0

func (r *CommunitiesResource) GetJoinRequests(ctx context.Context, params GetCommunityParams) ([]CommunityJoinRequest, error)

GetJoinRequests returns pending join requests (approval-required mode). Admin.

func (*CommunitiesResource) Join added in v0.3.0

Join joins a community. Public → member immediately; approval-required → the result has Pending = true (request queued).

func (*CommunitiesResource) Leave added in v0.3.0

Leave makes the bot leave the community.

func (*CommunitiesResource) List added in v0.3.0

List returns the communities the bot is a member of (each with the bot's Role).

func (*CommunitiesResource) ListAdmin added in v0.3.0

func (r *CommunitiesResource) ListAdmin(ctx context.Context) ([]Community, error)

ListAdmin returns only the communities where the bot is community admin.

Note: this is the role IN THE COMMUNITY — being admin of a group attached to a community does NOT make the bot admin of the community.

func (*CommunitiesResource) PreviewInvite added in v0.3.0

PreviewInvite returns the public preview of a community from an invite code.

func (*CommunitiesResource) PromoteMember added in v0.3.0

PromoteMember promotes ("admin") or demotes ("member") a member. The bot must be community admin. The member must already exist (add it first).

func (*CommunitiesResource) RejectGroupRequest added in v0.3.0

RejectGroupRequest rejects a group request. Admin.

func (*CommunitiesResource) RejectJoinRequest added in v0.3.0

RejectJoinRequest rejects a join request. Admin.

func (*CommunitiesResource) RemoveGroup added in v0.3.0

RemoveGroup unlinks a group from the community (community admin OR group admin).

RevokeInviteLink revokes an invite link. The bot must be community admin.

func (*CommunitiesResource) Update added in v0.3.0

Update modifies a community (admin). Only non-nil fields are changed.

type Community added in v0.3.0

type Community struct {
	ID                    int64   `json:"id"`
	Name                  string  `json:"name"`
	Description           *string `json:"description"`
	AvatarURL             *string `json:"avatar_url"`
	CreatedBy             string  `json:"created_by"`
	AnnouncementChannelID *int64  `json:"announcement_channel_id"`
	RequiresApproval      bool    `json:"requires_approval"`
	CreatedAt             string  `json:"created_at"` // ISO 8601
	// Role of the bot/user IN THE COMMUNITY ("member"|"admin"). Set by List() only.
	// Note: being admin of a GROUP attached to a community does NOT make you admin
	// of the community (distinct scopes).
	Role ParticipantRole `json:"role,omitempty"`
}

Community is a community the bot/user belongs to.

type CommunityActionResult added in v0.3.0

type CommunityActionResult struct {
	Done    bool `json:"done"`
	Pending bool `json:"pending"`
}

CommunityActionResult is returned by actions without a body. Done is true on success; Pending is true when a join is queued for approval.

type CommunityDetail added in v0.3.0

type CommunityDetail struct {
	Community Community         `json:"community"`
	Groups    []CommunityGroup  `json:"groups"`
	Members   []CommunityMember `json:"members"`
}

CommunityDetail is the full view returned by Get().

type CommunityGroup added in v0.3.0

type CommunityGroup struct {
	ID                int64   `json:"id"`
	Type              string  `json:"type"`
	Title             *string `json:"title"`
	AvatarURL         *string `json:"avatar_url"`
	Joined            bool    `json:"joined"`
	Pending           bool    `json:"pending"`
	ParticipantsCount int     `json:"participants_count"`
}

CommunityGroup is a group/channel linked to a community.

type CommunityGroupRequest added in v0.3.0

type CommunityGroupRequest struct {
	ID             int64  `json:"id"`
	CommunityID    int64  `json:"community_id"`
	ConversationID int64  `json:"conversation_id"`
	GroupName      string `json:"group_name"`
	RequestedBy    string `json:"requested_by"`
	Status         string `json:"status"`
	CreatedAt      string `json:"created_at"`
}

CommunityGroupRequest is a pending request from a group to join a community.

type CommunityInvite added in v0.3.0

type CommunityInvite struct {
	Code        string  `json:"code"`
	CommunityID int64   `json:"community_id"`
	CreatedBy   string  `json:"created_by"`
	MaxUses     int     `json:"max_uses"` // 0 = unlimited, 1+ = capped
	UseCount    int     `json:"use_count"`
	ExpiresAt   *string `json:"expires_at"` // ISO 8601 or nil if permanent
	RevokedAt   *string `json:"revoked_at"` // ISO 8601 or nil if active
	CreatedAt   string  `json:"created_at"`
}

CommunityInvite is an invite link to a community.

type CommunityInviteCodeParams added in v0.3.0

type CommunityInviteCodeParams struct {
	Code string `json:"code"`
}

type CommunityInvitePreview added in v0.3.0

type CommunityInvitePreview struct {
	Code          string  `json:"code"`
	CommunityID   int64   `json:"community_id"`
	CommunityName string  `json:"community_name"`
	MemberCount   int     `json:"member_count"`
	ExpiresAt     *string `json:"expires_at"`
	AvatarURL     *string `json:"avatar_url"`
	Description   *string `json:"description"`
}

CommunityInvitePreview is the public preview of a community via an invite code.

type CommunityJoinRequest added in v0.3.0

type CommunityJoinRequest struct {
	ID                 int64   `json:"id"`
	CommunityID        int64   `json:"community_id"`
	UserID             string  `json:"user_id"`
	Status             string  `json:"status"`
	CreatedAt          string  `json:"created_at"`
	RequesterName      string  `json:"requester_name,omitempty"`
	RequesterAvatarURL *string `json:"requester_avatar_url,omitempty"`
}

CommunityJoinRequest is a pending request from a user to join a community.

type CommunityMember added in v0.3.0

type CommunityMember struct {
	CommunityID int64           `json:"community_id"`
	UserID      string          `json:"user_id"`
	Role        ParticipantRole `json:"role"`
	JoinedAt    string          `json:"joined_at"`
	Name        string          `json:"name,omitempty"`
	AvatarURL   *string         `json:"avatar_url,omitempty"`
}

CommunityMember is a member of a community (enriched with name/avatar by Get()).

type CommunityRequestActionParams added in v0.3.0

type CommunityRequestActionParams struct {
	CommunityID int64 `json:"community_id"`
	RequestID   int64 `json:"request_id"`
}

type CreateChatInviteLinkParams added in v0.2.0

type CreateChatInviteLinkParams struct {
	ChatID int64 `json:"chat_id"`
	// MaxUses is 0 for unlimited, or a positive number to cap uses.
	MaxUses int `json:"max_uses,omitempty"`
	// ExpiresIn controls expiry: "1h", "24h", "7d", "30d", or "never" (default).
	ExpiresIn string `json:"expires_in,omitempty"`
}

CreateChatInviteLinkParams holds the parameters for creating an invite link. The bot must be admin of the conversation.

type CreateCommunityInviteLinkParams added in v0.3.0

type CreateCommunityInviteLinkParams struct {
	CommunityID int64  `json:"community_id"`
	MaxUses     int    `json:"max_uses,omitempty"`   // 0 = unlimited (default)
	ExpiresIn   string `json:"expires_in,omitempty"` // "1h"|"24h"|"7d"|"30d"|"never"
}

type CreateCommunityParams added in v0.3.0

type CreateCommunityParams struct {
	Name             string `json:"name"`
	Description      string `json:"description,omitempty"`
	AvatarURL        string `json:"avatar_url,omitempty"`
	RequiresApproval bool   `json:"requires_approval,omitempty"`
}

type CreateStoryParams added in v0.6.0

type CreateStoryParams struct {
	// Type is one of StoryImage, StoryVideo, StoryText, StoryPoll.
	Type string
	// Media is the image/video file — uploaded automatically by the SDK. Used for
	// image/video stories when MediaID is empty. Ignored for text/poll.
	Media *FileInput
	// MediaID is an alternative to Media: an already-uploaded media id.
	MediaID string
	Caption string
	// Link is a clickable CTA link shown over the story in the Kappela apps. When
	// set, the SDK encodes the caption as a JSON envelope ({text, link, linkLabel})
	// — the format the apps read (there is no separate backend field).
	Link string
	// LinkLabel is an optional label for the CTA link (e.g. "Shop now"). Requires Link.
	LinkLabel string
	// Audience is one of StoryAudienceAll (default), StoryAudienceSelected, StoryAudienceExcluded.
	Audience string
	// AudienceUserIDs is required when Audience is selected or excluded.
	AudienceUserIDs []string
}

CreateStoryParams holds the parameters for creating a story.

type DeleteMessageParams

type DeleteMessageParams struct {
	ChatID    int64  `json:"chat_id,omitempty"`
	UserID    string `json:"user_id,omitempty"`
	MessageID int64  `json:"message_id"`
}

DeleteMessageParams holds the parameters for deleting a message.

Recipient — set EITHER ChatID OR UserID (UUID); the conversation must exist.

type DeleteResult

type DeleteResult struct {
	Deleted bool `json:"deleted"`
}

DeleteResult is returned by the deleteMessage endpoint.

type EditMessageParams

type EditMessageParams struct {
	ChatID       int64           `json:"chat_id,omitempty"`
	UserID       string          `json:"user_id,omitempty"`
	MessageID    int64           `json:"message_id"`
	NewText      string          `json:"new_text,omitempty"`
	NewExtraData json.RawMessage `json:"new_extra_data,omitempty"`
}

EditMessageParams holds the parameters for editing a message.

Recipient — set EITHER ChatID OR UserID (UUID). With UserID the private conversation must already exist (you are editing an existing message).

type EditMessageResult

type EditMessageResult struct {
	Edited    bool  `json:"edited"`
	MessageID int64 `json:"message_id"`
}

EditMessageResult is returned after editing a message.

type ErrorCode

type ErrorCode string

ErrorCode is the machine-readable error code returned by the Kappela API.

const (
	ErrCodeUnauthorized       ErrorCode = "UNAUTHORIZED"
	ErrCodeForbidden          ErrorCode = "FORBIDDEN"
	ErrCodeNotFound           ErrorCode = "NOT_FOUND"
	ErrCodeInvalidField       ErrorCode = "INVALID_FIELD"
	ErrCodeMissingField       ErrorCode = "MISSING_FIELD"
	ErrCodeInternalError      ErrorCode = "INTERNAL_ERROR"
	ErrCodeServiceUnavailable ErrorCode = "SERVICE_UNAVAILABLE"
	ErrCodeConflict           ErrorCode = "CONFLICT"
	ErrCodeMethodNotAllowed   ErrorCode = "METHOD_NOT_ALLOWED"
	ErrCodeInvalidPath        ErrorCode = "INVALID_PATH"
	ErrCodeUpstreamError      ErrorCode = "UPSTREAM_ERROR"
)

type FileInput

type FileInput struct {
	Data        []byte
	Filename    string
	ContentType string
}

FileInput holds a file to be uploaded.

type GetChatAdministratorsParams added in v0.2.0

type GetChatAdministratorsParams struct {
	ChatID int64 `json:"chat_id"`
}

GetChatAdministratorsParams holds the parameters for fetching chat admins.

type GetChatAdministratorsResult added in v0.2.0

type GetChatAdministratorsResult struct {
	Admins []ChatMemberInfo `json:"admins"`
}

GetChatAdministratorsResult contains all admins of a group or channel.

type GetChatInviteLinksParams added in v0.2.0

type GetChatInviteLinksParams struct {
	ChatID int64 `json:"chat_id"`
}

GetChatInviteLinksParams holds the parameters for listing invite links.

type GetChatInviteLinksResult added in v0.2.0

type GetChatInviteLinksResult struct {
	InviteLinks []ChatInviteLink `json:"invite_links"`
}

GetChatInviteLinksResult contains all active invite links for a group or channel.

type GetChatMemberParams added in v0.2.0

type GetChatMemberParams struct {
	ChatID int64  `json:"chat_id"`
	UserID string `json:"user_id"`
}

GetChatMemberParams holds the parameters for looking up a single member.

type GetChatsParams

type GetChatsParams struct {
	Limit  int
	Offset int
}

GetChatsParams holds the parameters for listing chats.

type GetCommunityInviteLinksResult added in v0.3.0

type GetCommunityInviteLinksResult struct {
	Invites []CommunityInvite `json:"invites"`
}

GetCommunityInviteLinksResult wraps the invite links list.

type GetCommunityParams added in v0.3.0

type GetCommunityParams struct {
	CommunityID int64 `json:"community_id"`
}

type GetMyCommunitiesResult added in v0.3.0

type GetMyCommunitiesResult struct {
	Communities []Community `json:"communities"`
}

GetMyCommunitiesResult wraps the communities list.

type GetMyGroupsResult added in v0.2.0

type GetMyGroupsResult struct {
	Groups []BotGroupEntry `json:"groups"`
}

GetMyGroupsResult holds the list of groups and channels the bot belongs to.

type InlineKeyboard

type InlineKeyboard struct {
	InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"`
}

InlineKeyboard renders buttons attached to a message.

type InlineKeyboardButton

type InlineKeyboardButton struct {
	Text         string  `json:"text"`
	CallbackData *string `json:"callback_data,omitempty"`
	URL          *string `json:"url,omitempty"`
}

InlineKeyboardButton is a button inside an inline keyboard.

type KappelaError

type KappelaError struct {
	// Message is the human-readable error description from the API.
	Message string
	// Code is the machine-readable error code.
	Code ErrorCode
	// Status is the HTTP status code.
	Status int
	// RequestID can be quoted when contacting support.
	RequestID string
}

KappelaError is returned when the Kappela API responds with an error.

func (*KappelaError) Error

func (e *KappelaError) Error() string

type LeaveChatParams added in v0.2.0

type LeaveChatParams struct {
	ChatID int64 `json:"chat_id"`
}

LeaveChatParams holds the parameters for the bot to leave a group or channel.

type LeaveChatResult added in v0.2.0

type LeaveChatResult struct {
	Description string `json:"description"`
}

LeaveChatResult is returned after the bot leaves.

type Message

type Message struct {
	ID     int64 `json:"id"`
	ChatID int64 `json:"chat_id"`
	// ChatType is the type of conversation ("private", "group", "channel").
	// Always present on WS and webhook events; may be absent on history API results.
	ChatType        *ChatType       `json:"chat_type,omitempty"`
	SenderID        *string         `json:"sender_id"`
	Type            MessageType     `json:"type"`
	Text            *string         `json:"text"`
	MediaID         *string         `json:"media_id"`
	ExtraData       json.RawMessage `json:"extra_data"`
	Status          MessageStatus   `json:"status"`
	EditedAt        *int64          `json:"edited_at"`
	DeletedAt       *int64          `json:"deleted_at"`
	CreatedAt       int64           `json:"created_at"`
	ReplyToID       *int64          `json:"reply_to_id"`
	ReplyToSnapshot *ReplySnapshot  `json:"reply_to_snapshot"`
	Mentions        []string        `json:"mentions"`
	ForwardedFrom   json.RawMessage `json:"forwarded_from"`
	ExpiresAt       *int64          `json:"expires_at"`
	// SenderName is the display name of the sender.
	// Only present on messages in groups and channels — absent in private chats.
	SenderName      *string `json:"sender_name,omitempty"`
	SenderAvatarURL *string `json:"sender_avatar_url,omitempty"`
	ClientMsgID     string  `json:"client_msg_id,omitempty"`
	Width           *int    `json:"width,omitempty"`
	Height          *int    `json:"height,omitempty"`
}

Message represents a Kappela chat message.

func (*Message) GetSenderName added in v0.2.2

func (m *Message) GetSenderName() string

GetSenderName returns the sender display name, or "" if absent. Use this instead of dereferencing SenderName directly to avoid nil-pointer panics.

fmt.Sprintf("Hello %s!", msg.GetSenderName())  // safe

func (*Message) GetText added in v0.2.2

func (m *Message) GetText() string

GetText returns the message text, or "" if nil. Use this instead of dereferencing Text directly to avoid nil-pointer panics.

type MessageStatus

type MessageStatus string

MessageStatus is the delivery status of a message.

const (
	MessageStatusSent      MessageStatus = "sent"
	MessageStatusDelivered MessageStatus = "delivered"
	MessageStatusRead      MessageStatus = "read"
)

type MessageType

type MessageType string

MessageType is the content type of a message.

const (
	MessageTypeText     MessageType = "text"
	MessageTypeImage    MessageType = "image"
	MessageTypeVideo    MessageType = "video"
	MessageTypeAudio    MessageType = "audio"
	MessageTypeDocument MessageType = "document"
	MessageTypeSystem   MessageType = "system"
	MessageTypePoll     MessageType = "poll"
	MessageTypeSticker  MessageType = "sticker"
	MessageTypeLocation MessageType = "location"
	MessageTypeContact  MessageType = "contact"
)

type MessagesResource

type MessagesResource struct {
	// contains filtered or unexported fields
}

MessagesResource provides methods to send and manage messages.

func (*MessagesResource) Delete

Delete deletes a message sent by this bot or user.

func (*MessagesResource) Edit

Edit edits the text or inline keyboard of a message sent by this bot or user.

func (*MessagesResource) Send

Send sends a text message, with optional inline buttons or keyboard.

func (*MessagesResource) SendAudio

func (r *MessagesResource) SendAudio(ctx context.Context, params SendMediaParams) (*SendMediaResult, error)

SendAudio sends an audio file.

func (*MessagesResource) SendCarousel

SendCarousel sends a product or card carousel.

func (*MessagesResource) SendDocument

func (r *MessagesResource) SendDocument(ctx context.Context, params SendMediaParams) (*SendMediaResult, error)

SendDocument sends a document or generic file.

func (*MessagesResource) SendPhoto

func (r *MessagesResource) SendPhoto(ctx context.Context, params SendMediaParams) (*SendMediaResult, error)

SendPhoto sends a photo (image file).

func (*MessagesResource) SendTyping

func (r *MessagesResource) SendTyping(ctx context.Context, params SendTypingParams) (*TypingResult, error)

SendTyping shows or hides the typing indicator in a chat. IsTyping defaults to true when not set.

func (*MessagesResource) SendVideo

func (r *MessagesResource) SendVideo(ctx context.Context, params SendMediaParams) (*SendMediaResult, error)

SendVideo sends a video file.

type Participant

type Participant struct {
	ID        string  `json:"id"`
	Nom       string  `json:"nom"`
	IsBot     bool    `json:"is_bot"`
	IsPremium bool    `json:"is_premium"`
	AvatarURL *string `json:"avatar_url"`
	// Role is the member's role in the conversation.
	// Present on groups and channels; absent on private chats.
	Role *ParticipantRole `json:"role,omitempty"`
}

Participant is a member of a chat.

type ParticipantRole added in v0.2.0

type ParticipantRole string

ParticipantRole is the role of a member in a group or channel.

const (
	ParticipantRoleMember ParticipantRole = "member"
	ParticipantRoleAdmin  ParticipantRole = "admin"
)

type PrivacySetting

type PrivacySetting string

PrivacySetting is a user privacy configuration value.

const (
	PrivacyEveryone PrivacySetting = "everyone"
	PrivacyContacts PrivacySetting = "contacts"
	PrivacyNobody   PrivacySetting = "nobody"
)

type PromoteChatMemberParams added in v0.2.0

type PromoteChatMemberParams struct {
	ChatID int64  `json:"chat_id"`
	UserID string `json:"user_id"`
	// Role: ParticipantRoleAdmin promotes, ParticipantRoleMember demotes.
	Role ParticipantRole `json:"role"`
}

PromoteChatMemberParams holds the parameters for changing a member's role. The bot must be admin.

type PromoteChatMemberResult added in v0.2.0

type PromoteChatMemberResult struct {
	UserID string          `json:"user_id"`
	Role   ParticipantRole `json:"role"`
}

PromoteChatMemberResult is returned after a role change.

type PromoteCommunityMemberParams added in v0.3.0

type PromoteCommunityMemberParams struct {
	CommunityID int64           `json:"community_id"`
	UserID      string          `json:"user_id"`
	Role        ParticipantRole `json:"role"` // "admin" promotes, "member" demotes
}

type RemoveCommunityGroupParams added in v0.3.0

type RemoveCommunityGroupParams struct {
	CommunityID    int64 `json:"community_id"`
	ConversationID int64 `json:"conversation_id"`
}

type ReplyKeyboard

type ReplyKeyboard struct {
	Keyboard [][]ReplyKeyboardButton `json:"keyboard"`
}

ReplyKeyboard renders a custom keyboard shown below the message input field.

type ReplyKeyboardButton added in v0.2.0

type ReplyKeyboardButton struct {
	// Text is the label shown on the button.
	Text string
	// CallbackData is the value sent to the webhook when the button is pressed.
	// Defaults to Text when empty.
	CallbackData string
}

ReplyKeyboardButton is a single button in a reply or scroll keyboard.

Short form — label and callback value are identical:

ReplyKeyboardButton{Text: "Option A"}

Long form — different label and callback value:

ReplyKeyboardButton{Text: "✅ Confirmer", CallbackData: "confirm_yes"}

func (ReplyKeyboardButton) MarshalJSON added in v0.2.0

func (b ReplyKeyboardButton) MarshalJSON() ([]byte, error)

MarshalJSON serialises as a plain string when CallbackData is empty or equals Text, and as {"text":…,"callback_data":…} when they differ.

type ReplySnapshot

type ReplySnapshot struct {
	MessageID int64       `json:"message_id"`
	SenderID  *string     `json:"sender_id"`
	Type      MessageType `json:"type"`
	Text      *string     `json:"text"`
	MediaID   *string     `json:"media_id"`
}

ReplySnapshot is a lightweight snapshot of the message being replied to.

type RevokeChatInviteLinkParams added in v0.2.0

type RevokeChatInviteLinkParams struct {
	ChatID int64 `json:"chat_id"`
	// Code is the code field of the link to revoke.
	Code string `json:"code"`
}

RevokeChatInviteLinkParams holds the parameters for revoking an invite link.

type RevokeChatInviteLinkResult added in v0.2.0

type RevokeChatInviteLinkResult struct {
	Revoked bool   `json:"revoked"`
	Code    string `json:"code"`
}

RevokeChatInviteLinkResult is returned after revoking a link.

type RevokeCommunityInviteLinkParams added in v0.3.0

type RevokeCommunityInviteLinkParams struct {
	CommunityID int64  `json:"community_id"`
	Code        string `json:"code"`
}

type ScrollKeyboard

type ScrollKeyboard struct {
	ScrollKeyboard []ScrollKeyboardButton `json:"scroll_keyboard"`
}

ScrollKeyboard renders a horizontally scrollable chip bar below the message input.

type ScrollKeyboardButton added in v0.2.0

type ScrollKeyboardButton = ReplyKeyboardButton

ScrollKeyboardButton is a button in a scroll (horizontal chips) keyboard. Same short/long form as ReplyKeyboardButton.

type SendCarouselParams

type SendCarouselParams struct {
	ChatID   int64          `json:"chat_id,omitempty"`
	UserID   string         `json:"user_id,omitempty"`
	Text     string         `json:"text,omitempty"`
	Carousel []CarouselCard `json:"carousel"`
	// QuickReplyButtons are shown as chips below the carousel.
	// Accepts short form {Text: "label"} or long form {Text: "label", CallbackData: "value"}.
	QuickReplyButtons []ScrollKeyboardButton `json:"quick_reply_buttons,omitempty"`
	ReplyToID         *int64                 `json:"reply_to_id,omitempty"`
	DeletePrevious    bool                   `json:"delete_previous,omitempty"`
}

SendCarouselParams holds the parameters for sending a product carousel.

Recipient — set EITHER ChatID OR UserID (UUID); see SendMessageParams.

type SendCarouselResult

type SendCarouselResult struct {
	MessageID int64  `json:"message_id"`
	CreatedAt int64  `json:"created_at"`
	Type      string `json:"type"`
}

SendCarouselResult is returned after sending a carousel.

type SendMediaParams

type SendMediaParams struct {
	ChatID         int64
	UserID         string
	File           FileInput
	Caption        string
	ReplyToID      *int64
	DeletePrevious bool
	// ReplyMarkup accepts InlineKeyboard, ReplyKeyboard, or ScrollKeyboard.
	ReplyMarkup any
}

SendMediaParams holds the parameters for sending a photo, video, document, or audio.

Recipient — set EITHER ChatID OR UserID (UUID); see SendMessageParams.

type SendMediaResult

type SendMediaResult struct {
	MessageID int64  `json:"message_id"`
	CreatedAt int64  `json:"created_at"`
	MediaID   string `json:"media_id"`
}

SendMediaResult is returned after sending a media message.

type SendMessageParams

type SendMessageParams struct {
	ChatID int64  `json:"chat_id,omitempty"`
	UserID string `json:"user_id,omitempty"`
	Text   string `json:"text"`
	// ReplyMarkup accepts InlineKeyboard, ReplyKeyboard, or ScrollKeyboard.
	ReplyMarkup any `json:"reply_markup,omitempty"`
	// ActionButton renders a copy/link/join button at the foot of the bubble.
	ActionButton   *ActionButton `json:"action_button,omitempty"`
	ReplyToID      *int64        `json:"reply_to_id,omitempty"`
	DeletePrevious bool          `json:"delete_previous,omitempty"`
}

SendMessageParams holds the parameters for sending a text message.

Recipient — set EITHER ChatID OR UserID (UUID). With UserID the message is routed to your 1-to-1 private chat with that user: for a Bot the conversation must already exist (FORBIDDEN otherwise); for a User it is created if needed.

type SendResult

type SendResult struct {
	MessageID int64 `json:"message_id"`
	CreatedAt int64 `json:"created_at"`
}

SendResult is returned after sending a text message.

type SendTypingParams

type SendTypingParams struct {
	ChatID   int64  `json:"chat_id,omitempty"`
	UserID   string `json:"user_id,omitempty"`
	IsTyping *bool  `json:"is_typing,omitempty"`
}

SendTypingParams holds the parameters for the typing indicator. IsTyping defaults to true when nil (show indicator). Set to false to hide it.

Recipient — set EITHER ChatID OR UserID (UUID); see SendMessageParams.

type SetWebhookParams

type SetWebhookParams struct {
	URL    string  `json:"url"`
	Secret *string `json:"secret,omitempty"`
}

SetWebhookParams holds the parameters for registering a webhook.

type StoriesResource added in v0.6.0

type StoriesResource struct {
	// contains filtered or unexported fields
}

StoriesResource provides methods to create and manage stories (ephemeral, 24 h).

Stories are a user-only feature (their audience is based on your private conversation contacts) — available on User, not Bot. For image/video stories, the SDK uploads the file automatically (like Messages.SendPhoto) and uses the resulting media id.

func (*StoriesResource) Create added in v0.6.0

func (r *StoriesResource) Create(ctx context.Context, params CreateStoryParams) (*Story, error)

Create creates a story. For image/video, pass Media (uploaded automatically) or a pre-uploaded MediaID. For text/poll, just set Caption.

func (*StoriesResource) Delete added in v0.6.0

func (r *StoriesResource) Delete(ctx context.Context, storyID string) (*StoryActionResult, error)

Delete deletes one of your stories.

func (*StoriesResource) Get added in v0.6.0

func (r *StoriesResource) Get(ctx context.Context, storyID string) (*Story, error)

Get returns a single story by id (audience-checked server-side).

func (*StoriesResource) GetPreferences added in v0.6.0

func (r *StoriesResource) GetPreferences(ctx context.Context) (*StoryPreferences, error)

GetPreferences returns your default story audience preference.

func (*StoriesResource) GetViewers added in v0.6.0

func (r *StoriesResource) GetViewers(ctx context.Context, storyID string) ([]StoryView, error)

GetViewers lists who viewed one of your stories (owner only).

func (*StoriesResource) List added in v0.6.0

func (r *StoriesResource) List(ctx context.Context) ([]Story, error)

List returns the feed of your contacts' active stories.

func (*StoriesResource) ListMine added in v0.6.0

func (r *StoriesResource) ListMine(ctx context.Context) ([]Story, error)

ListMine returns your own stories.

func (*StoriesResource) SetPreferences added in v0.6.0

func (r *StoriesResource) SetPreferences(ctx context.Context, prefs StoryPreferences) (*StoryActionResult, error)

SetPreferences sets your default story audience preference.

func (*StoriesResource) UploadMedia added in v0.6.0

func (r *StoriesResource) UploadMedia(ctx context.Context, file FileInput) (*StoryMediaUpload, error)

UploadMedia uploads story media (image/video) and returns its media id. Usually you don't call this directly — Create with Media does it for you.

func (*StoriesResource) View added in v0.6.0

func (r *StoriesResource) View(ctx context.Context, storyID string) (*StoryActionResult, error)

View marks a story as viewed.

type Story added in v0.6.0

type Story struct {
	ID              string   `json:"id"`
	UserID          string   `json:"user_id"`
	MediaID         string   `json:"media_id"`
	MediaType       string   `json:"media_type"`
	Caption         string   `json:"caption"`
	ExpiresAt       string   `json:"expires_at"` // ISO 8601
	ViewCount       int      `json:"view_count"`
	CreatedAt       string   `json:"created_at"` // ISO 8601
	Audience        string   `json:"audience"`
	AudienceUserIDs []string `json:"audience_user_ids,omitempty"`
	// Enriched on read.
	AuthorName   string  `json:"author_name,omitempty"`
	AuthorAvatar *string `json:"author_avatar,omitempty"`
	ViewedByMe   bool    `json:"viewed_by_me,omitempty"`
	MediaURL     *string `json:"media_url,omitempty"`
}

Story is an ephemeral story (24 h).

type StoryActionResult added in v0.6.0

type StoryActionResult struct {
	Done bool `json:"done"`
}

StoryActionResult is returned by actions without a body (delete, view, set preferences).

type StoryMediaUpload added in v0.6.0

type StoryMediaUpload struct {
	MediaID      string `json:"media_id"`
	URL          string `json:"url"`
	Width        int    `json:"width,omitempty"`
	Height       int    `json:"height,omitempty"`
	ThumbnailURL string `json:"thumbnail_url,omitempty"`
	MediumURL    string `json:"medium_url,omitempty"`
}

StoryMediaUpload is returned when uploading story media (image/video).

type StoryPreferences added in v0.6.0

type StoryPreferences struct {
	Audience        string   `json:"audience"`
	AudienceUserIDs []string `json:"audience_user_ids"`
}

StoryPreferences holds the default audience preference for new stories.

type StoryView added in v0.6.0

type StoryView struct {
	StoryID      string  `json:"story_id"`
	ViewerID     string  `json:"viewer_id"`
	ViewedAt     string  `json:"viewed_at"` // ISO 8601
	ViewerName   string  `json:"viewer_name,omitempty"`
	ViewerAvatar *string `json:"viewer_avatar,omitempty"`
}

StoryView is a single view of a story (enriched with viewer name/avatar).

type TypingResult

type TypingResult struct {
	Typing bool `json:"typing"`
}

TypingResult is returned by the sendTyping endpoint.

type UpdateCommunityParams added in v0.3.0

type UpdateCommunityParams struct {
	CommunityID           int64   `json:"community_id"`
	Name                  *string `json:"name,omitempty"`
	Description           *string `json:"description,omitempty"`
	AvatarURL             *string `json:"avatar_url,omitempty"`
	AnnouncementChannelID *int64  `json:"announcement_channel_id,omitempty"`
	RequiresApproval      *bool   `json:"requires_approval,omitempty"`
}

UpdateCommunityParams updates only the non-nil fields.

type User

type User struct {
	// Messages provides methods to send and manage messages.
	Messages *MessagesResource
	// Chats provides methods to list and iterate over chats.
	Chats *ChatsResource
	// Webhooks provides methods to manage webhooks.
	Webhooks *WebhooksResource
	// Profile provides access to your own profile.
	Profile *UserProfileResource
	// Communities provides methods to manage communities (members, roles, invites, requests).
	Communities *CommunitiesResource
	// Stories provides methods to create and manage your stories (ephemeral, 24 h).
	Stories *StoriesResource
	// contains filtered or unexported fields
}

User is the Kappela personal automation client. Authenticate with a personal API key (sk_...) to send messages and receive events as yourself.

Example:

me := kappelas.NewUser("sk_...")

me.OnMessage(func(msg *kappelas.Message) {
    fmt.Println("New message:", msg.Text)
})

me.Start()
select {}

func NewUser

func NewUser(apiKey string, opts ...UserOption) *User

NewUser creates a User authenticated with the given personal API key.

func (*User) Connected

func (u *User) Connected() bool

Connected reports whether the WebSocket is currently open.

func (*User) GetAutomationStatus added in v0.4.0

func (u *User) GetAutomationStatus(ctx context.Context) (*AutomationStatus, error)

GetAutomationStatus reports whether this account's personal automations are paused.

func (*User) HandleWebhook

func (u *User) HandleWebhook(body []byte)

HandleWebhook processes a webhook payload sent by Kappela to your server.

Example (net/http):

http.HandleFunc("/kappela-webhook", func(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    me.HandleWebhook(body)
    w.WriteHeader(http.StatusOK)
})

func (*User) OnCallbackQuery

func (u *User) OnCallbackQuery(h func(*CallbackQuery))

OnCallbackQuery registers a handler called when a user clicks an inline button.

func (*User) OnConnected

func (u *User) OnConnected(h func())

OnConnected registers a handler called when the WebSocket connects (or reconnects).

func (*User) OnDisconnected

func (u *User) OnDisconnected(h func(code int, reason string))

OnDisconnected registers a handler called when the WebSocket disconnects.

func (*User) OnError

func (u *User) OnError(h func(error))

OnError registers a handler called on WebSocket or connection errors.

func (*User) OnMessage

func (u *User) OnMessage(h func(*Message))

OnMessage registers a handler called for every incoming message.

func (*User) PauseAutomationInChat added in v0.5.0

func (u *User) PauseAutomationInChat(ctx context.Context, chatID int64) (*ChatAutomationResult, error)

PauseAutomationInChat pauses your personal automations in ONE conversation only.

Use this to take over a single chat (e.g. you start replying to X yourself): your AI stops receiving messages from that conversation while it keeps handling all your other chats. Unlike PauseAutomations, this is scoped to a single conversation.

func (*User) PauseAutomations added in v0.4.0

func (u *User) PauseAutomations(ctx context.Context) (*AutomationStatus, error)

PauseAutomations pauses this account's personal automations.

While paused, the account stops receiving incoming messages over /v1/me (so an AI auto-responder is never triggered) and any send call is rejected with AUTOMATIONS_PAUSED. Useful when the human owner takes over the chat.

func (*User) Reply added in v0.6.0

func (u *User) Reply(ctx context.Context, event any, text string, opts ...SendMessageParams) (*SendResult, error)

Reply sends a text reply to a Message or CallbackQuery event.

  • When called with a *Message — sets ChatID and ReplyToID automatically (shows a quote banner).
  • When called with a *CallbackQuery — sets ChatID only (callback queries have no message ID).

Pass an optional SendMessageParams to attach a keyboard or set other options. ChatID, ReplyToID, and Text in opts are overwritten automatically.

Example:

me.OnMessage(func(msg *kappelas.Message) {
    me.Reply(context.Background(), msg, "Got it! 👋")
})

func (*User) ResumeAutomationInChat added in v0.5.0

func (u *User) ResumeAutomationInChat(ctx context.Context, chatID int64) (*ChatAutomationResult, error)

ResumeAutomationInChat resumes your personal automations in a conversation.

func (*User) ResumeAutomations added in v0.4.0

func (u *User) ResumeAutomations(ctx context.Context) (*AutomationStatus, error)

ResumeAutomations resumes this account's personal automations after PauseAutomations.

func (*User) Start

func (u *User) Start()

Start connects via WebSocket and begins receiving events in the background.

func (*User) Stop

func (u *User) Stop()

Stop closes the WebSocket connection.

type UserOption

type UserOption func(*userConfig)

UserOption configures a User.

func WithUserBaseURL

func WithUserBaseURL(u string) UserOption

WithUserBaseURL overrides the API base URL for a User client.

func WithUserMaxRetries

func WithUserMaxRetries(n int) UserOption

WithUserMaxRetries sets the maximum number of HTTP retry attempts for a User client.

func WithUserTimeout

func WithUserTimeout(d time.Duration) UserOption

WithUserTimeout sets the HTTP request timeout for a User client.

func WithUserWSMaxRetries

func WithUserWSMaxRetries(n int) UserOption

WithUserWSMaxRetries sets the maximum WebSocket reconnect attempts for a User client.

type UserProfile

type UserProfile struct {
	ID            string         `json:"id"`
	Username      string         `json:"username"`
	Nom           string         `json:"nom"`
	IsBot         bool           `json:"is_bot"`
	IsPremium     bool           `json:"is_premium"`
	AvatarURL     *string        `json:"avatar_url"`
	AllowGroupAdd PrivacySetting `json:"allow_group_add"`
	AllowCalls    PrivacySetting `json:"allow_calls"`
}

UserProfile is the profile returned for a personal account.

type UserProfileResource

type UserProfileResource struct {
	// contains filtered or unexported fields
}

UserProfileResource provides access to the user's own profile.

func (*UserProfileResource) Get

Get returns your own profile.

type WebhookDeleteResult

type WebhookDeleteResult struct {
	Active bool `json:"active"`
}

WebhookDeleteResult is returned after removing a webhook.

type WebhookInfo

type WebhookInfo struct {
	Active    bool    `json:"active"`
	URL       *string `json:"url"`
	CreatedAt *int64  `json:"created_at"`
}

WebhookInfo describes the current webhook configuration.

type WebhookSetResult

type WebhookSetResult struct {
	URL    string `json:"url"`
	Active bool   `json:"active"`
}

WebhookSetResult is returned after registering a webhook.

type WebhooksResource

type WebhooksResource struct {
	// contains filtered or unexported fields
}

WebhooksResource provides methods to manage webhooks.

func (*WebhooksResource) Delete

Delete removes the webhook. Events will no longer be delivered via HTTP POST.

func (*WebhooksResource) GetInfo

func (r *WebhooksResource) GetInfo(ctx context.Context) (*WebhookInfo, error)

GetInfo returns the current webhook status and URL.

func (*WebhooksResource) Set

Set registers a webhook URL. Use this for production deployments.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL