AGENT SKILL

slack-block-action-handlers

Slack 按钮点击 (block_action) 的确定性路由协议——新建审批卡按钮时必须在 `profiles/<your-profile>/scripts/handlers/` 下放同名 handler 脚本,adapter 按 action_id 直接执行(零 LLM、零 worker fork)。Use when 需要新建带按钮的 Slack 卡片、修改 handler 脚本、或排查按钮点击无响应。

使用场景邮件与沟通
License 许可
MIT
所属仓库
KarryViber/Orb
最近更新
2026-05-14
Stars
★ 66
Forks
8

01 前言 Front Matter

name
slack-block-action-handlers
provenance
user-authored
description
Slack 按钮点击 (block_action) 的确定性路由协议——新建审批卡按钮时必须在 `profiles/<your-profile>/scripts/handlers/` 下放同名 handler 脚本,adapter 按 action_id 直接执行(零 LLM、零 worker fork)。Use when 需要新建带按钮的 Slack 卡片、修改 handler 脚本、或排查按钮点击无响应。

03 SKILL.md 正文

Slack block_action Handlers

🚧 待 adapter 实现 — 本 skill 描述目标协议(见 specs/slack-block-action-handlers.md)。当前 src/adapters/slack.js:865 仍静默丢弃 block_action,按钮点击不会触发 handler。spec 落地后本标记移除。

外部脚本(cron / Python / Node)自推的审批卡按钮被点击后,Slack 回传 block_action 事件。Orb adapter 做确定性路由——按 action_id 找同名 handler 脚本执行,不走 LLM,不 fork worker。

发现规则

profiles/{profile}/scripts/handlers/{action_id}.{py,sh,js}
  • action_id 必须匹配正则 ^[a-z][a-z0-9_]{0,63}$(snake_case,防路径穿越)
  • 扩展名查找顺序:.py → .sh → .js
  • 未找到 handler → adapter chat.update 成 ⚠️ 未注册 handler: {action_id} 并 @Karry

现有 handlers(04-21)

orb_x_post_approve.py     — X 拟稿审批通过 → 发推
orb_x_post_reject.py      — X 拟稿拒绝
orb_x_reply_approve.py    — X 回复审批通过
orb_x_reply_reject.py     — X 回复拒绝

新增按钮前先看有没有可复用的 action_id 命名。

Handler 接口(stdin JSON)

adapter spawn handler 时通过 stdin 单行 JSON 传 context(避免 env 长度限制和 argv 注入):

{
  "action_id": "orb_x_post_approve",
  "value": "<button value 原样透传,可能是 base64 payload>",
  "user_id": "U0123456789",
  "channel": "C0AP013V056",
  "message_ts": "1776702715.774950",
  "thread_ts": "1776702715.774949",
  "profile": "<profile-name>",
  "response_url": "<Slack response_url>"
}

handler 模板(Python):

#!/usr/bin/env python3
import sys, json, os

ctx = json.loads(sys.stdin.read())
action = ctx["action_id"]
value = ctx["value"]
channel = ctx["channel"]
message_ts = ctx["message_ts"]

# 业务逻辑...

# 自己 chat.update 最终态(Slack Web API 或 response_url)
# adapter 不会替你改卡面!

生命周期(重点)

adapter 不持有 handler 进程。流程:

  1. Adapter 收到 block_action,ack Slack
  2. Adapter chat.update 原卡为 ⏳ 处理中… <@{user}> clicked \{action_id}``(禁用按钮,防重复点)
  3. Adapter spawn handler,detach + unref(不 wait、不超时、不 kill)
  4. Adapter 记 logs/handlers/pids.log 一行:{ts} pid={pid} action_id={id} message_ts={ts}
  5. Handler 自己跑完业务,自己 chat.update 最终态(用 response_url 或 Slack Web API token)

Handler 自律清单(必做)

  • [ ] 读 stdin 拿 context
  • [ ] 执行业务
  • [ ] chat.update 最终态(成功/失败/跳过都要更新,否则卡面永远显示 ⏳)
  • [ ] 业务自带超时(发推 10s / LLM 生成 60s),超时内部自己写 ❌ 超时 + exit
  • [ ] stdout 日志 adapter 捕获到 logs/handlers/{action_id}-{ts}.log

并发去重

adapter 维护 Set<message_ts>:

  • 点击时 message_ts 已在集合里 → 仅 ack,不再 spawn
  • 释放条件:chat.update 已变成非 ⏳ 处理中 状态(handler 通过 update 隐式宣告完成)
  • 兜底:10min 定时清理

安全边界

  • handler 路径硬限 profiles/{profile}/scripts/handlers/,.. / 绝对路径被拒
  • 执行用户同 daemon(没有额外 sandbox)
  • action_id 白名单正则已覆盖大部分注入面
  • value 字段 adapter 不解析,原样透传给 handler——handler 自己验证 payload(别信 value)

Gotchas

  1. handler 漏写 chat.update → 卡面永远 ⏳(不是 adapter bug,是脚本 bug)。模板必带 try/except + finally chat.update
  2. button value 超 2000 字 会被 Slack 截断。payload 太大就存文件,value 只放引用 id
  3. action_id 命名 要能表达「什么操作 + 什么对象」(好:orb_x_post_approve;坏:btn1)
  4. adapter 不重试。handler crash = 卡面永远 ⏳,Karry 手动点醒或查 logs/handlers/
  5. 新 handler 首次上线 在 DM 先跑一张测试卡,别直接在对外频道首发
  6. Python handler 缺依赖 spawn 时 silent 失败(stderr 进 log 文件,卡面不变);新增前先 python3 handlers/foo.py < test.json 跑一次

新建按钮的完整工作流

  1. 想好 action_id(snake_case,表达「操作+对象」)
  2. 复制最像的 existing handler 作模板
  3. 本地 dry-run:构造 test.json → python3 handlers/{new_id}.py < test.json,验证能 chat.update
  4. 在生成卡片的脚本里新增按钮:
    {"type": "button", "text": {"type": "plain_text", "text": "✅ Approve"}, "action_id": "new_action_id", "value": "..."}
    
  5. DM 先测,验证 adapter 路由 + handler chat.update 都走通
  6. 推到对外频道

04 同仓库更多 Skills

KarryViber
KarryViber/Orb

webapp-testing

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

测试与质量前端开发
★ 66
KarryViber
KarryViber/Orb

vendor-doc-actionability

对外合作文档(交付文档 / POC 文档 / Solution Doc / 提案书)必须为每个「需对方执行」的动作写清具体操作入口(URL / 工单系统 / 脚本路径 / 联系人 + 联系方式 / 步骤编号)。Use when 给客户、vendor、合作方写交付文档、POC 文档、Solution Doc、提案书;或检视已有对外文档是否抽象到无法执行。

★ 66
KarryViber
KarryViber/Orb

vendor-api-param-validation

第三方 API / SDK 集成开工前,强制先列参数 checklist 并对照官方文档逐项验证(名称 / 类型 / 必填 / 默认 / 上限),通过后再编码——不是「写完再调通」。Use when 新接 vendor API/SDK(Slack / LongPort / freee / LLM API 等)、既有集成追加新 endpoint 或新字段、用户说「调通某接口」「接入 X」「集成 X」。

开发工程师后端与接口
★ 66
KarryViber
KarryViber/Orb

truth-ladder

状态放行三层阶梯(attempted / observed / confirmed)—— 汇报任务、API、命令、消息或外部动作状态时强制分层,避免把"动作已发起"误写成"结果已达成"。Use when 准备写「任务已完成」「API 返回成功」「exit code 0」「用户已收到」「ok:true」类状态汇报;或在 cron 报告 / Slack 回报 / project status / receipt 中表达完成度时。

开发工程师
★ 66
KarryViber
KarryViber/Orb

thread-object-realignment

长 thread 切题后的对象重定协议——thread 历史 ≥5 条且检测到切题信号时,回复前必做「用户最后显式问题对象 vs 待发回复对象」一致性校验,不一致则改写或反问。Use when Slack thread 历史 ≥5 条且当前用户消息出现切题信号(主题词突变、问句指向新实体、时间线跳跃、出现「另外」「话说」「回到 X」「换个话题」等切题语,或 ≥3 turn 后用户显式问题对象与最近 assistant 回复对象不同)。

★ 66
KarryViber
KarryViber/Orb

theme-factory

Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.

视觉设计
★ 66