npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

qwen-channel-feishu-polling

v0.1.4

Published

Qwen Code channel plugin for Feishu (Lark) via REST API polling — no WebSocket or webhook required

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 (交互卡片流式更新)
    ↓
飞书群聊 (实时更新的卡片)

快速开始

第一步:创建飞书应用

  1. 打开 飞书开放平台,创建或选择一个应用
  2. 进入「添加应用能力」→ 启用机器人
  3. 进入「权限管理」→ 添加权限:
    • im:message — 读取和发送消息
    • im:message:send_as_bot — 以机器人身份发送消息
  4. 进入「事件与回调」→ 添加事件 im.message.receive_v1(接收消息)
  5. 创建版本并发布应用

第二步:获取凭证

你需要 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 等 |


常见问题

机器人不响应

  1. 确认 clientIdclientSecret 正确
  2. 确认应用有 im:message 权限
  3. 确认 chatId 是群聊 ID(以 oc_ 开头),不是群名称
  4. 确认 botOpenId 正确
  5. 查看终端输出是否有 API 报错

关于 @mention:当 groupPolicy 设为 "open"requireMention 设为 false 时,群聊中不需要 @机器人也会响应。

消息重复处理

插件使用永久去重机制(processingMessages Set),同一个 message_id 不会被处理两次。如果仍出现重复,检查是否重启了服务(重启后会跳过历史消息)。

卡片一直显示"生成中"

这通常表示 Agent 正在处理中,请等待。如果长时间无响应(超过 1 分钟),可能是 Agent 进程卡住,重启渠道服务:

qwen channel stop && qwen channel start my-feishu

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