Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
23 changes: 23 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,18 @@ 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) {
adapters.messenger = withRecording(
createMessengerAdapter({
userName: "Chat SDK Bot",
logger: logger.child("messenger"),
}),
"messenger",
MESSENGER_METHODS
);
}

// 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