跳转到内容

输入关键词开始搜索文档。

Messenger

最后更新 查看 Markdown 源文件@flue/messenger

快速开始

通过 Messenger 蓝图,将经过验证的 webhook 入口和应用自有 API 行为添加到现有 Flue 项目中。在终端或你选择的编码智能体中运行以下命令:

flue add channel messenger

概述

该蓝图安装 @flue/messenger,在源码根目录创建 channels/messenger.ts 模块,包含命名的 channel 和项目自有的 client 导出,并修改选定的智能体以绑定消息工具。

src/channels/messenger.ts(节选)
import { createMessengerChannel } from '@flue/messenger';
import { dispatch } from '@flue/runtime';
import { Assistant } from '../agents/assistant.ts';

export const channel = createMessengerChannel({
  verifyToken: process.env.MESSENGER_VERIFY_TOKEN!,
  appSecret: process.env.MESSENGER_APP_SECRET!,
  async webhook({ entry }) {
    for (const event of entry.messaging) {
      if (!event.message || event.message.is_echo) continue;
      const senderId = event.sender.id;
      if (!senderId) continue;

      await dispatch(Assistant, {
        id: channel.instanceId(senderId),
        message: {
          kind: 'signal',
          type: 'messenger.message.received',
          body: event.message.text ?? '',
          attributes: {
            senderId,
            recipientId: event.recipient.id,
            messageId: event.message.mid,
          },
        },
      });
    }
  },
});

新消息会被接收进绑定到该发送者的智能体;回声消息和其他事件类型被忽略。完整的生成模块还处理消息回执、帖子反应和消息传递确认,并让绑定的智能体通过 Graph API 发送消息。

挂载渠道

渠道仅在 app.ts 挂载的位置提供 HTTP 路由。挂载模块中命名导出的 channel:

src/app.ts
import { channel as messenger } from './channels/messenger.ts';

app.route('/channels/messenger', messenger.route());

channel.route() 是一个纯路由器工厂,相对于挂载路径提供渠道声明的路由。本指南中的 webhook 路径假定使用常规的 /channels/messenger 挂载;不同的挂载路径会相应调整。分发目标智能体模块携带 'use agent' 指令——该指令负责注册,因此仅用于分发的智能体不需要自己的 HTTP 挂载。

配置

变量用途
MESSENGER_VERIFY_TOKEN必需 — 验证 webhook 订阅请求。
MESSENGER_APP_SECRET必需 — 验证入站 webhook 负载签名。
MESSENGER_PAGE_ACCESS_TOKEN必需 — 认证出站 Graph API 调用。

它安装 @flue/messenger。蓝图创建一个渠道模块,导出命名的 channel 和项目自有的 client。

在 Meta 开发者门户中配置 webhook:

  1. 创建或选择一个 Meta 应用
  2. 启用 Messenger 产品
  3. 配置 webhook 回调 URL 和验证令牌
  4. 订阅消息事件

将 webhook URL 配置为:

https://example.com/channels/messenger/webhook

验证令牌、应用密钥和页面访问令牌是单独的凭据。

渠道模块

src/channels/messenger.ts
import { createMessengerChannel } from '@flue/messenger';
import { defineTool, dispatch, useModel } from '@flue/runtime';
import { Assistant } from '../agents/assistant.ts';

export const channel = createMessengerChannel({
  verifyToken: process.env.MESSENGER_VERIFY_TOKEN!,
  appSecret: process.env.MESSENGER_APP_SECRET!,

  // 路径:/channels/messenger/webhook
  async webhook({ entry }) {
    for (const event of entry.messaging) {
      // 过滤是应用策略:在 Meta 中订阅你想要的事件。
      if (event.message && !event.message.is_echo) {
        const senderId = event.sender.id;
        if (!senderId) continue;

        await dispatch(Assistant, {
          id: channel.instanceId(senderId),
          message: {
            kind: 'signal',
            type: 'messenger.message.received',
            body: event.message.text ?? '',
            attributes: {
              senderId,
              recipientId: event.recipient.id,
              messageId: event.message.mid,
            },
          },
        });
      }
    }
  },
});

export function sendMessage(ref: { recipientId: string }) {
  return defineTool({
    name: 'send_messenger_message',
    description: '向绑定到此智能体的 Messenger 对话发送消息。',
    input: v.object({ text: v.pipe(v.string(), v.minLength(1)) }),
    async run({ data: { text } }) {
      const response = await fetch(
        `https://graph.facebook.com/v19.0/me/messages?access_token=${process.env.MESSENGER_PAGE_ACCESS_TOKEN}`,
        {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            recipient: { id: ref.recipientId },
            message: { text },
          }),
        },
      );
      const result = await response.json();
      return { output: { messageId: result.message_id } };
    },
  });
}

每个经过验证的消息事件都会以其原生的 Messenger webhook 结构转发。选择对哪些事件采取行动是应用策略——在 Meta 中配置 webhook 订阅,并在处理程序中根据事件类型进行分支。

绑定工具

src/agents/assistant.ts
'use agent';
import { useInitialData, useModel, useTool } from '@flue/runtime';
import { sendMessage } from '../channels/messenger.ts';

const initialData = v.object({ recipientId: v.string() });

export function Assistant() {
  useModel('anthropic/claude-haiku-4-5');
  const data = useInitialData<v.InferOutput<typeof initialData>>();
  if (!data) throw new Error('此智能体由 Messenger 渠道分发创建。');
  useTool(sendMessage(data));
  return '向绑定的 Messenger 对话发布简洁的回答。';
}

Assistant.initialData = initialData;

模型选择消息内容。它不选择任意的 Messenger 对话、凭据或 API 方法。此工具创建普通的页面消息。工具接受来自可信应用代码的 recipientId。

交付和运行时行为

Messenger 交付是至少一次的。该渠道是无状态的,不会对事件进行去重。在不允许重复接收时,在分发之前在应用自有的持久存储中声明消息 ID。

该包在 Node 和 Cloudflare Workers 上运行,需要 Flue 的 nodejs_compat 设置。

参阅 @flue/messenger README。