dsh-onebot-paopao(泡泡)
⚠️ 测试中,请勿安装。 这是 0.5.6,还在测试:界面和平台说明这两周一直在改,改动还没经过完整的人工回归(面板各项、NapCat 起停、主动开口、B站 来料都还没逐个验过)。等这轮测试过了再放出来。
⚠️ major bug, do not install. The 0.5.0 defaults are broken: a fresh install has an empty event pool (no labels, so it never speaks), some settings still show raw millisecond values, and the dependency list is too heavy. Being fixed — please wait.
English | 中文
给 DeepSeek Harness 加上 QQ 通道,再加一层会自己找话说的陪伴层。A QQ channel and a companionship layer for dsh.
泡泡能做什么:按你设的权重自己决定什么时候开口、自己找 B站 视频发过来、听得懂语音、看得懂视频、认得出图片、B站 有新私信/@ 会递话头给会话——话永远是会话本体说的,插件只递话头,一个字都不代写。
本仓库是在 dsh-onebot 基础上二次开发的版本:加了陪伴层(泡泡)、B站 视频挑选与「看视频」工具,并把个人化内容全部挪进配置。上游的 QQ 通道部分保持原样。许可证由上游的 BSD-3-Clause 改为 MIT,两份许可全文见 THIRD-PARTY-NOTICES.md。
本插件把 dsh 变成一个 QQ 机器人(OneBot 11 协议,兼容 NapCat / Lagrange / LLOneBot / go-cqhttp), 与 dsh-vision 同款的外部插件形态:零 Python、纯 TS、 原生 Cordis 插件,挂载进 dsh 宿主进程,不改任何核心代码。
用户(QQ) ←→ NapCat ←→ dsh-onebot 插件 ←→ dsh Agent(每个会话一个)
├─ 反向 WS 服务器 / 正向 WS 客户端(自动重连)
├─ 入站:CQ 解析、图片下载、语音转写(STT)、引用/合并转发展开
└─ 出站:单条/t2i 卡片发送、Markdown 剥离、[[qq_forward]]、图片/语音/视频/文件工具
三分钟装好
装之前先确认这三样(缺一样都连不上 QQ):
- NapCat 要以管理员身份运行 —— 它要把自己注入 QQ 进程,不提权起不来(自动拉起时会卡 UAC 弹窗,插件会跳过并提示)。
- QQ 客户端版本要钉住 —— 插件不认识 QQ 协议,是 NapCat 按 QQ 的 build 号打补丁,所以 QQ 必须落在 NapCat 那张表里:表的上限随 NapCat 版本走(4.18.41 到
9.9.36-53644,4.18.42 到9.9.37-54276),实测最稳的是9.9.33-52230。QQ 自动更新到表外的新 build,NapCat 注入后没有二维码、QQ 还会弹「文件已损坏」,插件侧报PacketBackend 不支持当前QQ版本架构。 查自己的表:cd <NapCat目录> && grep -o -a "9[.]9[.][0-9]*-[0-9]*-x64" napcat.mjs | sort -u | tail -3。 修法是把 NapCat 用的 QQ 换回表内版本(单独拷一份 QQ 目录、改launcher.bat里的QQPath,不动系统 QQ),并把那份副本的自动更新关掉。 - VC++ 运行库 —— 缺了 NapCat 会 0xC0000135 秒退;装一下
winget install Microsoft.VCRedist.2015+.x64。
# 1. 装插件(二选一)
dsh plugin --profile desktop add github:ZhongYiSS/dsh-onebot-paopao
dsh plugin --profile desktop add dsh-onebot-paopao # 发到 npm 之后
# 2. 重启 DSH(插件要重启才挂得上)
# 3. Web 界面 → 设置 → 泡泡 → 总览 → 「检查环境」,缺什么按提示补
机器上没有 git(
github:简写要靠 git 去 clone):把 profile 的package.json里那一行换成 codeload 的 tar.gz 直链,pnpm 认这种依赖:"dsh-onebot-paopao": "https://codeload.github.com/ZhongYiSS/dsh-onebot-paopao/tar.gz/<commit>"或者npm pack出一个.tgz再装。装完记得重启 DSH。
然后:设置页「泡泡 → 第一次用」里点「启动 NapCat」→ 扫码登录一次 → 在「QQ 通道」填机器人 QQ 号 → 用另一个号给它发一句话,收到回复就算接通了。
装不上、要走源码安装、要手动合并 cordis.patch.yml 的,看下面的安装一节。
架构
用户(QQ) ←→ NapCat ←→ dsh-onebot 插件 ←→ dsh Agent(每个会话一个)
上游仓库
dsh-onebot里有一版可交互架构图(docs/);本 fork 没带图片资源,就不放失效链接了。
功能
| 类别 | 能力 |
|---|---|
| 连接 | 反向 WS(NapCat ws-reverse 拨入,默认端口 8643)或正向 WS(拨出,默认 ws://127.0.0.1:3001);断线自动重连(2s→60s 退避) |
| 入站 | 私聊/群聊、段数组优先解析(CQ 字符串回退)、CQ 反转义、@/回复触发检测(fail-closed;回复仅认机器人自己的消息)、图片四路解析(url/base64/file/hash)、大图自动压缩(长边 ≤inboundImageMaxPx,GIF 不压)、文件段双通道接收(CDN 直链 get_private_file_url + get_file base64/url 回退)、表情 id→emoji/卡片/戳一戳段类型、引用消息自动取原文(get_msg)、合并转发自动展开(get_forward_msg) |
| 语音 | ffmpeg 转 16kHz WAV + whisper 转写(openai-whisper / whisper.cpp / 自定义命令);非阻塞:语音消息先以 [语音] 占位进入回合(不阻塞回复),转写完成后以「(语音转写:…)」补递(默认超时 60s);转写失败/超时保留 [语音] 占位 |
| 文字图 | t2i 卡片渲染器(@napi-rs/canvas):标题/粗斜体/删除线/引用/列表/代码块/表格/行内 code 胶囊/彩色 emoji/中文标点禁则;与 Hermes 原版同款数值(800px/26px/禁则集合/右缘 790) |
| 出站 | 正文 ≤ textImageThreshold(默认 150)字符单条发送、超过阈值渲染 t2i 文字图卡片(AstrBot 风格:标题/引用/列表/表格/代码块/彩色 emoji;渲染失败、PNG 超 outboundImageMaxBytes 或 <=0 禁用卡片时回退单条纯文本)、Markdown 剥离为 QQ 纯文本、[[qq_forward]] 合并转发(群/私聊)、实时中间消息(interimMessages:每条中间文本立即发出、实时可见;各自在 interimRecallMs(默认 90s)后自动单独撤回;回合结束时先把整轮中间消息渲染成一张 t2i 小结卡、立即撤回仍在屏幕上的原文、再发送最终回复——不用回合末合并转发,避免长回合「原文超 2 分钟撤不回+转发卡重复」;interimRecall: false 时降级为只发不撤(无小结卡、不撤回))、宿主「计划书/提问卡」自动中继(模型调用 exit_plan_mode / ask_user_question 时把计划全文/问题选项发到 QQ)、正在输入提示(set_input_status,仅私聊) |
| 命令 | 斜杠命令(仅管理员):/new 开新会话、/stop 停止生成、/steer <补充内容> 向运行中的任务补充指令(无运行任务时回用法提示;详见下方命令表与 newMessagePolicy 配置)、/model 查看或切换当前会话模型(--default 修改部署默认;无参输出两级序号列表,回复序号选 provider → 再回复序号选模型)、/workspace 查看或切换工作区(无参编号列表,回复序号即选)、/preset 查看或切换 agent 预设(无参编号列表,回复序号即选)、/session 查看可切回历史会话并按序号切回(/new、/workspace、/preset 切换下来的旧会话进列表,可来回切,历史上下文恢复)、/status 会话全景、/doctor [export [参考号]](别名 /healthcheck)运行体检(export [参考号] 导出脱敏诊断包 qq-diagnostics-*.zip,文件名含触发参考号)、/retry 重跑上一条、/id 会话标识、/ver 版本、/ocr 识别最近图片、/mode 切换出站模式(跨重启持久化)、/plan 计划模式、/permission 切换宿主权限预设(QQ 别名 w=工作区可写+需审批、f=全权+免审批;完整预设名照传,未知名由宿主报错并回显可用列表;无参查看当前与可用列表,序号按 available 顺序)、/goal 目标记录(跨重启持久化)、/help 帮助;未知斜杠命令默认拦截并提示相近命令(unknownCommand: passthrough 改为透传给模型) |
| 工具 | qq_send_image(≤9 张,路径或 URL)、qq_send_voice、qq_send_video、qq_send_file、qq_send_forward、qq_napcat_api(14 个白名单 action)、qq_group_history(文件编辑工具 code_safe_edit 等已拆至独立插件 dsh-safe-edit,见下文「安全编辑」) |
| 权限 | 管理员白名单(ONEBOT_ALLOWED_USERS)、dm/group 策略(open/allowlist/disabled)、群聊 @提及 gating、受限用户 [受限用户:仅问答] 软限制、出站敏感内容审计 |
| 会话 | 每个 QQ 会话一个持久 Agent(session id 稳定派生),重启后自动 resume;按 agentPreset/workspacePath 挂载到 preset 与工作区;每轮结束 flush 落盘 |
| 运维 | 热加载:profile 层 patch 的 config 覆盖与 insert 新增/删除均热生效——插件 fiber 秒级原地重启(宿主进程不重启;代价:QQ 桥一次秒级重连、在途回合中断);touch/内容未变不触发任何动作;bundle 自带 patch、仓库模板、部署副本的 patch 文件与根配置 cordis.yml 的改动不热生效;临时媒体 TTL 过期清理 |
| 提示词 | 自动注入 QQ 平台说明(纯文本输出、图片/语音标注 [图片]/[语音] 占位、工具与命令指引、禁宿主交互卡);按每个 QQ 会话 agent 自身作用域注入,Web 会话不可见 |
陪伴层(泡泡)
除了「当 QQ 机器人」,本插件还带一层陪伴逻辑:让会话按自己的节奏找人说话。插件本身不代写一个字——它只掐表、递话头,说什么由模型自己决定。
| 能力 | 说明 |
|---|---|
| 权重 | 每个会话(私聊或群)各算一份权重,每拍按 risePerTick 上升;过了阈值才掷骰,触发概率 p = triggerPmax × 权重 / 100 |
| 扣分与补分 | 每轮固定扣 releaseBase;会话在回复末尾写 [SCORE 0-10](那是它自己的心情),插件按补分表退/追扣:10 分退 120%、5 分不退、1 分再扣 80%、0 分再扣 100%;下限 wFloor 可设 |
| 事件池 | 抽中的只是一个「词」(说句话 / 刷视频 / 找张图 / 上网看看 / 甩表情包 / 发语音 / 翻旧账 / 提醒 / 唱首歌),怎么做由会话自己定;份数、抽中加分、自定义事件都能在设置页改 |
| B站 | 按兴趣关键词轮转挑视频(不感兴趣的词、拉黑的 UP、口味账都能记);发之前会抽字幕、抽帧看画面、必要时听口播,再决定推不推 |
| 看视频 | qq_video_look:任意 B站 链接 / 本地文件 / 直链,quick(只读字幕)、normal(5 格九宫格)、deep(10 格);按镜头切分、按画面变化分配帧、宫格底下画音轨条;B站 视频走 playurl 直连下载(分片 Range + ffmpeg 合成),不依赖 yt-dlp |
| 定时话头 | 会话可以回 [LATER 25](分钟)或 [AT 23:30],插件到点叫醒它再说一轮;每天条数、最长几小时可配 |
| 免打扰 | 时间段内不涨分、不抽卡;绑定开关关掉整条停摆 |
| 记录 | 「做过的事」按会话分开,可切到积分视图看每一拍为什么没开口、扣了多少、补了多少 |
设置页分三块:陪伴(总览 / 会话 / 主动度 / 事件 / 做过的事 / 说明)、B站与视频、QQ 通道。
个人化的东西(人设补充
promptExtra、表情包目录memeDir、主人 QQownerQQ、发图白名单mediaFreeDirs/mediaDrawDirs、视觉模型与网关)全部走配置,代码里不留任何本机字面量;不配就是空,插件照常工作,只是不带那些人设和特权。
兼容性
| 项 | 要求 |
|---|---|
| dsh | ≥ 0.1.5-rc.1(engines.dsh:0.1.5/0.1.6/0.1.7 全线 + 0.2.0 线 ≥0.2.0-rc.1;@deepseek-ai/* peer 依赖同范围,JsonValue 由 dsh-util-values 提供) |
| Node.js | ≥ 22 |
| OneBot 11 实现 | NapCat / Lagrange / LLOneBot / go-cqhttp(reverse 或 forward WebSocket) |
| NapCat 权限 | 必须管理员运行(要注入 QQ 进程);非管理员时插件只跳过自动拉起,不会去弹 UAC |
| QQ 客户端 | 必须是 NapCat 适配表里的 build(上限随 NapCat 版本变:4.18.41 到 9.9.36-53644,4.18.42 到 9.9.37-54276),实测 9.9.33-52230 稳;表外的版本报 PacketBackend 不支持当前QQ版本架构 |
| VC++ 运行库 | 缺了 NapCat 秒退 0xC0000135;winget install Microsoft.VCRedist.2015+.x64 |
| 可选依赖 | 语音转写需 ffmpeg + whisper CLI;无字幕视频听声音需 Python + faster-whisper(转写脚本随包,不必自己找);B站 视频直连下载,不需要 yt-dlp(yt-dlp 只给站外链接用);t2i 文字图在 Linux 需 Noto CJK 字体 |
B站 功能要什么(分三档,别混)
| 想要 | 必须有 | 没有会怎样 |
|---|---|---|
| 扫码登录 B站 | Node ≥ 22、npm 依赖 qrcode、能访问 passport.bilibili.com |
缺 qrcode → 插件整个加载失败(面板空白/404,日志 Cannot find module 'qrcode');网络不通 → 点登录报「B站 没给二维码」 |
| 拿到视频链接 | 什么都不用装(走 api.bilibili.com 的 view + playurl + 分片下载) |
只有网络/风控会挡;yt-dlp 与此无关 |
| 拿到视频内容(字幕/画面) | 字幕:同样不用装 画面:ffmpeg + ffprobe( winget install Gyan.FFmpeg)宫格拼图: @napi-rs/canvas + fontkit(随包)让模型真看懂:设置里填 videoVisionModel / videoVisionBaseUrl / videoVisionApiKeyEnv |
没 ffmpeg → 抽不出帧;没配视觉模型 → 日志「仔细看没成:还没配看视频的视觉模型」;没字幕又没 Python+faster-whisper → 只能看画面 |
| 站外链接(YouTube 等) | yt-dlp |
报「下载站外视频需要 yt-dlp(B站 视频不用)」 |
link: 方式装的,第一次要在插件目录跑一次 npm install(qrcode/canvas 这些是普通依赖,不跑就没有);用 npm 包或 tgz 装的不必单独跑。
装完先做一次环境检查
node bin/doctor.mjs # 或 npx dsh-paopao-doctor
一条命令列出:Node 版本、ws/canvas/fontkit、ffmpeg/ffprobe、yt-dlp、Python 与 faster-whisper、whisper CLI、B站 登录、视觉模型、NapCat 目录与进程,每项后面直接跟「缺了怎么补」(含 winget install … 这类一行命令)。只查不装、不改系统。
看「必需 / 可选」那一列:必需 只有四项——Node ≥22、ws、@napi-rs/canvas、fontkit,它们红了才是「跑不起来」;其余全是 可选,黄了只少一块功能(比如没 ffmpeg 就看不了视频,但聊天照旧)。面板底部的结论也只按必需项算。设置页「陪伴 → 总览」里也有同一个按钮(「检查环境」)。
最后验证:2026-11-12(0.5.6:平台说明去掉本机专属内容(表情包不列文件名、记忆说明删掉、语音只讲发文件)、设置回显不再夹取、NapCat 启动脚本改 UTF-16LE、设置存盘到 settings.json;0.5.4:按外部审计修的 50 处——进程控制、出站 SSRF/钉地址、注入边界、稳定性;0.5.1 起:默认事件带词、秒/毫秒夹取、qrcode 依赖、主人 QQ 生效、私聊/群聊安全闸、群里跑受限预设、体检分必需/可选)——npm test 108 个用例全绿、node scripts/guard-publish.mjs 通过(111 个文件、17 个工具)。
安装
前置:dsh(≥0.1.5-rc.1)在 PATH 上;NapCat 或其他 OneBot 11 实现已运行。
npm 安装(发布通道,与下方源码安装二选一,勿混装)
dsh plugin --profile web add dsh-onebot-paopao
(或用 Web 界面的 Plugins 页安装。)随后:
在 profile 目录
<DSH_HOME>/profiles/web/的pnpm-workspace.yaml中写入两行 (实测 pnpm 12 不读项目级.npmrc,见docs/npm-e2e-report.mdD1):autoInstallPeers: false hoist: false——防止 pnpm 自动拉入 registry 上陈旧的
@deepseek-ai/*独立副本。npm/pnpm 版本差异: 此配置仅 pnpm 12 用户必须(pnpm 12 已不读.npmrc);npm 用户无此问题——实测 npm 默认 peer 自动安装拉入的 registry 现版本满足 peer 范围(docs/npm-e2e-report.md§1 #2)。 运行期 peer 统一由宿主解析层完成:插件 import 的@deepseek-ai/*只要声明在该包 peerDependencies 里,就会被宿主拦截并重定向到宿主安装树的同一实例, 无需任何 npm override。把包内
cordis.patch.yml模板的两行 insert 合并进 profile 层<DSH_HOME>/profiles/web/cordis.patch.yml:主条目name: 'dsh-onebot-paopao', host-plane 行name: 'dsh-onebot-paopao/settings-remote';config 覆盖须挂 patch 顶层同 id 行(详见模板头注释)。不要在 profile 的 package.json 手写任何
@deepseek-ai/*依赖——手写副本会 覆盖宿主安装树成为拦截目标,造成版本错位双包。若安装被兼容门拒绝(dsh 版本不在 peer 范围内),按官方口径用
dsh plugin allow-version ... --accept-risk显式豁免。
方式二:git clone 源码安装
git clone <repo> ~/dsh-plugins/dsh-onebot-paopao
cd ~/dsh-plugins/dsh-onebot-paopao
npm install --include=dev
./scripts/build.sh # 链接宿主 @deepseek-ai 包 + tsc 编译 src/ → lib/
部署副本必须带完整 node_modules 链接集(
scripts/link-host.sh产物):files只打包lib/, 但运行时 peer 依赖@deepseek-ai/*是链接到宿主安装的——只复制lib/会挂载失败(插件入口会 显式报「peer 依赖解析失败」错误)。宿主无dsh在 PATH 上(如 fnOS 应用形态)时用逃生门:DSH_ROOT=<宿主node_modules路径> ./scripts/build.sh。
fnOS 应用形态宿主构建:fnOS 应用中心安装的 dsh 没有 dsh CLI 在 PATH 上,build.sh 的自动定位
(PATH 二进制 → npx store)会失败并显式报 cannot locate the dsh install。此时用 DSH_ROOT 显式指定
宿主 node_modules 根(该目录下须有 @deepseek-ai/,否则报错退出而非静默回退):
DSH_ROOT=<fnOS 应用数据目录>/node_modules ./scripts/build.sh
构建完成后把部署副本(lib/ + 完整 node_modules 链接集)放到目标位置再挂载;运行副本缺链接集同样会挂载失败(见上方说明)。
挂载到 ~/.dsh/config.yaml(没有就新建):
- insert:
- id: dsh-onebot
name: '$HOME/dsh-plugins/dsh-onebot/lib/index.js'
config:
mode: reverse # reverse = NapCat 拨入;forward = 插件拨出
port: 8643
# accessToken: '与NapCat一致的token' # 可留空:反连模式会自动生成一个并写进 $DSH_HOME/onebot/access-token.json
# botQQ: '' # 留空自动从 meta 事件学习
adminUsers: ['你的QQ号'] # 必配:至少一个管理员,否则私聊/斜杠命令不可用
⚠️ 首次配置必须设置至少一个管理员(
adminUsers或环境变量ONEBOT_ALLOWED_USERS):dmPolicy: open(默认)只允许管理员私聊,斜杠命令也仅管理员可用;不设置则无人能对话。 开发调试可临时allowAllUsers: true(或ONEBOT_ALLOW_ALL_USERS=true)放行所有用户。
重启 dsh(dsh web 或你的启动方式),日志出现 [dsh-onebot] mounted 即挂载成功。
NapCat 侧(必须配置,两种模式二选一):
- reverse 模式(NapCat 拨入 dsh,推荐):NapCat 网络设置里新增「WebSocket 客户端」,
「上报地址」填 dsh 的 WS 地址
ws://<dsh 所在机器 IP>:<port>/ws(如ws://192.168.1.100:8643/ws), 「token」填插件accessToken相同的值;dsh 与 NapCat 不同机时不能用127.0.0.1。 - forward 模式(dsh 拨出到 NapCat):NapCat 网络设置里启用「WebSocket 服务端」(默认监听
0.0.0.0:3001),插件url配置为ws://<NapCat 所在机器 IP>:3001(同机可用ws://127.0.0.1:3001),token 两边一致。
两侧 token 必须一致;消息上报格式建议选「数组」(插件段数组优先解析,CQ 字符串仅回退)。
配置完成后重启 dsh,日志出现 [dsh-onebot] mounted 且 NapCat 显示连接成功即就绪。
部署位置要求:NapCat 必须部署在 dsh 可达的局域网内(同一网段/能互通),
WS 连接、图片下载、文件解析都依赖这条网络通路;NapCat 与 dsh 不在同一台机器时,
需要在 NapCat 侧开启「文件转 URL」开关,get_file 才会返回可下载的 http(s) url
(否则返回容器内路径,本插件无法访问)。
第一次用(从零把 QQ 接上)
插件本身不碰 QQ 协议,它只是等 NapCat 把一条 WebSocket 拨进来(默认 reverse 模式)。
设置页「陪伴 → 总览」最上面有一张第一次用的卡片,三盏灯分别提示:本插件在不在听、token 配没配、QQ 登没登。
装 NTQQ + NapCat,然后起 NapCat 并用手机 QQ 扫码登录一次(二维码在
<NapCat>/cache/qrcode.png,会自己刷新)。 命令行起法(Windows,管理员):REM NapCat 目录下,路径按自己装的改 cd /d D:\NapCat\NapCat.Shell launcher.bat REM 已经登录过一次、想免扫码:launcher.bat -q <机器人QQ号>想让它开机常驻,把上面这行做成计划任务或快捷方式即可;插件不需要任何额外的启动器。
在 NapCat 的 WebUI 里加一个「WebSocket 客户端」:地址填卡片上那个
ws://127.0.0.1:8643/ws, token 填插件里的accessToken(设置页「复制 token」按钮直接给明文)。 两边 token 必须一致;reverse模式下插件留空 token 会拒绝启动(fail-closed)。填了插件配置里的
napcatDir(NapCat 安装目录,含launcher.bat)之后,这张卡下面会多出 启动 NapCat / 重启 NapCat / 看二维码 / 刷新 四个按钮,二维码直接贴在卡里——不用再开小管家、也不用自己去找 png。 启动时若配了botQQ,会用launcher.bat -q <botQQ>试免扫码快速登录。 「重启」会先结束NapCatWinBootMain.exe和QQ.exe再拉起(Hook 是开机注入的,不杀 QQ 等于没重启), 期间会断线十几秒,需要重新扫码就得点「看二维码」。回设置页看三盏灯:都在听、token 一致、QQ 在线之后,再打开上面的「启用」。 拉一下
GET /api/bubble/channel,connected: true就是真的通了。
接好之后:/chats 能列出会话 → 总览里「+ 加个会话」挑一个 QQ 号或群 → 权重开始涨,剩下的它会自己来。
B站 那些能力(刷视频、看视频、回私信)在设置页「B站与视频」里单独登录,同样不依赖任何外部启动器。
配置
完整配置项见 src/index.ts 的 Config schema(schemastery 校验,均有默认值)。常用:
| 键 | 默认 | 说明 |
|---|---|---|
mode |
reverse |
reverse/forward |
host / port |
127.0.0.1 / 8643 |
reverse 监听;跨机部署(NapCat 从其他机器拨入)需显式改为 0.0.0.0 |
url |
ws://127.0.0.1:3001 |
forward 目标 |
reconnectMaxAttempts |
100 |
自动重连放弃上限:连续失败达到该次数后停止重连,日志输出上限值与恢复指引;0 = 无限重连(退避封顶 60s) |
accessToken |
空 | OneBot token;反连模式下留空会自动生成一个并落盘($DSH_HOME/onebot/access-token.json,设置页「复制 token」给明文),下次重启沿用;填了就按填的来;forward 可为空 |
botQQ |
空 | 机器人 QQ(空=自动学习) |
ignoreSelf |
true |
忽略机器人自己发出的消息(防自循环) |
requireMention |
true |
群聊需 @ 或回复机器人的消息才响应(回复他人消息不触发;被回复消息无法判定时回落视为提及,fail-open) |
rateLimitPerMinute |
30 |
每会话每分钟普通消息上限(60s 滑动窗口):超限跳过处理并限流提示(每窗口至多一条),命令不受限;0 = 禁用 |
dedupWindowSeconds |
300 |
入站消息去重窗口(秒,W2-②):同一会话内相同 message_id 在该窗口内重投(断线重连重放等)只处理第一条,后续静默跳过并记 debug;仅对会触发回复处理的消息事件生效(notice/meta 类不经过此闸);0 = 禁用去重 |
actionRatePerMinute |
20 |
主动写操作每分钟全桥合计上限(W2-③):约束 qq_send_image/voice/video/file/segments 与 qq_send_forward 等工具发起的主动发送(回合内的被动回复/interim/通知不受此闸,由 rateLimitPerMinute 与离线队列上限约束);超限拒发并记 warn 与审计;0 = 禁用分钟限额 |
actionRatePerDay |
500 |
主动写操作每日(本地自然日)全桥合计上限(W2-③):口径同 actionRatePerMinute;超限拒发并记 warn 与审计,次日自动恢复;0 = 禁用日限额 |
actionAuditEnabled |
true |
主动写审计(W2-③):每次主动写(成功/失败)与拒发事件追加写一行 JSON 到 mediaDir/qq-actions.log(含 ts/chatId/action/ok/reason);写失败仅 warn 不影响发送 |
traceEnabled |
false |
链路追踪(W1):为每条入站消息生成 traceId 并贯穿入站→模型回合→出站,决策事件(含限流/去重/白名单/@ 门控/闸门拒发与每个静默丢弃分支的中文 reason)逐行 JSON 落盘 mediaDir/qq-trace.jsonl(4MiB 改名轮转,保留 qq-trace.1/2.jsonl;同一原因 5 分钟限频;异步队列写不阻塞管线);默认关闭——关闭时不生成 traceId、不落盘、决策行为与 0.6.0 完全一致 |
traceLevel |
debug |
追踪事件级别(W1):debug 记录全部决策事件;warn 仅记录 ok:false(被拒/失败/丢弃)事件 |
logLevel |
info |
控制台日志级别门槛(debug < info < warn < error):低于该级别的日志不输出。debug 全部输出(含「session 事件早退」等调试行);info 常规信息(启动、连接建立、meta 事件等,默认);warn 仅警告与错误;error 仅错误 |
recordInbound |
false |
入站录制(W1):把收到的每条入站事件(含被门控跳过的,附当时决策 reason)按可回放形状逐行 JSON 追加写 mediaDir/qq-inbox.jsonl(2MiB 改名轮转,保留 qq-inbox.1/2.jsonl;注入的帧不会被二次录制,杜绝「注入→录制→回放→注入」自激);纯旁路:写失败仅 warn 限频,绝不影响回复管线 |
inboxRedact |
false |
录制脱敏(W1):落盘前把帧中 6 位以上的数字串(QQ 号/群号等)脱敏为前 3 位+****(数字型 id 字段一并处理),便于把录制文件发给别人离线回放(npm run replay) |
injectEnabled |
false |
事件注入通道(W1,仅调试用):开启后按 injectIntervalMs 轮询 mediaDir/qq-inject.jsonl,把新行(每行一个 OneBot 消息事件 JSON)喂进真实入站管线(trace 事件标 stage=inject);启动时已存在的历史行跳过并记一条说明。信任边界:注入是调试通道,默认全关,勿在生产开启 |
injectDryRun |
true |
注入干跑(W1,强烈建议保持 true):注入回合触发的全部出站写操作(发消息/撤回等,含异步模型回合的最终回复——拦截点在出站动作层)被拦截计数、原文进 trace 事件、绝不真发 QQ;真实消息到达同一会话立即解除拦截(不连坐真人);false = 注入内容会真实发送,仅限隔离环境实验 |
injectIntervalMs |
2000 |
注入队列轮询间隔(毫秒,W1;最小 500) |
unknownCommand |
intercept |
未知斜杠命令处置:intercept(默认)拦截并提示相近命令(前缀匹配优先,编辑距离 ≤2 兜底且仅输入长度 ≥4 时启用,至多 3 个候选;无候选提示发 /help 或去掉开头 / 重发);passthrough 维持旧行为透传给模型。非 /纯单词 开头的文本(如路径 /tmp/x)不受影响 |
newMessagePolicy |
queue |
任务运行中收到新普通消息的处理策略(P1):queue(默认)=排队——消息在当前任务结束后按序处理为后续回合,等待期间每运行回合至多回复一次排队提示;steer=插话——运行中收到的新普通纯文字消息作为补充指令加入当前任务(模型在下一步读取)并回复提交提示。例外:图片/文件/语音/带引用或合并转发消息与 [受限用户:仅问答] 群成员消息两种模式下都按排队处理(软限制不放宽、媒体管线不被绕过);自动插话资格由 autoSteerPolicy 控制;/steer 命令两种模式下均可用;无运行中任务时行为与该配置无关(与 0.8.1 一致)。设置页 behavior 组可改,热重启生效 |
autoSteerPolicy |
admin |
steer 模式下自动插话的发送者资格(T9,用户裁决 2026-10-12):admin(默认)=仅管理员可自动插话;all=所有非受限发送者均可。不具备资格的消息按排队处理(入队+排队提示);/steer 命令始终仅管理员、不受本项影响;queue 模式行为完全不变。设置页 behavior 组可改,热重启生效 |
deferredTimeoutMs |
600000 |
等待模型回复超时阈值(毫秒,T3/P2 超时补发):回合超过该时长没有任何模型输出时,向聊天回复一条超时提醒并把回合登记进补发注册表(继续运行、不放弃);回合完成后向原聊天补发最终文字(带「(超时补发)」前缀,走既有出站分段/t2i 规则),只补最终文字与终态通知——不重放审批/提问卡/文件工具调用/图片;发送明确失败最多尝试 3 次(退避 2s/10s),发送结果不确定(动作超时/非网关错误,消息可能已到达)时保留记录并停止自动重试防重复;注册表逐行 JSONL 落盘 mediaDir/deferred-delivery.jsonl(原子写、逐行损坏降级、缺失=全新),插件重启后自动恢复未完结条目的跟踪;/stop 只终止当前聊天提交的回合并显式终止该聊天的补发跟踪(其他聊天不受影响);正常完成(未超时)零注册表写入、零补发。设置页 behavior 组可改,热重启生效;0=关闭 |
dmPolicy |
open |
私聊策略:open(仅管理员)/allowlist(白名单)/disabled |
groupPolicy |
open |
群聊策略:open(所有人)/allowlist/disabled |
restrictedMemberPrefix |
true |
群聊非管理员消息注入 [受限用户:仅问答] 前缀(软限制) |
adminUsers |
[] |
管理员 QQ;也可用 ONEBOT_ALLOWED_USERS 环境变量。必须至少设置一个,否则私聊(dmPolicy=open)与斜杠命令无人可用 |
allowFrom / groupAllowFrom |
[] |
白名单用户/群 |
interimMessages |
true |
工具调用之间的中间文本是否立即发送;false 只发最终回复 |
interimRecall |
true |
中间消息撤回与回合末小结卡开关;false = 只发不撤(降级:无小结卡、不撤回,中间消息留在屏幕上) |
sendErrorNotice |
true |
一轮运行出错时向用户发送 ⚠️ 错误提示 |
sensitivePatterns |
[] |
出站敏感内容审计正则(内置 rm -rf/关机/删库/密钥 等模式;留空用内置默认) |
sttEnabled |
true |
语音转写(需 ffmpeg + whisper CLI) |
sttEngine |
auto |
STT 引擎:auto(自动探测 whisper-cli/whisper/mlx_whisper)/openai/whisper-cpp/custom |
sttModel |
small |
whisper 模型 |
sttCommand |
空 | custom 引擎的程序名/路径 |
sttArgs |
[] |
custom 引擎参数模板,{file} 与 {out} 会被替换 |
sttTimeoutMs |
60000 |
语音转写超时(毫秒;v0.4.0 起默认 60s,此前 300s):超时保留 [语音] 占位;<=0 回落内置 60s |
textImageThreshold |
150 |
t2i 卡片阈值:正文长度 ≤ 该值单条发送;> 该值渲染为文字图卡片。渲染失败、PNG 超 outboundImageMaxBytes 或 <=0 禁用卡片时回退单条纯文本 |
cardFooter |
dsh |
卡片页脚品牌("Powered by ") |
fontFiles / fontFamilies |
[] |
t2i 字体文件/家族覆盖(Linux 部署必看:需安装 Noto CJK) |
mediaDir |
<dsh-home>/media/onebot |
入站媒体/映射文件目录 |
tempTtlHours |
6 |
入站临时媒体文件保留时长(小时),到期自动清理 |
inboundImageMaxPx |
2048 |
入站图片长边上限(px):超过则等比压缩后交给视觉模型(透明 PNG 保留、GIF 不压);<=0 禁用(旧名 imageMaxSize deprecated,本版兼容读取) |
outboundImageMaxBytes |
8388608 |
出站图片大小上限(字节):t2i 小结卡等出站图片超限时自动回退纯文本(旧名 maxImageBytes deprecated,本版兼容读取) |
maxVoiceBytes |
15728640 |
出站语音大小上限(字节) |
maxFileBytes |
20971520 |
出站视频/文件大小上限(字节) |
inboundFileMaxBytes |
20971520 |
QQ 入站文件大小上限(字节):超限文件拒收并提示(旧名 maxInboundFileBytes deprecated,本版兼容读取) |
allowPrivateHosts |
false |
下载媒体 URL 时允许私网/环回地址(仅跳过私网检查,协议白名单与限长仍生效);仅本机反代等可信场景开启,公网部署保持 false |
agentPreset |
空 | 会话挂载的 agent preset(留空=默认) |
workspacePath |
空 | 会话挂载的工作区(留空=宿主进程 cwd;未配置时插件启动会 warn 一次建议显式配置——宿主未提供可编程查询的默认工作区)。/workspace 的 per-chat 覆盖已持久化进映射文件,重启不丢 |
chatIdleEvictDays |
7 |
会话空闲淘汰天数:chat 超过该天数无任何活动时,在下一条入站消息处理前清理其内存态 agent(会话先 flush 落盘、映射保留,同一 chat 之后再发消息可 resume 恢复原会话);0 = 禁用 |
⚠️ BREAKING(M0 安全加固):reverse 模式下连接必须带非空 token(fail-closed),但不再要求人手填——配置里留空时插件自动生成一个并落盘,设置页能复制;
host默认从0.0.0.0改为127.0.0.1(仅本机监听),跨机部署需显式配置host: 0.0.0.0。
环境变量:ONEBOT_ALLOWED_USERS(逗号分隔管理员)、ONEBOT_ALLOW_ALL_USERS=true(开发用)。
录制 · 回放 · 注入 · 体检(W1)
| 能力 | 开关(默认) | 用法 |
|---|---|---|
| 入站录制 | recordInbound(false)+ inboxRedact(false) |
开启后每条入站事件逐行 JSON 追加写 mediaDir/qq-inbox.jsonl(含被门控跳过的事件与当时决策 reason;2MiB 改名轮转);纯旁路,写失败仅 warn 限频 |
| 离线回放 | 无需开关 | npm run build 后 npm run replay -- <inbox.jsonl> [--media-dir <dir>] [--limit <n>] [--require-mention] [--bot-qq <qq>] [--admin <qq>] [--out report.json]:读录制文件喂真实入站管线(强制 dry-run:不启 WebSocket、出站写全部拦截计数),输出每条消息的 traceId+决策链汇总;不依赖宿主运行。回放门控取自命令行参数(默认全放行),需复现线上门控请传相同策略 |
| 事件注入 | injectEnabled(false)+ injectDryRun(true)+ injectIntervalMs(2000,最小 500) |
仅调试用:往 mediaDir/qq-inject.jsonl 追加一行 OneBot 消息事件 JSON(可直接复制 qq-inbox.jsonl 的行),桥按间隔轮询喂进真实管线;dry-run 下该回合全部出站写(含异步模型回复)被拦截计数、原文进 trace(stage=inject),绝不真发 QQ。启动时已存在的历史行跳过并记一条说明 |
| 体检+诊断包 | 无需开关 | QQ 里发 /doctor(仅管理员,等价别名 /healthcheck):连接态/重试与自愈/去重与写闸门计数/trace·inbox·audit 文件大小/最近 5 条 ok:false 事件/最近故障参考号;/doctor export [参考号] 打包 trace+inbox+actions log+脱敏配置快照为 mediaDir/qq-diagnostics-<ts>[-<参考号>].zip(参考号省略时自动用最近一次故障的参考号;token/accessToken 强制脱敏,含最终字节扫描 fail-closed) |
| 诊断参考号 | 无需开关 | 连接/桥类故障发 QQ-CONN-<时间戳><随机>、消息类故障发 MF-<时间戳><随机> 参考号(仅故障事件生成,正常流量零发号;参考号本身不含任何用户消息内容)。同一参考号贯穿四处:⚠️ 错误提示尾部、宿主日志「故障参考号 …」行、trace 事件 data.ref、/doctor 报告「最近故障参考号」节;诊断包导出文件名含触发参考号 |
设置页
dsh Web GUI 的设置页提供本插件的可视化配置(host-plane Remote onebotSettings,写入 profile 层
cordis.patch.yml 中 dsh-onebot 条目的 config 覆盖行——与手改 patch、宿主原生设置页是同一个持久层)。
三组 19 键
| 组 | 键 |
|---|---|
| connection(连接) | mode、host、port、url、accessToken、botQQ |
| permissions(权限) | requireMention、adminUsers、dmPolicy、groupPolicy、allowAllUsers、allowFrom、groupAllowFrom |
| behavior(行为) | interimMessages、interimRecall、interimRecallMs、sendErrorNotice、unknownCommand、rateLimitPerMinute、newMessagePolicy、autoSteerPolicy、deferredTimeoutMs |
accessToken 在设置页做密码型输入,快照返回侧脱敏(不回显明文);写入侧仍在 patch 文件明文落盘。
与 patch config 的优先级
schema 默认值 ← bundle/底层 patch(继承层) ← profile 覆盖行(设置页写这里);同一层内后写的 patch
条目胜出。设置页键与手改 patch 是同一层的同一行,不存在两套优先级。三组 19 键之外的键
(media/STT/性能/agentPreset 等低频键)不设 UI,仍在同一行 config 手改 patch。
生效方式
每次保存(设置页或手改同一覆盖行)= 插件 fiber 秒级原地重启生效,宿主进程与 Web GUI 不重启; 代价:QQ 桥一次秒级重连、在途回合中断、频控窗口与中间消息缓冲清零(会话映射/历史不受影响)。 与当前覆盖行全等的保存是 no-op,不触发重启;写入导致插件激活失败时 patch 文件自动回滚、旧配置继续运行。
会话工作区(workspace)选择
每个 QQ 会话创建时按以下优先级确定工作目录(写入 session meta,创建时冻结,不随配置变化):
- 该会话的
/workspace覆盖(per-chat 记录,跨/new保留) - 配置
workspacePath - 宿主进程 cwd(
process.cwd())
/workspace <目录> 切换(realpath + 目录校验):记录覆盖后会 retire 当前 agent,
下一条消息以新目录重建会话,旧会话保留在磁盘。/workspace 无参数输出编号列表
(当前目录标「← 当前」),回复 /workspace <序号> 即可切换;/workspace list 列出全部 workspace 记录。
/workspace 的 per-chat 覆盖已持久化进映射文件(chat-sessions.json,加法格式、兼容旧文件):
重启、会话恢复失败均不丢失,对 /new 同样保持——只要该 chat 用的是非默认目录,
重启后 /workspace 与新会话都会继续使用原目录。
会话自动挂载到 GUI 工作区:仅当会话 cwd 等于配置的 workspacePath(未配置时为宿主 cwd)
才自动创建 workspace;沿用旧 cwd 的遗留会话只在已有 workspace 拥有该路径时挂载,不会自动新建。
斜杠命令速查(全部仅管理员)
| 命令 | 作用 |
|---|---|
/new |
开启新会话(清空上下文,旧会话保留在磁盘) |
/stop |
停止当前生成 |
/steer <补充内容> |
向运行中的任务补充一条指令(agent 在下一步读取并回复提交提示);两种 newMessagePolicy 模式下均可用;无运行中任务或参数为空时回复中文用法提示(不炸、不静默) |
/model [--default] <provider> <model> |
查看或切换当前会话模型;--default 修改部署默认。无参输出两级序号列表:回复序号选 provider → 再回复序号选模型并切换当前会话 |
/workspace [路径|序号|list] |
查看或切换工作区;无参输出编号列表(标「← 当前」),回复 /workspace <序号> 即切换;/workspace list 列出全部记录 |
/preset [id|序号] |
查看当前/可用预设(无参编号列表,回复序号即选),或切换 preset(重建会话,新会话 header 记录) |
/session [序号] |
查看本会话可切回的历史会话(无参编号列表,每项含内容预览、建立时间与退休时间,id 截短显示;日志不可读的项显示「(内容不可读)」且不影响其余项),回复序号切回:当前会话退休进列表、目标会话恢复其历史上下文(含 per-chat 设置),支持来回切换;列表跨重启保留(switchable-sessions.json,每 chat 上限 20 条) |
/status |
会话全景:chat/session/preset/model/cwd/出站模式/agent/可切回数 状态 |
/retry |
重跑上一条用户消息(上一轮出错后重试) |
/id |
只看 chat/session/cwd(排查用) |
/ver |
插件版本 + git commit |
/ocr |
识别本会话最近一张入站图片(NapCat ocr_image) |
/mode [interim|instant] |
切换本会话出站模式(per-chat 覆盖,跨重启持久化) |
/plan [off|内容] |
宿主计划模式(/plan 进入;/plan off 直接退出,无 Web 审批卡;/plan <内容> 进入并处理该内容) |
/permission [w|f|预设名|序号] |
切换宿主权限 preset(沙箱模式+审批策略,切换立即生效):w/ws/write/工作区→workspace-write(工作区内可写+需审批)、f/full/danger/全权→danger-full-access(全盘读写+免审批);完整预设名照原样转发(未知名由宿主报错并回显可用列表);无参渲染中文权限菜单(当前权限+可用预设编号列表+用法行,未知预设名标「部署自定义」;宿主回文格式变化时回退转发原文+快捷提示),纯数字序号按 available 顺序(现场解析,不落快照) |
/goal [目标|clear] |
记录/更新本会话目标(每轮自动附带提醒,跨重启持久化) |
/help |
分组命令卡片(▍会话/输出/查询/操作/其他),每条带参数用法;报错提示均附用法或下一步动作 |
/preset 切换为进程内 per-chat 覆盖(跨 /new 保留):下一条消息重建会话并以新 preset
写入 header,重启后 resume 按记录恢复;/mode、/goal 的 per-chat 状态已持久化进映射文件(v0.4.0 起,跨重启
保留),/workspace 的覆盖同样持久化;/plan 仍为进程内覆盖,重启回退到默认。
/session 的可切回列表按 chat 持久化在媒体目录的 switchable-sessions.json(读写纪律与 retired-sessions.json
一致:文件缺失=全新、损坏保留内存、原子写;每 chat 上限 20 条、去重、最新在前)。列表每项渲染一条内容预览
(该会话事件日志第一条真实用户输入:kind 'user' 或本插件署名的 QQ 消息优先,缺失时回退任何 user/message;
提取全部 text 块拼接、剥去 QQ 入站的 包裹与拼接在边界外的可信前缀([受限用户:仅问答] / [HH:MM 昵称(QQ)],包裹不完整或空正文则原样回退)后换行折叠、按码点截断 ≤40 字,emoji 不拆半;找不到用户输入显示「(无对话内容)」)、
建立时间(会话 header 的 createdAt,取不到则省略)、退休时间与截短 id(前 8+…+后 8 字符,保留两侧便于辨认)。
预览经 sessionPersistence 的 read handle 冷读日志前 24 个事件取得(不开写所有权,读完即 close);单会话日志
缺失/损坏/读取失败只降级该项为「(内容不可读)」,其余项与切回功能不受影响。切换成功后目标会话解除退休
标记,重启 resume 与空闲淘汰后的再激活都走常规路径找回它;切回目标 resume 失败时该 id 立即硬退休并移出列表,
chat 回退到下一条消息新建会话,不会卡死。损坏(碰撞/恢复失败)退休的会话绝不进列表、不可切回。
序号选择基于命令输出列表的快照,5 分钟内有效(过期提示重新查看);纯数字参数仅在有对应有效列表时
按序号解释,否则维持原参数语义。例外:/permission 的纯数字序号不落快照,每次现场取宿主 available 列表解释(1/2),越界或不可解析时回退用法提示。未知斜杠命令默认拦截并提示相近命令,配置 unknownCommand: passthrough
可改为透传给模型。
安全编辑(code_safe_edit)
受守卫的宿主文件编辑由独立插件 dsh-safe-edit 提供(~/dsh-plugins/dsh-safe-edit/,2026-08-18 从本插件拆出,对所有通道全局注册)。三个工具:
思路借鉴 irmia_devkit_open 的 safe_edit(AGPL-3.0,独立 TypeScript 清洁实现)。工具与边界见 dsh-safe-edit 仓库/说明:
code_safe_edit:read → 路径白名单 → 自动备份 → 匹配(精确 → 剥行号前缀 → 空白对齐/Aider 式)→ 替换 → 语法检查(js/cjs/mjs 走node --check)→ 失败自动回滚code_safe_rollback/code_list_backups- 边界随会话 sandbox 策略:
danger-full-access无限制、workspace-write限会话工作区、read-only拒绝;无策略服务回落safeEditRoot(默认/Users/mario/workspace) - 模型可优先使用此工具(
code-safe-editskill 全渠道引导;QQ 平台提示词引导内置 read/edit 惯例,不绑定具体工具名)
dm / group 访问策略(初始化必选)
私聊(dmPolicy)与群聊(groupPolicy)各自三选一,选项含义如下:
| 选项 | dmPolicy(私聊) | groupPolicy(群聊) |
|---|---|---|
open |
仅管理员可私聊(adminUsers/ONEBOT_ALLOWED_USERS/ownerQQ;allowAllUsers 对私聊已经不管用——私聊等于这部电脑的完整控制权,不能交给任意人) |
所有群可聊(默认值已改成 allowlist,要放开得自己改;群内消息还受 requireMention 控制:需 @ 或回复机器人的消息才触发;群成员带 [受限用户:仅问答] 软限制) |
allowlist |
仅 allowFrom 白名单 QQ 可私聊(不要求是管理员) |
仅 groupAllowFrom 白名单群可聊 |
disabled |
私聊全部拒绝 | 群聊全部拒绝 |
初始化建议:
- 只想自己用 →
dmPolicy: open+ 配好adminUsers(私聊只有你能发); - 想开放给几个熟人 →
dmPolicy: allowlist+allowFrom: ['QQ1','QQ2']; - 群聊专用机器人 →
groupPolicy: open(配合默认requireMention: true,群成员需 @ 才触发); - 只允许特定群 →
groupPolicy: allowlist+groupAllowFrom。
t2i 字体依赖
文字图卡片需要三类字体(CJK / 等宽 / 彩色 emoji),插件启动时从系统与固定路径自动注册, 缺失时对应字符渲染为豆腐块。
macOS:零安装——自动使用系统自带 Hiragino Sans GB / Songti SC、Menlo、Apple Color Emoji。
Linux(Debian/Ubuntu,一条命令补齐):
sudo apt install fonts-noto-cjk fonts-dejavu-core fonts-noto-color-emoji依赖 提供文件(插件自动注册路径) 用途 fonts-noto-cjk/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc中文正文/标题(ttc 自动提取 SC 面,回退 JP/Mono 面) fonts-dejavu-core/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf代码块/行内 code 等宽 fonts-noto-color-emoji/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf彩色 emoji 可选 fonts-wqy-zenhei/fonts-wqy-microhei/usr/share/fonts/truetype/wqy/*.ttcCJK 备选(Noto 缺失时) 可选 fonts-unifont/usr/share/fonts/opentype/unifont/*.otf最后兜底 自定义:
fontFiles指定额外字体文件(重启生效);fontFamilies指定优先使用的家族名。 渲染器带墨水自检——缺字体的家族会被自动剔除并回退,不会静默出豆腐块卡片。
权限与数据
- 网络:与 OneBot 11 网关建立 WebSocket 连接(reverse 监听或 forward 拨出);入站图片/文件从 QQ CDN 下载。
- 文件:入站媒体与会话映射写入
<dsh-home>/media/onebot/(mediaDir,6 小时过期清理);会话数据由 dsh 宿主持久化。 - 系统调用:语音转写调用本机 ffmpeg 与 whisper CLI(可
sttEnabled: false关闭)。 - 敏感信息:
accessToken与管理员白名单存于 dsh 配置,不写入日志;出站内容经敏感信息审计。 - 不收集:无遥测,除用户配置的 OneBot 网关与图片 CDN 外不调用任何第三方服务。
给模型的平台说明(自动注入)
- QQ 不渲染 Markdown → 输出纯文本(编号/短横线列表、行内反引号)。
- 发图/文件/语音/视频用
qq_send_*工具;合并转发用qq_send_forward。 - 用户发来的图片/语音/视频在文本中标注为
[图片]/[语音]/[视频]占位(路径不进入文本);无可用看图工具时如实告知用户。 - 群聊消息带
[HH:MM 昵称(QQ)]前缀;受限用户消息带[受限用户:仅问答]前缀(仅回答,禁止文件/终端/配置操作)。 - 本通道为 QQ,宿主无 Web 交互卡:禁止调用
ask_user_question/exit_plan_mode(确认卡仅 Web 可用,会阻塞对话),提问/确认走纯文本;宿主计划模式下输出纯文本计划并提示「/plan off 退出」。 - 斜杠命令由插件拦截(18 个,仅管理员,
/help查看);未知斜杠命令默认拦截并提示相近命令,配置unknownCommand: passthrough时透传给模型;非/纯单词开头的文本(如路径)仍正常交给模型。 - 修改宿主文件走内置 read/edit(行级 hash 锚点、dsh-better-edit 自动 undo),勿用 write 整文件覆盖(清空 undo 历史)。
卸载
- 从
~/.dsh/profiles/<profile>/cordis.patch.yml删除 dsh-onebot 的 insert 条目; - 重启 dsh,日志不再出现
[dsh-onebot] mounted即卸载完成; - 可选:删除插件目录与
<dsh-home>/media/onebot/残留媒体。
开发
./scripts/build.sh # 编译 src/ → lib/
npm test # 99 个测试(node:test):权重 / 事件 / 话头 / 聊天强制事件 / 限制 / 会话钉 / 积分流水
要点(来自移植源 DEVLOG 的教训):
- CQ 反转义:NapCat 会把 URL 里的
&转成&,下载前必须反转义(CDN 403 的根因)。 - @ 检测 fail-closed:不知道机器人 QQ 时,群消息一律视为未 @,不自动回复。
- 断线即失败 pending:WS 断开时立即 reject 所有未完成 action,避免 10-30s 干等与泄漏。
- 重连去重:并发断线只允许一个重连任务,防止双 WS 连接。
- int(target) 兜底:chat_id 解析进 try/catch,坏目标不能炸掉宿主。
- 临时媒体 6h 过期清理:只写不删会无限堆积。
- t2i 按码点迭代:JS 字符串索引会拆开 emoji 代理对(高代理位被分类成 CJK → 渲染成黑色字形),绘制/测量必须用
Array.from/for...of。 - t2i 度量=绘制:换行/列宽统一走
segWidth(胶囊/粗体/斜体附加宽),像素级右缘验证 ≤790(非白判定not(r>245&&g>245&&b>245))。
故障排查
| 症状 | 原因与处理 |
|---|---|
| 群聊不响应 | requireMention: true 时需 @ 或回复机器人的消息才触发;@ 检测 fail-closed——确认 botQQ 已从 meta 事件学习,或显式配置 |
| 私聊发了完全没反应 | ①发送人不在管理员里:填 ownerQQ(面板「设置 → 其他」里叫「主人 QQ」)或 adminUsers;②你在用机器人自己的号发——ignoreSelf 默认 true,自己发的消息一律丢弃,换别的号测 |
| 图片下载 403 | NapCat 会把 URL 中的 & 转成 &(解析已自动反转义);仍失败可查日志中 media 下载行 |
| 文件接收失败 | 非本机部署 NapCat 时需开启「文件转 URL」开关,否则 get_file 返回容器内路径不可达;确认 dsh 与 NapCat 网络互通 |
| 文字图中文豆腐块 | Linux 未装 CJK 字体:apt install fonts-noto-cjk,并用 fontFiles 指定 SC 字体文件 |
| 崩溃循环 / 工具注册冲突 | 同一插件文件被 insert 两次(双实例)——检查 patch 无重复条目 |
| 语音一直只有 [语音] 占位、没有转写补递 | ffmpeg 或 whisper 不可用,或转写超时:安装后重启、调大 sttTimeoutMs,或 sttEnabled: false 关闭 |
| 插件挂载失败 / peer 依赖解析失败 | 部署副本缺完整 node_modules 链接集:只复制 lib/ 不够,运行时 peer 依赖 @deepseek-ai/* 需经 scripts/link-host.sh 链接到宿主安装;插件入口会 fail-loud 显式报「peer 依赖解析失败」,按报错补齐链接集后重启 |
build.sh 报 cannot locate the dsh install |
宿主无 dsh 在 PATH(如 fnOS 应用形态)且未找到 npx store:设 DSH_ROOT=<宿主 node_modules 路径> 后重跑 ./scripts/build.sh(该目录下须有 @deepseek-ai/,设错路径会显式报错而非静默回退) |
| 改了设置页 / patch 但不生效 | 按顺序排查:① 确认改的是被监视的文件——profile 层 <DSH_HOME>/profiles/web/cordis.patch.yml 或 home 层 <DSH_HOME>/cordis.patch.yml(仓库 patch 模板、部署副本、bundle 自带 patch 均不被监视,需同步到上述文件);② 内容须真的变化(touch 同内容文件是 no-op);③ 看宿主日志有无激活失败——insert 新增条目激活失败(如 peer 缺失)会显式报错、条目失活,等待重启重试;④ 设置页的持久层是 profile 层 dsh-onebot 覆盖行,改到别处(如已被宿主废弃改名的 settings.yaml)不会生效 |
| 日志在哪 | dsh 宿主日志;插件历史根因与修复见 DEVLOG.md |
| 凭参考号查故障全过程 | 任何 ⚠️ 错误提示/连接故障日志尾部的参考号(MF-… 消息类 / QQ-CONN-… 连接类)都可在 mediaDir/qq-trace.jsonl 中检索:grep <参考号> mediaDir/qq-trace.jsonl 即列出该次故障的全部决策事件(stage/reason/耗时/traceId)。也可在 QQ 里发 /doctor 查看「最近故障参考号」节(含每号的 traceId 与阶段),或 /doctor export <参考号> 导出文件名含该号的脱敏诊断包离线分析。参考号仅在故障事件生成,正常流量零开销,不含用户消息正文 |
开发记录
完整时间线/根因/修复见 DEVLOG.md(移植自 Hermes onebot 插件的 DEVLOG 惯例)。
License
MIT。上游 dsh-onebot 为 BSD-3-Clause, 本项目的设计还参考了 dsh-bili-scroll(MIT); 两份许可全文与第三方组件清单见 THIRD-PARTY-NOTICES.md。
致谢
- dsh-onebot —— QQ 通道、OneBot 11 接入、会话映射、t2i 卡片这些底座都是它的功劳,本项目是它的二次开发。BSD-3-Clause。
- dsh-bili-scroll —— B站 那一套(登录凭证约定、刷视频的判据思路、私信监看)参考了它。MIT。本项目没有复制它的源代码,只沿用了凭证路径约定
$DSH_HOME/bili-duty/bili_credential.json。 - NapCat —— 实际把 QQ 送出去的那一层。
- better-sqlite3、sharp、ws、@napi-rs/canvas —— 底下的轮子。
- faster-whisper —— 「看视频」没字幕时的转写(可选依赖)。
- FFmpeg / yt-dlp —— 视频下载、抽帧、调色(可选外部程序)。
No comments yet. Be the first to write one.