Documentation
¶
Overview ¶
Package core holds botbooter's platform-agnostic engine: the Bot type, its command/middleware dispatch, and the connection lifecycle.
Index ¶
- Variables
- func AdapterAs[T any](b *Bot) (T, bool)
- type Adapter
- type AdapterDeps
- type Attachment
- type AttachmentResolver
- type Bot
- func (b *Bot) AddHandler(cmd Command) error
- func (b *Bot) AddMiddleware(middleware Middleware)
- func (b *Bot) Connect(ctx context.Context) error
- func (b *Bot) Disconnect() error
- func (b *Bot) GetAttachments(message *Message) ([]Attachment, error)
- func (b *Bot) HandleFunc(pattern string, handler CommandHandler) error
- func (b *Bot) Reply(ctx context.Context, m *Message, text string) error
- func (b *Bot) ResolveAttachmentURL(ctx context.Context, att Attachment) (string, error)
- func (b *Bot) Run(ctx context.Context) error
- func (b *Bot) SendMessage(channelID, text string, opts ...SendOption) error
- func (b *Bot) SendMessageContext(ctx context.Context, channelID, text string, opts ...SendOption) error
- func (b *Bot) SetLogger(logger *slog.Logger)
- func (b *Bot) SetUnknownCommandHandler(handler CommandHandler)
- func (b *Bot) Start() error
- type BotType
- type CLIMessage
- type Command
- type CommandHandler
- type Message
- type Middleware
- type SendOption
- type SendOptions
Constants ¶
This section is empty.
Variables ¶
var ErrAlreadyConnected = errors.New("botbooter: already connected")
ErrAlreadyConnected is returned by Connect when the Bot is already connected.
var ErrNilMessage = errors.New("botbooter: nil message")
ErrNilMessage is returned by Bot methods handed a nil *Message argument.
var ErrUnknownBotType = errors.New("botbooter: unknown bot type")
ErrUnknownBotType is returned by Bot methods when the Bot has no adapter.
Functions ¶
Types ¶
type Adapter ¶
type Adapter interface {
Connect(ctx context.Context, deps AdapterDeps) error
Disconnect() error
Send(ctx context.Context, channelID, text string, opts SendOptions) error
Attachments(m *Message) ([]Attachment, error)
}
Adapter is the platform-specific half of a Bot. The Bot drives it through this interface, so the core has no compile-time dependency on any platform.
type AdapterDeps ¶
type AdapterDeps struct {
Dispatch func(ctx context.Context, m *Message)
Done func(err error)
Disconnect func() error
Logger *slog.Logger // always non-nil
}
AdapterDeps is the set of callbacks an Adapter uses to talk back to the Bot, plus the Bot's logger so adapter diagnostics route through the same sink.
type Attachment ¶
Attachment is a platform-agnostic file attached to a message.
type AttachmentResolver ¶ added in v0.2.0
type AttachmentResolver interface {
ResolveAttachmentURL(ctx context.Context, att Attachment) (string, error)
}
AttachmentResolver is an optional capability an Adapter may implement to turn an Attachment into a downloadable URL; adapters whose Attachment.URL is already usable ride the passthrough in Bot.ResolveAttachmentURL.
type Bot ¶
type Bot struct {
BotType BotType
// contains filtered or unexported fields
}
Bot is the platform-agnostic chat bot. Register handlers and middleware before Connect; after that, Connect/Run/Disconnect/Send are safe to call concurrently. Registering after Connect races the dispatch goroutine.
func (*Bot) AddHandler ¶
AddHandler registers cmd, compiling its Pattern and returning an error if it is not valid. Commands are matched in registration order, first match wins.
func (*Bot) AddMiddleware ¶
func (b *Bot) AddMiddleware(middleware Middleware)
AddMiddleware appends middleware to the dispatch chain, run in registration order.
func (*Bot) Connect ¶
Connect starts the adapter's event loop and returns without blocking. It returns ErrAlreadyConnected if a connection is already active, ErrUnknownBotType if the Bot has no adapter, or any error from the adapter's own Connect.
func (*Bot) Disconnect ¶
Disconnect tears down the active connection: it cancels the run context and runs the adapter's Disconnect exactly once. It is safe to call when not connected, returning ErrUnknownBotType only if the Bot has no adapter.
func (*Bot) GetAttachments ¶
func (b *Bot) GetAttachments(message *Message) ([]Attachment, error)
GetAttachments returns the platform-agnostic attachments of message. It returns ErrNilMessage if message is nil, or ErrUnknownBotType if the Bot has no adapter.
func (*Bot) HandleFunc ¶
func (b *Bot) HandleFunc(pattern string, handler CommandHandler) error
HandleFunc is a convenience wrapper around AddHandler.
func (*Bot) Reply ¶ added in v0.3.0
Reply is convenience sugar for replying into the thread or reply-chain of the inbound message m — it is exactly SendMessageContext(ctx, m.ChannelID, text, InReplyTo(m)). Each adapter derives its own platform-specific anchor; see SendOptions. It returns ErrNilMessage if m is nil, or ErrUnknownBotType if the Bot has no adapter.
func (*Bot) ResolveAttachmentURL ¶ added in v0.2.0
ResolveAttachmentURL returns a downloadable URL for att. If the adapter implements AttachmentResolver the call is delegated; otherwise att.URL is returned verbatim. It returns ErrUnknownBotType if the Bot has no adapter. An empty string with a nil error means "not resolvable", not a failure.
The result is consumed differently per platform:
- Discord: a signed CDN link (~24h); plain GET, consume promptly.
- Slack: not directly fetchable — download via the Slack Web API client (SlackClient(b).GetFileContext), which injects the bot token.
- Telegram: a plain GET on a secret, ~1h URL that embeds the bot token — never log or cache it. Each resolve logs a warning, suppressible via BOTBOOTER_TELEGRAM_SUPPRESS_URL_WARNING.
- WhatsApp: GET with an "Authorization: Bearer <token>" header (the Cloud API token used to send). Short-lived; consume promptly.
- Teams: a pre-authorized link carrying a short-lived token — consume promptly, never log or cache. Inline images may need an Authorization header this adapter does not yet supply.
- CLI: a local filesystem path (open with os.Open), not an HTTP URL.
func (*Bot) Run ¶
Run connects the Bot and blocks until ctx is canceled, the event loop ends, or Disconnect is called from elsewhere, then disconnects. A clean shutdown (ctx cancellation or a local Disconnect) returns nil rather than ctx.Err(), so callers can safely do log.Fatal(bot.Run(ctx)).
func (*Bot) SendMessage ¶
func (b *Bot) SendMessage(channelID, text string, opts ...SendOption) error
SendMessage sends text to channelID using a background context. Prefer SendMessageContext from within a handler so the send honors shutdown and cancellation; SendMessage's background context outlives Run's teardown.
func (*Bot) SendMessageContext ¶
func (b *Bot) SendMessageContext(ctx context.Context, channelID, text string, opts ...SendOption) error
SendMessageContext sends text to channelID, honoring ctx for cancellation. Pass InReplyTo or WithThreadID to thread the message onto an inbound one; with no options it is a plain channel message.
func (*Bot) SetLogger ¶ added in v0.3.0
SetLogger routes the Bot's and its adapter's diagnostics (panic recovery, shutdown warnings, webhook rejections) through logger instead of slog.Default. Like handler registration, call it before Connect.
func (*Bot) SetUnknownCommandHandler ¶
func (b *Bot) SetUnknownCommandHandler(handler CommandHandler)
SetUnknownCommandHandler sets the handler invoked when a message matches no registered command; if unset, unmatched messages are ignored.
type BotType ¶
type BotType int
BotType identifies the messaging platform a Bot is connected to.
type CLIMessage ¶
type CLIMessage struct {
Text string
Attachments []Attachment
}
CLIMessage is the raw payload of a message read from the CLI adapter.
type Command ¶
type Command struct {
Pattern string
Handler CommandHandler
// contains filtered or unexported fields
}
Command pairs a regular-expression Pattern with the Handler to run on a match.
type CommandHandler ¶
CommandHandler handles a dispatched message for a matched command.
type Message ¶
type Message struct {
ID string
UserID string
AuthorName string
ChannelID string
Content string
Timestamp time.Time
ReplyToID string
MentionedUserIDs []string
Raw any
}
Message is a platform-agnostic incoming message handed to command handlers. UserID, ChannelID and Content are always set; the remaining normalized fields are best-effort per platform. Raw carries the originating platform's untouched event; read it with the matching typed accessor (e.g. discord.RawEvent).
type Middleware ¶
type Middleware func(ctx context.Context, b *Bot, m *Message, next CommandHandler)
Middleware wraps message dispatch; it must call next to continue the chain.
type SendOption ¶ added in v0.3.0
type SendOption func(*SendOptions)
SendOption modifies a SendOptions. Construct them with InReplyTo / WithThreadID and pass them to Bot.SendMessageContext / Bot.SendMessage.
func InReplyTo ¶ added in v0.3.0
func InReplyTo(m *Message) SendOption
InReplyTo anchors the send on m so the adapter posts into m's thread or reply-chain, deriving the correct per-platform anchor itself.
func WithThreadID ¶ added in v0.3.0
func WithThreadID(id string) SendOption
WithThreadID anchors the send on a raw native id the adapter uses verbatim. It takes precedence over InReplyTo. The caller owns platform-correctness.
type SendOptions ¶ added in v0.3.0
SendOptions is the resolved set of per-send modifiers an Adapter reads off a Send call. Its zero value means "a plain channel message". A threading anchor is platform-specific, so each adapter derives its own from these fields:
- ReplyTo: reply anchored on this whole message; the adapter picks the correct native anchor (Slack thread_ts from ReplyToID; Discord/Telegram/ WhatsApp the replied-to/quoted message id).
- ThreadID: a raw native anchor supplied by the caller, used verbatim. It wins over ReplyTo when both are set. On Slack it is a thread_ts; on Discord/Telegram/WhatsApp a reply/quote message id (NOT a Discord thread-channel id).