messenger

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package messenger implements the Facebook Messenger (Meta Messenger Platform for Pages) adapter for the pigo chat gateway: webhook ingress over the Graph "page" object (hub handshake + HMAC-signed entry[].messaging[] events), final-message delivery via the Send API, sender-action typing, and direct-URL attachment download.

The adapter speaks only the official Graph API with caller-supplied credentials. Unsigned webhooks are refused by construction: New errors without an AppSecret. Messenger has no message editing, so Preview is a no-op and only the final reply is sent (no fake streaming); typing is sustained with re-fired typing_on sender actions instead.

Messenger is 1:1 only — every conversation is a DM, and PSIDs are scoped per page: the conversation key is the (page id, PSID) pair carried as Account + ChatID.

Index

Constants

View Source
const GraphVersion = "v23.0"

GraphVersion is the pinned Graph API version used for every call.

Variables

This section is empty.

Functions

This section is empty.

Types

type Adapter

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

Adapter is a chat.Adapter for the Messenger Platform Send API.

func New

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

New builds the adapter. It refuses to construct without Token, PageID, AppSecret, and VerifyToken.

Webhook events only flow after a one-time subscription BOTH ways: the app subscribes to the "page" webhook object (fields: messages, messaging_postbacks, message_deliveries, message_reads, message_echoes) in the app dashboard, AND the page must be subscribed to the app via

POST /{page-id}/subscribed_apps?subscribed_fields=messages,messaging_postbacks,message_deliveries,message_reads,message_echoes&access_token=<PAGE_TOKEN>

which returns {"success":true}. A missing page subscription is the usual cause of a silently dead webhook.

func (*Adapter) Account

func (a *Adapter) Account() string

Account returns the page id this adapter serves, matching the Account set on every normalized message. PSIDs are page-scoped, so the (page id, PSID) pair — Account + ChatID — is the conversation identity.

func (*Adapter) Download

func (a *Adapter) Download(ctx context.Context, ref chat.AttachmentRef) (io.ReadCloser, string, error)

Download implements chat.Adapter. Messenger attachment refs carry the direct CDN URL from the webhook payload (there is no media-id hop as on WhatsApp): the download is a plain unauthenticated GET, deliberately sent without the page token so it never leaks to Meta's CDN. The URLs expire and cannot be refreshed, so attachments must be downloaded promptly after ingress.

func (*Adapter) NewDelivery

func (a *Adapter) NewDelivery(key chat.ConversationKey, replyTo, resumePreviewID string) chat.Delivery

NewDelivery implements chat.Adapter. replyTo and resumePreviewID are ignored: the Send API has no quoted-reply parameter and no message editing, so the adapter never creates previews and crash recovery simply resends the final text.

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 HTTP handler for the Messenger webhook endpoint. GET answers the one-time subscribe handshake; POST verifies the X-Hub-Signature-256 HMAC over the raw body BEFORE parsing (Meta signs the exact bytes sent — never a re-serialization), walks entry[].messaging[], publishes every normalized message/postback, and forwards delivery/read watermarks to Options.OnWatermark. Echoes (message.is_echo — every message the page itself sends comes back as an event) are dropped before publish so the bot never answers itself. A publish error yields a 500 so Meta redelivers (the mid EventID dedupes downstream).

The handler never waits on turn processing: publish must enqueue and return (Meta requires a 200 within ~5s and disables webhooks that keep failing).

type GraphError

type GraphError struct {
	Code      int    `json:"code"`
	Subcode   int    `json:"error_subcode,omitempty"`
	Type      string `json:"type,omitempty"`
	Message   string `json:"message,omitempty"`
	FBTraceID string `json:"fbtrace_id,omitempty"`
	// RegainAfter is the throttle-recovery hint parsed from the
	// X-Business-Use-Case-Usage response header
	// (estimated_time_to_regain_access, reported in minutes); zero when the
	// header is absent or carries no hint.
	RegainAfter time.Duration `json:"-"`
}

GraphError is one Graph API error object from an HTTP error response envelope.

func (*GraphError) Error

func (e *GraphError) Error() string

Error implements error.

type Options

type Options struct {
	// Token is the long-lived Page Access Token (Bearer). Required.
	Token string
	// PageID is the Facebook Page the token belongs to. It becomes the
	// adapter account identity; PSIDs are scoped to it. Required.
	PageID string
	// AppSecret signs inbound webhooks (X-Hub-Signature-256). Required:
	// unsigned webhooks are not an option.
	AppSecret string
	// VerifyToken answers the one-time GET subscribe handshake. Required.
	VerifyToken string
	// BaseURL overrides the Graph endpoint (tests). Default
	// https://graph.facebook.com.
	BaseURL string
	// HTTPClient overrides the HTTP client. Default: 30s timeout.
	HTTPClient *http.Client
	// OnWatermark receives delivery/read watermark callbacks from the
	// message_deliveries and message_reads webhook fields. Default: no-op.
	// Watermarks are telemetry, not per-message state: everything the page
	// sent before the watermark timestamp is delivered/read.
	OnWatermark func(Watermark)
	// TypingInterval is the typing_on re-fire cadence while a turn is
	// generating (the indicator auto-expires after ~20s). Default 15s.
	TypingInterval time.Duration
	// Logger receives non-fatal diagnostics (failed sender actions).
	// Optional.
	Logger *slog.Logger
}

Options configures the Messenger adapter.

type Watermark

type Watermark struct {
	// Kind is "delivery" or "read".
	Kind string
	// PageID is the page the receipt belongs to.
	PageID string
	// PSID is the page-scoped id of the user who received/read.
	PSID string
	// Watermark is the epoch-millisecond cutoff: everything sent before it
	// is delivered/read.
	Watermark int64
	// MIDs lists the delivered message ids; delivery-only and often absent.
	MIDs []string
}

Watermark is one delivery/read receipt from the message_deliveries or message_reads webhook fields. Messenger receipts are watermarks, not per-message state: every message the page sent before the watermark timestamp is delivered (or read). Treat them as telemetry only.

Jump to

Keyboard shortcuts

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