DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

1497105876 /

1497105876/dsh-qq-chat-with-other

Verified

QQ 机器人插件 for DeepSeek Harness:多账号、私聊/群聊双配置隔离、群消息归档、语音条与本地曲库工具

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: master@3ac10229

@gw/dsh-qq-chat-with-other

给 DeepSeek Harness 用的 QQ 机器人插件。只接 QQ 官方机器人,多账号并存;私聊与群聊彻底完全隔离——每个账号的私聊侧(dm)和群聊侧(group)各有独立的一套模型、预设、权限档位、工作区、发送方式、工具可见性、提示词块,互不影响。

2026-10-26 结构升级:账号配置拆成 dm / group 两个 SideConfig。旧版单侧配置不迁移——读到旧账号只保留连接信息(AppID/密钥引用/主人),行为配置重置为默认,在设置页重新配。

跟本机已有插件的关系:im-connect 是多平台通用方案,本插件只做 QQ、按自己的用法定制;两边互不影响(配置目录、工作区、凭据引用全部分开)。

每个功能的详细说明(配置在哪、什么时候生效、已知限制)在 docs/,按功能一篇,索引见 docs/README.md。这份 README 只讲装法、整体结构和平台限制。


装

插件按 DSH bundle 打包,仓库里带好了构建产物 lib/,装完不需要自己 build。要求 Node ≥ 22.19(用了内置 node:sqlite),DSH 0.2.1-rc.2 验证过。

# 装到你的 profile(web / desktop / qq …,用你实际在跑的那个)
dsh plugin --profile <profile> add github:1497105876/dsh-qq-chat-with-other

装完重启 Host(后端插件只在启动时加载,刷新网页没用)。

想手动装也行:

  1. profile 的 package.json 里 dependencies 加 "@gw/dsh-qq-chat-with-other": "github:1497105876/dsh-qq-chat-with-other";
  2. 同一个文件的 dsh.profile.bundles 数组末尾追加 "@gw/dsh-qq-chat-with-other";
  3. 在 profile 目录跑 pnpm install,然后重启 Host。

卸载反过来:两处删掉再 pnpm install。

装好之后第一次启动是空的:设置页会提示扫码录入一个机器人账号(AppID/AppSecret 由扫码流程写入,密钥转存 DSH 凭据存储,不落明文)。

搭配 QQ 模式 preset(推荐)

插件本身只管「QQ 通道」,提示词、工具集、记忆这些是 agent preset 的事。作者自用的 @local/dsh-qq-preset 就是为此写的:

  • 把系统提示词换成自己的 prompts/(人设、群规、长期记忆协议),DSH 默认段一概不进;
  • 工具形态 native,文件/检索/终端/后台任务/联网/子任务全开,与标准模式对齐;
  • 群聊侧可以再按账号把 bash、pwsh、edit 这些遮掉(本插件设置页「工具可见性」)。

不装 preset 也能用,只是提示词与工具集走 DSH 默认(standard),QQ 机器人会表现得像普通网页会话。

本机部署备注(作者自己)

  • 装在 qq profile,用 link:Z:/dsh/@gw/dsh-qq-chat-with-other 指向源码目录:改 client.js / YAML 不用重装,改 src/*.ts 仍要 npm run build;
  • desktop profile 被 Electron 独占管理,dsh plugin add 可能报错,手动改文件更稳;
  • 两个 profile 的配置目录、工作区、凭据引用完全分开,可以并行跑 A/B。

部署前必看:IP 白名单

QQ 开放平台对新增机器人默认启用 IP 白名单,不填公网 IP 无法提审上线。本机走公网 IP 的话,把宽带回公网 IP 填到平台的「发布设置 / IP 白名单」里。沙箱环境另有一个域名,本插件不接沙箱。


账号录入

三条路,按场景选:

场景 做法
第一次,手上还没有任何账号 改 cordis.patch.yml 的 config.accounts,启动后自动导入(密钥随即转进凭据,patch 里那段就可以删了)
已经有一个账号在跑,想加第二个 私聊那个机器人发 /qr,二维码图片会发到 QQ 里,手机扫码即录
日常维护 Web 设置页(见下)

配置落在插件所在 profile 目录内:<profile>/dsh-qq-chat-with-other/channels.json。 不要直接编辑这个文件 —— 内存态为准,外部改动会被下一次 flush 覆盖。要改就走设置页或指令。


Web 设置页

装在宿主设置里(「QQ 机器人」那一节,2026-10-26 二次重做为 Tab 三级结构)。宿主三栏布局不动,插件只在设置面板里叠加。布局:

  • 顶栏:标题 + 刷新 + 扫码添加账号。扫码面板自带「取消」按钮(调 DELETE /qr,扫码会话立即作废)。

  • 账号卡片横排:状态灯 + 备注 + AppID 尾巴 + 启用主开关(在这就能关停某个账号,关了会断开网关)。 控件一律优先用宿主原语(照 dsh-im-connect 的共识):按钮走宿主 Button(variant / size / icon 都是官方 props)、开关走宿主 Switch、二级标签走宿主 SegmentedTabs、下拉走宿主 Menu(portal 弹出菜单 + Button 触发器)、图标优先用宿主官方图标集(IconPlusOutlineRegular 这类,取不到才用照抄官方 path 的内联 SVG)。只有宿主没有的东西(卡片、网格、块、预览、历史、徽章)才自绘,且全部吃官方令牌 --dsw-alias-*。原语拿不到时逐级降级到原生 button / select,绝不白屏。

  • 主 Tab 栏(官方下划线式):【私聊|群聊|账号与连接|全局】

  • 子 Tab 栏(宿主原生分段控件,每个主 Tab 下再细分):

主 Tab 子页
私聊 模型|行为|提示词|工具|准入
群聊 模型|行为|提示词|工具|群规则(触发策略 + 身份前缀)
账号与连接 连接(AppID/Secret/主人/备注/启用)|诊断(测试连接)|绑定(会话列表 + 解绑)|危险区
全局 工作区(根目录)|群归档(开关 + 单群/全部清空)

私聊与群聊是两套完全独立的同构子页:模型、行为、提示词、工具各管各的,群聊多一个「群规则」、私聊多一个「准入」。

提示词块(照 dsh-text-inject 的块编辑体验做):每侧一套,在「提示词」子页里管理。

  • 块卡片:开关 + 块名 + 描述(一行)+ 徽章组(@位置 / 字数 / 已停用)+ 折叠箭头;点卡片就地展开编辑区。停用的块展开后顶部有醒目停用条。
  • 编辑区(内嵌灰底):块名 → 描述(只给人看,不进提示词)→ 注入位置胶囊(弹菜单选「第 N 位 @order」,决定拼在注入文本里的先后)→ 正文 → 变量 chip(点击在光标处插入 {{变量}})→ footer(字数 + 删除 + 完成)。
  • 模型视角(预览):调 Host 端点拿替换变量后的真实文本,按块分段落展示(可折叠看全文)+ 统计条(启用块数、总字数、停用 / 空正文计数)。
  • 历史版本:每次保存自动备份(上限 30 份),列表带与当前版本的行级差异 +N / -N(LCS 算的),行内二次确认后恢复。

工具可见性(只做禁用):两个框——左框=未禁用的工具、右框=已禁用的工具(界面文案仍沿用「启用的工具」「停用的工具」「全部停用 / 全部启用」字样,语义按禁用理解),点左侧工具=禁用、点右侧工具=取消禁用;保存写 deny 名单(keep 留空),名单外(以后新增)的工具默认未禁用。管理动作只有「禁用」一个方向,没有独立的「启用」设置——解禁就是从禁用名单里移出(点右侧框里的条目)。兼容旧 keep 白名单:读到 keep 非空时按白名单摆框,一旦在设置页编辑就转成 deny 名单。清单 = 全局注册表 ∪ 会话观测(preset 以 agent 作用域注册的 read/write/edit、bash/pwsh、subagent 等只在组装结果里出现,QQ 跑过消息后自动补全,存 channels.json 同目录的 observed-tools.json,删掉重启即清空);右侧框还能手动填名字,提前遮蔽清单里没有的工具。

改动即保存(下拉 / 开关改了立刻存,文本类焦点离开才存),没有「保存修改」按钮。

重启语义(2026-10-26 起):只有连接信息变化(重置 AppSecret、切启用开关)才重启 QQ 网关;改备注名、提示词块、模型这些只落盘,不会打断在线会话。改模型/预设只影响新开的会话。

引擎参数的生效优先级:cordis.patch.yml 里显式配置的键 > 设置页保存值 > 内置默认。patch 显式配置过的键在设置页锁定(带「patch 已配置」徽章,改不了);设置页保存的值落 channels.json 顶层 engine 字段,保存即热生效(合并窗口与限流直接推给运行中的引擎,无需重启)。

新增账号只走扫码(2026-10-26 起撤掉了「手动填写 AppID + AppSecret」)。原因很直接:主人身份 = 扫码回调里的 user_openid,手动填的号拿不到这个,建出来既没主人、也没人能用管理指令。「主人」不提供手动设置,设置页没有也不该有填主人 openid 的入口。

指令白名单整条链路已删除(2026-10-26,字段 + 接口 + 设置页一起没了)。后果是群里没人能用管理类指令(群里 isAdmin 恒为 false)—— 要用管理指令就私聊机器人。能发管理指令的只剩「主人 + 准入白名单里的人」。

准入白名单(allowlist)只有一条写入路径:陌生人私聊 → 顶部待审批点批准。设置页展示白名单成员(只读)但没有手动增删入口,/channels/:id/allowlist 接口已删除(2026-10-26)。私聊准入默认「所有人」,只有设回「仅白名单用户」时这套才起作用。

会话绑定管理:账号 Tab 里列出该账号全部「聊天 → 会话」绑定(类型 / chatId / 会话 id / 最近活跃),可逐条解绑(行内二次确认)——解绑后该聊天下一条消息会开新会话,等于在那个聊天里发 /new。

数据走 Host 侧 HTTP 接口 /api/plugins/qq-chat-with-other/*(src/web-api.ts)。

设置页已经在真实宿主里用起来了(2026-10-25 起在 qq profile 日常操作)。改 client.js 之后仍然要过两道关:scripts/verify-client.mjs 静态自检 + scripts/smoke-client.mjs 运行时渲染冒烟(中英 key 必须对齐)。


隔离模型

配置层:一个账号 = 连接信息 + dm(私聊侧)+ group(群聊侧)两个 SideConfig,两侧完全隔离——私聊换模型/换预设/收紧权限/遮工具,群聊一点都不受影响,反过来也一样。

工作区根目录默认 Z:\dsh\gwqqbot(设置页可改),展开方式(账号根按侧取:侧里配了 cwd 用侧的,没配落全局根):

<账号根>/<botId>/users/<userId>/     私聊
<账号根>/<botId>/groups/<groupId>/   群聊

DSH 的会话按 cwd 分桶存,所以目录一分,会话记录、长期记忆、文件读写全部物理隔离 —— 不需要额外开关,也没有共享模式。

群聊是整群共用一个工作区,群友的区别在两点:

  • 每条入站消息带发言人身份前缀(缺省 [昵称(id后8位)] 内容),模型能分辨「这句是谁说的」;
  • 群记忆按人记事,骨架 # 记忆 / ## 群友 / ## 约定 / ## 事件。

身份前缀可以每个账号单独配(identity,群聊专属,设置页「群聊 · 身份前缀」卡,字段勾选 chips + 包裹样式下拉)。字段集照搬 dsh-im 的上下文增强(channel conversationType senderId senderName conversationTitle chatId threadId botId sentAt),另加 QQ 场景自己的 idTail(id 后 8 位)—— 共 10 个;按勾选顺序拼。包裹样式是下拉选的现成品,不用手写格式:bracket 缺省 [小明(…MBER0001)] / colon 小明: / plain 小明 。QQ 拿不到的两个(conversationTitle 群名、threadId 话题)勾了也会自动省略,跟 dsh-im「只发送勾选且可用的字段」的行为一致。enabled: false 整个关掉。什么都不配 = 缺省 [昵称(id后8位)]。带附件的群消息一样带前缀。(自定义 template 只留在 patch 引导里,设置页不再提供——手写占位符那条路撤了。)

群里不给每个成员各开一个工作区(那跟群隔离语义冲突)。

长期记忆与提示词块

  • 记忆:每个工作区 memory\MEMORY.md,pre-step 自动注入。内容没变不重复注入;文件不存在时注入引导协议让机器人自己建;超长按头 70% / 尾 30% 截断(上限 8000 字);origin === 'subagent' 的子 agent 不注入。机制照抄旧 preset 的 memoryMessage。
  • 提示词块(分侧,设置页直存):每侧(dm / group)一套 PromptBlock 列表。启用的块按 order 排序、拼接、替换 {{变量}} 后每轮作为一条 user 消息注入(内容没变不重注;date/weekday 日级变化一天最多重注一次)。块在设置页增删/排序/启停,保存即生效,自动备份可恢复。
  • 路线选择:走的是 pre-step 消息注入,不注册任何提示词段——complete 段全局只允许一个(多渠道抢同一段必炸);非 complete 的 section 注册在插件自身作用域,绑了 preset(complete 段)时还会被整体替换。消息注入是唯一在「绑/不绑 preset」两种场景都稳定的通道。
  • 跟 preset 的分工(自动,不用你操心):渠道只要绑了 agentPreset,记忆就全交给 preset —— 否则像 dsh-qq-preset 这种自带记忆注入 + complete 提示词段的,模型一轮会收到两份 MEMORY.md。提示词块是例外:你在设置页给该侧写了块就照常注入(显式配置优先)。搭配 dsh-qq-preset 时:系统人设归 preset 的 prompt.md,私聊/群聊的差异规矩写在插件设置页的块里,两层共存。
  • hindsight 那套记忆注入会被拦掉,不混进 QQ 工作区。

从网络来的 userId / groupId 先过 safeSegment:不合规退化成哈希,绝不让它带斜杠进路径。


每个账号能单独配什么

账号 = 连接信息(顶层共享)+ dm / group 两个完全隔离的侧(下表 dm.* / group.*):

字段 说明
appId / appSecretRef 凭据(账号级),AppSecret 走 DSH credentials 引用,不落明文
ownerOpenId 扫码拿到的主人 openid,默认管理员。只由扫码写入,设置页不提供手填
dm.model / group.model(provider/model/reasoningEffort) 每侧独立的模型与思考强度。一侧没配退另一侧,两侧都空退 Host 默认;都取不到就明确报错。reasoningEffort 的可选值不写死——由 Host 的模型目录按当前模型给出
dm.agentPreset / group.agentPreset 每侧独立绑定哪个 agent 预设,重开会话后生效
dm.permission / group.permission 每侧独立的权限档位;留空退 Host 默认
dm.cwd / group.cwd 每侧独立的工作区根(绝对路径),留空用 <root>/<botId>/
dm.sendMode / group.sendMode 每侧独立的发送方式:queue 排队 / steer 插队
dm.tools / group.tools 每侧独立的工具可见性(keep/deny)。设置页两框点选(存 deny 名单,只做禁用);QQ 里 /plugin 只能查看(list)和禁用(off),改的也是当前聊天那一侧
dm.promptBlocks / group.promptBlocks 每侧独立的提示词块列表(name/desc/enabled/order/text,text 支持 {{变量}})。设置页块管理器编辑,保存自动备份
privateAccess 私聊专属:approved 只放行白名单 / all 放行所有人。默认 all
pendingMode 私聊专属:未授权用户私聊 silent 静默 / approval 进待审批。仅在 privateAccess = approved 时有效
groupTrigger 群聊专属:at 只响应 @ / all 群里每条消息都响应(含 @)/ none 不接群消息
identity 群聊专属身份前缀:enabled(默认开)/ fields(10 个可选,默认 senderName + idTail)/ style(包裹样式下拉:bracket 缺省不落盘 / colon / plain)/ template(自定义模板,只留在 patch 引导里)

权限档位与「命令执行」

三档:read-only / workspace-write / danger-full-access(id 运行时从 Host 读)。

「能不能动电脑」由这个档位决定,不需要另造开关。 边界:

  • 读文件不算命令执行 —— 任何档位都允许读;
  • read-only 下不能创建 / 移动 / 删除文件,但能读;
  • workspace-write 的可写根就是会话自己的 cwd,天然防越权(群聊默认这档);
  • 沙箱是会话级、跟着渠道走,没有每用户粒度的沙箱权限;用户隔离靠工作区 + 渠道配置。

工具可见性(/plugin)

/plugin list 看清单、off <名…> 禁用。只做禁用,不提供启用——on / only / reset 子命令已删(2026-10-29),解禁去设置页工具页点「已禁用的工具」里的条目。

这只是可见性管理——DSH 插件是 profile 全局的,没法按账号安装 / 卸载真正的插件。遮蔽会打日志(「保留 X;遮蔽 N 个」)。

清单里会带上本插件自己注册的五个工具,统一 qq_chat_gw_ 前缀(一眼看出是谁注册的):qq_chat_gw_return_file(出站文件)、qq_chat_gw_send_voice(语音条)、qq_chat_gw_send_music(本地曲库点歌)、qq_chat_gw_group_history / qq_chat_gw_group_file(群归档)。2026-10-29 之前叫 qq_return_file / qq_group_history / qq_group_file,旧配置里的这三个名字读盘时会自动换成新名(src/tool-names.ts 是唯一来源)。


指令

/help /new(/clear)/status(/current)/id /doctor /models /model /reasoning /presets /preset /permission /workspace /session /sessions /history /stop(/cancel)/queue /steer /prompt /plugin /qr

  • 重置会话是 /new 或 /clear,没有 /reset;权限走档位配置。
  • 管理类指令(/model /reasoning /preset /permission /doctor /queue /steer /plugin /sessions /history)只能私聊 + 主人 / 白名单用户。/help 只列你真能用的指令。
  • 群里没人能用管理指令:群里 isAdmin 恒为 false,指令白名单那条链路已经删了(字段/接口/UI 都没了),没有旁路可走。要管机器人就私聊。
  • /compact 不拦,交给 DSH 自带的 compact。

审批与提问(无人值守,不交互)

QQ 机器人没有人在屏幕前点「批准」,所以这两类请求一律不渲染菜单、不等答复:

  • 审批(危险命令、权限升级):直接按拒绝处理。能不能真做成由沙箱档位决定,模型会自己知道并被挡下。danger-full-access 下压根不会触发审批,这条 mostly 是给 read-only / workspace-write 兜底。
  • 提问(ask_user_question):直接返回空答案跳过,模型接着演,轮次不卡住。

两条都只记 Host 日志,不在 QQ 里发任何提示。


发消息的策略

被动和主动都做:

  • 有可用 msg_id → 走被动回复(带 msg_id + msg_seq);有效期按平台窗口夹:群聊 5 分钟、私聊 55 分钟;
  • 没有凭证或超时 → 自动改走主动消息通道(同一接口,msg_id 和 msg_seq 都不带 —— msg_seq 是配合 msg_id 用的,单独出现平台不认);
  • 带 msg_id 被平台明确拒绝(400/403,多半是 msg_id 过期或回复次数用完)→ 自动去掉 msg_id 用主动通道再试一次,日志记 被动回复被拒…改走主动通道;429 / 超时不重试;
  • 429 = 平台没受理,不可重试(官方定义)。丢掉并记日志,绝不重发 —— 因为不知道到底发出去没有;
  • markdown 被拒按 errCode 降级:50055/50057 只降级本次,50056(未开通)永久关掉 markdown 通道;
  • 长回复按 1800 字符切段,按 markdown 结构切:装得下的代码块和 GFM 表格整块搬;装不下的代码块按内容切、每片重新补一对围栏(围栏长度按内容里已有的反引号推算,避免嵌套失效);装不下的表格按行切、每片重复表头;超长单行硬切时不会把代理对切成两半。每片同时产出一份纯文本,markdown 被平台拒时直接发它,不用回头再洗一遍;
  • 每用户每分钟限 20 条。

域名注意:OpenAPI 统一用 https://api.bot.qq.com。官方 2026-08-10 起把旧域名 api.sgroup.qq.com 下线了 —— im-connect 1.0.14 用的还是老域名,本插件用新的。


发图片 / 视频 / 文件 / 语音(出站富文本,2026-10-27 起)

出站富文本走「工具登记 → turn 结束统一发送」,对标 dsh-im 的 dsh_im_return_file:

  1. 插件注册两个工具,参数都是 path(必须落在当前会话工作区内,单文件 ≤ 20 MiB,一轮合计 ≤ 4 个):
    • qq_chat_gw_return_file —— 图片 / 视频 / 文件;音频走这条会显示成文件卡片;
    • qq_chat_gw_send_voice —— 只收音频,按语音条发(手机上可点开播放)。语音上传失败会自动改发成文件卡片并告诉你原因。
    • qq_chat_gw_send_music —— 从本地曲库(patch musicRoot,默认 Z:\music\QQmusic)按关键字找歌,flac 自动转 128k mp3 再按语音条发;模型只给关键字,不能传路径。详见 docs/本地音乐.md。 工具成功只代表已排队;
  2. 本轮 turn/end 后,引擎把排队的文件逐个走 sendFile(/files 上传 + msg_type 7,file_type 按魔数嗅探 1/2/3/4)发进对应聊天;
  3. 发送失败双通道反馈:QQ 里发一条可见提示 + 回执记下来,下一轮 pre-step 以 <qq-delivery> 注入给模型 —— 模型永远拿不到「假成功」;
  4. 富文本使用指导通过 pre-step 消息注入(每会话一次,<qq-media>):教模型用工具、明说 markdown 图片语法(![alt](url))在这条通道发不出去、只会变 [图片:xx] 占位文本。

配套改动:

  • dsh-qq-preset 的 present 行已停用 —— QQ 会话里它没有接收方,是「假成功」的另一个入口;
  • markdown 被拒的另一套 bizCode 40034090(HTTP 4xx + code)也接进了降级集合,只降级本次,不触发 50056 的永久关闭;
  • 修正 sendFile 端点拼接:上传必须是 /v2/{users|groups}/{openid}/files(原先拼成 …/messages/files,HTTP 405「请求方法非法」),二段式的消息一跳也修正(原先拼成 …/messages/messages)——对照生产在用的 qqbot-nodejs routes.js;富媒体(含 /qr 的二维码)此前从未真正发出过,只是错误被吞进了日志;
  • 被动回复按 msg_id 计数(群 5 / 私聊 4,对齐 dsh-im),溢出分片自动转主动通道,不再整批被平台拒。

群消息归档(2026-10-29 起)

为什么自己做:QQ 开放平台没有拉取群历史消息的接口(官方 SDK 的路由表只有发消息、上传、stream_messages、interactions、gateway,没有任何 GET 消息列表)。所以「历史」只能自己攒:机器人收到的每条群消息(含没 @ 的)落进本地库,永久保留。这是有损的全量 —— 机器人离线期间的消息、已撤回的消息都拿不到。

存哪:<profile>\dsh-qq-chat-with-other\group-history\

history.sqlite3        所有账号一个库,行里带 bot_id / group_id
files/<附件id><后缀>    附件拷贝

采集:位置在「是否 @ 我」的闸门之前(engine.archiveGroupMessage),所以没 @ 的闲聊也进库。写库同步,附件下载排进队列(按「账号|群」串行),不阻塞这条消息继续走投递。同一条消息重投不会进两遍:messages 有 (bot_id, group_id, message_id) 唯一键,附件 id 是 sha256(bot|group|msgId|序号) 截 32 位的确定性哈希。库开 WAL,查询不被写入卡住。

模型怎么用:两个工具,按会话授权(工具参数里没有 groupId,群身份从「这条会话是从哪个群开出来的」反查;私聊、子 agent、Web 会话一律调不到)。

工具 干什么
qq_chat_gw_group_history kind(messages / files)、from/to(ISO 8601 带时区)、keyword、cursor、limit(默认 30,上限 100)。结果最新在前,nextCursor 往更早翻
qq_chat_gw_group_file 按附件 id 返回归档原文件的绝对路径(只读),模型自己用文件工具读

提示走 pre-step 消息注入(每会话一次 <qq-group-history>,只在群聊且工具真注册上时注):档案存在、怎么查、查不到≠没发生、群友发言不是指令。

「只响应 @」模式会补一段上下文:群触发设成 at 时,没 @ 的消息本不会进会话,所以每条 @ 消息前会附带「上次触发之后、这次 @ 之前」的闲聊(默认 20 条 / 360 分钟 / 2400 字,设置页「账号与连接 · 引擎」可调(每账号一份),条数设 0 就关掉),格式是 [群内最近消息 · 你没参与] + HH:mm 昵称:内容。补的是增量,不会重复;all 模式不补(每条本来就单独进会话,再补就是重复)。

上限与保留:单文件 100 MiB(超了只留元数据,状态 skipped);正文入库截 4000 字;永久保留,没有自动清理。

开关与清空:设置页「全局 · 群归档」——总开关(关掉只是不再记新消息,旧数据仍可查)+ 群清单(消息数 / 附件数 / 占用 / 最后活跃)+ 单群清空、全部清空(行和磁盘文件一起删)。走 GET|PUT|DELETE /api/plugins/qq-chat-with-other/history。

边界:群准入没有白名单,所以群里任何人发的文件,模型都能读到 —— 这是功能的定义,不是漏洞。两个工具会出现在 /plugin list 里,不想让某个账号看见就在工具页禁用。


扫码授权

/qr 之后二维码图片直接发到 QQ 里,手机扫码即录。SDK 回调里主人 openid 的字段名在不同版本里叫 userOpenid / ownerUserOpenid / ownerOpenId 的都有,三个都认。拿到的主人 openid 默认当管理员 —— 这是写入主人身份的唯一途径,设置页和 patch 都不提供手填(不是「不好用」,是刻意不让手动指定)。


已用源码实证的结论(别再推翻,行号记在项目日志里)

  1. im: 前缀不能让会话脱离任务列表。 im-connect 自己的约定,DSH 全仓库零引用;唯一显示闸门只看 origin / blank / archived。本插件接受 QQ 会话出现在网页任务列表里。
  2. prepend: true 时后注册的 handler 在最外层(cordis 用 unshift + shift,同一端,方向不反转)。都不带 prepend 时相反。
  3. 工具层没有技能白名单。 只过滤 skill-catalog 只能让模型「看不见」,用户手打技能名照样能加载(/技能名 手势连 disable-model-invocation 都不拦)。所以本插件不做每账号技能隔离 —— 交给专门管 skill 的插件。
  4. 回复收口不能用 agent/status。 官方明令禁止;多个 followup 会共享一个 running 区间。用持久事件 assistant/message(surfaceOp === 'append')判定「这条回复说完了」,turn/end 只在报错时补提示。
  5. session header 只有 9 个字段,origin 只能是 'subagent'。agent/session-start 事件不存在。
  6. 用户主动停止 = agent.cancel({ kind: 'user' }),插件侧没有别的中断入口。

平台限制(做不了的,不是漏了)

  • QQ Bot API 不支持编辑已发送消息,也没有表情表态、没有 typing indicator。所以流式输出做不了 —— 现在用「整轮结束后一次发 + 超长自动分片」替代。这是平台限制,不是实现选择。
  • 消息超过 2000 字符会被平台自动截断,所以分片上限压到 1800 给序号前缀留余量。
  • 思考强度档位不是本插件定的。每个模型支持哪些档位由 Host 的模型目录给出,本插件只负责透传;拿不到目录时只显示「模型默认」,不猜。

能力边界(明确不做)

  • 只做 QQ。 不做微信 / 飞书 / 钉钉 / 企微 / Telegram 等,也不预留多平台抽象层。
  • 不做按账号安装 / 卸载真正的 DSH 插件(profile 全局限制)。所有「插件管理」只是可见性层面。
  • 不做每账号技能隔离(见上第 3 条)。
  • 群聊非 @ 消息:groupTrigger = all 时可以响应。平台侧只需在 QQ 开放平台后台给机器人开启**「接收所有消息」**——GROUP_MESSAGE_CREATE 和 GROUP_AT_MESSAGE_CREATE 共用同一个 intent 位(1<<25),网关订阅不用改。开了之后即使账号配的是 at,也只会按 mentioned 过滤、不会漏掉真正的 @ 消息。
  • 不做群内每人一个工作区;不做每用户粒度的沙箱权限。
  • 不迁移、不接管现有 im-connect 的渠道与 preset,不读写 ~/.dsh/dsh-im-connect\。

开发

npm run build   # tsc → lib/
npm test        # build + node --test(全部离线可跑)

测试分八组:basic(队列/去重/合并/分片/限流/路径/会话id/持久化/入站解析/身份前缀/配置迁移)、enhancements(首条短窗口、友好报错)、scope(记忆/提示词/与 preset 的分工/工具可见性/群归档指导)、group-history(归档入库去重/翻页/时间窗/关键词字面匹配/附件取用/清空/授权边界)、outbound-files(路径圈禁/上限/语音工具/回执文案)、music(曲库匹配/双源优先级/转码分支/候选分流)、web-api(起真 HTTP server 打接口)、engine-config(引擎参数热生效/patch 锁定/绑定解绑/重启语义)、qr(扫码字段与 PNG 渲染)。

前端另有两道关,改完 client.js 都要过:

node scripts/verify-client.mjs                            # 静态自检:入口形状、React 符号、端点对齐、语法、扫码取消、
                                                          # 以及「带 hooks 的组件必须走 h(组件, props)」
SMOKE_TAB=dm SMOKE_SUBTAB=prompts node scripts/smoke-client.mjs   # 运行时渲染:指定主 Tab + 子 Tab
SMOKE_TAB=group SMOKE_SUBTAB=rules node scripts/smoke-client.mjs
# 子 Tab 组合共 17 个:dm·group 各 model/behavior/prompts/tools/(access|rules)、
# account 的 connection/test/bindings/danger、global 的 workspace/engine/history
SMOKE_TAB=dm SMOKE_SUBTAB=access SMOKE_PRIVATE_ACCESS=approved node scripts/smoke-client.mjs  # 准入=仅白名单分支

# 两个正交开关:
SMOKE_PRIMITIVES=1 node scripts/smoke-client.mjs   # 喂一套宿主原语替身,覆盖「原语可用」的真实渲染路径
SMOKE_NAV=1 node scripts/smoke-client.mjs          # 把 17 个子页逐个点一遍,校验每个组件 hooks 数量稳定

铁律:带 hooks 的子组件必须 h(组件, props) 渲染,不能直呼 组件({...})。直呼会让子组件的 hooks 寄生到父组件的 hook 链上,切换子页时 hook 数量变化 → React 崩(2026-10-26「点提示词卡死」 就是这个原因)。SMOKE_NAV=1 是它的回归测试:renderComponent 会比对每轮的 hooks 数量,一变就抛错。

目录:src/ 源码,lib/ 编译产物,client.js 浏览器端设置页,cordis.patch.yml bundle 入口,tests/ 单测,docs/ 功能文档。

—/ 5

No ratings yet

Verified DSH bundle

Commit 3ac10229a758

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout