Slack Events API 对话
Slack App 可以通过 Events API 接收消息,再用 Web API 回复。本文推荐本地或无公网入口场景使用 Socket Mode,已有成熟公网网关时使用 HTTP Request URL;核验日期为 2026-09-08,未安装到真实工作区验证。
- Incoming Webhook 只负责投递,不能替代 Events API 接收用户消息。
- Socket Mode 与 HTTP Events API 是同一事件体系的两种传输方式,应用逻辑无需写两套。
- 群聊主路径订阅
app_mention;私信通常还需按实际范围订阅消息事件及相应 Scope。 - Slack 不自动提供 LLM。规则引擎、业务 API 和 AI 都通过
chat.postMessage返回。
- 有权在目标 Workspace 创建、安装或审批 Slack App。
- 准备测试频道,并允许邀请 Bot。
- Socket Mode 需要 App-Level Token;HTTP 模式需要公网 HTTPS Request URL 和 Signing Secret。
- 服务端能持久化事件去重键和线程上下文。
- 在 Slack API Apps 创建 From scratch 应用并选择工作区。
- 在 OAuth & Permissions 添加
chat:write与app_mentions:read;私信和其他消息类型按官方事件页补充最小 Scope。 - 在 Event Subscriptions 订阅 Bot Event
app_mention。 - 二选一配置传输:开启 Socket Mode 并创建含
connections:write的 App-Level Token,或填写 HTTP Request URL。 - 安装或重新安装应用,保存 Bot User OAuth Token(通常为
xoxb-)。 - 邀请 Bot 进入测试频道,用
@Bot /ping验证。
HTTP Events API 首次配置会发送 url_verification,服务应返回 challenge。事件回调在 event_callback 内携带 event_id;使用它做事件去重,不要只用消息文本。
HTTP 模式必须用原始请求体、X-Slack-Request-Timestamp 和 X-Slack-Signature 按官方 Signing Secrets 规则验签,并拒绝时间戳偏差过大的请求。Socket Mode 由官方 SDK 管理连接确认,但仍要对 event_id 去重。
{ "type": "event_callback", "event_id": "Ev00000001", "event": { "type": "app_mention", "user": "U0001", "text": "<@U_BOT> /ping", "channel": "C0001", "ts": "1700000000.000001", "thread_ts": "1700000000.000000" }}会话键建议为 slack:{team_id}:{channel}:{thread_ts || ts}。根消息没有 thread_ts 时,以它自己的 ts 作为线程根。
最小发送验证
Section titled “最小发送验证”curl -X POST 'https://slack.com/api/chat.postMessage' \ -H 'Authorization: Bearer SLACK_BOT_TOKEN' \ -H 'Content-Type: application/json; charset=utf-8' \ -d '{"channel":"CHANNEL_ID","thread_ts":"THREAD_TS","text":"pong"}'const response = await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', headers: { Authorization: `Bearer ${process.env.SLACK_BOT_TOKEN}`, 'Content-Type': 'application/json; charset=utf-8', }, body: JSON.stringify({ channel: process.env.CHANNEL_ID, thread_ts: process.env.THREAD_TS, text: 'pong' }),});const result = await response.json();if (!response.ok || !result.ok) throw new Error(JSON.stringify(result));import osimport requests
response = requests.post( "https://slack.com/api/chat.postMessage", headers={"Authorization": f"Bearer {os.environ['SLACK_BOT_TOKEN']}"}, json={"channel": os.environ["CHANNEL_ID"], "thread_ts": os.environ["THREAD_TS"], "text": "pong"}, timeout=15,)response.raise_for_status()result = response.json()if not result.get("ok"): raise RuntimeError(result)普通 API 双向回复
Section titled “普通 API 双向回复”收到 app_mention 后移除 Bot mention,优先解析 /ping、/status 等命令。用 chat.postMessage 回复原频道,并传入 thread_ts: event.thread_ts || event.ts,让一轮对话固定在原线程中。
HTTP 入口应在 3 秒内确认事件,再异步发送消息。Slack 可能带 X-Slack-Retry-Num 和 X-Slack-Retry-Reason 重试,不能把“已返回 200”当作无需去重。
AI 对话扩展
Section titled “AI 对话扩展”const threadRoot = event.thread_ts ?? event.ts;const conversationKey = `slack:${teamId}:${event.channel}:${threadRoot}`;const answer = await generateReply({ messages: await history.withUserMessage(conversationKey, stripMention(event.text)), signal: AbortSignal.timeout(20_000),});await postMessage({ channel: event.channel, thread_ts: threadRoot, text: answer });需要读取线程历史时,申请并使用与会话类型匹配的最小 Scope;否则只保存 Bot 自己处理过的上下文。不要假设 Bot 能读取所在频道的所有历史。
安全与可靠性
Section titled “安全与可靠性”- HTTP 模式先验签再解析 JSON,并校验请求时间戳;Socket Mode 的 Token 也只保存在服务端。
- 使用
event_id做事件去重,忽略bot_id或bot_message,防止 Bot 自己触发自己。 - Bot Token、Signing Secret 和 App-Level Token 分开存储、轮换和授权。
- 按
Retry-After处理 429;chat.postMessage对单频道有特殊速率限制,不应并发刷屏。 - 对群聊只响应 mention 或明确命令,并对工具调用实施服务端权限检查和提示词注入防护。
收不到 app_mention:检查 Bot Event、app_mentions:read、应用是否重新安装,以及 Bot 是否已进入频道。
not_in_channel:邀请 Bot 进入频道;不要通过扩大 Token 权限掩盖成员关系问题。
消息重复回复:对 event_id 建唯一键,并正确处理 Slack 重试头。
HTTP URL 验证失败:url_verification 需要及时返回 challenge;不要在这一步调用模型。