qwen-channel-feishu-polling
v0.1.4
Published
Qwen Code channel plugin for Feishu (Lark) via REST API polling — no WebSocket or webhook required
Maintainers
Readme
qwen-channel-feishu-polling
Qwen Code 飞书消息渠道插件 —— 纯 HTTP 轮询模式,无需 WebSocket,无需 Webhook,无需公网 IP。
专为企业内网设计:当 WebSocket 连接和 Webhook 回调被内网策略阻断,但出站 HTTP 请求(访问飞书 Open API)正常时,本插件通过定时轮询飞书消息 REST API 实现消息收发。
特性
- ✅ 纯 HTTP 轮询 —— 不需要 WebSocket、Webhook、公网 URL
- ✅ 交互卡片流式输出(实时 Markdown 渲染)
- ✅ 收到消息自动加"了解"表情反馈
- ✅ 群聊 @机器人 自动识别
- ✅ 消息去重(永久去重,无 TTL)
- ✅ 同一群聊复用同一个 session(串行执行,避免竞态条件)
- ✅ 连续消息自动合并(collect 模式)
- ✅ 兼容 Qwen Code 渠道插件体系
架构
飞书群聊消息
↓ (每 3 秒 GET /im/v1/messages)
FeishuPollingChannel (本插件)
↓ (构建 Envelope → 串行 handleInbound)
ChannelBase (访问控制 / 会话路由 / 命令处理 / collect 模式)
↓ (ACP 协议)
qwen-code --acp (Agent 进程)
↓ (response chunk)
FeishuPollingChannel → POST /im/v1/messages (交互卡片流式更新)
↓
飞书群聊 (实时更新的卡片)快速开始
第一步:创建飞书应用
- 打开 飞书开放平台,创建或选择一个应用
- 进入「添加应用能力」→ 启用机器人
- 进入「权限管理」→ 添加权限:
im:message— 读取和发送消息im:message:send_as_bot— 以机器人身份发送消息
- 进入「事件与回调」→ 添加事件
im.message.receive_v1(接收消息) - 创建版本并发布应用
第二步:获取凭证
你需要 4 个值:
| 凭证 | 获取方式 |
|------|----------|
| clientId (App ID) | 飞书开放平台 → 凭证与基础信息 |
| clientSecret (App Secret) | 飞书开放平台 → 凭证与基础信息 |
| botOpenId | 调用 GET /bot/v3/info 接口,或从应用设置中获取 |
| chatId | 在群聊中发送一条消息,通过开放平台「调试工具」查看消息事件中的 chat_id |
💡 小技巧:也可以用飞书 API 调试台在线获取 chatId: 打开 https://open.feishu.cn/api-explorer → 选择「获取用户或机器人所在的群列表」接口调用即可。
第三步:安装
方式 A:npm 安装(推荐,内网有 npmmirror 镜像)
# 直接安装(无需账号密码)
npm install qwen-channel-feishu-polling
# 注册为 Qwen Code 扩展
qwen extensions link ./node_modules/qwen-channel-feishu-polling方式 B:源码安装(内网无 npm 镜像)
# 把项目目录拷贝到内网电脑
cd qwen-channel-feishu-polling
# 链接本地 Qwen Code 的 channel-base
npm install /path/to/qwen-code/packages/channels/base
npm run build
# 注册为 Qwen Code 扩展
qwen extensions link .⚠️ 如果
qwen extensions link不可用,也可以手动编辑~/.qwen/extensions.json添加扩展配置。
第四步:配置
编辑 ~/.qwen/settings.json,在 channels 下添加:
{
"channels": {
"my-feishu": {
"type": "feishu-polling",
"clientId": "cli_xxxxxxxxxxxxxxxx",
"clientSecret": "你的 App Secret",
"chatId": "oc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"botOpenId": "ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"pollInterval": 3000,
"senderPolicy": "open",
"sessionScope": "user",
"cwd": "/path/to/your/project",
"groupPolicy": "open",
"dispatchMode": "collect",
"groups": {
"*": {
"requireMention": false
}
}
}
}
}说明:
clientId/clientSecret— 替换为你的飞书应用凭证chatId— 替换为你要监听的群聊 ID(以oc_开头)botOpenId— 替换为你的机器人 Open ID(以ou_开头)cwd— 替换为 Agent 工作目录dispatchMode: "collect"— 连续消息自动合并requireMention: false— 群聊中不需要 @机器人
第五步:启动
qwen channel start my-feishu启动成功后会看到:
[FeishuPolling:my-feishu] Skipped 50 existing messages.
[FeishuPolling:my-feishu] Connected. Polling chat=oc_xxx every 3000ms.打开飞书群聊发消息,3-5 秒后会看到流式交互卡片响应 🎉
配置项说明
| 配置项 | 必填 | 默认值 | 说明 |
|--------|------|--------|------|
| type | ✅ | — | 固定为 "feishu-polling" |
| clientId | ✅ | — | 飞书应用 App ID |
| clientSecret | ✅ | — | 飞书应用 App Secret |
| chatId | ✅ | — | 监听的群聊/私聊 ID |
| botOpenId | ✅ | — | 机器人的 Open ID(用于识别 @mention) |
| pollInterval | ❌ | 3000 | 轮询间隔(毫秒),建议 ≥ 3000 避免触发限流 |
| senderPolicy | ❌ | "open" | 访问控制:"open" / "allowlist" / "pairing" |
| sessionScope | ❌ | "user" | 会话范围:"user" / "thread" / "single" |
| cwd | ✅ | — | Agent 工作目录 |
| groupPolicy | ❌ | "disabled" | 群聊策略:"disabled" / "allowlist" / "open" |
| dispatchMode | ❌ | "steer" | 消息调度模式:"collect" / "steer" / "followup" |
| groups.*.requireMention | ❌ | true | 群聊是否需要 @机器人才响应 |
| proxy | ❌ | — | HTTP 代理地址(仅影响飞书 API 请求,不影响 Agent/模型请求) |
dispatchMode 模式说明
| 模式 | 行为 |
|------|------|
| collect | 连续消息自动合并为一个 prompt 发给 Agent(推荐) |
| steer | 新消息中断当前响应,重新生成 |
| followup | 新消息排队等待,当前响应完成后依次处理 |
环境变量方式
如果不想把密钥写在配置文件中,可以用环境变量引用:
{
"clientId": "${FEISHU_APP_ID}",
"clientSecret": "${FEISHU_APP_SECRET}",
"chatId": "${FEISHU_CHAT_ID}",
"botOpenId": "${FEISHU_BOT_OPEN_ID}"
}使用方法
基本用法
在飞书群聊中直接发消息,机器人会自动响应:
你:你好
机器人:你好!我是 Qwen Code,有什么可以帮你的?斜杠命令
插件支持以下内置命令:
| 命令 | 作用 |
|------|------|
| /help | 显示所有可用命令 |
| /clear | 清除当前 session,下一条消息开启新对话 |
| /reset | 同 /clear(别名) |
| /new | 同 /clear(别名) |
| /status | 显示当前 session 状态 |
| /model | 查看当前模型,或切换模型(如 /model glm-5.2) |
Agent 的其他斜杠命令也会自动可用,用
/help查看完整列表。
会话管理
- 同一个群聊复用同一个 session,Agent 会记住对话上下文
- 发
/new或/clear清除 session,下一条消息开始新对话 - 消息串行处理:连续发多条消息时,前一个处理完后才会处理下一个
群聊 @mention
groupPolicy: "open"+requireMention: false→ 不需要 @机器人也会响应groupPolicy: "open"+requireMention: true→ 必须 @机器人才响应- 私聊不需要 @
连续消息合并
配置 dispatchMode: "collect" 后,在 Agent 处理期间发送的连续消息会自动合并为一个 prompt:
你:你好
你:测试闭环 ← 这两条会被合并
你:all
机器人:(一次性收到 "你好\n\n测试闭环\n\nall" 并回复)注意:只有在 Agent 正在处理时收到的消息才会被合并。如果 Agent 空闲时快速发多条,会串行处理。
监听多个群聊
每个插件实例监听一个 chatId。如需监听多个群聊,创建多个实例:
{
"channels": {
"feishu-team-a": {
"type": "feishu-polling",
"clientId": "cli_xxx",
"clientSecret": "xxx",
"chatId": "oc_aaaaaaaa",
"botOpenId": "ou_xxx",
"senderPolicy": "open",
"sessionScope": "user",
"cwd": "/path/to/project-a",
"groupPolicy": "open",
"dispatchMode": "collect",
"groups": { "*": { "requireMention": false } }
},
"feishu-team-b": {
"type": "feishu-polling",
"clientId": "cli_xxx",
"clientSecret": "xxx",
"chatId": "oc_bbbbbbbb",
"botOpenId": "ou_xxx",
"senderPolicy": "open",
"sessionScope": "user",
"cwd": "/path/to/project-b",
"groupPolicy": "open",
"dispatchMode": "collect",
"groups": { "*": { "requireMention": false } }
}
}
}与内置飞书渠道对比
| 特性 | 内置 feishu | 本插件 feishu-polling |
|------|------------|----------------------|
| 连接方式 | WebSocket / Webhook | HTTP 轮询 |
| 实时性 | ✅ 毫秒级 | ⚠️ 3-5 秒延迟 |
| 内网可用 | ❌ 需要出站 WS 或入站 HTTP | ✅ 仅需出站 HTTP |
| 交互卡片 | ✅ | ✅ |
| 流式输出 | ✅ | ✅ |
| 停止按钮 | ✅ 可点击停止 | ❌ 不显示(轮询模式无法接收回调) |
| 图片/文件 | ✅ | ❌ |
| 引用回复 | ✅ | ❌ |
| 多群聊 | ✅ | ⚠️ 每群一个实例 |
| 会话管理 | ✅ | ✅ /new /clear /status |
| 斜杠命令 | ✅ | ✅ /help /model 等 |
常见问题
机器人不响应
- 确认
clientId、clientSecret正确 - 确认应用有
im:message权限 - 确认
chatId是群聊 ID(以oc_开头),不是群名称 - 确认
botOpenId正确 - 查看终端输出是否有 API 报错
关于 @mention:当
groupPolicy设为"open"且requireMention设为false时,群聊中不需要 @机器人也会响应。
消息重复处理
插件使用永久去重机制(processingMessages Set),同一个 message_id 不会被处理两次。如果仍出现重复,检查是否重启了服务(重启后会跳过历史消息)。
卡片一直显示"生成中"
这通常表示 Agent 正在处理中,请等待。如果长时间无响应(超过 1 分钟),可能是 Agent 进程卡住,重启渠道服务:
qwen channel stop && qwen channel start my-feishucraft/drainMidTurnQueue 错误
日志中出现 Method not found: craft/drainMidTurnQueue 是 Qwen Code ACP 协议的已知警告,不影响 Agent 正常响应,可以忽略。
Token 获取失败
- 检查内网是否能访问
https://open.feishu.cn - 如果内网有自定义域名(如
feishu.mycompany.com),需要修改源码中的FEISHU_BASE常量
消息延迟太大
- 减小
pollInterval(如 2000ms),但注意不要低于 1000ms 以免触发飞书 API 限流 - 确认网络延迟正常(
curl -w "%{time_total}" https://open.feishu.cn)
找不到 @qwen-code/channel-base
# 找到 Qwen Code 的安装目录
which qwen
# 然后链接到本地的 channel-base
npm install /path/to/qwen-code/packages/channels/base内网代理访问飞书 API
如果内网需要通过代理才能访问飞书 API,在配置中加 proxy 字段:
{
"channels": {
"my-feishu": {
"type": "feishu-polling",
"clientId": "...",
"clientSecret": "...",
"chatId": "...",
"botOpenId": "...",
"proxy": "http://proxy.company.com:8080"
}
}
}或者设置系统环境变量:
HTTPS_PROXY=http://proxy.company.com:8080 qwen channel start my-feishu注意:代理仅影响飞书 API 请求(轮询消息、发送卡片等),不影响 Agent 子进程与模型 API 的通信。两者互不干扰。
开发
# 安装依赖
npm install
# 类型检查
npx tsc --noEmit
# 编译
npm run build
# 本地调试(链接模式)
qwen extensions link .
qwen channel start my-feishu项目结构
qwen-channel-feishu-polling/
├── package.json # npm 包配置
├── tsconfig.json # TypeScript 配置
├── qwen-extension.json # Qwen Code 扩展 manifest
├── README.md # 本文档
└── src/
├── index.ts # 插件入口(导出 plugin 对象)
├── feishu-client.ts # 飞书 REST API 客户端
└── adapter.ts # ChannelBase 适配器实现License
MIT
