Skip to content

文档同步

本页由 plugins/adapters/slack/README.md 自动生成。请修改包内 README 后运行 pnpm sync:adapter-docs。

@zhin.js/adapter-slack ​

Zhin.js Slack 适配器(Plugin Runtime),优先 Socket Mode,也可经 Runtime Host HTTP Events API 收发消息。

功能 ​

  • Socket Mode(默认):WebSocket 长连接,无需公网 URL
  • HTTP Events API:httpHostToken POST(签名验证),非 legacy host-router/Koa
  • 入站经 endpoint[endpointSpec].events;出站 send({ conversation, payload }) → chat.postMessage / Block Kit
  • 约定式 defineAdapter / definePlugin(无需 usePlugin)
  • Block Kit 按钮、斜杠命令、消息编辑、表情反应等(见 agents/slack/skills/slack-*/tools/)

渐进式回复 ​

streaming 默认 true,可在插件或单个 endpoint 配置中设为 false,恢复普通完整发送。原生流使用同一条消息的 chat.startStream、chat.appendStream 与 chat.stopStream;全程固定为 chunks 模式,正文和 Markdown 按已确认的源码前缀追加。

私聊和普通频道都需要既有线程或明确的引用目标;普通频道还需要可信的发言者和 workspace。缺少这些信息时保留普通发送,不为获得流式能力创建新线程。引用和 @ 前缀只提交一次;未知结果不重发。媒体和按钮由统一发送链按原始顺序投递,不重复上传。

streamingIntervalMs 默认 1000 毫秒,用于后续回复更新间隔;首段立即发送,中间更新由统一回复控制器合并。

原生流的累计正文上限为 12,000 字符,总时长上限为 10 分钟,追加内容不会延长时限。平台明确返回已有流已结束时,纯文字回复保留已确认正文,在同一话题的新消息中继续未发送的部分;发送结果未知时不重发。详见 Slack 原生流限制。

纯文字可以在已确认的容量或时限边界续条。Markdown 保留单容器内的流式发送;需要续容器但无法保证独立格式时,会停止并保留已确认部分,不降级为纯文本。

安装 ​

bash
pnpm add @zhin.js/adapter-slack

Plugin Runtime ​

  • @zhin.js/adapter — 约定式 adapters/slack/index.ts(defineAdapter)
  • @zhin.js/core — endpoint[endpointSpec].events 入站、outboundMessageToken 出站
  • @zhin.js/host-http — 仅 HTTP 模式需要 httpHostToken 注册 Events 路由
  • zhin.js — plugin.ts(definePlugin)
  • 配置经插件 schema.json 落到顶层 <instanceKey>

入站:endpoint[endpointSpec].events.emit('message.receive', { conversation, message: { conversation, id }, content: text, sender, metadata })(conversation 为 ConversationRef:channel_type: im → kind private,其余 → kind group;线程根 ts 进 threadId)

出站:send({ conversation, payload }) → Web API(conversation.id 为 channel,conversation.threadId 为 thread_ts)

平台权限(platform permit) ​

  • plugin.ts 已注册 checker,Runtime Tool 权限统一经 Core canAccessTool();当前 Slack 入站没有可靠 sender role 时,受限工具按 fail-closed 拒绝,不会静默放行。

模式对比 ​

模式socketMode适用场景额外字段
Socket Mode(默认)true本地/内网,无需公网 URLappToken(xapp-...)
HTTP Eventsfalse生产环境有公网 HTTPSsigningSecret + Runtime Host

前置条件 ​

  1. 创建 Slack App,安装到 Workspace,并授予收发消息所需 OAuth scopes。
  2. Socket Mode 创建 connections:write App-Level Token;HTTP 模式配置 Signing Secret 与 Events URL。
  3. 订阅需要的 bot events,并把应用加入目标频道。

最小配置(Socket Mode) ​

yaml
configVersion: 2
# zhin.config.yml(Plugin Runtime)
plugins:
  slack: "@zhin.js/adapter-slack"
slack:
  socketMode: true # 默认 true,可省略
  endpoints:
    - id: my-slack-bot
      token: ${SLACK_BOT_TOKEN}
      appToken: ${SLACK_APP_TOKEN}

多 workspace:一个插件实例挂多个 endpoint(endpoints 数组逐项覆盖顶层字段,id 必填):

yaml
configVersion: 2
plugins:
  slack: "@zhin.js/adapter-slack"
slack:
  endpoints:
    - id: team-a
      token: ${SLACK_BOT_TOKEN_A}
      appToken: ${SLACK_APP_TOKEN_A}
    - id: team-b
      token: ${SLACK_BOT_TOKEN_B}
      appToken: ${SLACK_APP_TOKEN_B}

HTTP Events 配置 ​

yaml
configVersion: 2
plugins:
  slack: "@zhin.js/adapter-slack"
slack:
  socketMode: false
  webhookPath: /slack/events # 可选,默认 /slack/events
  endpoints:
    - id: my-slack-bot
      token: ${SLACK_BOT_TOKEN}
      signingSecret: ${SLACK_SIGNING_SECRET}

主配置 plugins 映射需引用 @zhin.js/adapter-slack(instanceKey: slack)。 HTTP 模式下 Runtime Host(http)须已 listen;Slack App 的 Event Subscriptions / Interactivity / Slash Commands Request URL 指向 https://your-domain/slack/events。

环境变量 ​

变量说明
SLACK_BOT_TOKEN示例中由 YAML ${SLACK_BOT_TOKEN} 引用的 Bot User OAuth Token;变量名可自行定义
SLACK_APP_TOKEN示例中由 YAML ${SLACK_APP_TOKEN} 引用的 App-Level Token;变量名可自行定义
SLACK_SIGNING_SECRET示例中由 YAML ${SLACK_SIGNING_SECRET} 引用的 Signing Secret;变量名可自行定义

消息格式 ​

出站(Markdown → mrkdwn) ​

通用 Markdown(如 **粗体**)会转换为 Slack mrkdwn,并通过 Block Kit section 发送。

入站(mrkdwn → Markdown) ​

Slack mrkdwn通用 Markdown
*bold***bold**
_italic_*italic*
~strike~~~strike~~
<url|text>[text](url)

AI 工具 ​

类别路径
Permit 词汇PERMITS.md
平台工具(11 个)agents/slack/skills/slack-{channels,messages,users}/tools/(邀请、话题、反应、置顶、编辑等)
技能说明agents/slack/skills/slack-channels/SKILL.md、agents/slack/skills/slack-messages/SKILL.md、agents/slack/skills/slack-users/SKILL.md

限制 ​

  • 入站 mrkdwn → Markdown 为启发式转换
  • Modals / Select menus — 暂不支持
  • OAuth 安装流程 — 暂不支持
  • 旧 usePlugin、defineAdapter.create、公开 Endpoint 基类与 host-router 生产入口已删除;平台 Adapter 继承统一 Adapter 基类,Endpoint 继承实际原生 Client

Console 页面 ​

启动后打开 Console 的「Slack」页(地址 /slack),可以:

  • 查看运行中的连接、点「测试连接」做连通性诊断;
  • 在账号库保存、修改、启动、停止、替换或删除该 Adapter 的账号;新增仅保存,不创建 SDK Client,需手动启动;
  • 如果适配器要求登录,页面会显示 LoginAssist 的扫码 / 滑块 / 设备确认待办;

读取需要 slack:read(read 级别,内置角色 viewer 及以上都有);增删改账号等写操作需要 slack:write(operate 级别,operator 及以上都有,绑定到本适配器的角色也算),首次迁移还需管理员身份;全局所有者不受限,见插件权限的级别。页面里的「添加 endpoint」等价于原来的 slack endpoint add。

首次修改启动配置里的既有账号前,管理员必须核对清单并确认一次迁移。原 canonical 身份与历史保留,完整账号配置加密进入 Host 数据库;此后账号管理只写数据库,不修改启动文件或 .env,不重启进程。没有持久化账号端口的环境可使用静态配置连接,动态写操作明确不可用。

启停意图在进程重启后恢复。停止或删除会取消并关闭目标账号的在途操作,其他账号继续运行;删除保留历史。已启动账号的连接配置更新先关闭旧 incarnation,再仅重连该账号;平台身份变化按替换处理,分配新内部身份、保留旧历史,新账号不继承旧 Session、个人设置或启动意图。提交结果未知时先刷新核实,不自动重复 CAS 或平台操作。

Slack 的 bot token 更新通过只读 auth.test 核验 workspace 与 bot user。App-Level Token 和 HTTP signing secret 没有对应的只读身份核验接口,变更须使用显式账号替换;不以 bot token 的身份结果冒充这两项凭据的身份。

故障排查 ​

现象排查
Socket Mode 无法连接检查 xapp- Token、Socket Mode 与 connections:write scope
HTTP Events 返回 401检查 Signing Secret、原始请求体、服务器时钟与反向代理
频道消息未触发确认事件订阅、OAuth scopes,并邀请 App 进入频道
线程回复跑到主频道检查入站 thread_ts 是否保留为 Conversation threadId

许可证 ​

MIT

发送失败与回执 ​

成功发送返回平台 ts 组成的消息引用,不以本地时间替代缺失回执。 文件上传失败会让本次发送失败;分段消息中某段缺少有效回执时,停止后续分段。 前面已成功的文件或分段不会自动回滚;发送失败可能表示部分已送达,请先核对目标会话再重试。

Socket Mode 可控断线验收 ​

streamProxy: { port: 18443, serverName: "实际网关域名" } 使用 SDK 合法 clientOptions.agent。配置网关的 WSS 仅改 TCP 路由为 127.0.0.1:18443,保留真实 URL、Host、TLS SNI、证书链和域名校验。SDK 的 discovery API 仍用标准 HTTPS Agent 直连 slack.com;其他网关及 WebSocket Upgrade 到 API 域名均明确拒绝,不绕代理。日志仅包含安全 hostname,不打印完整 URL/query/token。

验收项目通过 SLACK_STREAM_PROXY_PORT / SLACK_STREAM_PROXY_SERVER_NAME 进程变量临时覆盖,需同时填写,留空直连。未知域名可先设占位值,从拒绝日志获取真实 hostname,再启动 scripts/platform-acceptance/tcp-fault-proxy.mjs --upstream-host 实际网关域名 --upstream-port 443 --port 18443 --control-port 18444;将 serverName 同步为此域名。不要覆盖已有 .env。平台更换动态网关时停止验收并更新固定上游,不能直连继续取恢复证据。

Socket SDK 的自动重连已关闭;start/stop/reconnect 由统一 Endpoint lifecycle 管理,SDK 保留协议心跳。断线销毁当前 Agent/Socket,重连新建实例;旧实例回调不进入消息链,stop 取消待重连。可观察 Endpoint transportState 的 open/重连/stopped。

先验证正常消息,再 POST 代理 /cut,观察 Socket 关闭和生命周期退出 open;POST /recover,观察新连接 open,再发送唯一新 probe 验证真实入站及可见回复。切断只覆盖 Socket 入站,普通 Web API 出站仍直连,不代表所有网络恢复或离线消息无损。完成后停止代理、移除临时覆盖。实际 SDK 的本地 TLS/hello 握手、断线恢复与防绕过回归通过,真实平台结果另记。

出站失败与重试边界 ​

默认 WebClient 禁用 SDK 网络/5xx 自动重试,并以 rejectRateLimitedCalls: true 返回 429,不在后台排队重发。平台明确返回拒绝记为 rejected;连接中断、408、5xx、缺少真实时间戳记为 unknown,调用者不得把 unknown 当成“未发送”盲目重试。实机 WSS 故障代理只控制入站长连接,不能作为 Web API 故障证据。本地真实 SDK HTTP fixture 已验证每种故障只出现一个 POST;不代表真实租户已通过。

SDK 官方选项依据:Slack Web API 文档。若需 API 专用故障入口,应在专用测试实例 WebClient 的正式 agent 选项注入固定 loopback TCP 路由,保留真实 slack.com URL、SNI 和证书校验;验收项目现提供独立 webApiProxy / SLACK_WEB_API_PROXY_PORT,不能用 WSS proxy 冒充 API proxy。

专用 Web API 故障入口 ​

可选 endpoint webApiProxy: { port: 18560 } 仅作用于该实例的 JSON Web API 客户端。目的地固定 slack.com:443,真实 HTTPS URL/SNI/证书校验保留,TCP 只路由到 127.0.0.1:18560;错误域名拒绝、不回退直连。默认关闭,与 Socket streamProxy 独立;SocketModeClient 的连接发现和 WSS 不走此代理。停止 endpoint 会销毁专用 agent。上传使用其他域名,此专用代理明确拒绝,故只用于 JSON消息/引用/按钮/链接探针,不据此验收文件上传。

使用现有透明TCP故障代理:

bash
node scripts/platform-acceptance/tcp-fault-proxy.mjs --upstream-host slack.com --upstream-port 443 --port 18560 --control-port 18561
SLACK_WEB_API_PROXY_PORT=18560 pnpm --filter platform-acceptance-bot dev:slack

启动代理 forwarding 后先发新的 /acceptance probe:slack-api-baseline,确认真实回执;control POST /cut 只切 API TCP,WSS保持连接,再发独立新样本并确认报告 unknown且无后台重发。POST /recover 后先确认旧样本没有补发,再发新样本验证恢复。每个未知样本都保留并检查平台可见消息;禁止因为 unknown 重发同一业务。TCP cut 发生在请求前不能证明“平台已接收但回执丢失”,这一情况已有本地 TLS fixture 在服务收到 POST 后切断覆盖,实机未单独证明。

成员身份 ​

Slack 消息事件不带成员身份(工作区角色需额外 users.info 查询),不上报 sender.platform,能力画像也不声明 scene.platformStanding。详见 ADR 0056 §1。