内容
# 间谍
一个适用于 macOS 和 Linux 的 Slack CLI 客户端,可以自动从您已经登录的 Slack 桌面应用程序中进行身份验证。无需 OAuth,无需应用安装,无需令牌输入。
```
$ spy auth
身份验证为 Tom Harris @ Acme
工作空间:acme (T024D6SRW80)
用户 ID: U01ABCDEF
网址: https://acme.slack.com/
$ spy ch
# general (412 成员) C012ABCDEF
# random (388 成员) C013BCDEFG
🔒 leadership (12 成员) C014CDEFGH
...
$ spy r general 5
[2026-05-25 09:14:22] Anjali Patel:
咖啡聊天 10:30?
[2026-05-25 09:18:51] Marcus Lee [3 回复]:
部署看起来是绿色的 — 有人反对继续吗?
...
```
## 需求
- macOS 或 Linux(身份验证流程读取 Slack 桌面应用程序的本地数据)
- Slack 桌面应用程序,已登录到至少一个工作空间:
- **macOS** — 应用商店或直接下载构建
- **Linux** — 原生(`.deb`/`.rpm`),Snap 或 Flatpak 构建
- Go 1.21+ 来从源代码构建
## 安装
```bash
git clone https://github.com/tomharris/spy
cd spy
go build -o bin/spy ./cmd/spy
# 将 bin/spy 放在您的 PATH 中,或者:
go install ./cmd/spy
```
在 **macOS** 上,运行的第一个命令会触发一个钥匙串提示,请求允许读取“Slack 安全存储”条目。单击 **始终允许** — 否则每次后续调用都会提示。
在 **Linux** 上,如果您的登录钥匙串已锁定,您可能会在 `spy` 读取 `v11` 加密 cookie 时收到一次性解锁对话框。旧版 `v10`(“peanuts”)加密的 cookie 不需要任何提示。
## 命令
### 读取
| 命令 | 别名 | 功能 |
| --- | --- | --- |
| `spy auth` | | 验证身份并打印工作空间/用户信息 |
| `spy channels` | `ch` | 列出公共和私人频道 |
| `spy users` | `u` | 列出工作空间用户 |
| `spy dms` | `dm` | 列出直接消息对话 |
| `spy read <channel\|@user> [count]` | `r` | 读取最近的消息 |
| `spy thread <channel> <ts> [count]` | `t` | 读取线程中的回复 |
| `spy search <query...> [count]` | | 全工作空间搜索消息 |
| `spy pins <channel>` | `pin` | 列出固定项目 |
| `spy activity` | `a` | 未读和提及计数 |
| `spy unread` | `ur` | 与活动类似,但仅限未读,不包括静音 |
| `spy starred` | `star` | 重要用户和固定项目 |
| `spy saved [count]` | `sv` | 稍后保存的项目(`--all` 包括已完成) |
### 寫入
| 命令 | 别名 | 功能 |
| --- | --- | --- |
| `spy send <channel\|@user> <msg...>` | `s` | 发送消息 |
| `spy react <channel> <ts> <emoji>` | | 添加表情反应 |
| `spy draft <channel> <msg...>` | | 保存草稿(出现在 Slack UI 中) |
| `spy draft thread <channel> <ts> <msg...>` | | 保存草稿线程回复 |
| `spy draft user <user> <msg...>` | | 保存草稿直接消息 |
| `spy draft drop <draft_id>` | | 删除草稿 |
| `spy drafts` | | 列出活动草稿 |
### 工作空间
| 命令 | 功能 |
| --- | --- |
| `spy workspaces` | 列出所有已登录的工作空间 |
| `spy workspaces use <id>` | 设置默认工作空间 |
| `spy workspaces refresh` | 从 Slack 应用程序重新提取令牌 |
### MCP 服务器
| 命令 | 功能 |
| --- | --- |
| `spy mcp` | 作为 MCP 服务器运行(stdio),将每个读写命令公开为工具 |
### 频道引用
在任何命令需要 `<channel>` 的地方,它接受:频道名称(`general`,`#general`),用户句柄(`@anjali`,`anjali`),用户 ID(`U01…`),或频道/直接消息 ID(`C01…`,`D01…`)。
### 读取标志
`spy read` 支持 `--ts`(显示原始 Slack 时间戳 — 有助于复制用于 `thread`/`react`), `--threads`(自动内联展开所有线程),和 `--from`/`--to YYYY-MM-DD`(严格 — 无效日期会大声失败而不是静默强制)。
## 工作空间
如果您登录到多个工作空间,`spy` 不会自动为您选择一个。您必须:
- 在每次调用时传递 `--workspace <domain|team_id>` (或 `-w`),或
- 在您的环境中设置 `SPY_WORKSPACE`,或
- 运行 `spy workspaces use <id>` 以持久化默认设置。
标识符与 URL 子域名(`acme` 来自 `acme.slack.com`)或团队 ID(`T024D6SRW80`)完全匹配。故意没有模糊匹配 — 身份验证模糊性不应是沉默的。
```bash
spy -w acme ch # 一次性
SPY_WORKSPACE=acme spy ch # 每 shell 默认
spy workspaces use acme # 持久默认
```
每个工作空间都有自己的缓存目录,因此当您切换时,缓存不会相互污染。
## JSON 输出
每个命令都接受 `--json` 用于脚本编写:
```bash
spy ch --json | jq '.channels[] | select(.is_private) | .name'
spy r general 100 --json --from 2026-05-20 | jq '.messages | length'
```
## 用作 MCP 服务器
`spy mcp` 通过 stdio 运行,并将每个读写命令公开为工具,因此您可以将其连接到 Claude Desktop、Claude Code 或任何其他 MCP 感知客户端。服务器在启动时使用与 CLI 相同的规则选择一个工作空间;如果您想要多个工作空间,请启动单独的进程。
克劳德桌面(macOS:`~/Library/Application Support/Claude/claude_desktop_config.json`; Linux:`~/.config/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"spy": {
"command": "/absolute/path/to/spy",
"args": ["mcp"],
"env": { "SPY_WORKSPACE": "acme" }
}
}
}
```
克劳德代码(`claude mcp add`):
```bash
claude mcp add spy /absolute/path/to/spy mcp -e SPY_WORKSPACE=acme
```
服务器为每个读取命令(`auth`、`channels`、`users`、`dms`、`read`、`thread`、`search`、`pins`、`activity`、`unread`、`starred`、`saved`、`drafts_list`)和每个写入命令(`send`、`react`、`draft_channel`、`draft_thread`、`draft_user`、`draft_drop`)注册工具。工具参数镜像 CLI 标志;频道引用接受相同的形式(名称,`@handle`,用户 ID,频道 ID)。
## 工作原理
Slack 桌面应用程序将其会话存储为两部分:
- 一个工作空间特定的 `xoxc-…` 令牌,存储在 Slack 数据目录的 `Local Storage/leveldb/` 下的 LevelDB 存储中。
- 一个账户级别的 `xoxd-…` 会话 cookie,存储在 SQLite Cookies 文件中,使用 AES-128-CBC 加密。
`spy` 复制这些文件到 `/tmp`(Slack 在运行时对原始文件保持独占锁),在纯 Go 中解密 cookie,扫描 LevelDB 以查找令牌候选,并通过 Slack 的 `auth.test` 验证每个令牌以了解团队身份。令牌缓存在 `~/.local/spy/workspaces/<team_id>/workspace.json`(模式 `0600`)并在后续运行中重用。如果令牌过期,`spy` 在下一个 `invalid_auth` 时重新提取。
平台之间只有两件事情不同 — Slack 目录的位置和 cookie 的 AES 密钥获取方式:
| | Slack 数据目录 | Cookie AES 密钥 |
| --- | --- | --- |
| **macOS** | `~/Library/Application Support/Slack` (直接下载),或 `com.tinyspeck.slackmacgap` 应用商店容器 | “Slack 安全存储”密码来自登录钥匙串(PBKDF2-SHA1,1003 次迭代) |
| **Linux** | `~/.config/Slack` (原生),或 Snap / Flatpak 等价物 | `v10` cookie 使用 Chromium 的硬编码 `"peanuts"` 密钥;`v11` cookie 使用 freedesktop Secret Service(GNOME 钥匙串,KWallet,…)通过 D-Bus 读取的“Slack 安全存储”密钥(PBKDF2-SHA1,1 次迭代) |
唯一的外壳输出是在 macOS 上使用 `security find-generic-password` 读取钥匙串条目。Linux Secret Service 查找是纯 Go D-Bus,而其他所有操作 — cookie 解密,LevelDB 扫描,Web API 客户端 — 都是两个平台上的纯 Go。
## 缓存
解析后的用户和频道列表缓存在每个工作空间 5 分钟,位于 `~/.local/spy/workspaces/<team_id>/{users,channels}.json`。传递 `--refresh` 以绕过它们。针对具有约 4000 个用户的工作空间进行冷查询需要约 500ms;暖查询需要约 80ms。
## 隐私 / 范围
`spy` 读取已经存在于您机器上的文件,并且除 Slack 的 API 外,从不将它们发送到其他任何地方,使用您已经在 Slack 应用程序中授权的凭证。没有遥测数据,也没有第三方服务参与。磁盘缓存包含您的令牌 — 请像对待其他凭证存储一样对待 `~/.local/spy/`。
本项目与 Slack Technologies 无关,也未得到其认可。
## 许可证
在 [MIT 许可证](LICENSE) 下发布。
```
MIT License
Copyright (c) 2026 Tom Harris
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```