googlechat

package
v0.3.4 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 24 Imported by: 0

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

View Source
const DefaultBaseURL = "https://chat.googleapis.com"

DefaultBaseURL is the production Chat API endpoint.

View Source
const DefaultCertURL = "https://www.googleapis.com/service_accounts/v1/jwk/chat@system.gserviceaccount.com"

DefaultCertURL serves the JWKS Google signs inbound app events with.

View Source
const DefaultTokenURL = "https://oauth2.googleapis.com/token"

DefaultTokenURL is the production OAuth2 token endpoint.

Variables

This section is empty.

Functions

func ChunkText

func ChunkText(text string, limit int) []string

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

func FormatText(markdown string) string

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":...}}).

func (*APIError) Error

func (e *APIError) Error() string

Error implements error.

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

func New(opts Options) (*Adapter, error)

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

func (a *Adapter) Account() string

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) Platform

func (a *Adapter) Platform() string

Platform implements chat.Adapter.

func (*Adapter) Webhook

func (a *Adapter) Webhook(publish func(chat.Message) error) http.Handler

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.

Jump to

Keyboard shortcuts

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