一键导入
slack
// Use when AI encounters Slack permalinks (https://*.slack.com/archives/...), Slack IDs (C.../U.../D.../F...), thread 讨论上下文, 搜索 Slack 历史消息, 按邮箱/handle 反查用户, 浏览频道最近动态, 下载 Slack 附件(截图/日志/CSV/PDF), 或需要把 @handle / #name / ^subteam 解析成 ID 时.
// Use when AI encounters Slack permalinks (https://*.slack.com/archives/...), Slack IDs (C.../U.../D.../F...), thread 讨论上下文, 搜索 Slack 历史消息, 按邮箱/handle 反查用户, 浏览频道最近动态, 下载 Slack 附件(截图/日志/CSV/PDF), 或需要把 @handle / #name / ^subteam 解析成 ID 时.
| name | slack |
| description | Use when AI encounters Slack permalinks (https://*.slack.com/archives/...), Slack IDs (C.../U.../D.../F...), thread 讨论上下文, 搜索 Slack 历史消息, 按邮箱/handle 反查用户, 浏览频道最近动态, 下载 Slack 附件(截图/日志/CSV/PDF), 或需要把 @handle / #name / ^subteam 解析成 ID 时. |
只读 Slack Web API 的 9 个子命令封装,统一入口 scripts/slack.py。
不写消息 / 不加 reaction / 不 mark-read —— 这些动作由调用方 Bot 自己处理,client 层有硬编码白名单拦截。
触发信号(AI 看到任一信号即考虑调用):
https://*.slack.com/archives/... 链接C[A-Z0-9]{8,} / U[A-Z0-9]{8,} / D[A-Z0-9]{8,} / F[A-Z0-9]{8,} 风格 IDp123456789012345 风格的消息 ts术语对照:
| 用户说法 | 含义 |
|---|---|
| slack / 消息 / DM / 频道 / thread / permalink | 本 skill |
Slack URL https://xxx.slack.com/archives/... | get / replies / files_download --url 入参 |
@handle / @name / #channel / ^subteam | resolve / users / channels 入参 |
ts / thread_ts | Slack 时间戳 ID(1234567890.123456) |
入口:
.agent-slack/skills/slack/scripts/slack.py
查找顺序:./.agent-slack/skills/slack/scripts/slack.py → <git-root>/.agent-slack/skills/slack/scripts/slack.py → <git-root>/slack/scripts/slack.py(仓库开发路径)→ find . -path '*skills/slack/scripts/slack.py'。
所有调用 = python3 <path> <subcommand> [flags];禁止 import sk.* 绕过入口。
子命令表:
| 子命令 | 作用 | 典型入参 |
|---|---|---|
get | 读单条消息 | --url <permalink> |
replies | 读整个 thread | --url <任一消息 permalink> |
history | 浏览频道最近消息 | --channel <#name|C...> --limit <n|1d|2h> |
search | 全域搜消息(xoxp 专属) | <query 字符串,原生 modifier> |
files_download | 下附件 | --file-id F... 或 --url <permalink> |
channels | 搜/列频道(走 cache) | --query <name> / --type public |
users | 查人(ID/email/name) | --id U... / --email ... / --query <name> |
resolve | 字符串 → 对象 | resolve "#eng" / "@alice" / "^oncall" / U.../C.../F.../email |
cache_refresh | 刷本地 users/channels/subteams 字典 | — |
每条都支持 --help。大输出用 --output <file>(stdout 只打 saved: <path>)。
| 项 | 说明 |
|---|---|
| Python | ≥ 3.9 |
| 依赖 | 无(只用标准库 urllib,零 pip install) |
| 配置 | 复制 slack/.env.example → slack/.env,至少填 SLACK_BOT_TOKEN |
Token 路由(client 自动挑):
| 操作 | Token 优先级 |
|---|---|
除 search 外全部 | SLACK_BOT_TOKEN → SLACK_USER_TOKEN → SLACK_TOKEN |
search | SLACK_USER_TOKEN → SLACK_TOKEN(必须 xoxp,bot token 会被 Slack 拒) |
Cache 自动刷新(Chunk 4):channels / users --query / resolve "#name|@handle|^handle" 在 cache 不存在或 > TTL 时会同步刷一次;单条 lookup(users --id/--email、resolve U.../C.../email)走 live API,不触发刷新。消息类命令(get/replies/history/search)不会自动刷新,避免高频放大成全量 users.list。
Cache 环境变量:SLACK_SKILL_CACHE_TTL=<秒>(默认 3 天)、SLACK_SKILL_CACHE_NO_AUTO=1(关自动刷新)、SLACK_SKILL_CACHE_DIR=<path>(覆盖默认 $PWD/.agent-slack/cache/slack/)。
所有响应形状:消息对象一律含 ts / author / text_raw(保留 <@U> <#C> 原始 token)/ text_rendered(@name #channel 还原,Block Kit 走 Markdown)/ blocks_rendered / files / reactions / raw(原始 payload)。二次处理用 text_raw,喂 AI/人读用 text_rendered。
# 单条
python3 slack/scripts/slack.py get --url "<permalink>"
# 整个 thread(permalink 任意一条即可)
python3 slack/scripts/slack.py replies --url "<permalink>" [--limit 20] [--output /tmp/t.json]
replies --limit 算的是 回复数,root 总是返回。
# 按条数
python3 slack/scripts/slack.py history --channel "#eng" --limit 20
# 按时间窗口(m/h/d/w)
python3 slack/scripts/slack.py history --channel "C08P..." --limit 1d
# 连 thread 回复一起抓(适合做"今天频道全景")
python3 slack/scripts/slack.py history --channel "#eng" --limit 1d --include-thread-replies
--limit 两种语义:纯数字 = 条数;<n>{m|h|d|w} = 时间窗口(不限条数)。
python3 slack/scripts/slack.py channels --query release --sort popularity --limit 10
python3 slack/scripts/slack.py users --email alice@example.com
python3 slack/scripts/slack.py resolve "@alice" # → user
python3 slack/scripts/slack.py resolve "#eng-release" # → channel
python3 slack/scripts/slack.py resolve "^oncall" # → subteam
python3 slack/scripts/slack.py resolve U06GM8PAFEX # → 直接 live lookup
resolve 返回 { kind: user|channel|subteam, resolved, candidates?, cache_refresh? };拿到 id 再喂 history --channel <id> / replies --url ...。
# query 原样透传给 Slack,原生 modifier 全可用
python3 slack/scripts/slack.py search "deploy in:#eng from:@alice after:2026-01-01" --limit 50
python3 slack/scripts/slack.py search "incident-4712" --sort timestamp --sort-dir asc --limit 200
--limit 跨页自动翻(--max-pages 默认 5,Slack 单页上限 100)。返回的 matches 同样走 render + preload_users。
Slack 的 url_private 需要 Bearer token,AI 工具层没有 token → 不下载就看不见图/PDF/CSV。
# 读消息时顺便下(最常用)
python3 slack/scripts/slack.py get --url "<permalink>" --download-files [--types image,pdf]
# 只下单个文件
python3 slack/scripts/slack.py files_download --file-id F012ABC --types all --out /tmp/f/
每个 files[i] 会被填 local_path / download_category / download_status,顶层有 downloads: { total_files, downloaded, skipped, errored, files_dir }。
--types 类别:text(post/snippet/md/csv/log/源码后缀 py/js/ts/go/java/rb/json/yaml/sql …)、image、video、audio、pdf、archive、other、all。--download-files 默认走 SLACK_SKILL_DOWNLOAD_TYPES,缺省是 text(AI 上下文友好)。
幂等:文件名 {file_id}{ext};再跑一次 → download_status: skipped:already_exists。想重下手动 rm。
命令层遇 Slack API 错会打 error: <method>: <code> | <detail> 到 stderr,已知 code 紧跟一行 hint: ...。
| Slack error | 含义 | hint |
|---|---|---|
channel_not_found | 频道 ID/名不存在或 token 无权 | slack.py channels --query <name> 找正确 id |
not_in_channel | Bot 没加该私密频道 | Slack 里 /invite @your-bot |
user_not_found | 用户不存在或 token 看不到 | slack.py users --query <name> 或传完整 U0... |
not_allowed_token_type | 该接口只收 xoxp | search 必须配 SLACK_USER_TOKEN |
missing_scope | scope 不足 | 对照 .env.example 补 scope → 重新 Install to Workspace |
invalid_auth / not_authed | token 无效/过期/未配 | 检查 .env;xoxp 可能被 SSO 失效 |
ratelimited | 速率限制 | skill 已自动 Retry-After;仍超限说明并发过大 |
message_not_found | permalink 指向消息不存在/已删 | thread 回复的 permalink 必须带 ?thread_ts=<root_ts>;Slack UI 复制出来的天然就有,自己拼 URL 容易漏 |
file_not_found | file id 不存在或 token 看不到 | 用 get/replies 响应里的 files[].id(F 开头) |
thread_not_found | 该消息不是 thread 首帖 / 还没回复 | 改用 get;或传某条回复的 permalink |
退出码:2 = token 缺失 / argparse 错;3 = 触发只读白名单(调用了非白名单方法);4 = 参数非法(格式错 / channel 不在 SLACK_SKILL_ALLOWED_CHANNELS);1 = 其他(含未识别的 Slack API error)。
Bearer token 永不出现在任何日志/错误信息(sk/files.py 走 redact())。
import sk.* 或绕过 slack.py 入口.env 或 shell envtext_rendered 做正则提数 —— 用 text_rawhistory --limit 1d / 批量 search 直接 stdout —— 用 --output <file>,否则 AI 上下文爆炸