Notifications & Integrations

DSH-bot

aitcmhk-web/DSH-bot · ★ 1

aitcmhk-web

Telegram and WeChat bridges plus handoff continuity for DeepSeek Harness (DSH)

README body is shown in Chinese.

License
MIT
Added
2026-10-30
Maintenance
active
Install kind
github
npm
dsh-botplugin
Manifest details
Manifest status
Declared
Manifest path
package.json#dsh
Manifest checked
2026-10-30T09:38:25.277Z
Lifecycle scripts
None found

Static manifest checks report declarations only; they are not compatibility or security review.

安装

dsh plugin --profile web add 'github:aitcmhk-web/DSH-bot#8ff0188a5f122c260f0ed6977785369925727bd9'

GitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试

For install and environment setup, plus plugin development tutorials, see DSH community docs ↗

README

Source README · body: zh · Read Chinese README

Table of contents
  1. 环境要求
  2. 安装方式一:在线安装(两条命令,推荐)
  3. 开发者:本地安装(用你手上这份源码)
  4. 安装方式二:下载安装包(不想敲命令的)
  5. 怎么确认装对了
  6. 配置项速查
  7. 模型流量闸(每分钟限额,2026-10-19 加)
  8. 会话压缩口径(随版本下发,2026-10-19 加)
  9. 远程审批(手机点按钮,不用守着电脑)
  10. 多端互通(TG ⇄ 微信,同一个大脑)
  11. 指令
  12. 记忆(流水账 + handoff)
  13. 微信入口(扫码绑定)
  14. ⚠️ 三条硬注意事项
  15. 已知限制
  16. 等待上限 90 分钟(2026-10-20 起;进度哨兵已删)
  17. 发版规矩(2026-10-19 定)
  18. 许可

把 Telegram / 微信变成 DSH 的聊天入口的插件。三大功能:Telegram 入口、微信入口、handoff 记忆接续,外加语音转文字。

🔰 第一次接触、想知道"这东西到底能干什么" → 先看 功能说明.txt(大白话版,不用懂技术)。 装的时候怎么填、怎么启动 → 看 使用说明.txt。下面这份是给要自己动手的人的完整文档。

环境要求

  • Node ≥ 18
  • 机器上装好 DSH(有 dsh 命令;没装的话先跑下面第 0 步)
  • 一个 Telegram bot token(在 Telegram 里找 @BotFather 发送 /newbot 新建,不要和其他 bot 共用)

安装分成两件事,一个脚本管一头,互不越界: setupdsh = 升级(Git、Node、DSH 本体,外加已经装过机器人的工作区里的插件:装 + 升级); setupbot = 建/选工作区、首次装插件、绑 TG / 微信、生成工作区里那 5 个文件(英文名,双击或终端里跑都行)。 机器上没有 dsh 时 setupbot 会自己把 setupdsh 跑一遍,所以顺序不用记。

安装方式一:在线安装(两条命令,推荐)

  1. 先装/升级本体(Git、Node、DSH 一起办,可重复跑):

    curl -fsSL https://raw.githubusercontent.com/aitcmhk-web/DSH-bot/main/setupdsh.sh | sh
    
  2. 再装机器人 —— 把下面这条整段贴进终端,回车:

    curl -fsSL https://raw.githubusercontent.com/aitcmhk-web/DSH-bot/main/setupbot.sh | sh
    

    它自己列出/新建工作区、自己把插件装好,然后只问你三件事: 工作区叫什么(直接回车就叫 mybot)、Telegram token(@BotFather 那串)、微信怎么绑(可选)。 每样都是「输个编号」或「粘一下回车」,不用你编辑任何文件。

  3. 跑完它会把两个词装进 PATH —— 以后升级(本体 + 插件)敲 setupdsh; 重新绑 TG、重新绑微信敲 setupbot:

    setupdsh
    setupbot
    
  4. 启动:照着它最后打出来的那行,或者跑 ~/DSH/<工作区名>/start.command (同目录里另有 stop.command、restart.command、install-autostart.command、uninstall-autostart.command)。

那五个文件名字一律英文:有桌面双击,没桌面(云主机 / 只有终端)就 cd ~/DSH/<工作区名> && ./start.command。中文文件名在纯终端里得切输入法,太别扭。

开发者:本地安装(用你手上这份源码)

已经有一份源码(比如你自己 clone 下来的、或者要改着调试的),就用 file: 指向那个目录:

dsh plugin --profile mybot add file:/绝对路径/dsh-botplugin

例如源码在 ~/DSH/botplugin:

dsh plugin --profile mybot add file:$HOME/DSH/botplugin

⚠️ file: 后面不能写 ~ —— 它不会展开波浪号,会当成字面路径找不到。用 $HOME/ 或完整绝对路径。

用 file: 装出来的是软链 —— 改完源码不用重装,重启 profile 就生效。 调试的时候用这个;平时用上面的 git 方式。两种选一种,别都装 (会装出两份同名插件,加载哪份不好说)。

⚠️ 路径必须是绝对路径,且指向含 package.json 的那一层(也就是仓库根), 不要指到 src/ 或上一层。

安装方式二:下载安装包(不想敲命令的)

  1. 下载安装包:dsh-mybot-安装包.zip(在本页文件列表里,点它再点 Download / Download raw)。
  2. 解压,双击里面的 安装-mybot.command。
  3. 接下来和在线方式完全一样:问你工作区名字(直接回车就叫 mybot)→ 粘 token → 微信要不要绑。 窗口最后会停住等你按回车,结果不会一闪而过。
  4. 以后每次使用:跑 ~/DSH/<工作区名>/start.command(stop.command / restart.command / install-autostart.command / uninstall-autostart.command 是同一个目录里的另外四个文件)。 想重新绑 TG / 微信:把 安装-mybot.command 再双击一次(或在终端敲 setupbot)。 想升级(Git / Node / DSH 本体 / 已经装过机器人的工作区里的插件):在终端敲 setupdsh(安装包里也带了这个脚本)。

两种方式装出来的是同一个插件,跑的是同一个 setupbot.sh,选一种就行。 安装包方式适合完全不想碰命令行的人;在线方式第一次要粘两条命令,之后也只剩 setupdsh / setupbot 两个词。

怎么确认装对了

脚本跑到最后会打「✅ 复核通过:配置已经生效」—— 看到这句就是装对了,它紧接着会给你启动命令。

没看到这句、或者它报「插件没装上」= 没装成,插件不会生效(而且不报错,只会静默什么都没发生)。

配置项速查

配置 说明
telegramToken / telegramApiRoot TG 必填;apiRoot 指向代理时改
telegramAllowedUsers 允许的 TG 用户 ID;空 = 首个发消息者认领为主人
weixinToken / weixinApiRoot / weixinAccountFile 微信入口(通常用扫码登录,不用填 token)
routes + defaultRouteKey 手工指定模型档位;留空 = 自动跟随 web 端「设置 → 模型」
cwd 模型的工作目录
memoryDir / memoryScript handoff 记忆接续:默认开启(<工作目录>/memory + 插件自带的记账程序),不用配;要关闭把 memoryDir 设成空串
restartCommand 重启用的启动器 .command 路径;不填默认找工作目录里的 start.command(兼容老的 启动-<工作区名>.command / 启动-mybot.command)
turnTimeoutMs 单轮超时,默认 90 分钟(2026-10-20 由 30 抬上来:满 30 分钟就判超时并把这一轮答案丢掉)
asrBackend / asrWhisperBin / asrPythonBin 语音转文字。默认 sensevoice(阿里 FunASR,中文准),不填路径就自动找 /opt/homebrew/bin/python3.11;本机没装时,安装收尾和发语音都会回一条可粘贴的安装命令(⛔ 不自动装)。whisper 中文差,只有显式填 asrBackend: whisper 才用
llmBudget 模型流量闸:滚动 60 秒内请求数与 token 数封顶(默认 50 次 / 90 万 token),超了排队等,别撞供应商的 TPM 限额。字段:enabled(默认 true,关掉=不限流)/ tpm / rpm / 输出预留(每次先占多少输出 token,默认 8000)/ 最长等待毫秒(默认 10 分钟,等满就放行并打告警)

模型流量闸(每分钟限额,2026-10-19 加)

一回合里跑工具、开子代理都会反复发模型请求,一分钟里的累计很容易顶到供应商的 TPM/RPM 限额(撞上就是 429)。插件在宿主 llm 的 llm/stream 上挂了一道闸 (src/llm-budget.js):滚动 60 秒窗口内 请求数 ≤ rpm、token 合计 ≤ tpm, 快满时让下一次调用排队等到窗口腾位,⛔ 不丢请求、⛔ 不改流里的内容。

  • 记账口径:调前按请求体的可读字符量预估(中文 1 token/字、其余 1/4,另加输出预留), 流里收到宿主 usage 块后换成实际用量(替换,不叠加);失败/中断的那次仍按预估留在账上(保守)。
  • fail-open:单次最多等 最长等待毫秒(默认 10 分钟),等满仍无额度就放行一次并打告警—— 宁可偶尔超一次,也不让整台 bot 卡死。
  • 子代理与工具循环一样算:它们都走同一条 waterfall,所以一处就管住全机流量。
  • 默认口径:rpm 50 次 / tpm 90 万 token(2026-10-19 定的最终值)。供应商(百度千帆)的 TPM 限额是 100 万,90 万是留一成余量。一条请求就带十几万 token(整段历史重发),所以一分钟 大约放六七发 —— 这是硬约束,要更快只能少发请求,⛔ 不能靠抬闸。 ⛔ 历史上曾抬到 300 万(当时误判「供应商限的是请求密度、不是 token 量」),拿到实数后已收回。
  • 关掉:配置里 llmBudget.enabled: false(出问题时可一键停,不影响启动)。

会话压缩口径(随版本下发,2026-10-19 加)

插件作为 profile 层装上时,会顺手把会话压缩的口径打进 profile:本包的 cordis.patch.yml(package.json 的 dsh.bundle.patch)多打一行 compaction-basic。 宿主代码默认 thresholdRatio 0.8 —— 在百万窗口上等于 80 万 token 才压缩, 正常对话根本触发不到,历史只能近乎全量重发,越聊越慢。

本包一律下发比例制(两个数都随窗口缩放,任何窗口都自洽):

键 值 作用
thresholdRatio 0.15 窗口的 15% 触发压缩(百万窗口 = 15 万 token)
retainRatio 0.08 压缩后按窗口留约 8%(百万窗口 ≈ 5 万 token)
headroomTokens 8192 预留空间。出厂默认 65536 比小窗口本身还大,会把压力预算算成负数

⛔ 不要再用 retainTokens(绝对保留量):它是绝对值、不随窗口缩放,宿主会校验 「保留量必须小于触发线」,小窗口模型(如 6.5 万窗口)配上几万的绝对值会被判非法并抛 TargetPressureConfigError —— 压缩整个失效、历史无限增长,直到上下文溢出。

补丁顺序是「出厂 → 本插件 → 用户 profile 的 cordis.patch.yml」,最后一个写的人赢, 所以用户 profile 想改仍盖得住本包,这里只是把合理默认变成可分发的东西。

远程审批(手机点按钮,不用守着电脑)

DSH 的审批服务用 waterfall 广播 approval/request,插件把它桥接到 Telegram—— 推一张和 web 端审批面板同口径的卡片(等待审批 / 已允许 / 已拒绝 / 已取消 / 已超时

  • 命令、工作目录、原因),下面挂两个内联按钮【✅ 允许一次】【❌ 拒绝】。 点完卡片收成终态并去掉按钮。

桥是只读旁路:卡片渲染失败、编辑失败、按钮超时,统统只记日志, 绝不影响审批本身的结果(该通过的照常通过)。 行动端入口在 src/approval-bridge.js,默认超时 10 分钟。

多端互通(TG ⇄ 微信,同一个大脑)

src/hub.js 是所有端点的唯一交汇点,纯逻辑、零依赖。两条规则,都是「同步到所有」:

方向 行为
端点到 hub 同步给 ① DSH ② 其他所有端点
DSH 到 hub 同步发到所有端点

新增端点 = 加一个数组项(O(n)),不是两两镜像(O(n²))。 微信消息并入主人的 TG 会话,所以两边聊的是同一段上下文, 在微信里 /new、/model 操作的是同一个会话。

⛔ 这里刻意不做去重/合并:TG 和微信各进来一条就是两次真实输入,进来几条算几条。

指令

/whoami /status /model /new(新会话) /restart(重启进程,先留档) /help

/restart 与老 bot 同路:先把进展写进 handoff,再由进程外的接力脚本延迟几秒杀掉宿主并重新拉起(有 start.command(兼容老的 启动-<工作区名>.command / 启动-mybot.command)就重开终端窗口;没有就按原始命令行后台拉起)。两者都没有时退化为只重开会话。重启完成后发条消息,新会话自动带上刚才的记忆。

记忆(流水账 + handoff)

默认开启,什么都不用配:

  • 流水账:每轮对话原文追加到 <工作目录>/memory/conversation-cache/raw/ledger/,按月一个文件,只增不删。
  • handoff:/new、切换模型(本次会话满 20 条才写,调试期反复切不会覆盖)、/restart(无条件)和启动补写时,把最近 20 条进展落盘到 <工作目录>/memory/handoff/handoff.md。
  • 冷启动注入:新会话的第一条消息会自动带上最近一次 handoff,模型直接接着上段干,不用你复述上文。
  • 记账程序随插件自带(vendor/conversation-cache/),不依赖机器上是否装过 DSH 的其它项目。
  • 想换位置或关闭:配置里写 memoryDir(自定路径;空串 = 关闭)、memoryScript(自定程序路径)。

微信入口(扫码绑定)

  1. 运行(本目录下):

    node weixin-login.mjs --out 你的账号文件路径
    
  2. 手机微信扫终端二维码(约 2 分钟过期,自动刷新;备用链接发到手机微信里点开)。

  3. 凭据写入账号文件(0600)→ 在配置里加 weixinAccountFile: 那个路径 → 重启即启用。

  4. 扫码的这个微信就是主人,只有它能和 bot 对话。

⚠️ 三条硬注意事项

  1. 一个 token 只能一个进程。两个进程抢同一个 token,先启动的会 409 冲突。
  2. Telegram 游标在内存里:重启后从当前时刻开始拉消息,不补发离线期间的旧消息。
  3. 409 冲突时插件只停自己的轮询并打日志,不会拖垮宿主;原因要去启动日志里看(通常是同一个 token 另有进程在用)。

已知限制

  • 微信扫码工具用 macOS 原生 CoreImage 渲染二维码(零依赖),Windows/Linux 机器上会退化为只打印链接。
  • 回答由节点(hub)广播到所有端点(2026-10-19 定):大模型 → 节点 → 广播,含来源通道本身,出口只有一个。 进度占位消息只报进度,⛔ 收尾不在它上面写「✅ 已回复」(2026-10-20 起:回答进窗口后把气泡删掉),⛔ 不再承载最终回答 —— 同一条消息上出现两个写者 (进度编辑 / 收尾回答)时,迟到的进度编辑会把回答盖回去,而且日志里看不出任何异常(2026-10-19 线上丢件的成因)。
  • Telegram 限流(429)不丢答案:发消息这一层(sendRich)照 Telegram 给的 retry_after 排队重发 (单次最多等 90s、一轮最多 3 分钟,等待总预算可用 DSH_BOTPLUGIN_RATE_LIMIT_BUDGET_MS 调整,默认 180000); 回答广播到 TG 失败时,整篇回答落盘到 DSH_BOT_DIR/未送达/(可用 DSH_BOTPLUGIN_UNDELIVERED_DIR 指定别的目录), 同时把占位消息改成「❌ 回答未送达…已存到 <路径>」——旧版遇限流会留下永远的「⏳ 正在处理…」并丢掉答案。
  • 进度消息只显示「正在做什么」(2026-10-18 口径,同日修订):正文里带 [进度] … 标记的行,加工具的中文动作标签, 标签后面接具体在干什么——如「🏃 跑命令:grep -rn notes src/index.js」「🛠 改文件:服务.mjs」 (只留文件名,不带目录)。模型思考(reasoning 块)一律不上屏;口令类字面量自动打码 (PGPASSWORD=***、Bearer ***)。抠不出细节的工具只留标签、不编占位词;认不出的工具原样给个 🔧 名字。 工具标签可用 DSH_BOTPLUGIN_PROGRESS_TOOLS=0 关掉,只留进度行。
  • 进度占位消息的版式(2026-10-19 定):顶行 = 我最新说的那句话——它顶掉原来「⏳ 正在处理…」那几个字, 我还没开口时才显示那句占位语;⛔ 不显示已耗时。底下滚动的是操作(工具标签、📌 进度行,最近 3 条), 与顶行互不影响。只在我出新话(或新操作)时编辑一次,⛔ 不做定时刷新(旧版每 2s/4s 刷一次, 秒数一变就编辑,内容其实没变,白撞限流)。吐字速度小尾巴跟回答走,气泡上不重复显示。 上面顶行出全文、底下三条操作行照旧掐字(2026-10-25 先定「你的输出不要截断 120 字、出全文」, 同日再厘清「底下那三条操作提示不变」):顶行那句走 顶行_MAX = 0=不掐(truncate(text, max) 见 max ≤ 0 即原样返回);📌 进度行与工具提示仍各自掐到 120/90/60/80/40 字 (操作行_MAX = 120 + 摘要() 里那几个数)——两个常量各管一路:同日早些时候那次「一律出全文」 把操作行也放开了、版式被撑散,故拆开;要收更紧就改 操作行_MAX 与那几个数。 收尾不写那一行「✅ 已回复」(2026-10-20 起):回答进窗口后把气泡删掉;没送达才保留气泡并改标「未送达」。 收尾前先等「已经在飞」的那次进度编辑落地,⛔ 不让它回头盖掉进度文本。

等待上限 90 分钟(2026-10-20 起;进度哨兵已删)

  • 为什么抬:原来是 30 分钟。turnTimeoutMs 一到就判「等待回复超时」,这一轮的答案当场丢掉, 主人只会看到一句 ❌ 等待回复超时(30 分钟) —— 白干一轮。老板 2026-10-20 定:抬到 90 分钟。
  • 等久一点 ≠ 让你多等:它只是「最多等多久才放弃」的上限;活干几分钟就是几分钟。
  • 配置:turnTimeoutMs(毫秒,默认 90 * 60 * 1000)。超时那句话跟着实际值现算 (runtime.js:等待回复超时(${Math.round(timeoutMs / 60000)} 分钟)),⛔ 不再写死「30 分钟」。
  • 回退:改回 30 * 60 * 1000 即回到旧行为。
  • ⛔ 进度哨兵已删除(2026-10-20 老板定「哨兵不起作用,删除哨兵」):旧版在进程外放一个 哨兵.mjs, 每 20 分钟用主 bot 令牌代报一句「还在干活」。老板判定它不起作用,改走「抬等待上限」这一条路 —— 模块 src/progress-sentinel.js、四项配置(sentinelEnabled/sentinelScript/sentinelStateFile/ sentinelIntervalMinutes)、自测件 test-progress-sentinel.mjs,以及项目根的 哨兵.mjs/哨兵状态.json 全部删除。回退办法:装回 v1.1.2。

发版规矩(2026-10-19 定)

  • 版本号末段最多到 99:第 100 版起直接进位 —— 1.0.99 → 1.1.1,⛔ 不再出现 1.0.100 这种末段三位数。
  • 升版本号必须同一个 commit 里一起改三处:package.json 的 version、package-lock.json(两处)、setupdsh.sh 的 BOTPLUGIN_SPEC 默认 tag。本仓 pre-commit 闸(.githooks/发版对账.sh)会拦「只改一边」与「末段 > 99」。
  • 发完把提交与 git tag v<版本> 一起推到 origin;装到本机用 npm install github:aitcmhk-web/DSH-bot#v<版本>(⛔ 别用裸 npm install:旧锁文件会把版本带回上一个 tag)。

许可

MIT License(全文见 LICENSE):可以自由使用、修改、再发布,保留版权声明即可;软件不提供任何担保。