Documentation
¶
Overview ¶
Package googlechat implements the Google Chat adapter for the pigo chat gateway: an HTTP-endpoint Chat app with bearer-JWT-verified ingress, service-account authenticated delivery, and authenticated media download.
Inbound interaction events are verified against Google's published JWKS (issuer chat@system.gserviceaccount.com, audience = the numeric project number) before parsing; New refuses to construct without the audience or the service-account key, so neither trust boundary is skippable. Replies are always asynchronous via spaces.messages.create — the synchronous 200-body reply is deliberately never used. Final messages use client-assigned ids derived from the inbound event, making delivery idempotent and crash-safe. Google Chat has no typing indicator, so Typing is a no-op; D28 also keeps Preview final-only.
Index ¶
- Constants
- func ChunkText(text string, limit int) []string
- func FormatText(markdown string) string
- type APIError
- type Adapter
- func (a *Adapter) Account() string
- func (a *Adapter) Download(ctx context.Context, ref chat.AttachmentRef) (io.ReadCloser, string, error)
- func (a *Adapter) NewDelivery(key chat.ConversationKey, replyTo string, resumePreviewID string) chat.Delivery
- func (a *Adapter) Platform() string
- func (a *Adapter) Webhook(publish func(chat.Message) error) http.Handler
- type Options
Constants ¶
const DefaultBaseURL = "https://chat.googleapis.com"
DefaultBaseURL is the production Chat API endpoint.
const DefaultCertURL = "https://www.googleapis.com/service_accounts/v1/jwk/chat@system.gserviceaccount.com"
DefaultCertURL serves the JWKS Google signs inbound app events with.
const DefaultTokenURL = "https://oauth2.googleapis.com/token"
DefaultTokenURL is the production OAuth2 token endpoint.
Variables ¶
This section is empty.
Functions ¶
func ChunkText ¶
ChunkText splits text into chunks of at most limit characters, preferring line boundaries and never splitting inside a code fence: the fence is closed at the chunk edge and reopened in the next chunk. A single line longer than the budget is hard-cut. Empty input yields no chunks.
func FormatText ¶
FormatText converts common markdown to Google Chat's dialect: **b**/__b__ → *b*, *i* → _i_, ~~s~~ → ~s~, [text](url) → <url|text>, headings → *bold* lines. Fenced code blocks are kept as triple-backtick blocks; the language token on the opening fence is dropped because Chat would display it literally. Lists, quotes, inline code, and plain underscores pass through unchanged (Chat renders them natively).
ponytail: line/regex transform, not a goldmark AST walk; markup inside inline code spans is rewritten too — accepted ceiling, matching the WhatsApp adapter.
Types ¶
type APIError ¶
type APIError struct {
// HTTPStatus is the HTTP status code of the response.
HTTPStatus int
// Code is the numeric code from the error envelope (matches HTTPStatus
// in practice).
Code int
// Status is the canonical gRPC status name, e.g. "NOT_FOUND",
// "ALREADY_EXISTS", "RESOURCE_EXHAUSTED".
Status string
// Message is the server-provided description.
Message string
// RetryAfter is the Retry-After header delay when the server sent one.
RetryAfter time.Duration
}
APIError is one Chat API error envelope ({"error":{"code":...,"message":...,"status":...}}).
type Adapter ¶
type Adapter struct {
// contains filtered or unexported fields
}
Adapter is the Google Chat implementation of chat.Adapter. Final and notification writes are serialized per space at the Chat API's 1/s quota.
func New ¶
New builds the adapter. It refuses to construct without ProjectNumber (inbound events could not be verified) or a parseable service-account key (outbound calls could not be authenticated). It performs no network calls.
func (*Adapter) Account ¶
Account returns the numeric project number — the Chat app's identity — matching the Account set on every normalized message.
func (*Adapter) Download ¶
func (a *Adapter) Download(ctx context.Context, ref chat.AttachmentRef) (io.ReadCloser, string, error)
Download implements chat.Adapter: an authenticated GET /v1/media/{resourceName}?alt=media for UPLOADED_CONTENT attachments (the only kind ingress emits refs for; Drive-sourced files are not downloadable with the chat.bot scope and are surfaced as text notes).
func (*Adapter) NewDelivery ¶
func (a *Adapter) NewDelivery(key chat.ConversationKey, replyTo string, resumePreviewID string) chat.Delivery
NewDelivery implements chat.Adapter. replyTo is the inbound message.name; the first final-message id is derived from it, so a crashed turn's retry addresses the same client-assigned message. Google Chat is final-only, so resumePreviewID is ignored.
func (*Adapter) Webhook ¶
Webhook returns the interaction-event endpoint handler. Every POST is authenticated by its bearer JWT (401 on failure) before the body is trusted. MESSAGE events are normalized and published; every other event type is acknowledged and ignored. The success response is always an empty 200 — replies happen asynchronously. A publish failure answers 500 so Google redelivers (the message.name EventID dedupes downstream).
type Options ¶
type Options struct {
// ProjectNumber is the numeric GCP project number (NOT the project id).
// It is the required audience of every inbound event JWT. Required.
ProjectNumber string
// CredentialsJSON is the downloaded service-account key file
// (client_email + private_key). It signs the outbound JWT-bearer
// assertion for the chat.bot scope. Required.
CredentialsJSON []byte
// BaseURL overrides the Chat API endpoint (tests). Default
// [DefaultBaseURL].
BaseURL string
// TokenURL overrides the OAuth2 token endpoint (tests). It is also the
// aud claim of the signed assertion. Default [DefaultTokenURL].
TokenURL string
// CertURL overrides where the inbound-event JWKS is fetched from
// (tests). Default [DefaultCertURL].
CertURL string
// HTTPClient overrides the HTTP client. Default: 30s timeout.
HTTPClient *http.Client
// Logger receives non-fatal diagnostics. Optional.
Logger *slog.Logger
}
Options configures New.