@chat-adapter/whatsapp 4.21.0 → 4.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -206,6 +206,7 @@ declare function splitMessage(text: string): string[];
206
206
  */
207
207
  declare class WhatsAppAdapter implements Adapter<WhatsAppThreadId, WhatsAppRawMessage> {
208
208
  readonly name = "whatsapp";
209
+ readonly lockScope: "channel";
209
210
  readonly persistMessageHistory = true;
210
211
  readonly userName: string;
211
212
  private readonly accessToken;
package/dist/index.js CHANGED
@@ -347,6 +347,7 @@ function splitMessage(text) {
347
347
  }
348
348
  var WhatsAppAdapter = class {
349
349
  name = "whatsapp";
350
+ lockScope = "channel";
350
351
  persistMessageHistory = true;
351
352
  userName;
352
353
  accessToken;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { extractCard, ValidationError } from \"@chat-adapter/shared\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Attachment,\n Author,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FormattedContent,\n Logger,\n RawMessage,\n ReactionEvent,\n StreamChunk,\n StreamOptions,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport {\n ConsoleLogger,\n convertEmojiPlaceholders,\n defaultEmojiResolver,\n getEmoji,\n Message,\n} from \"chat\";\nimport { cardToWhatsApp, decodeWhatsAppCallbackData } from \"./cards\";\nimport { WhatsAppFormatConverter } from \"./markdown\";\nimport type {\n WhatsAppAdapterConfig,\n WhatsAppContact,\n WhatsAppInboundMessage,\n WhatsAppInteractiveMessage,\n WhatsAppMediaResponse,\n WhatsAppRawMessage,\n WhatsAppSendResponse,\n WhatsAppThreadId,\n WhatsAppWebhookPayload,\n} from \"./types\";\n\n/** Default Graph API version */\nconst DEFAULT_API_VERSION = \"v21.0\";\n\n/** Maximum message length for WhatsApp Cloud API */\nconst WHATSAPP_MESSAGE_LIMIT = 4096;\n\n/**\n * Split text into chunks that fit within WhatsApp's message limit,\n * breaking on paragraph boundaries (\\n\\n) when possible, then line\n * boundaries (\\n), and finally at the character limit as a last resort.\n */\nexport function splitMessage(text: string): string[] {\n if (text.length <= WHATSAPP_MESSAGE_LIMIT) {\n return [text];\n }\n\n const chunks: string[] = [];\n let remaining = text;\n\n while (remaining.length > WHATSAPP_MESSAGE_LIMIT) {\n const slice = remaining.slice(0, WHATSAPP_MESSAGE_LIMIT);\n\n // Try to break at a paragraph boundary\n let breakIndex = slice.lastIndexOf(\"\\n\\n\");\n if (breakIndex === -1 || breakIndex < WHATSAPP_MESSAGE_LIMIT / 2) {\n // Try a line boundary\n breakIndex = slice.lastIndexOf(\"\\n\");\n }\n if (breakIndex === -1 || breakIndex < WHATSAPP_MESSAGE_LIMIT / 2) {\n // Hard break at the limit\n breakIndex = WHATSAPP_MESSAGE_LIMIT;\n }\n\n chunks.push(remaining.slice(0, breakIndex).trimEnd());\n remaining = remaining.slice(breakIndex).trimStart();\n }\n\n if (remaining.length > 0) {\n chunks.push(remaining);\n }\n\n return chunks;\n}\n\n// Re-export types\nexport type {\n WhatsAppAdapterConfig,\n WhatsAppMediaResponse,\n WhatsAppRawMessage,\n WhatsAppThreadId,\n} from \"./types\";\n\n/**\n * WhatsApp adapter for chat SDK.\n *\n * Supports messaging via the WhatsApp Business Cloud API (Meta Graph API).\n * All conversations are 1:1 DMs between the business phone number and users.\n *\n * @example\n * ```typescript\n * import { Chat } from \"chat\";\n * import { createWhatsAppAdapter } from \"@chat-adapter/whatsapp\";\n * import { MemoryState } from \"@chat-adapter/state-memory\";\n *\n * const chat = new Chat({\n * userName: \"my-bot\",\n * adapters: {\n * whatsapp: createWhatsAppAdapter(),\n * },\n * state: new MemoryState(),\n * });\n * ```\n */\nexport class WhatsAppAdapter\n implements Adapter<WhatsAppThreadId, WhatsAppRawMessage>\n{\n readonly name = \"whatsapp\";\n readonly persistMessageHistory = true;\n readonly userName: string;\n\n private readonly accessToken: string;\n private readonly appSecret: string;\n private readonly phoneNumberId: string;\n private readonly verifyToken: string;\n private readonly graphApiUrl: string;\n private chat: ChatInstance | null = null;\n private readonly logger: Logger;\n private _botUserId: string | null = null;\n private readonly formatConverter = new WhatsAppFormatConverter();\n\n /** Bot user ID used for self-message detection */\n get botUserId(): string | undefined {\n return this._botUserId ?? undefined;\n }\n\n constructor(config: WhatsAppAdapterConfig) {\n this.accessToken = config.accessToken;\n this.appSecret = config.appSecret;\n this.phoneNumberId = config.phoneNumberId;\n this.verifyToken = config.verifyToken;\n this.logger = config.logger;\n this.userName = config.userName;\n const apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;\n this.graphApiUrl = `https://graph.facebook.com/${apiVersion}`;\n }\n\n /**\n * Initialize the adapter and fetch business profile info.\n */\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n\n // The bot's \"user ID\" is the phone number ID\n this._botUserId = this.phoneNumberId;\n this.logger.info(\"WhatsApp adapter initialized\", {\n phoneNumberId: this.phoneNumberId,\n });\n }\n\n /**\n * Handle incoming webhook from WhatsApp.\n *\n * Handles both the GET verification challenge and POST event notifications.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/guides/set-up-webhooks\n */\n async handleWebhook(\n request: Request,\n options?: WebhookOptions\n ): Promise<Response> {\n // Handle webhook verification challenge (GET request)\n if (request.method === \"GET\") {\n return this.handleVerificationChallenge(request);\n }\n\n const body = await request.text();\n this.logger.debug(\"WhatsApp webhook raw body\", {\n body: body.substring(0, 500),\n });\n\n // Verify request signature (X-Hub-Signature-256 header)\n const signature = request.headers.get(\"x-hub-signature-256\");\n if (!this.verifySignature(body, signature)) {\n return new Response(\"Invalid signature\", { status: 401 });\n }\n\n // Parse the JSON payload\n let payload: WhatsAppWebhookPayload;\n try {\n payload = JSON.parse(body);\n } catch {\n this.logger.error(\"WhatsApp webhook invalid JSON\", {\n contentType: request.headers.get(\"content-type\"),\n bodyPreview: body.substring(0, 200),\n });\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n // Process entries\n for (const entry of payload.entry) {\n for (const change of entry.changes) {\n if (change.field !== \"messages\") {\n continue;\n }\n\n const { value } = change;\n\n // Process incoming messages\n if (value.messages) {\n for (const message of value.messages) {\n try {\n this.handleInboundMessage(\n message,\n value.contacts?.[0],\n value.metadata.phone_number_id,\n options\n );\n } catch (error) {\n this.logger.error(\"Failed to handle inbound message\", {\n messageId: message.id,\n error,\n });\n }\n }\n }\n }\n }\n\n return new Response(\"ok\", { status: 200 });\n }\n\n /**\n * Handle the webhook verification challenge from Meta.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/guides/set-up-webhooks\n */\n private handleVerificationChallenge(request: Request): Response {\n const url = new URL(request.url);\n const mode = url.searchParams.get(\"hub.mode\");\n const token = url.searchParams.get(\"hub.verify_token\");\n const challenge = url.searchParams.get(\"hub.challenge\");\n\n if (mode === \"subscribe\" && token === this.verifyToken) {\n this.logger.info(\"WhatsApp webhook verification succeeded\");\n return new Response(challenge ?? \"\", { status: 200 });\n }\n\n this.logger.warn(\"WhatsApp webhook verification failed\", {\n mode,\n tokenMatch: token === this.verifyToken,\n });\n return new Response(\"Forbidden\", { status: 403 });\n }\n\n /**\n * Verify webhook signature using HMAC-SHA256 with the App Secret.\n *\n * @see https://developers.facebook.com/docs/graph-api/webhooks/getting-started#verification-requests\n */\n private verifySignature(body: string, signature: string | null): boolean {\n if (!signature) {\n return false;\n }\n\n const expectedSignature = `sha256=${createHmac(\"sha256\", this.appSecret).update(body).digest(\"hex\")}`;\n\n try {\n return timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature)\n );\n } catch {\n return false;\n }\n }\n\n /**\n * Handle an inbound message from a user.\n */\n private handleInboundMessage(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n phoneNumberId: string,\n options?: WebhookOptions\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring message\");\n return;\n }\n\n // Handle reactions separately\n if (inbound.type === \"reaction\" && inbound.reaction) {\n this.handleReaction(inbound, contact, phoneNumberId, options);\n return;\n }\n\n // Handle interactive message replies (button clicks)\n if (inbound.type === \"interactive\" && inbound.interactive) {\n this.handleInteractiveReply(inbound, contact, phoneNumberId, options);\n return;\n }\n\n // Handle legacy button responses (from template quick replies)\n if (inbound.type === \"button\" && inbound.button) {\n this.handleButtonResponse(inbound, contact, phoneNumberId, options);\n return;\n }\n\n // Extract text content based on message type\n const text = this.extractTextContent(inbound);\n if (text === null) {\n this.logger.debug(\"Unsupported message type, ignoring\", {\n type: inbound.type,\n messageId: inbound.id,\n });\n return;\n }\n\n const threadId = this.encodeThreadId({\n phoneNumberId,\n userWaId: inbound.from,\n });\n\n const message = this.buildMessage(\n inbound,\n contact,\n threadId,\n text,\n phoneNumberId\n );\n this.chat.processMessage(this, threadId, message, options);\n }\n\n /**\n * Handle reaction events.\n */\n private handleReaction(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n phoneNumberId: string,\n options?: WebhookOptions\n ): void {\n if (!(this.chat && inbound.reaction)) {\n return;\n }\n\n const threadId = this.encodeThreadId({\n phoneNumberId,\n userWaId: inbound.from,\n });\n\n const rawEmoji = inbound.reaction.emoji;\n // Empty emoji means reaction was removed\n const added = rawEmoji !== \"\";\n const emojiValue = added ? getEmoji(rawEmoji) : getEmoji(\"\");\n\n const user: Author = {\n userId: inbound.from,\n userName: contact?.profile.name || inbound.from,\n fullName: contact?.profile.name || inbound.from,\n isBot: false,\n isMe: false,\n };\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added,\n user,\n messageId: inbound.reaction.message_id,\n threadId,\n raw: inbound,\n };\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n\n /**\n * Handle interactive message replies (button/list selection).\n */\n private handleInteractiveReply(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n phoneNumberId: string,\n options?: WebhookOptions\n ): void {\n if (!(this.chat && inbound.interactive)) {\n return;\n }\n\n const threadId = this.encodeThreadId({\n phoneNumberId,\n userWaId: inbound.from,\n });\n\n const { interactive } = inbound;\n let rawId: string;\n let fallbackValue: string;\n\n if (interactive.type === \"button_reply\" && interactive.button_reply) {\n rawId = interactive.button_reply.id;\n fallbackValue = interactive.button_reply.title;\n } else if (interactive.type === \"list_reply\" && interactive.list_reply) {\n rawId = interactive.list_reply.id;\n fallbackValue = interactive.list_reply.title;\n } else {\n return;\n }\n\n const { actionId, value } = decodeWhatsAppCallbackData(rawId);\n\n this.chat.processAction(\n {\n adapter: this,\n actionId,\n value: value ?? fallbackValue,\n user: {\n userId: inbound.from,\n userName: contact?.profile.name || inbound.from,\n fullName: contact?.profile.name || inbound.from,\n isBot: false,\n isMe: false,\n },\n messageId: inbound.id,\n threadId,\n raw: inbound,\n },\n options\n );\n }\n\n /**\n * Handle legacy button responses (from template quick replies).\n */\n private handleButtonResponse(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n phoneNumberId: string,\n options?: WebhookOptions\n ): void {\n if (!(this.chat && inbound.button)) {\n return;\n }\n\n const threadId = this.encodeThreadId({\n phoneNumberId,\n userWaId: inbound.from,\n });\n\n this.chat.processAction(\n {\n adapter: this,\n actionId: inbound.button.payload,\n value: inbound.button.text,\n user: {\n userId: inbound.from,\n userName: contact?.profile.name || inbound.from,\n fullName: contact?.profile.name || inbound.from,\n isBot: false,\n isMe: false,\n },\n messageId: inbound.id,\n threadId,\n raw: inbound,\n },\n options\n );\n }\n\n /**\n * Extract text content from an inbound message.\n * Returns null for unsupported message types.\n */\n private extractTextContent(message: WhatsAppInboundMessage): string | null {\n switch (message.type) {\n case \"text\":\n return message.text?.body ?? null;\n case \"image\":\n return message.image?.caption ?? \"[Image]\";\n case \"document\":\n return (\n message.document?.caption ??\n `[Document: ${message.document?.filename ?? \"file\"}]`\n );\n case \"audio\":\n return \"[Audio message]\";\n case \"voice\":\n return \"[Voice message]\";\n case \"video\":\n return \"[Video]\";\n case \"sticker\":\n return \"[Sticker]\";\n case \"location\": {\n const loc = message.location;\n if (loc) {\n const parts = [`[Location: ${loc.latitude}, ${loc.longitude}`];\n if (loc.name) {\n parts[0] = `[Location: ${loc.name}`;\n }\n if (loc.address) {\n parts.push(loc.address);\n }\n return `${parts.join(\" - \")}]`;\n }\n return \"[Location]\";\n }\n default:\n return null;\n }\n }\n\n /**\n * Build a Message from a WhatsApp inbound message.\n */\n private buildMessage(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n threadId: string,\n text: string,\n phoneNumberId?: string\n ): Message<WhatsAppRawMessage> {\n const author: Author = {\n userId: inbound.from,\n userName: contact?.profile.name || inbound.from,\n fullName: contact?.profile.name || inbound.from,\n isBot: false,\n isMe: false,\n };\n\n const formatted: FormattedContent = this.formatConverter.toAst(text);\n\n const raw: WhatsAppRawMessage = {\n message: inbound,\n contact,\n phoneNumberId: phoneNumberId || this.phoneNumberId,\n };\n\n const attachments = this.buildAttachments(inbound);\n\n return new Message<WhatsAppRawMessage>({\n id: inbound.id,\n threadId,\n text,\n formatted,\n raw,\n author,\n metadata: {\n dateSent: new Date(Number.parseInt(inbound.timestamp, 10) * 1000),\n edited: false,\n },\n attachments,\n });\n }\n\n /**\n * Build attachments from an inbound message.\n */\n private buildAttachments(inbound: WhatsAppInboundMessage): Attachment[] {\n const attachments: Attachment[] = [];\n\n if (inbound.image) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.image.id,\n \"image\",\n inbound.image.mime_type\n )\n );\n }\n\n if (inbound.document) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.document.id,\n \"file\",\n inbound.document.mime_type,\n inbound.document.filename\n )\n );\n }\n\n if (inbound.audio) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.audio.id,\n \"audio\",\n inbound.audio.mime_type\n )\n );\n }\n\n if (inbound.video) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.video.id,\n \"video\",\n inbound.video.mime_type\n )\n );\n }\n\n if (inbound.voice) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.voice.id,\n \"audio\",\n inbound.voice.mime_type,\n \"voice\"\n )\n );\n }\n\n if (inbound.sticker) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.sticker.id,\n \"image\",\n inbound.sticker.mime_type,\n \"sticker\"\n )\n );\n }\n\n if (inbound.location) {\n const loc = inbound.location;\n const lat = Number(loc.latitude);\n const lng = Number(loc.longitude);\n if (Number.isFinite(lat) && Number.isFinite(lng)) {\n const mapUrl = `https://www.google.com/maps?q=${lat},${lng}`;\n attachments.push({\n type: \"file\",\n name: loc.name || \"Location\",\n url: mapUrl,\n mimeType: \"application/geo+json\",\n });\n }\n }\n\n return attachments;\n }\n\n /**\n * Build a single media attachment with a lazy fetchData function.\n */\n private buildMediaAttachment(\n mediaId: string,\n type: Attachment[\"type\"],\n mimeType: string,\n name?: string\n ): Attachment {\n return {\n type,\n mimeType,\n name,\n fetchData: () => this.downloadMedia(mediaId),\n };\n }\n\n /**\n * Download media from WhatsApp.\n *\n * WhatsApp media is fetched in two steps:\n * 1. GET the media metadata to obtain the download URL\n * 2. GET the actual binary data from the download URL\n *\n * @param mediaId - The media ID from the inbound message\n * @returns The media data as a Buffer\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#download-media\n */\n async downloadMedia(mediaId: string): Promise<Buffer> {\n // Step 1: Get the media URL\n const metaResponse = await fetch(`${this.graphApiUrl}/${mediaId}`, {\n headers: { Authorization: `Bearer ${this.accessToken}` },\n });\n\n if (!metaResponse.ok) {\n const errorBody = await metaResponse.text();\n this.logger.error(\"Failed to get media URL\", {\n status: metaResponse.status,\n body: errorBody,\n mediaId,\n });\n throw new Error(\n `Failed to get media URL: ${metaResponse.status} ${errorBody}`\n );\n }\n\n const mediaInfo: WhatsAppMediaResponse =\n (await metaResponse.json()) as WhatsAppMediaResponse;\n\n // Step 2: Download the actual file\n const dataResponse = await fetch(mediaInfo.url, {\n headers: { Authorization: `Bearer ${this.accessToken}` },\n });\n\n if (!dataResponse.ok) {\n this.logger.error(\"Failed to download media\", {\n status: dataResponse.status,\n mediaId,\n });\n throw new Error(`Failed to download media: ${dataResponse.status}`);\n }\n\n const arrayBuffer = await dataResponse.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n\n /**\n * Send a message to a WhatsApp user.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages\n */\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n const { userWaId } = this.decodeThreadId(threadId);\n\n // Check if this is a card with interactive buttons\n const card = extractCard(message);\n if (card) {\n const result = cardToWhatsApp(card);\n if (result.type === \"interactive\") {\n // Convert emoji placeholders in interactive message fields\n const interactive = JSON.parse(\n convertEmojiPlaceholders(\n JSON.stringify(result.interactive),\n \"whatsapp\"\n )\n );\n return this.sendInteractiveMessage(threadId, userWaId, interactive);\n }\n return this.sendTextMessage(\n threadId,\n userWaId,\n convertEmojiPlaceholders(result.text, \"whatsapp\")\n );\n }\n\n // Regular text message\n const body = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"whatsapp\"\n );\n return this.sendTextMessage(threadId, userWaId, body);\n }\n\n /**\n * Split text into chunks that fit within WhatsApp's message limit,\n * breaking on paragraph boundaries (\\n\\n) when possible, then line\n * boundaries (\\n), and finally at the character limit as a last resort.\n */\n splitMessage(text: string): string[] {\n return splitMessage(text);\n }\n\n /**\n * Send a single text message via the Cloud API (must be within the\n * 4096-character limit).\n */\n private async sendSingleTextMessage(\n threadId: string,\n to: string,\n text: string\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n const response = await this.graphApiRequest<WhatsAppSendResponse>(\n `/${this.phoneNumberId}/messages`,\n {\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to,\n type: \"text\",\n text: { preview_url: false, body: text },\n }\n );\n\n if (!(response.messages?.length && response.messages[0]?.id)) {\n throw new Error(\n \"WhatsApp API did not return a message ID for text message\"\n );\n }\n const messageId = response.messages[0].id;\n\n return {\n id: messageId,\n threadId,\n raw: {\n message: {\n id: messageId,\n from: this.phoneNumberId,\n timestamp: String(Math.floor(Date.now() / 1000)),\n type: \"text\",\n text: { body: text },\n },\n phoneNumberId: this.phoneNumberId,\n },\n };\n }\n\n /**\n * Send a text message, splitting into multiple messages if it exceeds\n * WhatsApp's 4096-character limit. Returns the last message sent.\n */\n private async sendTextMessage(\n threadId: string,\n to: string,\n text: string\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n const chunks = this.splitMessage(text);\n let result: RawMessage<WhatsAppRawMessage> | undefined;\n\n for (const chunk of chunks) {\n result = await this.sendSingleTextMessage(threadId, to, chunk);\n }\n\n return result as RawMessage<WhatsAppRawMessage>;\n }\n\n /**\n * Send an interactive message (buttons or list) via the Cloud API.\n */\n private async sendInteractiveMessage(\n threadId: string,\n to: string,\n interactive: WhatsAppInteractiveMessage\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n const response = await this.graphApiRequest<WhatsAppSendResponse>(\n `/${this.phoneNumberId}/messages`,\n {\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to,\n type: \"interactive\",\n interactive,\n }\n );\n\n if (!(response.messages?.length && response.messages[0]?.id)) {\n throw new Error(\n \"WhatsApp API did not return a message ID for interactive message\"\n );\n }\n const messageId = response.messages[0].id;\n\n return {\n id: messageId,\n threadId,\n raw: {\n message: {\n id: messageId,\n from: this.phoneNumberId,\n timestamp: String(Math.floor(Date.now() / 1000)),\n type: \"interactive\",\n },\n phoneNumberId: this.phoneNumberId,\n },\n };\n }\n\n /**\n * Edit a message. Not supported by WhatsApp Cloud API — throws an error.\n *\n * Callers should use postMessage directly if they want to send a follow-up.\n */\n async editMessage(\n _threadId: string,\n _messageId: string,\n _message: AdapterPostableMessage\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n throw new Error(\n \"WhatsApp does not support editing messages. Use postMessage to send a new message instead.\"\n );\n }\n\n /**\n * Stream a message by buffering all chunks and sending as a single message.\n * WhatsApp doesn't support message editing, so we can't do incremental updates.\n */\n async stream(\n threadId: string,\n textStream: AsyncIterable<string | StreamChunk>,\n _options?: StreamOptions\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n let accumulated = \"\";\n for await (const chunk of textStream) {\n if (typeof chunk === \"string\") {\n accumulated += chunk;\n } else if (chunk.type === \"markdown_text\") {\n accumulated += chunk.text;\n }\n }\n return this.postMessage(threadId, { markdown: accumulated });\n }\n\n /**\n * Delete a message. Not supported by WhatsApp Cloud API — throws an error.\n */\n async deleteMessage(_threadId: string, _messageId: string): Promise<void> {\n throw new Error(\"WhatsApp does not support deleting messages.\");\n }\n\n /**\n * Add a reaction to a message.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/reaction-messages\n */\n async addReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void> {\n const { userWaId } = this.decodeThreadId(threadId);\n const emojiStr = this.resolveEmoji(emoji);\n\n await this.graphApiRequest(`/${this.phoneNumberId}/messages`, {\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to: userWaId,\n type: \"reaction\",\n reaction: {\n message_id: messageId,\n emoji: emojiStr,\n },\n });\n }\n\n /**\n * Remove a reaction from a message.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/reaction-messages\n */\n async removeReaction(\n threadId: string,\n messageId: string,\n _emoji: EmojiValue | string\n ): Promise<void> {\n const { userWaId } = this.decodeThreadId(threadId);\n\n // WhatsApp removes reactions by sending an empty emoji\n await this.graphApiRequest(`/${this.phoneNumberId}/messages`, {\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to: userWaId,\n type: \"reaction\",\n reaction: {\n message_id: messageId,\n emoji: \"\",\n },\n });\n }\n\n /**\n * Start typing indicator.\n *\n * WhatsApp supports typing indicators via the messages endpoint.\n * The indicator displays for up to 25 seconds or until the next message.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/mark-messages-as-read\n */\n async startTyping(_threadId: string, _status?: string): Promise<void> {\n // WhatsApp Cloud API does not support typing indicators.\n }\n\n /**\n * Fetch messages. Not supported by WhatsApp Cloud API.\n *\n * WhatsApp does not provide an API to retrieve message history.\n */\n async fetchMessages(\n _threadId: string,\n _options?: FetchOptions\n ): Promise<FetchResult<WhatsAppRawMessage>> {\n this.logger.debug(\n \"fetchMessages not supported on WhatsApp - message history is not available via Cloud API\"\n );\n return { messages: [] };\n }\n\n /**\n * Fetch thread info.\n */\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { phoneNumberId, userWaId } = this.decodeThreadId(threadId);\n\n return {\n id: threadId,\n channelId: `whatsapp:${phoneNumberId}`,\n channelName: `WhatsApp: ${userWaId}`,\n isDM: true,\n metadata: { phoneNumberId, userWaId },\n };\n }\n\n /**\n * Encode a WhatsApp thread ID.\n *\n * Format: whatsapp:{phoneNumberId}:{userWaId}\n */\n encodeThreadId(platformData: WhatsAppThreadId): string {\n return `whatsapp:${platformData.phoneNumberId}:${platformData.userWaId}`;\n }\n\n /**\n * Decode a WhatsApp thread ID.\n *\n * Format: whatsapp:{phoneNumberId}:{userWaId}\n */\n decodeThreadId(threadId: string): WhatsAppThreadId {\n if (!threadId.startsWith(\"whatsapp:\")) {\n throw new ValidationError(\n \"whatsapp\",\n `Invalid WhatsApp thread ID: ${threadId}`\n );\n }\n\n const withoutPrefix = threadId.slice(9);\n if (!withoutPrefix) {\n throw new ValidationError(\n \"whatsapp\",\n `Invalid WhatsApp thread ID format: ${threadId}`\n );\n }\n\n const parts = withoutPrefix.split(\":\");\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n throw new ValidationError(\n \"whatsapp\",\n `Invalid WhatsApp thread ID format: ${threadId}`\n );\n }\n\n return {\n phoneNumberId: parts[0],\n userWaId: parts[1],\n };\n }\n\n /**\n * Derive channel ID from a WhatsApp thread ID.\n * On WhatsApp every conversation is a 1:1 DM, so channel === thread.\n */\n channelIdFromThreadId(threadId: string): string {\n return threadId;\n }\n\n /**\n * All WhatsApp conversations are DMs.\n */\n isDM(_threadId: string): boolean {\n return true;\n }\n\n /**\n * Open a DM with a user. Returns the thread ID for the conversation.\n *\n * For WhatsApp, this simply constructs the thread ID since all\n * conversations are inherently DMs. Note: you can only message users\n * who have messaged you first (within the 24-hour window) or\n * via approved template messages.\n */\n async openDM(userId: string): Promise<string> {\n return this.encodeThreadId({\n phoneNumberId: this.phoneNumberId,\n userWaId: userId,\n });\n }\n\n /**\n * Parse platform message format to normalized format.\n */\n parseMessage(raw: WhatsAppRawMessage): Message<WhatsAppRawMessage> {\n const text = this.extractTextContent(raw.message) || \"\";\n const formatted: FormattedContent = this.formatConverter.toAst(text);\n const attachments = this.buildAttachments(raw.message);\n const threadId = this.encodeThreadId({\n phoneNumberId: raw.phoneNumberId,\n userWaId: raw.message.from,\n });\n\n return new Message<WhatsAppRawMessage>({\n id: raw.message.id,\n threadId,\n text,\n formatted,\n author: {\n userId: raw.message.from,\n userName: raw.contact?.profile.name || raw.message.from,\n fullName: raw.contact?.profile.name || raw.message.from,\n isBot: false,\n isMe: raw.message.from === this._botUserId,\n },\n metadata: {\n dateSent: new Date(Number.parseInt(raw.message.timestamp, 10) * 1000),\n edited: false,\n },\n attachments,\n raw,\n });\n }\n\n /**\n * Render formatted content to WhatsApp markdown.\n */\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n\n /**\n * Mark an inbound message as read.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/mark-messages-as-read\n */\n async markAsRead(messageId: string): Promise<void> {\n await this.graphApiRequest(`/${this.phoneNumberId}/messages`, {\n messaging_product: \"whatsapp\",\n status: \"read\",\n message_id: messageId,\n });\n }\n\n // =============================================================================\n // Private helpers\n // =============================================================================\n\n /**\n * Make a request to the Meta Graph API.\n */\n private async graphApiRequest<T = unknown>(\n path: string,\n body: unknown\n ): Promise<T> {\n const response = await fetch(`${this.graphApiUrl}${path}`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n this.logger.error(\"WhatsApp API error\", {\n status: response.status,\n body: errorBody,\n path,\n });\n throw new Error(`WhatsApp API error: ${response.status} ${errorBody}`);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Resolve an emoji value to a unicode string.\n */\n private resolveEmoji(emoji: EmojiValue | string): string {\n return defaultEmojiResolver.toGChat(emoji);\n }\n}\n\n/**\n * Factory function to create a WhatsApp adapter.\n *\n * @example\n * ```typescript\n * const adapter = createWhatsAppAdapter({\n * accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,\n * appSecret: process.env.WHATSAPP_APP_SECRET!,\n * phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID!,\n * verifyToken: process.env.WHATSAPP_VERIFY_TOKEN!,\n * });\n * ```\n */\nexport function createWhatsAppAdapter(config?: {\n accessToken?: string;\n apiVersion?: string;\n appSecret?: string;\n logger?: Logger;\n phoneNumberId?: string;\n userName?: string;\n verifyToken?: string;\n}): WhatsAppAdapter {\n const logger = config?.logger ?? new ConsoleLogger(\"info\").child(\"whatsapp\");\n\n const accessToken = config?.accessToken ?? process.env.WHATSAPP_ACCESS_TOKEN;\n if (!accessToken) {\n throw new ValidationError(\n \"whatsapp\",\n \"accessToken is required. Set WHATSAPP_ACCESS_TOKEN or provide it in config.\"\n );\n }\n\n const appSecret = config?.appSecret ?? process.env.WHATSAPP_APP_SECRET;\n if (!appSecret) {\n throw new ValidationError(\n \"whatsapp\",\n \"appSecret is required. Set WHATSAPP_APP_SECRET or provide it in config.\"\n );\n }\n\n const phoneNumberId =\n config?.phoneNumberId ?? process.env.WHATSAPP_PHONE_NUMBER_ID;\n if (!phoneNumberId) {\n throw new ValidationError(\n \"whatsapp\",\n \"phoneNumberId is required. Set WHATSAPP_PHONE_NUMBER_ID or provide it in config.\"\n );\n }\n\n const verifyToken = config?.verifyToken ?? process.env.WHATSAPP_VERIFY_TOKEN;\n if (!verifyToken) {\n throw new ValidationError(\n \"whatsapp\",\n \"verifyToken is required. Set WHATSAPP_VERIFY_TOKEN or provide it in config.\"\n );\n }\n\n const userName =\n config?.userName ?? process.env.WHATSAPP_BOT_USERNAME ?? \"whatsapp-bot\";\n\n return new WhatsAppAdapter({\n accessToken,\n apiVersion: config?.apiVersion,\n appSecret,\n phoneNumberId,\n verifyToken,\n userName,\n logger,\n });\n}\n","/**\n * Convert CardElement to WhatsApp interactive messages or text fallback.\n *\n * WhatsApp supports two types of interactive messages:\n * - Reply buttons: up to 3 buttons (title max 20 chars)\n * - List messages: up to 10 rows across sections (title max 24 chars)\n *\n * Cards that exceed these limits fall back to formatted text messages.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/interactive-messages\n */\n\nimport type {\n ActionsElement,\n ButtonElement,\n CardChild,\n CardElement,\n FieldsElement,\n TextElement,\n} from \"chat\";\nimport type { WhatsAppInteractiveMessage } from \"./types\";\n\nconst CALLBACK_DATA_PREFIX = \"chat:\";\n\ninterface WhatsAppCardActionPayload {\n a: string;\n v?: string;\n}\n\n/** Maximum number of reply buttons WhatsApp allows */\nconst MAX_REPLY_BUTTONS = 3;\n\n/** Maximum character length for a button title */\nconst MAX_BUTTON_TITLE_LENGTH = 20;\n\n/** Maximum character length for the body text */\nconst MAX_BODY_LENGTH = 1024;\n\n/**\n * Result of converting a CardElement. Either an interactive message\n * (when buttons fit WhatsApp constraints) or a text fallback.\n */\nexport type WhatsAppCardResult =\n | { interactive: WhatsAppInteractiveMessage; type: \"interactive\" }\n | { text: string; type: \"text\" };\n\n/**\n * Encode an action ID and optional value into a callback data string.\n * Format: \"chat:{json}\" where json is { a: actionId, v?: value }\n */\nexport function encodeWhatsAppCallbackData(\n actionId: string,\n value?: string\n): string {\n const payload: WhatsAppCardActionPayload = { a: actionId };\n if (typeof value === \"string\") {\n payload.v = value;\n }\n return `${CALLBACK_DATA_PREFIX}${JSON.stringify(payload)}`;\n}\n\n/**\n * Decode callback data from a WhatsApp interactive reply.\n * Returns the actionId and optional value.\n */\nexport function decodeWhatsAppCallbackData(data?: string): {\n actionId: string;\n value: string | undefined;\n} {\n if (!data) {\n return { actionId: \"whatsapp_callback\", value: undefined };\n }\n\n // Passthrough for legacy or externally-generated button IDs that don't\n // use the chat: prefix — treat the raw string as both actionId and value.\n if (!data.startsWith(CALLBACK_DATA_PREFIX)) {\n return { actionId: data, value: data };\n }\n\n try {\n const decoded = JSON.parse(\n data.slice(CALLBACK_DATA_PREFIX.length)\n ) as WhatsAppCardActionPayload;\n\n if (typeof decoded.a === \"string\" && decoded.a) {\n return {\n actionId: decoded.a,\n value: typeof decoded.v === \"string\" ? decoded.v : undefined,\n };\n }\n } catch {\n // Malformed JSON after prefix — fall back to passthrough.\n }\n\n // Same passthrough as non-prefixed data: treat raw string as both fields.\n return { actionId: data, value: data };\n}\n\n/**\n * Convert a CardElement to a WhatsApp message payload.\n *\n * If the card has action buttons that fit WhatsApp's constraints\n * (max 3 buttons, titles max 20 chars), produces an interactive\n * button message. Otherwise, produces a text fallback.\n */\nexport function cardToWhatsApp(card: CardElement): WhatsAppCardResult {\n const actions = findActions(card.children);\n const actionButtons = actions ? extractReplyButtons(actions) : null;\n\n // If we have valid buttons, produce an interactive message\n if (actionButtons && actionButtons.length > 0) {\n const bodyText = buildBodyText(card);\n\n return {\n type: \"interactive\",\n interactive: {\n type: \"button\",\n ...(card.title\n ? { header: { type: \"text\", text: truncate(card.title, 60) } }\n : {}),\n body: {\n text: truncate(\n bodyText || \"Please choose an option\",\n MAX_BODY_LENGTH\n ),\n },\n action: {\n buttons: actionButtons.map((btn) => ({\n type: \"reply\" as const,\n reply: {\n id: encodeWhatsAppCallbackData(btn.id, btn.value),\n title: truncate(btn.label, MAX_BUTTON_TITLE_LENGTH),\n },\n })),\n },\n },\n };\n }\n\n // Fallback to text\n return {\n type: \"text\",\n text: cardToWhatsAppText(card),\n };\n}\n\n/**\n * Convert a CardElement to WhatsApp-formatted text.\n *\n * Used as fallback when interactive messages can't represent the card.\n * Uses WhatsApp markdown: *bold*, _italic_, ~strikethrough~.\n */\nexport function cardToWhatsAppText(card: CardElement): string {\n const lines: string[] = [];\n\n if (card.title) {\n lines.push(`*${escapeWhatsApp(card.title)}*`);\n }\n\n if (card.subtitle) {\n lines.push(escapeWhatsApp(card.subtitle));\n }\n\n if ((card.title || card.subtitle) && card.children.length > 0) {\n lines.push(\"\");\n }\n\n if (card.imageUrl) {\n lines.push(card.imageUrl);\n lines.push(\"\");\n }\n\n for (let i = 0; i < card.children.length; i++) {\n const child = card.children[i];\n const childLines = renderChild(child);\n\n if (childLines.length > 0) {\n lines.push(...childLines);\n\n if (i < card.children.length - 1) {\n lines.push(\"\");\n }\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate plain text fallback from a card (no formatting).\n */\nexport function cardToPlainText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(card.title);\n }\n\n if (card.subtitle) {\n parts.push(card.subtitle);\n }\n\n for (const child of card.children) {\n const text = childToPlainText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\");\n}\n\n// =============================================================================\n// Private helpers\n// =============================================================================\n\nfunction renderChild(child: CardChild): string[] {\n switch (child.type) {\n case \"text\":\n return renderText(child);\n\n case \"fields\":\n return renderFields(child);\n\n case \"actions\":\n return renderActions(child);\n\n case \"section\":\n return child.children.flatMap(renderChild);\n\n case \"image\":\n if (child.alt) {\n return [`${child.alt}: ${child.url}`];\n }\n return [child.url];\n\n case \"divider\":\n return [\"---\"];\n\n default:\n return [];\n }\n}\n\nfunction renderText(text: TextElement): string[] {\n switch (text.style) {\n case \"bold\":\n return [`*${escapeWhatsApp(text.content)}*`];\n case \"muted\":\n return [`_${escapeWhatsApp(text.content)}_`];\n default:\n return [escapeWhatsApp(text.content)];\n }\n}\n\nfunction renderFields(fields: FieldsElement): string[] {\n return fields.children.map(\n (field) =>\n `*${escapeWhatsApp(field.label)}:* ${escapeWhatsApp(field.value)}`\n );\n}\n\nfunction renderActions(actions: ActionsElement): string[] {\n const buttonTexts = actions.children.map((button) => {\n if (button.type === \"link-button\") {\n return `${escapeWhatsApp(button.label)}: ${button.url}`;\n }\n return `[${escapeWhatsApp(button.label)}]`;\n });\n\n return [buttonTexts.join(\" | \")];\n}\n\nfunction childToPlainText(child: CardChild): string | null {\n switch (child.type) {\n case \"text\":\n return child.content;\n case \"fields\":\n return child.children.map((f) => `${f.label}: ${f.value}`).join(\"\\n\");\n case \"actions\":\n return null;\n case \"section\":\n return child.children.map(childToPlainText).filter(Boolean).join(\"\\n\");\n default:\n return null;\n }\n}\n\n/**\n * Find the first ActionsElement in a list of card children.\n */\nfunction findActions(children: CardChild[]): ActionsElement | null {\n for (const child of children) {\n if (child.type === \"actions\") {\n return child;\n }\n if (child.type === \"section\") {\n const nested = findActions(child.children);\n if (nested) {\n return nested;\n }\n }\n }\n return null;\n}\n\n/**\n * Extract reply buttons from an ActionsElement, only if they fit\n * WhatsApp constraints (max 3 buttons, each with an ID).\n */\nfunction extractReplyButtons(actions: ActionsElement): ButtonElement[] | null {\n const buttons: ButtonElement[] = [];\n\n for (const child of actions.children) {\n if (child.type === \"button\" && child.id) {\n buttons.push(child);\n }\n // Link buttons can't be WhatsApp reply buttons — skip them\n }\n\n if (buttons.length === 0) {\n return null;\n }\n\n // WhatsApp allows max 3 reply buttons — take the first 3\n return buttons.slice(0, MAX_REPLY_BUTTONS);\n}\n\n/**\n * Build body text from card content (excluding actions).\n */\nfunction buildBodyText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.subtitle) {\n parts.push(card.subtitle);\n }\n\n for (const child of card.children) {\n if (child.type === \"actions\") {\n continue;\n }\n const text = childToPlainText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Escape WhatsApp formatting characters.\n */\nfunction escapeWhatsApp(text: string): string {\n return text\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\*/g, \"\\\\*\")\n .replace(/_/g, \"\\\\_\")\n .replace(/~/g, \"\\\\~\")\n .replace(/`/g, \"\\\\`\");\n}\n\n/**\n * Truncate text to a maximum length, adding ellipsis if needed.\n */\nfunction truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n return `${text.slice(0, maxLength - 1)}\\u2026`;\n}\n","/**\n * WhatsApp-specific format conversion using AST-based parsing.\n *\n * WhatsApp uses a markdown-like format with some differences from standard:\n * - Bold: *text* (single asterisk, not double)\n * - Italic: _text_\n * - Strikethrough: ~text~ (single tilde, not double)\n * - Monospace: ```text```\n *\n * @see https://faq.whatsapp.com/539178204879377\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n type Content,\n isTableNode,\n parseMarkdown,\n type Root,\n stringifyMarkdown,\n tableToAscii,\n walkAst,\n} from \"chat\";\nexport class WhatsAppFormatConverter extends BaseFormatConverter {\n /**\n * Convert an AST to WhatsApp markdown format.\n *\n * Transforms unsupported nodes (headings, thematic breaks, tables)\n * into WhatsApp-compatible equivalents, then converts standard markdown\n * bold/strikethrough to WhatsApp syntax.\n */\n fromAst(ast: Root): string {\n const transformed = walkAst(structuredClone(ast), (node: Content) => {\n // Headings -> bold paragraph (flatten nested strong to avoid ***)\n if (node.type === \"heading\") {\n const heading = node as Content & { children: Content[] };\n const children = heading.children.flatMap((child) =>\n child.type === \"strong\"\n ? (child as Content & { children: Content[] }).children\n : [child]\n );\n return {\n type: \"paragraph\",\n children: [{ type: \"strong\", children }],\n } as Content;\n }\n // Thematic breaks -> text separator\n if (node.type === \"thematicBreak\") {\n return {\n type: \"paragraph\",\n children: [{ type: \"text\", value: \"━━━\" }],\n } as Content;\n }\n // Tables -> code blocks (same as Telegram)\n if (isTableNode(node)) {\n return {\n type: \"code\" as const,\n value: tableToAscii(node),\n lang: undefined,\n } as Content;\n }\n return node;\n });\n // Use _ for emphasis and - for bullets so the only * in output is **strong**\n const markdown = stringifyMarkdown(transformed, {\n emphasis: \"_\",\n bullet: \"-\",\n }).trim();\n return this.toWhatsAppFormat(markdown);\n }\n\n /**\n * Parse WhatsApp markdown into an AST.\n *\n * Transforms WhatsApp-specific formatting to standard markdown first,\n * then parses with the standard parser.\n */\n toAst(markdown: string): Root {\n const standardMarkdown = this.fromWhatsAppFormat(markdown);\n return parseMarkdown(standardMarkdown);\n }\n\n /**\n * Render a postable message to WhatsApp-compatible string.\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return message;\n }\n if (\"raw\" in message) {\n return message.raw;\n }\n if (\"markdown\" in message) {\n return this.fromMarkdown(message.markdown);\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n return super.renderPostable(message);\n }\n\n /**\n * Convert remaining standard markdown markers to WhatsApp format.\n * The stringifier already outputs _italic_ and - bullets.\n * This only converts **bold** -> *bold* and ~~strike~~ -> ~strike~.\n */\n private toWhatsAppFormat(text: string): string {\n let result = text;\n // Convert **bold** -> *bold*\n result = result.replace(/\\*\\*(.+?)\\*\\*/g, \"*$1*\");\n // Convert ~~strikethrough~~ -> ~strikethrough~\n result = result.replace(/~~(.+?)~~/g, \"~$1~\");\n return result;\n }\n\n /**\n * Convert WhatsApp format to standard markdown.\n * Converts single-asterisk bold to double-asterisk bold,\n * and single-tilde strikethrough to double-tilde strikethrough.\n *\n * Careful not to convert _italic_ (which is the same in both formats).\n */\n private fromWhatsAppFormat(text: string): string {\n // Convert *bold* to **bold** (single * not preceded/followed by *, no newlines)\n let result = text.replace(\n /(?<!\\*)\\*(?!\\*)([^\\n*]+?)(?<!\\*)\\*(?!\\*)/g,\n \"**$1**\"\n );\n // Convert ~strike~ to ~~strike~~ (single ~ not preceded/followed by ~, no newlines)\n result = result.replace(/(?<!~)~(?!~)([^\\n~]+?)(?<!~)~(?!~)/g, \"~~$1~~\");\n return result;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/cards.ts","../src/markdown.ts"],"sourcesContent":["import { createHmac, timingSafeEqual } from \"node:crypto\";\nimport { extractCard, ValidationError } from \"@chat-adapter/shared\";\nimport type {\n Adapter,\n AdapterPostableMessage,\n Attachment,\n Author,\n ChatInstance,\n EmojiValue,\n FetchOptions,\n FetchResult,\n FormattedContent,\n Logger,\n RawMessage,\n ReactionEvent,\n StreamChunk,\n StreamOptions,\n ThreadInfo,\n WebhookOptions,\n} from \"chat\";\nimport {\n ConsoleLogger,\n convertEmojiPlaceholders,\n defaultEmojiResolver,\n getEmoji,\n Message,\n} from \"chat\";\nimport { cardToWhatsApp, decodeWhatsAppCallbackData } from \"./cards\";\nimport { WhatsAppFormatConverter } from \"./markdown\";\nimport type {\n WhatsAppAdapterConfig,\n WhatsAppContact,\n WhatsAppInboundMessage,\n WhatsAppInteractiveMessage,\n WhatsAppMediaResponse,\n WhatsAppRawMessage,\n WhatsAppSendResponse,\n WhatsAppThreadId,\n WhatsAppWebhookPayload,\n} from \"./types\";\n\n/** Default Graph API version */\nconst DEFAULT_API_VERSION = \"v21.0\";\n\n/** Maximum message length for WhatsApp Cloud API */\nconst WHATSAPP_MESSAGE_LIMIT = 4096;\n\n/**\n * Split text into chunks that fit within WhatsApp's message limit,\n * breaking on paragraph boundaries (\\n\\n) when possible, then line\n * boundaries (\\n), and finally at the character limit as a last resort.\n */\nexport function splitMessage(text: string): string[] {\n if (text.length <= WHATSAPP_MESSAGE_LIMIT) {\n return [text];\n }\n\n const chunks: string[] = [];\n let remaining = text;\n\n while (remaining.length > WHATSAPP_MESSAGE_LIMIT) {\n const slice = remaining.slice(0, WHATSAPP_MESSAGE_LIMIT);\n\n // Try to break at a paragraph boundary\n let breakIndex = slice.lastIndexOf(\"\\n\\n\");\n if (breakIndex === -1 || breakIndex < WHATSAPP_MESSAGE_LIMIT / 2) {\n // Try a line boundary\n breakIndex = slice.lastIndexOf(\"\\n\");\n }\n if (breakIndex === -1 || breakIndex < WHATSAPP_MESSAGE_LIMIT / 2) {\n // Hard break at the limit\n breakIndex = WHATSAPP_MESSAGE_LIMIT;\n }\n\n chunks.push(remaining.slice(0, breakIndex).trimEnd());\n remaining = remaining.slice(breakIndex).trimStart();\n }\n\n if (remaining.length > 0) {\n chunks.push(remaining);\n }\n\n return chunks;\n}\n\n// Re-export types\nexport type {\n WhatsAppAdapterConfig,\n WhatsAppMediaResponse,\n WhatsAppRawMessage,\n WhatsAppThreadId,\n} from \"./types\";\n\n/**\n * WhatsApp adapter for chat SDK.\n *\n * Supports messaging via the WhatsApp Business Cloud API (Meta Graph API).\n * All conversations are 1:1 DMs between the business phone number and users.\n *\n * @example\n * ```typescript\n * import { Chat } from \"chat\";\n * import { createWhatsAppAdapter } from \"@chat-adapter/whatsapp\";\n * import { MemoryState } from \"@chat-adapter/state-memory\";\n *\n * const chat = new Chat({\n * userName: \"my-bot\",\n * adapters: {\n * whatsapp: createWhatsAppAdapter(),\n * },\n * state: new MemoryState(),\n * });\n * ```\n */\nexport class WhatsAppAdapter\n implements Adapter<WhatsAppThreadId, WhatsAppRawMessage>\n{\n readonly name = \"whatsapp\";\n readonly lockScope = \"channel\" as const;\n readonly persistMessageHistory = true;\n readonly userName: string;\n\n private readonly accessToken: string;\n private readonly appSecret: string;\n private readonly phoneNumberId: string;\n private readonly verifyToken: string;\n private readonly graphApiUrl: string;\n private chat: ChatInstance | null = null;\n private readonly logger: Logger;\n private _botUserId: string | null = null;\n private readonly formatConverter = new WhatsAppFormatConverter();\n\n /** Bot user ID used for self-message detection */\n get botUserId(): string | undefined {\n return this._botUserId ?? undefined;\n }\n\n constructor(config: WhatsAppAdapterConfig) {\n this.accessToken = config.accessToken;\n this.appSecret = config.appSecret;\n this.phoneNumberId = config.phoneNumberId;\n this.verifyToken = config.verifyToken;\n this.logger = config.logger;\n this.userName = config.userName;\n const apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;\n this.graphApiUrl = `https://graph.facebook.com/${apiVersion}`;\n }\n\n /**\n * Initialize the adapter and fetch business profile info.\n */\n async initialize(chat: ChatInstance): Promise<void> {\n this.chat = chat;\n\n // The bot's \"user ID\" is the phone number ID\n this._botUserId = this.phoneNumberId;\n this.logger.info(\"WhatsApp adapter initialized\", {\n phoneNumberId: this.phoneNumberId,\n });\n }\n\n /**\n * Handle incoming webhook from WhatsApp.\n *\n * Handles both the GET verification challenge and POST event notifications.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/guides/set-up-webhooks\n */\n async handleWebhook(\n request: Request,\n options?: WebhookOptions\n ): Promise<Response> {\n // Handle webhook verification challenge (GET request)\n if (request.method === \"GET\") {\n return this.handleVerificationChallenge(request);\n }\n\n const body = await request.text();\n this.logger.debug(\"WhatsApp webhook raw body\", {\n body: body.substring(0, 500),\n });\n\n // Verify request signature (X-Hub-Signature-256 header)\n const signature = request.headers.get(\"x-hub-signature-256\");\n if (!this.verifySignature(body, signature)) {\n return new Response(\"Invalid signature\", { status: 401 });\n }\n\n // Parse the JSON payload\n let payload: WhatsAppWebhookPayload;\n try {\n payload = JSON.parse(body);\n } catch {\n this.logger.error(\"WhatsApp webhook invalid JSON\", {\n contentType: request.headers.get(\"content-type\"),\n bodyPreview: body.substring(0, 200),\n });\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n // Process entries\n for (const entry of payload.entry) {\n for (const change of entry.changes) {\n if (change.field !== \"messages\") {\n continue;\n }\n\n const { value } = change;\n\n // Process incoming messages\n if (value.messages) {\n for (const message of value.messages) {\n try {\n this.handleInboundMessage(\n message,\n value.contacts?.[0],\n value.metadata.phone_number_id,\n options\n );\n } catch (error) {\n this.logger.error(\"Failed to handle inbound message\", {\n messageId: message.id,\n error,\n });\n }\n }\n }\n }\n }\n\n return new Response(\"ok\", { status: 200 });\n }\n\n /**\n * Handle the webhook verification challenge from Meta.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/guides/set-up-webhooks\n */\n private handleVerificationChallenge(request: Request): Response {\n const url = new URL(request.url);\n const mode = url.searchParams.get(\"hub.mode\");\n const token = url.searchParams.get(\"hub.verify_token\");\n const challenge = url.searchParams.get(\"hub.challenge\");\n\n if (mode === \"subscribe\" && token === this.verifyToken) {\n this.logger.info(\"WhatsApp webhook verification succeeded\");\n return new Response(challenge ?? \"\", { status: 200 });\n }\n\n this.logger.warn(\"WhatsApp webhook verification failed\", {\n mode,\n tokenMatch: token === this.verifyToken,\n });\n return new Response(\"Forbidden\", { status: 403 });\n }\n\n /**\n * Verify webhook signature using HMAC-SHA256 with the App Secret.\n *\n * @see https://developers.facebook.com/docs/graph-api/webhooks/getting-started#verification-requests\n */\n private verifySignature(body: string, signature: string | null): boolean {\n if (!signature) {\n return false;\n }\n\n const expectedSignature = `sha256=${createHmac(\"sha256\", this.appSecret).update(body).digest(\"hex\")}`;\n\n try {\n return timingSafeEqual(\n Buffer.from(signature),\n Buffer.from(expectedSignature)\n );\n } catch {\n return false;\n }\n }\n\n /**\n * Handle an inbound message from a user.\n */\n private handleInboundMessage(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n phoneNumberId: string,\n options?: WebhookOptions\n ): void {\n if (!this.chat) {\n this.logger.warn(\"Chat instance not initialized, ignoring message\");\n return;\n }\n\n // Handle reactions separately\n if (inbound.type === \"reaction\" && inbound.reaction) {\n this.handleReaction(inbound, contact, phoneNumberId, options);\n return;\n }\n\n // Handle interactive message replies (button clicks)\n if (inbound.type === \"interactive\" && inbound.interactive) {\n this.handleInteractiveReply(inbound, contact, phoneNumberId, options);\n return;\n }\n\n // Handle legacy button responses (from template quick replies)\n if (inbound.type === \"button\" && inbound.button) {\n this.handleButtonResponse(inbound, contact, phoneNumberId, options);\n return;\n }\n\n // Extract text content based on message type\n const text = this.extractTextContent(inbound);\n if (text === null) {\n this.logger.debug(\"Unsupported message type, ignoring\", {\n type: inbound.type,\n messageId: inbound.id,\n });\n return;\n }\n\n const threadId = this.encodeThreadId({\n phoneNumberId,\n userWaId: inbound.from,\n });\n\n const message = this.buildMessage(\n inbound,\n contact,\n threadId,\n text,\n phoneNumberId\n );\n this.chat.processMessage(this, threadId, message, options);\n }\n\n /**\n * Handle reaction events.\n */\n private handleReaction(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n phoneNumberId: string,\n options?: WebhookOptions\n ): void {\n if (!(this.chat && inbound.reaction)) {\n return;\n }\n\n const threadId = this.encodeThreadId({\n phoneNumberId,\n userWaId: inbound.from,\n });\n\n const rawEmoji = inbound.reaction.emoji;\n // Empty emoji means reaction was removed\n const added = rawEmoji !== \"\";\n const emojiValue = added ? getEmoji(rawEmoji) : getEmoji(\"\");\n\n const user: Author = {\n userId: inbound.from,\n userName: contact?.profile.name || inbound.from,\n fullName: contact?.profile.name || inbound.from,\n isBot: false,\n isMe: false,\n };\n\n const event: Omit<ReactionEvent, \"adapter\" | \"thread\"> = {\n emoji: emojiValue,\n rawEmoji,\n added,\n user,\n messageId: inbound.reaction.message_id,\n threadId,\n raw: inbound,\n };\n\n this.chat.processReaction({ ...event, adapter: this }, options);\n }\n\n /**\n * Handle interactive message replies (button/list selection).\n */\n private handleInteractiveReply(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n phoneNumberId: string,\n options?: WebhookOptions\n ): void {\n if (!(this.chat && inbound.interactive)) {\n return;\n }\n\n const threadId = this.encodeThreadId({\n phoneNumberId,\n userWaId: inbound.from,\n });\n\n const { interactive } = inbound;\n let rawId: string;\n let fallbackValue: string;\n\n if (interactive.type === \"button_reply\" && interactive.button_reply) {\n rawId = interactive.button_reply.id;\n fallbackValue = interactive.button_reply.title;\n } else if (interactive.type === \"list_reply\" && interactive.list_reply) {\n rawId = interactive.list_reply.id;\n fallbackValue = interactive.list_reply.title;\n } else {\n return;\n }\n\n const { actionId, value } = decodeWhatsAppCallbackData(rawId);\n\n this.chat.processAction(\n {\n adapter: this,\n actionId,\n value: value ?? fallbackValue,\n user: {\n userId: inbound.from,\n userName: contact?.profile.name || inbound.from,\n fullName: contact?.profile.name || inbound.from,\n isBot: false,\n isMe: false,\n },\n messageId: inbound.id,\n threadId,\n raw: inbound,\n },\n options\n );\n }\n\n /**\n * Handle legacy button responses (from template quick replies).\n */\n private handleButtonResponse(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n phoneNumberId: string,\n options?: WebhookOptions\n ): void {\n if (!(this.chat && inbound.button)) {\n return;\n }\n\n const threadId = this.encodeThreadId({\n phoneNumberId,\n userWaId: inbound.from,\n });\n\n this.chat.processAction(\n {\n adapter: this,\n actionId: inbound.button.payload,\n value: inbound.button.text,\n user: {\n userId: inbound.from,\n userName: contact?.profile.name || inbound.from,\n fullName: contact?.profile.name || inbound.from,\n isBot: false,\n isMe: false,\n },\n messageId: inbound.id,\n threadId,\n raw: inbound,\n },\n options\n );\n }\n\n /**\n * Extract text content from an inbound message.\n * Returns null for unsupported message types.\n */\n private extractTextContent(message: WhatsAppInboundMessage): string | null {\n switch (message.type) {\n case \"text\":\n return message.text?.body ?? null;\n case \"image\":\n return message.image?.caption ?? \"[Image]\";\n case \"document\":\n return (\n message.document?.caption ??\n `[Document: ${message.document?.filename ?? \"file\"}]`\n );\n case \"audio\":\n return \"[Audio message]\";\n case \"voice\":\n return \"[Voice message]\";\n case \"video\":\n return \"[Video]\";\n case \"sticker\":\n return \"[Sticker]\";\n case \"location\": {\n const loc = message.location;\n if (loc) {\n const parts = [`[Location: ${loc.latitude}, ${loc.longitude}`];\n if (loc.name) {\n parts[0] = `[Location: ${loc.name}`;\n }\n if (loc.address) {\n parts.push(loc.address);\n }\n return `${parts.join(\" - \")}]`;\n }\n return \"[Location]\";\n }\n default:\n return null;\n }\n }\n\n /**\n * Build a Message from a WhatsApp inbound message.\n */\n private buildMessage(\n inbound: WhatsAppInboundMessage,\n contact: WhatsAppContact | undefined,\n threadId: string,\n text: string,\n phoneNumberId?: string\n ): Message<WhatsAppRawMessage> {\n const author: Author = {\n userId: inbound.from,\n userName: contact?.profile.name || inbound.from,\n fullName: contact?.profile.name || inbound.from,\n isBot: false,\n isMe: false,\n };\n\n const formatted: FormattedContent = this.formatConverter.toAst(text);\n\n const raw: WhatsAppRawMessage = {\n message: inbound,\n contact,\n phoneNumberId: phoneNumberId || this.phoneNumberId,\n };\n\n const attachments = this.buildAttachments(inbound);\n\n return new Message<WhatsAppRawMessage>({\n id: inbound.id,\n threadId,\n text,\n formatted,\n raw,\n author,\n metadata: {\n dateSent: new Date(Number.parseInt(inbound.timestamp, 10) * 1000),\n edited: false,\n },\n attachments,\n });\n }\n\n /**\n * Build attachments from an inbound message.\n */\n private buildAttachments(inbound: WhatsAppInboundMessage): Attachment[] {\n const attachments: Attachment[] = [];\n\n if (inbound.image) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.image.id,\n \"image\",\n inbound.image.mime_type\n )\n );\n }\n\n if (inbound.document) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.document.id,\n \"file\",\n inbound.document.mime_type,\n inbound.document.filename\n )\n );\n }\n\n if (inbound.audio) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.audio.id,\n \"audio\",\n inbound.audio.mime_type\n )\n );\n }\n\n if (inbound.video) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.video.id,\n \"video\",\n inbound.video.mime_type\n )\n );\n }\n\n if (inbound.voice) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.voice.id,\n \"audio\",\n inbound.voice.mime_type,\n \"voice\"\n )\n );\n }\n\n if (inbound.sticker) {\n attachments.push(\n this.buildMediaAttachment(\n inbound.sticker.id,\n \"image\",\n inbound.sticker.mime_type,\n \"sticker\"\n )\n );\n }\n\n if (inbound.location) {\n const loc = inbound.location;\n const lat = Number(loc.latitude);\n const lng = Number(loc.longitude);\n if (Number.isFinite(lat) && Number.isFinite(lng)) {\n const mapUrl = `https://www.google.com/maps?q=${lat},${lng}`;\n attachments.push({\n type: \"file\",\n name: loc.name || \"Location\",\n url: mapUrl,\n mimeType: \"application/geo+json\",\n });\n }\n }\n\n return attachments;\n }\n\n /**\n * Build a single media attachment with a lazy fetchData function.\n */\n private buildMediaAttachment(\n mediaId: string,\n type: Attachment[\"type\"],\n mimeType: string,\n name?: string\n ): Attachment {\n return {\n type,\n mimeType,\n name,\n fetchData: () => this.downloadMedia(mediaId),\n };\n }\n\n /**\n * Download media from WhatsApp.\n *\n * WhatsApp media is fetched in two steps:\n * 1. GET the media metadata to obtain the download URL\n * 2. GET the actual binary data from the download URL\n *\n * @param mediaId - The media ID from the inbound message\n * @returns The media data as a Buffer\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#download-media\n */\n async downloadMedia(mediaId: string): Promise<Buffer> {\n // Step 1: Get the media URL\n const metaResponse = await fetch(`${this.graphApiUrl}/${mediaId}`, {\n headers: { Authorization: `Bearer ${this.accessToken}` },\n });\n\n if (!metaResponse.ok) {\n const errorBody = await metaResponse.text();\n this.logger.error(\"Failed to get media URL\", {\n status: metaResponse.status,\n body: errorBody,\n mediaId,\n });\n throw new Error(\n `Failed to get media URL: ${metaResponse.status} ${errorBody}`\n );\n }\n\n const mediaInfo: WhatsAppMediaResponse =\n (await metaResponse.json()) as WhatsAppMediaResponse;\n\n // Step 2: Download the actual file\n const dataResponse = await fetch(mediaInfo.url, {\n headers: { Authorization: `Bearer ${this.accessToken}` },\n });\n\n if (!dataResponse.ok) {\n this.logger.error(\"Failed to download media\", {\n status: dataResponse.status,\n mediaId,\n });\n throw new Error(`Failed to download media: ${dataResponse.status}`);\n }\n\n const arrayBuffer = await dataResponse.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n\n /**\n * Send a message to a WhatsApp user.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages\n */\n async postMessage(\n threadId: string,\n message: AdapterPostableMessage\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n const { userWaId } = this.decodeThreadId(threadId);\n\n // Check if this is a card with interactive buttons\n const card = extractCard(message);\n if (card) {\n const result = cardToWhatsApp(card);\n if (result.type === \"interactive\") {\n // Convert emoji placeholders in interactive message fields\n const interactive = JSON.parse(\n convertEmojiPlaceholders(\n JSON.stringify(result.interactive),\n \"whatsapp\"\n )\n );\n return this.sendInteractiveMessage(threadId, userWaId, interactive);\n }\n return this.sendTextMessage(\n threadId,\n userWaId,\n convertEmojiPlaceholders(result.text, \"whatsapp\")\n );\n }\n\n // Regular text message\n const body = convertEmojiPlaceholders(\n this.formatConverter.renderPostable(message),\n \"whatsapp\"\n );\n return this.sendTextMessage(threadId, userWaId, body);\n }\n\n /**\n * Split text into chunks that fit within WhatsApp's message limit,\n * breaking on paragraph boundaries (\\n\\n) when possible, then line\n * boundaries (\\n), and finally at the character limit as a last resort.\n */\n splitMessage(text: string): string[] {\n return splitMessage(text);\n }\n\n /**\n * Send a single text message via the Cloud API (must be within the\n * 4096-character limit).\n */\n private async sendSingleTextMessage(\n threadId: string,\n to: string,\n text: string\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n const response = await this.graphApiRequest<WhatsAppSendResponse>(\n `/${this.phoneNumberId}/messages`,\n {\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to,\n type: \"text\",\n text: { preview_url: false, body: text },\n }\n );\n\n if (!(response.messages?.length && response.messages[0]?.id)) {\n throw new Error(\n \"WhatsApp API did not return a message ID for text message\"\n );\n }\n const messageId = response.messages[0].id;\n\n return {\n id: messageId,\n threadId,\n raw: {\n message: {\n id: messageId,\n from: this.phoneNumberId,\n timestamp: String(Math.floor(Date.now() / 1000)),\n type: \"text\",\n text: { body: text },\n },\n phoneNumberId: this.phoneNumberId,\n },\n };\n }\n\n /**\n * Send a text message, splitting into multiple messages if it exceeds\n * WhatsApp's 4096-character limit. Returns the last message sent.\n */\n private async sendTextMessage(\n threadId: string,\n to: string,\n text: string\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n const chunks = this.splitMessage(text);\n let result: RawMessage<WhatsAppRawMessage> | undefined;\n\n for (const chunk of chunks) {\n result = await this.sendSingleTextMessage(threadId, to, chunk);\n }\n\n return result as RawMessage<WhatsAppRawMessage>;\n }\n\n /**\n * Send an interactive message (buttons or list) via the Cloud API.\n */\n private async sendInteractiveMessage(\n threadId: string,\n to: string,\n interactive: WhatsAppInteractiveMessage\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n const response = await this.graphApiRequest<WhatsAppSendResponse>(\n `/${this.phoneNumberId}/messages`,\n {\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to,\n type: \"interactive\",\n interactive,\n }\n );\n\n if (!(response.messages?.length && response.messages[0]?.id)) {\n throw new Error(\n \"WhatsApp API did not return a message ID for interactive message\"\n );\n }\n const messageId = response.messages[0].id;\n\n return {\n id: messageId,\n threadId,\n raw: {\n message: {\n id: messageId,\n from: this.phoneNumberId,\n timestamp: String(Math.floor(Date.now() / 1000)),\n type: \"interactive\",\n },\n phoneNumberId: this.phoneNumberId,\n },\n };\n }\n\n /**\n * Edit a message. Not supported by WhatsApp Cloud API — throws an error.\n *\n * Callers should use postMessage directly if they want to send a follow-up.\n */\n async editMessage(\n _threadId: string,\n _messageId: string,\n _message: AdapterPostableMessage\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n throw new Error(\n \"WhatsApp does not support editing messages. Use postMessage to send a new message instead.\"\n );\n }\n\n /**\n * Stream a message by buffering all chunks and sending as a single message.\n * WhatsApp doesn't support message editing, so we can't do incremental updates.\n */\n async stream(\n threadId: string,\n textStream: AsyncIterable<string | StreamChunk>,\n _options?: StreamOptions\n ): Promise<RawMessage<WhatsAppRawMessage>> {\n let accumulated = \"\";\n for await (const chunk of textStream) {\n if (typeof chunk === \"string\") {\n accumulated += chunk;\n } else if (chunk.type === \"markdown_text\") {\n accumulated += chunk.text;\n }\n }\n return this.postMessage(threadId, { markdown: accumulated });\n }\n\n /**\n * Delete a message. Not supported by WhatsApp Cloud API — throws an error.\n */\n async deleteMessage(_threadId: string, _messageId: string): Promise<void> {\n throw new Error(\"WhatsApp does not support deleting messages.\");\n }\n\n /**\n * Add a reaction to a message.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/reaction-messages\n */\n async addReaction(\n threadId: string,\n messageId: string,\n emoji: EmojiValue | string\n ): Promise<void> {\n const { userWaId } = this.decodeThreadId(threadId);\n const emojiStr = this.resolveEmoji(emoji);\n\n await this.graphApiRequest(`/${this.phoneNumberId}/messages`, {\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to: userWaId,\n type: \"reaction\",\n reaction: {\n message_id: messageId,\n emoji: emojiStr,\n },\n });\n }\n\n /**\n * Remove a reaction from a message.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/reaction-messages\n */\n async removeReaction(\n threadId: string,\n messageId: string,\n _emoji: EmojiValue | string\n ): Promise<void> {\n const { userWaId } = this.decodeThreadId(threadId);\n\n // WhatsApp removes reactions by sending an empty emoji\n await this.graphApiRequest(`/${this.phoneNumberId}/messages`, {\n messaging_product: \"whatsapp\",\n recipient_type: \"individual\",\n to: userWaId,\n type: \"reaction\",\n reaction: {\n message_id: messageId,\n emoji: \"\",\n },\n });\n }\n\n /**\n * Start typing indicator.\n *\n * WhatsApp supports typing indicators via the messages endpoint.\n * The indicator displays for up to 25 seconds or until the next message.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/mark-messages-as-read\n */\n async startTyping(_threadId: string, _status?: string): Promise<void> {\n // WhatsApp Cloud API does not support typing indicators.\n }\n\n /**\n * Fetch messages. Not supported by WhatsApp Cloud API.\n *\n * WhatsApp does not provide an API to retrieve message history.\n */\n async fetchMessages(\n _threadId: string,\n _options?: FetchOptions\n ): Promise<FetchResult<WhatsAppRawMessage>> {\n this.logger.debug(\n \"fetchMessages not supported on WhatsApp - message history is not available via Cloud API\"\n );\n return { messages: [] };\n }\n\n /**\n * Fetch thread info.\n */\n async fetchThread(threadId: string): Promise<ThreadInfo> {\n const { phoneNumberId, userWaId } = this.decodeThreadId(threadId);\n\n return {\n id: threadId,\n channelId: `whatsapp:${phoneNumberId}`,\n channelName: `WhatsApp: ${userWaId}`,\n isDM: true,\n metadata: { phoneNumberId, userWaId },\n };\n }\n\n /**\n * Encode a WhatsApp thread ID.\n *\n * Format: whatsapp:{phoneNumberId}:{userWaId}\n */\n encodeThreadId(platformData: WhatsAppThreadId): string {\n return `whatsapp:${platformData.phoneNumberId}:${platformData.userWaId}`;\n }\n\n /**\n * Decode a WhatsApp thread ID.\n *\n * Format: whatsapp:{phoneNumberId}:{userWaId}\n */\n decodeThreadId(threadId: string): WhatsAppThreadId {\n if (!threadId.startsWith(\"whatsapp:\")) {\n throw new ValidationError(\n \"whatsapp\",\n `Invalid WhatsApp thread ID: ${threadId}`\n );\n }\n\n const withoutPrefix = threadId.slice(9);\n if (!withoutPrefix) {\n throw new ValidationError(\n \"whatsapp\",\n `Invalid WhatsApp thread ID format: ${threadId}`\n );\n }\n\n const parts = withoutPrefix.split(\":\");\n if (parts.length !== 2 || !parts[0] || !parts[1]) {\n throw new ValidationError(\n \"whatsapp\",\n `Invalid WhatsApp thread ID format: ${threadId}`\n );\n }\n\n return {\n phoneNumberId: parts[0],\n userWaId: parts[1],\n };\n }\n\n /**\n * Derive channel ID from a WhatsApp thread ID.\n * On WhatsApp every conversation is a 1:1 DM, so channel === thread.\n */\n channelIdFromThreadId(threadId: string): string {\n return threadId;\n }\n\n /**\n * All WhatsApp conversations are DMs.\n */\n isDM(_threadId: string): boolean {\n return true;\n }\n\n /**\n * Open a DM with a user. Returns the thread ID for the conversation.\n *\n * For WhatsApp, this simply constructs the thread ID since all\n * conversations are inherently DMs. Note: you can only message users\n * who have messaged you first (within the 24-hour window) or\n * via approved template messages.\n */\n async openDM(userId: string): Promise<string> {\n return this.encodeThreadId({\n phoneNumberId: this.phoneNumberId,\n userWaId: userId,\n });\n }\n\n /**\n * Parse platform message format to normalized format.\n */\n parseMessage(raw: WhatsAppRawMessage): Message<WhatsAppRawMessage> {\n const text = this.extractTextContent(raw.message) || \"\";\n const formatted: FormattedContent = this.formatConverter.toAst(text);\n const attachments = this.buildAttachments(raw.message);\n const threadId = this.encodeThreadId({\n phoneNumberId: raw.phoneNumberId,\n userWaId: raw.message.from,\n });\n\n return new Message<WhatsAppRawMessage>({\n id: raw.message.id,\n threadId,\n text,\n formatted,\n author: {\n userId: raw.message.from,\n userName: raw.contact?.profile.name || raw.message.from,\n fullName: raw.contact?.profile.name || raw.message.from,\n isBot: false,\n isMe: raw.message.from === this._botUserId,\n },\n metadata: {\n dateSent: new Date(Number.parseInt(raw.message.timestamp, 10) * 1000),\n edited: false,\n },\n attachments,\n raw,\n });\n }\n\n /**\n * Render formatted content to WhatsApp markdown.\n */\n renderFormatted(content: FormattedContent): string {\n return this.formatConverter.fromAst(content);\n }\n\n /**\n * Mark an inbound message as read.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/mark-messages-as-read\n */\n async markAsRead(messageId: string): Promise<void> {\n await this.graphApiRequest(`/${this.phoneNumberId}/messages`, {\n messaging_product: \"whatsapp\",\n status: \"read\",\n message_id: messageId,\n });\n }\n\n // =============================================================================\n // Private helpers\n // =============================================================================\n\n /**\n * Make a request to the Meta Graph API.\n */\n private async graphApiRequest<T = unknown>(\n path: string,\n body: unknown\n ): Promise<T> {\n const response = await fetch(`${this.graphApiUrl}${path}`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n this.logger.error(\"WhatsApp API error\", {\n status: response.status,\n body: errorBody,\n path,\n });\n throw new Error(`WhatsApp API error: ${response.status} ${errorBody}`);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Resolve an emoji value to a unicode string.\n */\n private resolveEmoji(emoji: EmojiValue | string): string {\n return defaultEmojiResolver.toGChat(emoji);\n }\n}\n\n/**\n * Factory function to create a WhatsApp adapter.\n *\n * @example\n * ```typescript\n * const adapter = createWhatsAppAdapter({\n * accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,\n * appSecret: process.env.WHATSAPP_APP_SECRET!,\n * phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID!,\n * verifyToken: process.env.WHATSAPP_VERIFY_TOKEN!,\n * });\n * ```\n */\nexport function createWhatsAppAdapter(config?: {\n accessToken?: string;\n apiVersion?: string;\n appSecret?: string;\n logger?: Logger;\n phoneNumberId?: string;\n userName?: string;\n verifyToken?: string;\n}): WhatsAppAdapter {\n const logger = config?.logger ?? new ConsoleLogger(\"info\").child(\"whatsapp\");\n\n const accessToken = config?.accessToken ?? process.env.WHATSAPP_ACCESS_TOKEN;\n if (!accessToken) {\n throw new ValidationError(\n \"whatsapp\",\n \"accessToken is required. Set WHATSAPP_ACCESS_TOKEN or provide it in config.\"\n );\n }\n\n const appSecret = config?.appSecret ?? process.env.WHATSAPP_APP_SECRET;\n if (!appSecret) {\n throw new ValidationError(\n \"whatsapp\",\n \"appSecret is required. Set WHATSAPP_APP_SECRET or provide it in config.\"\n );\n }\n\n const phoneNumberId =\n config?.phoneNumberId ?? process.env.WHATSAPP_PHONE_NUMBER_ID;\n if (!phoneNumberId) {\n throw new ValidationError(\n \"whatsapp\",\n \"phoneNumberId is required. Set WHATSAPP_PHONE_NUMBER_ID or provide it in config.\"\n );\n }\n\n const verifyToken = config?.verifyToken ?? process.env.WHATSAPP_VERIFY_TOKEN;\n if (!verifyToken) {\n throw new ValidationError(\n \"whatsapp\",\n \"verifyToken is required. Set WHATSAPP_VERIFY_TOKEN or provide it in config.\"\n );\n }\n\n const userName =\n config?.userName ?? process.env.WHATSAPP_BOT_USERNAME ?? \"whatsapp-bot\";\n\n return new WhatsAppAdapter({\n accessToken,\n apiVersion: config?.apiVersion,\n appSecret,\n phoneNumberId,\n verifyToken,\n userName,\n logger,\n });\n}\n","/**\n * Convert CardElement to WhatsApp interactive messages or text fallback.\n *\n * WhatsApp supports two types of interactive messages:\n * - Reply buttons: up to 3 buttons (title max 20 chars)\n * - List messages: up to 10 rows across sections (title max 24 chars)\n *\n * Cards that exceed these limits fall back to formatted text messages.\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api/messages/interactive-messages\n */\n\nimport type {\n ActionsElement,\n ButtonElement,\n CardChild,\n CardElement,\n FieldsElement,\n TextElement,\n} from \"chat\";\nimport type { WhatsAppInteractiveMessage } from \"./types\";\n\nconst CALLBACK_DATA_PREFIX = \"chat:\";\n\ninterface WhatsAppCardActionPayload {\n a: string;\n v?: string;\n}\n\n/** Maximum number of reply buttons WhatsApp allows */\nconst MAX_REPLY_BUTTONS = 3;\n\n/** Maximum character length for a button title */\nconst MAX_BUTTON_TITLE_LENGTH = 20;\n\n/** Maximum character length for the body text */\nconst MAX_BODY_LENGTH = 1024;\n\n/**\n * Result of converting a CardElement. Either an interactive message\n * (when buttons fit WhatsApp constraints) or a text fallback.\n */\nexport type WhatsAppCardResult =\n | { interactive: WhatsAppInteractiveMessage; type: \"interactive\" }\n | { text: string; type: \"text\" };\n\n/**\n * Encode an action ID and optional value into a callback data string.\n * Format: \"chat:{json}\" where json is { a: actionId, v?: value }\n */\nexport function encodeWhatsAppCallbackData(\n actionId: string,\n value?: string\n): string {\n const payload: WhatsAppCardActionPayload = { a: actionId };\n if (typeof value === \"string\") {\n payload.v = value;\n }\n return `${CALLBACK_DATA_PREFIX}${JSON.stringify(payload)}`;\n}\n\n/**\n * Decode callback data from a WhatsApp interactive reply.\n * Returns the actionId and optional value.\n */\nexport function decodeWhatsAppCallbackData(data?: string): {\n actionId: string;\n value: string | undefined;\n} {\n if (!data) {\n return { actionId: \"whatsapp_callback\", value: undefined };\n }\n\n // Passthrough for legacy or externally-generated button IDs that don't\n // use the chat: prefix — treat the raw string as both actionId and value.\n if (!data.startsWith(CALLBACK_DATA_PREFIX)) {\n return { actionId: data, value: data };\n }\n\n try {\n const decoded = JSON.parse(\n data.slice(CALLBACK_DATA_PREFIX.length)\n ) as WhatsAppCardActionPayload;\n\n if (typeof decoded.a === \"string\" && decoded.a) {\n return {\n actionId: decoded.a,\n value: typeof decoded.v === \"string\" ? decoded.v : undefined,\n };\n }\n } catch {\n // Malformed JSON after prefix — fall back to passthrough.\n }\n\n // Same passthrough as non-prefixed data: treat raw string as both fields.\n return { actionId: data, value: data };\n}\n\n/**\n * Convert a CardElement to a WhatsApp message payload.\n *\n * If the card has action buttons that fit WhatsApp's constraints\n * (max 3 buttons, titles max 20 chars), produces an interactive\n * button message. Otherwise, produces a text fallback.\n */\nexport function cardToWhatsApp(card: CardElement): WhatsAppCardResult {\n const actions = findActions(card.children);\n const actionButtons = actions ? extractReplyButtons(actions) : null;\n\n // If we have valid buttons, produce an interactive message\n if (actionButtons && actionButtons.length > 0) {\n const bodyText = buildBodyText(card);\n\n return {\n type: \"interactive\",\n interactive: {\n type: \"button\",\n ...(card.title\n ? { header: { type: \"text\", text: truncate(card.title, 60) } }\n : {}),\n body: {\n text: truncate(\n bodyText || \"Please choose an option\",\n MAX_BODY_LENGTH\n ),\n },\n action: {\n buttons: actionButtons.map((btn) => ({\n type: \"reply\" as const,\n reply: {\n id: encodeWhatsAppCallbackData(btn.id, btn.value),\n title: truncate(btn.label, MAX_BUTTON_TITLE_LENGTH),\n },\n })),\n },\n },\n };\n }\n\n // Fallback to text\n return {\n type: \"text\",\n text: cardToWhatsAppText(card),\n };\n}\n\n/**\n * Convert a CardElement to WhatsApp-formatted text.\n *\n * Used as fallback when interactive messages can't represent the card.\n * Uses WhatsApp markdown: *bold*, _italic_, ~strikethrough~.\n */\nexport function cardToWhatsAppText(card: CardElement): string {\n const lines: string[] = [];\n\n if (card.title) {\n lines.push(`*${escapeWhatsApp(card.title)}*`);\n }\n\n if (card.subtitle) {\n lines.push(escapeWhatsApp(card.subtitle));\n }\n\n if ((card.title || card.subtitle) && card.children.length > 0) {\n lines.push(\"\");\n }\n\n if (card.imageUrl) {\n lines.push(card.imageUrl);\n lines.push(\"\");\n }\n\n for (let i = 0; i < card.children.length; i++) {\n const child = card.children[i];\n const childLines = renderChild(child);\n\n if (childLines.length > 0) {\n lines.push(...childLines);\n\n if (i < card.children.length - 1) {\n lines.push(\"\");\n }\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate plain text fallback from a card (no formatting).\n */\nexport function cardToPlainText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(card.title);\n }\n\n if (card.subtitle) {\n parts.push(card.subtitle);\n }\n\n for (const child of card.children) {\n const text = childToPlainText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\");\n}\n\n// =============================================================================\n// Private helpers\n// =============================================================================\n\nfunction renderChild(child: CardChild): string[] {\n switch (child.type) {\n case \"text\":\n return renderText(child);\n\n case \"fields\":\n return renderFields(child);\n\n case \"actions\":\n return renderActions(child);\n\n case \"section\":\n return child.children.flatMap(renderChild);\n\n case \"image\":\n if (child.alt) {\n return [`${child.alt}: ${child.url}`];\n }\n return [child.url];\n\n case \"divider\":\n return [\"---\"];\n\n default:\n return [];\n }\n}\n\nfunction renderText(text: TextElement): string[] {\n switch (text.style) {\n case \"bold\":\n return [`*${escapeWhatsApp(text.content)}*`];\n case \"muted\":\n return [`_${escapeWhatsApp(text.content)}_`];\n default:\n return [escapeWhatsApp(text.content)];\n }\n}\n\nfunction renderFields(fields: FieldsElement): string[] {\n return fields.children.map(\n (field) =>\n `*${escapeWhatsApp(field.label)}:* ${escapeWhatsApp(field.value)}`\n );\n}\n\nfunction renderActions(actions: ActionsElement): string[] {\n const buttonTexts = actions.children.map((button) => {\n if (button.type === \"link-button\") {\n return `${escapeWhatsApp(button.label)}: ${button.url}`;\n }\n return `[${escapeWhatsApp(button.label)}]`;\n });\n\n return [buttonTexts.join(\" | \")];\n}\n\nfunction childToPlainText(child: CardChild): string | null {\n switch (child.type) {\n case \"text\":\n return child.content;\n case \"fields\":\n return child.children.map((f) => `${f.label}: ${f.value}`).join(\"\\n\");\n case \"actions\":\n return null;\n case \"section\":\n return child.children.map(childToPlainText).filter(Boolean).join(\"\\n\");\n default:\n return null;\n }\n}\n\n/**\n * Find the first ActionsElement in a list of card children.\n */\nfunction findActions(children: CardChild[]): ActionsElement | null {\n for (const child of children) {\n if (child.type === \"actions\") {\n return child;\n }\n if (child.type === \"section\") {\n const nested = findActions(child.children);\n if (nested) {\n return nested;\n }\n }\n }\n return null;\n}\n\n/**\n * Extract reply buttons from an ActionsElement, only if they fit\n * WhatsApp constraints (max 3 buttons, each with an ID).\n */\nfunction extractReplyButtons(actions: ActionsElement): ButtonElement[] | null {\n const buttons: ButtonElement[] = [];\n\n for (const child of actions.children) {\n if (child.type === \"button\" && child.id) {\n buttons.push(child);\n }\n // Link buttons can't be WhatsApp reply buttons — skip them\n }\n\n if (buttons.length === 0) {\n return null;\n }\n\n // WhatsApp allows max 3 reply buttons — take the first 3\n return buttons.slice(0, MAX_REPLY_BUTTONS);\n}\n\n/**\n * Build body text from card content (excluding actions).\n */\nfunction buildBodyText(card: CardElement): string {\n const parts: string[] = [];\n\n if (card.subtitle) {\n parts.push(card.subtitle);\n }\n\n for (const child of card.children) {\n if (child.type === \"actions\") {\n continue;\n }\n const text = childToPlainText(child);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Escape WhatsApp formatting characters.\n */\nfunction escapeWhatsApp(text: string): string {\n return text\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\\*/g, \"\\\\*\")\n .replace(/_/g, \"\\\\_\")\n .replace(/~/g, \"\\\\~\")\n .replace(/`/g, \"\\\\`\");\n}\n\n/**\n * Truncate text to a maximum length, adding ellipsis if needed.\n */\nfunction truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n return `${text.slice(0, maxLength - 1)}\\u2026`;\n}\n","/**\n * WhatsApp-specific format conversion using AST-based parsing.\n *\n * WhatsApp uses a markdown-like format with some differences from standard:\n * - Bold: *text* (single asterisk, not double)\n * - Italic: _text_\n * - Strikethrough: ~text~ (single tilde, not double)\n * - Monospace: ```text```\n *\n * @see https://faq.whatsapp.com/539178204879377\n */\n\nimport {\n type AdapterPostableMessage,\n BaseFormatConverter,\n type Content,\n isTableNode,\n parseMarkdown,\n type Root,\n stringifyMarkdown,\n tableToAscii,\n walkAst,\n} from \"chat\";\nexport class WhatsAppFormatConverter extends BaseFormatConverter {\n /**\n * Convert an AST to WhatsApp markdown format.\n *\n * Transforms unsupported nodes (headings, thematic breaks, tables)\n * into WhatsApp-compatible equivalents, then converts standard markdown\n * bold/strikethrough to WhatsApp syntax.\n */\n fromAst(ast: Root): string {\n const transformed = walkAst(structuredClone(ast), (node: Content) => {\n // Headings -> bold paragraph (flatten nested strong to avoid ***)\n if (node.type === \"heading\") {\n const heading = node as Content & { children: Content[] };\n const children = heading.children.flatMap((child) =>\n child.type === \"strong\"\n ? (child as Content & { children: Content[] }).children\n : [child]\n );\n return {\n type: \"paragraph\",\n children: [{ type: \"strong\", children }],\n } as Content;\n }\n // Thematic breaks -> text separator\n if (node.type === \"thematicBreak\") {\n return {\n type: \"paragraph\",\n children: [{ type: \"text\", value: \"━━━\" }],\n } as Content;\n }\n // Tables -> code blocks (same as Telegram)\n if (isTableNode(node)) {\n return {\n type: \"code\" as const,\n value: tableToAscii(node),\n lang: undefined,\n } as Content;\n }\n return node;\n });\n // Use _ for emphasis and - for bullets so the only * in output is **strong**\n const markdown = stringifyMarkdown(transformed, {\n emphasis: \"_\",\n bullet: \"-\",\n }).trim();\n return this.toWhatsAppFormat(markdown);\n }\n\n /**\n * Parse WhatsApp markdown into an AST.\n *\n * Transforms WhatsApp-specific formatting to standard markdown first,\n * then parses with the standard parser.\n */\n toAst(markdown: string): Root {\n const standardMarkdown = this.fromWhatsAppFormat(markdown);\n return parseMarkdown(standardMarkdown);\n }\n\n /**\n * Render a postable message to WhatsApp-compatible string.\n */\n override renderPostable(message: AdapterPostableMessage): string {\n if (typeof message === \"string\") {\n return message;\n }\n if (\"raw\" in message) {\n return message.raw;\n }\n if (\"markdown\" in message) {\n return this.fromMarkdown(message.markdown);\n }\n if (\"ast\" in message) {\n return this.fromAst(message.ast);\n }\n return super.renderPostable(message);\n }\n\n /**\n * Convert remaining standard markdown markers to WhatsApp format.\n * The stringifier already outputs _italic_ and - bullets.\n * This only converts **bold** -> *bold* and ~~strike~~ -> ~strike~.\n */\n private toWhatsAppFormat(text: string): string {\n let result = text;\n // Convert **bold** -> *bold*\n result = result.replace(/\\*\\*(.+?)\\*\\*/g, \"*$1*\");\n // Convert ~~strikethrough~~ -> ~strikethrough~\n result = result.replace(/~~(.+?)~~/g, \"~$1~\");\n return result;\n }\n\n /**\n * Convert WhatsApp format to standard markdown.\n * Converts single-asterisk bold to double-asterisk bold,\n * and single-tilde strikethrough to double-tilde strikethrough.\n *\n * Careful not to convert _italic_ (which is the same in both formats).\n */\n private fromWhatsAppFormat(text: string): string {\n // Convert *bold* to **bold** (single * not preceded/followed by *, no newlines)\n let result = text.replace(\n /(?<!\\*)\\*(?!\\*)([^\\n*]+?)(?<!\\*)\\*(?!\\*)/g,\n \"**$1**\"\n );\n // Convert ~strike~ to ~~strike~~ (single ~ not preceded/followed by ~, no newlines)\n result = result.replace(/(?<!~)~(?!~)([^\\n~]+?)(?<!~)~(?!~)/g, \"~~$1~~\");\n return result;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chat-adapter/whatsapp",
3
- "version": "4.21.0",
3
+ "version": "4.22.0",
4
4
  "description": "WhatsApp adapter for chat - WhatsApp Business Cloud API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,8 +16,8 @@
16
16
  "dist"
17
17
  ],
18
18
  "dependencies": {
19
- "@chat-adapter/shared": "4.21.0",
20
- "chat": "4.21.0"
19
+ "@chat-adapter/shared": "4.22.0",
20
+ "chat": "4.22.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^25.3.2",