Documentation
¶
Overview ¶
Package channels is the channel-adapter registry plus adapters. It ships a FakeAdapter (for tests) and a reference WebhookAdapter (HTTP POST delivery). Platform-specific adapters (Slack, Discord, ...) follow the WebhookAdapter shape and register the same way.
Index ¶
- type Adapter
- type DiscordAdapter
- type EmailAdapter
- type FakeAdapter
- type GoogleChatAdapter
- type IMessageAdapter
- type MatrixAdapter
- type MattermostAdapter
- type Registry
- type SignalAdapter
- type SlackAdapter
- type TeamsAdapter
- type TelegramAdapter
- type WebchatAdapter
- type WebchatMessage
- type WebhookAdapter
- type WhatsAppAdapter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Adapter ¶
type Adapter interface {
Name() string
Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
}
Adapter delivers an outbound message to a concrete platform and returns the platform-assigned message ID.
type DiscordAdapter ¶
type DiscordAdapter struct {
AdapterName string
Token string
// BaseURL defaults to defaultDiscordBaseURL; overridable for tests.
BaseURL string
Client *http.Client
}
DiscordAdapter is a concrete, stdlib-only Adapter that delivers an outbound message via the Discord REST API create-message endpoint. It follows the TelegramAdapter shape: it sits behind the Adapter interface and adds no dependency. The channel is taken from MessageOut.PlatformID; a numeric ThreadID is treated as the id of a message to reply to (message_reference), so replies thread; a non-numeric ThreadID is ignored. The returned platform message id is the Discord message snowflake id.
SECURITY: the bot token is sent in the Authorization header ("Bot <token>"). The adapter NEVER includes the token in returned errors — error strings are redacted first — so a token cannot leak into logs.
func NewDiscordAdapter ¶
func NewDiscordAdapter(name, token string) *DiscordAdapter
NewDiscordAdapter constructs a DiscordAdapter. name defaults to "discord"; the client gets a default 15s timeout.
func (*DiscordAdapter) Deliver ¶
func (a *DiscordAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content to the channel in msg.PlatformID via create-message and returns the Discord message snowflake id as the platform message id.
type EmailAdapter ¶
type EmailAdapter struct {
AdapterName string
// Host and Port address the SMTP submission server (e.g. smtp.gmail.com:587).
Host string
Port int
// Username / Password authenticate to the server (PLAIN over STARTTLS).
Username string
Password string
// From is the envelope + header sender address.
From string
// contains filtered or unexported fields
}
EmailAdapter is a concrete, stdlib-only Adapter that delivers an outbound message as email over SMTP (net/smtp). It works with any SMTP submission server, including Gmail (smtp.gmail.com:587 with an app password) — satisfying the "Email / Gmail" channel via the universal protocol rather than a provider-specific API.
The recipient is MessageOut.PlatformID (an email address). The subject is derived from the first line of the content; a non-empty MessageOut.ThreadID is echoed into the In-Reply-To / References headers so replies thread in the recipient's client. Deliver returns the generated RFC 5322 Message-ID as the platform message id (SMTP itself returns none).
Inbound ingestion (IMAP / Gmail Pub-Sub) is intentionally out of scope here — the task lists it as optional — so this adapter is send-only, matching the Adapter interface.
SECURITY: the SMTP password is never placed in the message and is redacted from every returned error, so it cannot leak into logs.
func NewEmailAdapter ¶
func NewEmailAdapter(name, host string, port int, username, password, from string) *EmailAdapter
NewEmailAdapter constructs an EmailAdapter. name defaults to "email"; port defaults to 587 (the submission port) when non-positive.
func (*EmailAdapter) Deliver ¶
func (a *EmailAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content as an email to the address in msg.PlatformID and returns the generated Message-ID as the platform message id.
type FakeAdapter ¶
type FakeAdapter struct {
AdapterName string
// contains filtered or unexported fields
}
FakeAdapter records the messages it is asked to deliver. It is exported for use in tests across the host tree.
func NewFakeAdapter ¶
func NewFakeAdapter(name string) *FakeAdapter
NewFakeAdapter constructs a FakeAdapter with the given name (defaults to "fake").
func (*FakeAdapter) Deliver ¶
func (f *FakeAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver records msg and returns a synthetic platform message ID.
func (*FakeAdapter) Delivered ¶
func (f *FakeAdapter) Delivered() []contract.MessageOut
Delivered returns a copy of the messages delivered so far.
type GoogleChatAdapter ¶
type GoogleChatAdapter struct {
AdapterName string
// Token is the Google Chat bot/service-account OAuth2 access token (Bearer).
Token string
// BaseURL defaults to defaultGoogleChatBaseURL; overridable for tests.
BaseURL string
Client *http.Client
}
GoogleChatAdapter is a concrete, stdlib-only Adapter that delivers an outbound message to a Google Chat space via the REST API `POST {base}/v1/{space}/messages`. It follows the SlackAdapter shape: it sits behind the Adapter interface and adds no dependency.
The space is taken from MessageOut.PlatformID (a space id such as "AAAA1234" or the full resource "spaces/AAAA1234"; a bare id is normalized to the "spaces/" form). A non-empty MessageOut.ThreadID maps to the message `thread.threadKey` so messages with the same key group into one thread. The returned platform message id is the created message resource `name`.
SECURITY: the bot/service-account access token is sent in the Authorization header (never the URL), and the adapter NEVER includes the token in returned errors — error strings are redacted first — so a token cannot leak into logs even if a transport error or upstream payload were ever to echo it.
func NewGoogleChatAdapter ¶
func NewGoogleChatAdapter(name, token string) *GoogleChatAdapter
NewGoogleChatAdapter constructs a GoogleChatAdapter. name defaults to "googlechat"; the client gets a default 15s timeout.
func (*GoogleChatAdapter) Deliver ¶
func (a *GoogleChatAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content to the space in msg.PlatformID and returns the created message resource name as the platform message id.
func (*GoogleChatAdapter) Name ¶
func (a *GoogleChatAdapter) Name() string
Name returns the adapter name.
type IMessageAdapter ¶
type IMessageAdapter struct {
AdapterName string
// contains filtered or unexported fields
}
IMessageAdapter delivers an outbound message via Apple Messages on a macOS host, by driving Messages.app through AppleScript (`osascript`). It is the only adapter that bridges a LOCAL app rather than an HTTP API, so it runs only when the control-plane host is macOS with Messages signed in. The recipient (a phone number or Apple ID) is taken from MessageOut.PlatformID.
There is no network token; the trust surface is the local Messages account.
func NewIMessageAdapter ¶
func NewIMessageAdapter(name string) *IMessageAdapter
NewIMessageAdapter constructs an IMessageAdapter. name defaults to "imessage".
func (*IMessageAdapter) Deliver ¶
func (a *IMessageAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content to the recipient in msg.PlatformID via Messages.app. It fails fast off macOS — the bridge cannot work without Messages.app.
func (*IMessageAdapter) Name ¶
func (a *IMessageAdapter) Name() string
Name returns the adapter name.
type MatrixAdapter ¶
type MatrixAdapter struct {
AdapterName string
// HomeserverURL is the base URL of the Matrix homeserver (e.g.
// "https://matrix.example.org"). Required. Overridable for tests.
HomeserverURL string
// Token is the Matrix access token (Bearer).
Token string
Client *http.Client
}
MatrixAdapter is a concrete, stdlib-only Adapter that delivers an outbound message via the Matrix client-server API `PUT /_matrix/client/v3/rooms/{roomId} /send/m.room.message/{txnId}` endpoint. It follows the SlackAdapter shape: it sits behind the Adapter interface and adds no dependency.
The room is taken from MessageOut.PlatformID (e.g. "!abc:example.org"). A non-empty MessageOut.ThreadID maps to an `m.relates_to` thread relation so the message lands in that thread. Each send uses a fresh transaction id, making the PUT idempotent. The returned platform message id is the Matrix `event_id`.
Unlike the fixed-host platforms, Matrix is self-hosted, so HomeserverURL is required configuration (there is no default homeserver).
SECURITY: the access token is sent in the Authorization header (never the URL), and the adapter NEVER includes the token in returned errors — error strings are redacted first — so a token cannot leak into logs even if a transport error or upstream payload were ever to echo it.
func NewMatrixAdapter ¶
func NewMatrixAdapter(name, homeserverURL, token string) *MatrixAdapter
NewMatrixAdapter constructs a MatrixAdapter. name defaults to "matrix"; the client gets a default 15s timeout.
func (*MatrixAdapter) Deliver ¶
func (a *MatrixAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content as an m.text message to the room in msg.PlatformID and returns the Matrix event_id as the platform message id.
type MattermostAdapter ¶ added in v0.1.126
MattermostAdapter delivers an outbound message to Mattermost via an Incoming Webhook URL. It is stdlib-only and follows the TeamsAdapter shape.
SECURITY: the webhook URL embeds a secret token, so it is sent only in the request line (never logged) and is ALWAYS redacted from returned errors.
func NewMattermostAdapter ¶ added in v0.1.126
func NewMattermostAdapter(name, webhookURL string) *MattermostAdapter
func (*MattermostAdapter) Deliver ¶ added in v0.1.126
func (a *MattermostAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
func (*MattermostAdapter) Name ¶ added in v0.1.126
func (a *MattermostAdapter) Name() string
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds the available channel adapters keyed by Name. It is mutex-guarded and safe for concurrent use.
type SignalAdapter ¶
type SignalAdapter struct {
AdapterName string
// BaseURL is the signal-cli-rest-api base (e.g. http://127.0.0.1:8080).
BaseURL string
// Number is the registered sender number (E.164, e.g. +15551234567).
Number string
Client *http.Client
}
SignalAdapter delivers an outbound message to Signal via a signal-cli REST bridge (e.g. bbernhard/signal-cli-rest-api) running host-side. Signal has no official bot API, so the host operates a registered signal-cli number and the adapter POSTs to its /v2/send endpoint. It is stdlib-only and follows the SlackAdapter shape. The recipient is taken from MessageOut.PlatformID (a phone number or group id); the sender is the adapter's configured Number.
func NewSignalAdapter ¶
func NewSignalAdapter(name, baseURL, number string) *SignalAdapter
NewSignalAdapter constructs a SignalAdapter. name defaults to "signal"; the client gets a default 15s timeout.
func (*SignalAdapter) Deliver ¶
func (a *SignalAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content to the recipient in msg.PlatformID via the bridge's /v2/send and returns the message timestamp as the platform message id.
type SlackAdapter ¶
type SlackAdapter struct {
AdapterName string
Token string
// BaseURL defaults to defaultSlackBaseURL; overridable for tests.
BaseURL string
Client *http.Client
}
SlackAdapter is a concrete, stdlib-only Adapter that delivers an outbound message via the Slack Web API `chat.postMessage` method. It follows the TelegramAdapter shape: it sits behind the Adapter interface and adds no dependency. The channel is taken from MessageOut.PlatformID; a ThreadID maps to Slack's thread_ts so replies thread correctly. The returned platform message id is the Slack message `ts`.
SECURITY: the bot token is sent in the Authorization header (not the URL). The adapter NEVER includes the token in returned errors — error strings are redacted first — so a token cannot leak into logs even if a transport error were ever to echo a header.
func NewSlackAdapter ¶
func NewSlackAdapter(name, token string) *SlackAdapter
NewSlackAdapter constructs a SlackAdapter. name defaults to "slack"; the client gets a default 15s timeout.
func (*SlackAdapter) Deliver ¶
func (a *SlackAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content to the channel in msg.PlatformID via chat.postMessage and returns the Slack message ts as the platform message id.
type TeamsAdapter ¶
TeamsAdapter delivers an outbound message to Microsoft Teams via an Incoming Webhook URL. It is stdlib-only and follows the SlackAdapter shape. The webhook URL is per-channel and configured host-side, so a message's PlatformID is advisory only — the destination is the webhook itself. Incoming Webhooks are outbound-only (one-way), which is exactly what a delivery adapter needs.
SECURITY: the webhook URL embeds a secret token, so it is sent only in the request line (never logged) and is ALWAYS redacted from returned errors.
func NewTeamsAdapter ¶
func NewTeamsAdapter(name, webhookURL string) *TeamsAdapter
NewTeamsAdapter constructs a TeamsAdapter. name defaults to "teams"; the client gets a default 15s timeout.
func (*TeamsAdapter) Deliver ¶
func (a *TeamsAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver posts msg.Content to the configured Teams Incoming Webhook. Incoming Webhooks return HTTP 200 with the body "1" on success and do not assign a retrievable message id, so the trimmed response body is returned as the id.
type TelegramAdapter ¶
type TelegramAdapter struct {
AdapterName string
Token string
// BaseURL defaults to defaultTelegramBaseURL; overridable for tests.
BaseURL string
Client *http.Client
}
TelegramAdapter is a concrete, stdlib-only Adapter that delivers an outbound message via the Telegram Bot API `sendMessage` method. It follows the WebhookAdapter shape: it sits behind the Adapter interface and adds no dependency. The chat is taken from MessageOut.PlatformID; a numeric ThreadID maps to a forum-topic message_thread_id. The returned platform message id is the Telegram message_id.
SECURITY: the bot token is part of the request URL (Telegram's design). The adapter NEVER includes the token in returned errors — transport errors (whose url.Error carries the URL) are redacted first — so a token cannot leak into logs.
func NewTelegramAdapter ¶
func NewTelegramAdapter(name, token string) *TelegramAdapter
NewTelegramAdapter constructs a TelegramAdapter. name defaults to "telegram"; the client gets a default 15s timeout.
func (*TelegramAdapter) Deliver ¶
func (a *TelegramAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content to the chat in msg.PlatformID via sendMessage and returns the Telegram message_id as the platform message id.
func (*TelegramAdapter) Name ¶
func (a *TelegramAdapter) Name() string
Name returns the adapter name.
type WebchatAdapter ¶
type WebchatAdapter struct {
AdapterName string
// contains filtered or unexported fields
}
WebchatAdapter is an in-process channel adapter for the web console's chat playground. Unlike the platform adapters, it does not call out to an external service: an agent's outbound message destined for "webchat" is buffered in memory, keyed by conversation (MessageOut.PlatformID), and the browser polls for it via the API. It is the delivery half of feeding the normal router/delivery path; the inbound half is the API handler calling the router.
func NewWebchatAdapter ¶
func NewWebchatAdapter(name string) *WebchatAdapter
NewWebchatAdapter constructs a WebchatAdapter. name defaults to "webchat".
func (*WebchatAdapter) Deliver ¶
func (a *WebchatAdapter) Deliver(_ context.Context, msg contract.MessageOut) (string, error)
Deliver buffers the agent's outbound message for the browser to poll. The conversation is MessageOut.PlatformID; a message without one is dropped (there is no browser to route it to). The returned platform id is the local message id.
func (*WebchatAdapter) Drain ¶
func (a *WebchatAdapter) Drain(conversationID string) []WebchatMessage
Drain returns and clears the buffered replies for a conversation, so the browser polls incrementally. It never returns nil (an empty slice for none).
type WebchatMessage ¶
type WebchatMessage struct {
ID string `json:"id"`
ConversationID string `json:"conversationId"`
ThreadID string `json:"threadId,omitempty"`
Content string `json:"content"`
Timestamp string `json:"timestamp"`
}
WebchatMessage is one buffered agent reply, as the browser renders it.
type WebhookAdapter ¶
WebhookAdapter is a concrete, stdlib-only Adapter that delivers an outbound message by POSTing it as JSON to a configured URL. It is the reference HTTP-based channel: platform-specific adapters (Slack, Discord, ...) can follow the same shape. The platform message ID is taken from the response (a JSON {"id": "..."} body, or the X-Message-Id header), falling back to a synthetic id.
func NewWebhookAdapter ¶
func NewWebhookAdapter(name, url string) *WebhookAdapter
NewWebhookAdapter constructs a WebhookAdapter. name defaults to "webhook"; a nil client gets a default with a 15s timeout.
func (*WebhookAdapter) Deliver ¶
func (a *WebhookAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver POSTs msg as JSON to the configured URL.
type WhatsAppAdapter ¶
type WhatsAppAdapter struct {
AdapterName string
// Token is the WhatsApp Cloud API access token (Bearer).
Token string
// PhoneNumberID is the sender's WhatsApp phone-number id; it forms the path
// segment `{PhoneNumberID}/messages`.
PhoneNumberID string
// BaseURL defaults to defaultWhatsAppBaseURL; overridable for tests.
BaseURL string
Client *http.Client
}
WhatsAppAdapter is a concrete, stdlib-only Adapter that delivers an outbound message via the WhatsApp Cloud API (Meta Graph API) `POST {phoneNumberID}/ messages` endpoint. It follows the SlackAdapter/TelegramAdapter shape: it sits behind the Adapter interface and adds no dependency.
The recipient is taken from MessageOut.PlatformID (the wa_id / phone number in E.164 form). A non-empty MessageOut.ThreadID maps to the WhatsApp reply `context.message_id` so replies quote the prior message. The returned platform message id is the WhatsApp `wamid` from `messages[0].id`.
Beyond text, Deliver's sibling SendDocument satisfies the "send a file" capability by posting a `document` message (a hosted link). The interface only requires text Deliver, so file sending is an extra method on the concrete type rather than a contract change.
SECURITY: the access token is sent in the Authorization header (never the URL), and the adapter NEVER includes the token in returned errors — error strings are redacted first — so a token cannot leak into logs even if a transport error or upstream payload were ever to echo it.
func NewWhatsAppAdapter ¶
func NewWhatsAppAdapter(name, token, phoneNumberID string) *WhatsAppAdapter
NewWhatsAppAdapter constructs a WhatsAppAdapter. name defaults to "whatsapp"; the client gets a default 15s timeout.
func (*WhatsAppAdapter) Deliver ¶
func (a *WhatsAppAdapter) Deliver(ctx context.Context, msg contract.MessageOut) (string, error)
Deliver sends msg.Content as a text message to the recipient in msg.PlatformID and returns the WhatsApp message id (wamid) as the platform message id.
func (*WhatsAppAdapter) Name ¶
func (a *WhatsAppAdapter) Name() string
Name returns the adapter name.
func (*WhatsAppAdapter) SendDocument ¶
func (a *WhatsAppAdapter) SendDocument(ctx context.Context, to, link, filename, caption string) (string, error)
SendDocument delivers a hosted file (by link) as a WhatsApp `document` message to `to`, with an optional filename and caption, and returns the wamid. This is the adapter's "send a file" capability; it is not part of the Adapter interface (the queue carries text), so callers use the concrete type.