Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-facebook-adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chat-adapter/messenger": minor
---

Add Messenger adapter with support for messages, reactions, postbacks, typing indicators, and webhook verification
10 changes: 10 additions & 0 deletions apps/docs/adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@
"beta": true,
"readme": "https://github.com/vercel/chat/tree/main/packages/adapter-whatsapp"
},
{
"name": "Messenger",
"slug": "messenger",
"type": "platform",
"description": "Build bots for Facebook Messenger with support for templates, buttons, reactions, and postbacks.",
"packageName": "@chat-adapter/messenger",
"icon": "messenger",
"beta": true,
"readme": "https://github.com/vercel/chat/tree/main/packages/adapter-messenger"
},
{
"name": "Web",
"slug": "web",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ioredis,
linear,
memory,
messenger,
postgres,
redis,
slack,
Expand All @@ -41,6 +42,7 @@ const iconMap: Record<
postgres,
memory,
whatsapp,
messenger,
};

interface AdapterCardProps {
Expand Down
80 changes: 40 additions & 40 deletions apps/docs/content/docs/adapters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,55 @@ Ready to build your own? Follow the [building](/docs/contributing/building) guid

### Messaging

| Feature | [Slack](/adapters/slack) | [Teams](/adapters/teams) | [Google Chat](/adapters/google-chat) | [Discord](/adapters/discord) | [Telegram](/adapters/telegram) | [GitHub](/adapters/github) | [Linear](/adapters/linear) | [WhatsApp](/adapters/whatsapp) |
|---------|-------|-------|-------------|---------|---------|--------|--------|-----------|
| Post message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
| Edit message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
| Delete message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
| File uploads | <Check /> | <Check /> | <Cross /> | <Check /> | <Warn /> Single file | <Cross /> | <Cross /> | <Check /> Images, audio, docs |
| Streaming | <Check /> Native | <Warn /> Native (DMs) / Post+Edit | <Warn /> Post+Edit | <Warn /> Post+Edit | <Warn /> Post+Edit | <Cross /> | <Cross /> | <Cross /> |
| Scheduled messages | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| Feature | [Slack](/adapters/slack) | [Teams](/adapters/teams) | [Google Chat](/adapters/google-chat) | [Discord](/adapters/discord) | [Telegram](/adapters/telegram) | [GitHub](/adapters/github) | [Linear](/adapters/linear) | [WhatsApp](/adapters/whatsapp) | [Messenger](/adapters/messenger) |
|---------|-------|-------|-------------|---------|---------|--------|--------|-----------|-----------|
| Post message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
| Edit message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
| Delete message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
| File uploads | <Check /> | <Check /> | <Cross /> | <Check /> | <Warn /> Single file | <Cross /> | <Cross /> | <Check /> Images, audio, docs | <Cross /> |
| Streaming | <Check /> Native | <Warn /> Native (DMs) / Post+Edit | <Warn /> Post+Edit | <Warn /> Post+Edit | <Warn /> Post+Edit | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| Scheduled messages | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |

### Rich content

| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|
| Card format | Block Kit | Adaptive Cards | Google Chat Cards | Embeds | Markdown + inline keyboard buttons | GFM Markdown | Markdown | WhatsApp templates |
| Buttons | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Inline keyboard callbacks | <Cross /> | <Cross /> | <Check /> Interactive replies |
| Link buttons | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Inline keyboard URLs | <Cross /> | <Cross /> | <Cross /> |
| Select menus | <Check /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| Tables | <Check /> Block Kit | <Check /> GFM | <Warn /> ASCII | <Check /> GFM | <Warn /> ASCII | <Check /> GFM | <Check /> GFM | <Cross /> |
| Fields | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Template variables |
| Images in cards | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> | <Check /> |
| Modals | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp | Messenger |
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|-----------|
| Card format | Block Kit | Adaptive Cards | Google Chat Cards | Embeds | Markdown + inline keyboard buttons | GFM Markdown | Markdown | WhatsApp templates | Generic/Button Templates |
| Buttons | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Inline keyboard callbacks | <Cross /> | <Cross /> | <Check /> Interactive replies | <Warn /> Max 3, postback |
| Link buttons | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Inline keyboard URLs | <Cross /> | <Cross /> | <Cross /> | <Check /> |
| Select menus | <Check /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| Tables | <Check /> Block Kit | <Check /> GFM | <Warn /> ASCII | <Check /> GFM | <Warn /> ASCII | <Check /> GFM | <Check /> GFM | <Cross /> | <Warn /> ASCII |
| Fields | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Template variables | <Warn /> ASCII |
| Images in cards | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> | <Check /> | <Check /> |
| Modals | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |

### Conversations

| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|
| Slash commands | <Check /> | <Cross /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| Mentions | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
| Add reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
| Remove reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Warn /> | <Warn /> | <Cross /> |
| Typing indicator | <Cross /> | <Check /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
| DMs | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> |
| Ephemeral messages | <Check /> Native | <Cross /> | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| User lookup ([`getUser`](/docs/api/chat#getuser)) | <Check /> | <Warn /> Cached | <Warn /> Cached | <Check /> | <Warn /> Seen users | <Check /> | <Check /> | <Cross /> |
| Parent subject ([`message.subject`](/docs/subject)) | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Check /> | <Cross /> |
| Native client ([`.client`](/docs/api/chat#getadapter)) | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Check /> | <Cross /> |
| Custom API endpoint (`apiUrl`) | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp | Messenger |
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|-----------|
| Slash commands | <Check /> | <Cross /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| Mentions | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> |
| Add reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
| Remove reactions | <Check /> | <Cross /> | <Check /> | <Check /> | <Check /> | <Warn /> | <Warn /> | <Cross /> | <Cross /> |
| Typing indicator | <Cross /> | <Check /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Check /> |
| DMs | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> | <Check /> |
| Ephemeral messages | <Check /> Native | <Cross /> | <Check /> Native | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> |
| User lookup ([`getUser`](/docs/api/chat#getuser)) | <Check /> | <Warn /> Cached | <Warn /> Cached | <Check /> | <Warn /> Seen users | <Check /> | <Check /> | <Cross /> | <Warn /> Cached |
| Parent subject ([`message.subject`](/docs/subject)) | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
| Native client ([`.client`](/docs/api/chat#getadapter)) | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Cross /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
| Custom API endpoint (`apiUrl`) | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |

### Message history

| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp |
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|
| Fetch messages | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Cached | <Check /> | <Check /> | <Warn /> Cached sent messages only |
| Fetch single message | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Warn /> Cached | <Cross /> | <Cross /> | <Warn /> Cached sent messages only |
| Fetch thread info | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
| Fetch channel messages | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Cached | <Check /> | <Cross /> | <Warn /> Cached sent messages only |
| List threads | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> | <Cross /> |
| Fetch channel info | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
| Post channel message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> |
| Feature | Slack | Teams | Google Chat | Discord | Telegram | GitHub | Linear | WhatsApp | Messenger |
|---------|-------|-------|-------------|---------|----------|--------|--------|-----------|-----------|
| Fetch messages | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Cached | <Check /> | <Check /> | <Warn /> Cached sent messages only | <Warn /> Cached sent messages only |
| Fetch single message | <Check /> | <Cross /> | <Cross /> | <Cross /> | <Warn /> Cached | <Cross /> | <Cross /> | <Warn /> Cached sent messages only | <Warn /> Cached |
| Fetch thread info | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> |
| Fetch channel messages | <Check /> | <Check /> | <Check /> | <Check /> | <Warn /> Cached | <Check /> | <Cross /> | <Warn /> Cached sent messages only | <Warn /> Cached |
| List threads | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
| Fetch channel info | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> |
| Post channel message | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> | <Check /> | <Check /> |

<Callout type="info">
<Warn /> indicates partial support — the feature works with limitations. See individual adapter pages for details.
Expand Down
16 changes: 16 additions & 0 deletions apps/docs/lib/logos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -511,3 +511,19 @@ export const whatsapp = (props: ComponentProps<"svg">) => (
</defs>
</svg>
);

export const messenger = (props: ComponentProps<"svg">) => (
<svg
fill="none"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
clipRule="evenodd"
d="M0 7.76C0 3.3 3.5 0 8 0s8 3.3 8 7.76-3.5 7.76-8 7.76q-1.22 0-2.32-.3a.6.6 0 0 0-.42.03l-1.6.7a.64.64 0 0 1-.89-.57l-.04-1.42a.6.6 0 0 0-.22-.46A7.6 7.6 0 0 1 0 7.76M5.55 6.3 3.2 10.03c-.23.36.21.76.55.5l2.52-1.91a.5.5 0 0 1 .58 0l1.87 1.4a1.2 1.2 0 0 0 1.73-.32l2.36-3.73c.22-.36-.22-.76-.56-.5L9.73 7.38a.5.5 0 0 1-.58 0l-1.87-1.4a1.2 1.2 0 0 0-1.73.32"
fill="currentColor"
fillRule="evenodd"
/>
</svg>
);
5 changes: 5 additions & 0 deletions examples/nextjs-chat/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ BOT_USERNAME=mybot
# DISCORD_BOT_TOKEN=your-bot-token
# DISCORD_PUBLIC_KEY=your-public-key

# Facebook Messenger (optional)
# FACEBOOK_APP_SECRET=your-app-secret
# FACEBOOK_PAGE_ACCESS_TOKEN=your-page-access-token
# FACEBOOK_VERIFY_TOKEN=your-verify-token

# GitHub (optional) - use PAT OR GitHub App, not both
# PAT authentication:
# GITHUB_TOKEN=ghp_xxxxxxxxxxxx
Expand Down
1 change: 1 addition & 0 deletions examples/nextjs-chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@ai-sdk/react": "^3.0.176",
"@chat-adapter/discord": "workspace:*",
"@chat-adapter/messenger": "workspace:*",
"@chat-adapter/gchat": "workspace:*",
"@chat-adapter/github": "workspace:*",
"@chat-adapter/linear": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function POST(
}

// GET handler — serves as health check, but also forwards to webhook handler
// for platforms that need GET verification (e.g. WhatsApp challenge-response)
// for platforms that need GET verification (e.g. WhatsApp/Facebook challenge-response)
export async function GET(
request: Request,
{ params }: { params: Promise<{ platform: string }> }
Expand Down
34 changes: 34 additions & 0 deletions examples/nextjs-chat/src/lib/adapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import {
} from "@chat-adapter/gchat";
import { createGitHubAdapter, type GitHubAdapter } from "@chat-adapter/github";
import { createLinearAdapter, type LinearAdapter } from "@chat-adapter/linear";
import {
createMessengerAdapter,
type MessengerAdapter,
} from "@chat-adapter/messenger";
import { createSlackAdapter, type SlackAdapter } from "@chat-adapter/slack";
import { createTeamsAdapter, type TeamsAdapter } from "@chat-adapter/teams";
import {
Expand All @@ -30,6 +34,7 @@ export interface Adapters {
gchat?: GoogleChatAdapter;
github?: GitHubAdapter;
linear?: LinearAdapter;
messenger?: MessengerAdapter;
slack?: SlackAdapter;
teams?: TeamsAdapter;
telegram?: TelegramAdapter;
Expand Down Expand Up @@ -93,6 +98,12 @@ const LINEAR_METHODS = [
"addReaction",
"fetchMessages",
];
const MESSENGER_METHODS = [
"postMessage",
"startTyping",
"openDM",
"fetchMessages",
];
const TELEGRAM_METHODS = [
"postMessage",
"editMessage",
Expand Down Expand Up @@ -139,6 +150,29 @@ export function buildAdapters(): Adapters {
);
}

// Messenger adapter (optional) - env vars: FACEBOOK_APP_SECRET, FACEBOOK_PAGE_ACCESS_TOKEN, FACEBOOK_VERIFY_TOKEN
if (
process.env.FACEBOOK_APP_SECRET &&
process.env.FACEBOOK_PAGE_ACCESS_TOKEN &&
process.env.FACEBOOK_VERIFY_TOKEN
) {
try {
adapters.messenger = withRecording(
createMessengerAdapter({
userName: "Chat SDK Bot",
logger: logger.child("messenger"),
}),
"messenger",
MESSENGER_METHODS
);
} catch (err) {
console.warn(
"[chat] Failed to create messenger adapter:",
err instanceof Error ? err.message : err
);
}
}

// Slack adapter (optional) - env vars: SLACK_SIGNING_SECRET + (SLACK_BOT_TOKEN or SLACK_CLIENT_ID/SECRET)
if (process.env.SLACK_SIGNING_SECRET) {
adapters.slack = withRecording(
Expand Down
32 changes: 28 additions & 4 deletions examples/nextjs-chat/src/lib/bot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const AI_MENTION_REGEX = /\bAI\b/i;
const DISABLE_AI_REGEX = /disable\s*AI/i;
const ENABLE_AI_REGEX = /enable\s*AI/i;
const DM_ME_REGEX = /^dm\s*me$/i;
const POSTCARD_TRIGGER_REGEX = /^post-card$/i;

// Hardcoded user key for testing the Transcripts API — every inbound message
// is persisted under this single key, so you can exercise append/list/delete
Expand Down Expand Up @@ -181,7 +182,26 @@ bot.onMemberJoinedChannel(async (event) => {

// Handle direct messages — AI conversation by default
// This fires on every DM, regardless of subscription status
bot.onDirectMessage(async (_thread, message, channel) => {
bot.onDirectMessage(async (thread, message, channel) => {
if (POSTCARD_TRIGGER_REGEX.test(message.text.trim())) {
await thread.post(
<Card title={`${emoji.sparkles} Test Menu`}>
<Text>Test these button actions:</Text>
<Actions>
<Button id="hello" style="primary">
Say Hello
</Button>
<Button id="info">Show Info</Button>
<Button id="who-am-i">Who Am I</Button>
<Button id="goodbye" style="danger">
Goodbye
</Button>
</Actions>
</Card>
);
return;
}

await channel.startTyping("Thinking...");
let history: AiMessage[];
try {
Expand All @@ -201,7 +221,7 @@ bot.onDirectMessage(async (_thread, message, channel) => {
}
try {
const result = await agent.stream({ prompt: history });
await channel.post(result.fullStream);
await thread.post(result.fullStream);
} catch (err) {
console.error("Error in DM AI response:", err);
await channel.post(
Expand Down Expand Up @@ -987,9 +1007,13 @@ bot.onReaction(["thumbs_up", "heart", "fire", "rocket"], async (event) => {
return;
}

// GChat and Teams bots cannot add reactions via their APIs
// GChat, Teams, and Messenger bots cannot add reactions via their APIs
// Respond with a message instead
if (event.adapter.name === "gchat" || event.adapter.name === "teams") {
if (
event.adapter.name === "gchat" ||
event.adapter.name === "teams" ||
event.adapter.name === "messenger"
) {
await event.adapter.postMessage(
event.threadId,
`Thanks for the ${event.rawEmoji}!`
Expand Down
Loading
Loading