whatsapp

package
v0.4.10 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package whatsapp implements the WhatsApp Business Cloud API adapter for the orb chat gateway: webhook ingress (hub handshake + HMAC-signed events), final-message delivery with reply threading, and authenticated media download.

The adapter speaks only the official Graph Cloud API with caller-supplied credentials. Unsigned webhooks are refused by construction: New errors without an AppSecret, matching the Hermes reference stance. The Cloud API has no message editing, so Preview is a no-op and only the final reply is sent (no fake streaming).

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

func ChunkText

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

ChunkText splits text into chunks of at most limit characters (runes), preferring paragraph breaks, then line breaks, then spaces, then a hard cut. Empty input yields no chunks.

ponytail: chunk boundaries ignore code fences — a >4096-char fence splits mid-block; accepted ceiling.

func FormatText

func FormatText(markdown string) string

FormatText converts common markdown to WhatsApp markup: **b**/__b__ → *b*, ~~s~~ → ~s~, headings → *bold* lines, [text](url) → "text (url)". Fenced code blocks are kept as triple-backtick blocks (WhatsApp renders them as monospace); the language token on the opening fence is dropped because WhatsApp would display it literally.

ponytail: line/regex transform, not a goldmark AST walk; single-asterisk and single-underscore emphasis pass through unchanged (md italic renders as WhatsApp bold/italic respectively — accepted ceiling).

func StatusRank

func StatusRank(status string) int

StatusRank orders status values so out-of-order callbacks can be reduced to the furthest-progressed state: read > delivered > sent, with the terminal "failed" above all and unknown values at 0.

Types

type Adapter

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

Adapter is a chat.Adapter for the WhatsApp Business Cloud API.

func New

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

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

func (*Adapter) Account

func (a *Adapter) Account() string

Account returns the business phone number id this adapter serves, 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: media id → metadata (url) → authenticated download. Media URLs expire after ~5 minutes, so a failed download refetches the metadata once for a fresh URL before giving up.

func (*Adapter) NewDelivery

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

NewDelivery implements chat.Adapter. resumePreviewID is ignored: the Cloud API cannot edit messages, so this 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 Cloud API webhook endpoint. GET answers the one-time subscribe handshake; POST verifies the X-Hub-Signature-256 HMAC over the raw body BEFORE parsing, publishes every normalized message, and forwards statuses[] to Options.OnStatus. A publish error yields a 500 so Meta redelivers (the wamid EventID dedupes downstream).

type GraphError

type GraphError struct {
	Code      int    `json:"code"`
	Subcode   int    `json:"error_subcode,omitempty"`
	Type      string `json:"type,omitempty"`
	Title     string `json:"title,omitempty"`
	Message   string `json:"message,omitempty"`
	ErrorData struct {
		Details string `json:"details,omitempty"`
	} `json:"error_data,omitempty"`
	FBTraceID string `json:"fbtrace_id,omitempty"`
}

GraphError is one Graph API error object, either from an HTTP error response envelope or from a statuses[].errors[] entry.

func (*GraphError) Error

func (e *GraphError) Error() string

Error implements error.

type Options

type Options struct {
	// Token is the Graph API access token (Bearer). Required.
	Token string
	// PhoneNumberID is the business phone number id calls are made on
	// behalf of. Required.
	PhoneNumberID 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
	// OnStatus receives delivery status callbacks from the statuses[]
	// webhook array. Default: no-op. Statuses arrive out of order; keep the
	// highest [StatusRank] per message id.
	OnStatus func(Status)
}

Options configures the WhatsApp Cloud API adapter.

type Status

type Status struct {
	// MessageID is the wamid of the outbound message the status refers to.
	MessageID string `json:"id"`
	// Status is "sent", "delivered", "read", or "failed".
	Status string `json:"status"`
	// Timestamp is epoch seconds, as a string (all WhatsApp timestamps are).
	Timestamp string `json:"timestamp"`
	// RecipientID is the recipient wa_id.
	RecipientID string `json:"recipient_id"`
	// Errors is populated on "failed" statuses.
	Errors []GraphError `json:"errors,omitempty"`
}

Status is one entry of the statuses[] webhook array. The lifecycle per message id is sent → delivered → read (or the terminal failed), but callbacks arrive out of order: consumers must keep the highest StatusRank seen per MessageID instead of trusting arrival order.

Jump to

Keyboard shortcuts

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