# @addozhang/dsh-discord
[English](README.md) | 中文
[](https://www.npmjs.com/package/@addozhang/dsh-discord)
[](https://github.com/addozhang/dsh-discord/actions/workflows/ci.yml)
[](./LICENSE)
[](./package.json)
[](https://dshfind.com/en/plugins/addozhang/dsh-discord?ref=badge)
[DeepSeek Harness](https://github.com/deepseek-ai) 的 Discord 优先适配器:在 Discord 服务器中运行 DSH 会话——@机器人下一个任务,线程即开,回答流式输出,审批与提问以按钮形式送到手机上,随手即可回应。
零额外进程:适配器是 DSH 插件,直接挂载到你的 `dsh web` profile。会话状态保存在 DSH,持久绑定保存在 profile 的存储域中。
## 功能
- **@提及驱动会话** — 在已绑定的频道中,被授权的 `@机器人 <任务>` 会锚定一个线程(你的消息成为首帖)、创建 DSH 会话,并且至多提交一次。线程内的后续消息无需 @ 即可排队。消息附带的图片会一并送达:在严格的大小与主机白名单约束下从 Discord CDN 下载,并作为 image parts 提交给多模态模型。
- **流式渲染** — typing 指示、单条头消息原地编辑、逐工具活动行、代码围栏感知的长文分段、一次性收尾;Turn 结束时活动消息会被删除。
- **审批与提问** — DSH ask 帧渲染为按钮、下拉菜单与自由文本弹窗。所有权强制校验(提问者——或后续 Turn 的线程属主——才能点击),超时清扫 fail-closed,结算后的控件原地置灰。
- **会话控制** — `/steer`、`/stop`、`/queue list|remove` 带运行所有权校验;`/project bind|list|info` 与 `/session resume` 管理 Guild↔工作区绑定与历史会话;`/guild forget` 供操作员清理。
- **模型切换** — `/model show` 读取会话的实时模型目录;`/model select` 走交互式 provider → 模型 → 推理强度级联,也可直接填写 `provider/model` 应用。默认对所有授权成员开放,可收紧为仅 Host 操作员。
- **设置卡片,开箱双语** — Token 引导与连接/断开(存入 Host 凭据服务,绝不写入设置或日志)、服务器白名单、自动归档与语言。所有 Discord 可见文案提供中英双语;Bot 默认跟随 DSH 语言偏好,也可从卡片固定。
- **安全设计** — 显式服务器白名单内的 deny-first 授权。提及抑制双保险:每条请求携带 `allowed_mentions`,外加 wire body 的字节级提及中和。DSH 提交至多一次,并做保留 unknown 的对账——结果不明的投递绝不盲目重发。绑定重启后持久;READY 扫描会重建被删除的 category/控制频道,而把被删除的工作区频道视为用户意图(解除映射,workspace 保持可重新绑定)。
## 环境要求
- [dsh CLI](https://www.npmjs.com/package/@deepseek-ai/dsh) `0.1.1-rc.2` 或更新(web profile)
- Node.js `^22.19.0 || >=24`
- 一个 Discord 应用(含 Bot 用户),并在开发者门户启用 **MESSAGE CONTENT** 特权 intent(Developer Portal → 你的应用 → Bot → Privileged Gateway Intents)
## 安装
使用 dsh CLI 安装——它会自动把包装进 profile 并注册 bundle:
```sh
dsh plugin --profile web add @addozhang/dsh-discord
```
然后重启 `dsh web` 并刷新浏览器。`dsh plugin` 会自动对账 profile 的 bundle 列表——无需手动编辑。
升级与卸载使用同一条命令:
```sh
dsh plugin --profile web up @addozhang/dsh-discord # 升级;bundle 列表会重新对账
dsh plugin --profile web rm @addozhang/dsh-discord # 卸载;先执行 /guild forget 清理适配器记录
```
如果不用 CLI、手工管理 profile,等价做法是在 profile 目录里用 pnpm 安装本包,并在 `dsh.profile.bundles` 中自行登记:
```json
{
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "@addozhang/dsh-discord"] } }
}
```
## 配置
全部配置项位于 `dsh-discord` 设置命名空间,既可以在设置卡片中修改,也可以直接编辑 profile 的用户设置(`settings.yaml`):
| 配置项 | 默认值 | 说明 |
|---|---|---|
| `enabled` | `false` | 适配器总开关;卡片 Connect 在 Token 存入后启动。 |
| `allowedGuildIds` | `[]` | 服务器白名单。白名单之外零响应、零 DSH 调用。 |
| `memberUserIds` / `memberRoleIds` | `[]` | 白名单 Guild 内的成员级授权。 |
| `administratorUserIds` / `administratorRoleIds` | `[]` | 工作区管理员级(`/project bind`)。 |
| `deniedUserIds` / `deniedRoleIds` | `[]` | 拒绝名单;优先级高于上述一切授权。 |
| `hostOperatorUserIds` | `[]` | Host 操作员(`/guild forget`;启用 `modelSelectOperatorOnly` 后也包括 `/model select`)。 |
| `modelSelectOperatorOnly` | `false` | 将 `/model select` 限制为 Host 操作员(仅 `settings.yaml`,卡片不展示)。默认 `false`:任何授权成员均可切换,且切换仍会更新 Host 默认。 |
| `defaultVerbosity` | `essential-tools` | 工具活动行粒度:`text-only`、`essential-tools`、`full-tools`。 |
| `language` | `auto` | Bot 可见文案语言:`auto` 跟随 DSH 语言偏好(非中文渲染英文),或固定 `zh`/`en`。 |
| `streamUpdateIntervalMs` | `800` | 流式编辑合并间隔(250–10000)。 |
| `typingIntervalMs` | `7000` | typing 心跳(1000–30000)。 |
| `approvalTimeoutMs` | `600000` | 审批超时(30000–86400000);超时自动拒绝。 |
| `questionTimeoutMs` | `1800000` | 问题超时(30000–86400000);超时取消所属 Turn。 |
| `threadAutoArchiveMinutes` | `1440` | 任务线程自动归档:60、1440、4320 或 10080。 |
```yaml
dsh-discord:
allowedGuildIds: ["1517134847850709032"]
language: auto
```
设置卡片暴露三个高频项(服务器白名单、自动归档、语言)以及连接与 Token 面;其余键完全支持通过 `settings.yaml` 配置。非法的已存配置会保留最近一次有效配置。
## 初始设置
1. 以至少以下权限把 Bot 邀请到你的服务器:查看频道、**管理频道**(适配器要创建自己的分类和工作区频道)、发送消息、创建公共线程、在线程中发送消息、上传文件、读取消息历史。
2. 启动 profile 并打开 Web 界面。
3. 在 **Settings → Discord** 中粘贴 Bot Token(开发者门户 → 你的应用 → Bot → Reset Token)并点击 **Connect**。Token 由 Host 凭据服务保存——绝不写入设置、日志或客户端。
4. 填写 **允许的服务器**(服务器 ID 获取方式:Discord 开发者模式 → 右键服务器 → 复制服务器 ID)。白名单之外的一切都会被忽略。
5. `/model select` 默认对任何授权成员开放(单人自用部署)。如需限制为 Host 操作员:在 `hostOperatorUserIds` 中加入其用户 ID,并把 `modelSelectOperatorOnly` 设为 `true`(`settings.yaml`)。`/guild forget` 始终需要 Host 操作员。
6. 选择 Bot 语言,然后在已绑定的频道 @机器人 开始会话。
## 命令
| 命令 | 位置 | 说明 |
|---|---|---|
| `/project bind` | 任意频道 | 将 Guild 绑定到工作区(管理员;会创建主频道) |
| `/project list` / `info` | 任意频道 | 列出工作区 / 查看当前频道绑定 |
| `/queue list`, `/queue remove` | 会话线程 | 查看与移除待处理队列 |
| `/steer`, `/stop` | 会话线程 | 插话或取消运行中的 Turn(仅属主) |
| `/model show` / `select` | 会话线程 | 查看实时模型目录;`select` 不带参数时走交互式 provider → 模型 → 推理强度级联(默认对所有授权成员开放) |
| `/session resume` | 项目频道 | 自动补全选择本工作区的历史会话(显示标题与时间,最新优先),恢复为当前频道的新线程;空白、已挂线程、subagent、已归档的会话不会出现 |
| `/guild forget` | 任意频道 | 仅操作员:移除适配器记录 |
## 设计说明
- 适配器为 function/namespace 插件(`inject: ['apiProxy', 'credentials', 'settings', 'storageDomain', 'connection']`),将 Discord Gateway、命令面、流式渲染器与设置卡片挂载到 DSH web profile。
- 设置卡片是首次使用的引导面:Token 输入通过插件管理通道写入凭据服务的 `DSH_DISCORD_BOT_TOKEN` 引用,然后触发启动链。断开连接保留凭据;留空重连直接使用已存 Token。
- 发布工作流通过 npm trusted publishing (OIDC) 认证——任何地方都不保存发布凭证。
- 适配器启动链带代际计数,Connect/Disconnect 与初始启动竞争时只会产生一个 Gateway。
- 凭据探测会回退到 `resolve()`:Host 的 `describe()` 不识别环境变量来源的值——已连接的适配器不会被误报为未配置。
- 适配器日志默认静默:流程记录走 Host 的 debug 级别,失败形态的事件升到 warn——默认级别下不会向 DSH 进程打印任何内容。
- 链路级 trace:启动前设置 `DSH_DISCORD_TRACE=1` 可将 mux 帧、丢弃点与投递结果输出到 stderr(默认静默)。存在原因:rc.2 Host 未为插件日志接线任何 exporter,也没有日志级别开关——`logger.debug` 输出不可见;Host 提供等价机制后应移除。
## 已知限制与推迟项
- **`/preset`、`/skill`、`/host` 保持注销状态** — 控制模块已实现并通过单元测试,待路由接线时回归(`/preset` 的会话线程守卫一并处理)。
- **verbosity 为全局设置**(DSH 生态有按频道设置的先例)。
- **经 Kimaki 对齐后有意推迟**:reconcile-interactions 接线、ask 等待期暂停 typing、fail-closed 绑定/会话属主 store 接线、凭据轮换监听。
- **已知张力**:流式编辑 250ms 下限与高负载下 Discord 编辑预算的冲突(429 自愈),以及 typing 缺少时长上限看门狗。
## 开发
```sh
pnpm install --ignore-scripts
pnpm test # 683 tests incl. gateway/REST twin E2E
pnpm typecheck
pnpm lint
pnpm build # lib + client bundle
```
在 profile 中试用本地构建:
```sh
pnpm pack --pack-destination /tmp
dsh plugin --profile add file:/tmp/addozhang-dsh-discord-.tgz
```
`dsh plugin` 会把相对路径锚定到调用目录,因此在已执行 `pnpm build` 的仓库里也可以直接 `dsh plugin --profile add ../fiber`。重复 pack + add 即可刷新已安装副本,然后重启 `dsh web`。
发布由 [GitHub Actions](./.github/workflows/publish.yml) 经 npm trusted publishing (OIDC) 完成(`npm version && git push --follow-tags`)——任何地方都不保存发布凭证。实现遵循 `openspec/changes/build-discord-native-adapter/` 中的 OpenSpec 变更(设计、能力 spec、验证清单、评审报告)。
## 许可证
MIT