DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

9178159 /

9178159/dsh-napcat-bot

Verified

DeepSeek Harness 插件:内置 NapCat 的 QQ 机器人(识图 / 智能语音 / 随缘主动发言 / 联网搜索)

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHubProject homepage
READMESource: main@26d747f2

dsh-napcat-bot

release license platform deps

DeepSeek Harness (DSH) 插件:内置 NapCat(随 tgz 一起分发),启用即自动启动、监听 6099,并且通过 NapCat 官方 WebUI API 自动开启 OneBot 正向 HTTP + 正向 WS,WS 断线自动重连、进程异常自动拉起,让 AI 通过 QQ 私聊 / 群聊与真人对话。

开箱即用:安装并启用插件 → 重启 DSH → 打开侧边【设置 → 集成 → NapCat QQ 机器人】→ 在「NapCat WebUI」标签页扫码登录 QQ → 完事。 接口地址、端口、OneBot access_token、WebUI 管理 Token 全部由插件自动生成与写入,不用手填、不用手抄、不用调试连接。 OneBot WS 固定 ws://127.0.0.1:3001;如果历史配置被写成了 6099,插件会自动纠正并落盘。

零外部 npm 依赖、可离线运行;不带 pnpm-lock.yaml,不会触发 pnpm 供应链校验。

安装

  • 推荐:从 Releases 下载 dsh-napcat-bot-x.y.z.tgz,在 DSH 插件市场「导入本地包」里选择它。
  • 源码:本仓库不含 vendor/(内置 NapCat 本体约 111MB 的第三方二进制,用 Release 分发更合适)。 clone 后可直接读改 lib/;要打包成可安装 tgz,请把 NapCat 解压到 vendor/napcat/ 再 tar -czf。

装好必须完全退出 DSH 再启动:服务端 lib/index.js 只在启动时加载一次。

2.8.0:联网搜索(手动 + 不懂自动查)

搜索后端直接用 DSH 自带的 ctx.web.search()(dsh-base 已挂 @deepseek-ai/dsh-web + dsh-web-search-deepseek,走 DeepSeek 原生搜索),所以不需要另填搜索接口和 Key;面板也支持填 SearXNG 等自定义接口。

两类触发

类型 条件 例子
手动(原有) 命中关键词:帮我搜 / 搜一下 / 查一下 / 上网查 / 搜索 搜一下今天金价
自动(新增) 本地规则先判:时效信息(新闻/价格/时刻/表/发售/比分)、现实实体(地点/参数/规格/教程/怎么设置)、带疑问但拿不准;拿不准时再让模型回一个词确认 今天金价多少?(没说搜,也去查)

不搜的情况:纯闲聊、情绪陪伴(想你了/抱抱/晚安)、玩梗(哈哈哈/笑死/这梗)、主观感受、人设对话(你叫什么)、 以及去掉标点后不足 4 个字的语气词。

优先级

强制语音 > 手动搜索 > 自动搜索 > 识图解读 > 普通闲聊 所以「发个语音说你好」永远直接朗读、不会去搜;发图 + 「搜一下这是啥」会先让主模型用一句短话点出图里是什么,再拿这句话去搜。

兜底与容错

  • 搜索失败 / 无结果 → 回 呜,鱼鱼的小鳍划不动搜索引擎,暂时查不到这个哦(手动、自动同一套)。
  • 没有可用搜索后端时(宿主没挂 web 且没填自定义接口)→ 直接跳过搜索、照常回答,不会突然回「查不到」。
  • 所有搜索都 try/catch,超时 20 秒、同类查询 10 分钟内走缓存,插件不崩不刷屏。

配置 / 规则 / 日志

  • 面板「联网搜索」:总开关、不懂自动搜索(默认开)、自动搜索前模型二次确认、自定义接口地址、Key、每次取几条。
  • 管理 → 规则:可对单个私聊/群单独「不联网」或「只手动搜」。
  • 日志:[SEARCH] manual:<关键词> / auto:<原因> / ok provider=… n=… ms / fail … / skip <原因> / image-hint <提词>。

2.7.0:识图默认「读梗接话」,下指令才描述画面

以前默认提示词是「简短描述这张图片内容」——本质是在让模型看图汇报,发张表情包回来一段画面说明,很出戏。 2.7.0 把两种模式分开,优先级由你这句话决定:

你说的话 模式 模型被要求做什么
只发图 / 「哈哈哈哈」/「你看这个」 接梗模式(默认) 禁止描述物体、角色、文字、构图;只判断你发图的意图,读懂梗和情绪,直接接梗互动(例:呜!你好坏,居然喊一车肥鱼来欺负我🥺)
「描述一下这张图」「说说图片里面有什么」「图里长啥样」「详细讲讲画面」 描述模式 客观描述画面:元素、角色、文字(有就念出来)、构图
  • 命中词表在 lib/vision.js 的 DESCRIBE_KEYWORDS,只收「明确针对图片内容」的说法,所以「你长啥样呀」不会误触发。
  • 描述模式的提示词是硬编码的 DESCRIBE_PROMPT,不受面板提示词影响;面板里的「识图提示词(默认模式)」只管接梗模式。
  • 兜底与容错:默认模式若模型仍在「图片里有…」式汇报,会自动带更硬指令重试一次;重试仍汇报就照发(不冷场、不死循环)。
  • 历史默认提示词(「简短描述这张图片内容」等)在 __schemaVersion=10 迁移时自动换成新默认,用户自己改过的提示词保留。

其余链路一律没动:图片仍走 ctx.attachments 附件引用 + V4.1-Flash 原生视觉(同一条 user 消息里 text + image),识图开关、规则面板按会话覆盖、TTS 四种模式与强制语音、随缘主动发言、记忆、NapCat、风控全部原样。

新增日志:[VISION] mode: meme(默认接梗,不报画面) / describe:<命中词> / report-retry。

2.6.0:主动发言改成「随缘」(去掉倒计时与次数上限,只留冷却)

2.5.0 是「安静满 N 秒 → 说一条 → 你必须回话才会有下一条」,本质还是个定时器,而且被「一轮一条」卡住。 2.6.0 换成随机心情判定:

维度 现在的行为
触发方式 安静下来后,每隔 20~45 秒(带抖动)做一次心情判定,没有固定倒计时
次数 完全取消上限(没有每日/每轮封顶),冷却过完、心情到了就来找你
概率 每次判定先抽「心情档位」(12% 忽然很想你 p=0.45 / 43% 一般 p=0.18 / 45% 懒得开口 p=0.07),再掷一次是否命中 —— 所以节奏天然不均匀,可能一晚上两三次,也可能一整天不吭声
冷却 发出一条后强制冷却(默认 180 秒,面板滑块 60~1800),冷却期内绝不再发
对话中 正在生成回复、或刚说过话(冷却内)→ 一律不插话
告别 收到「晚安 / 先睡了 / 先不聊了 / 晚点再说 / 拜拜…」→ 暂停随机触发,直到你重新发消息才恢复
群聊 默认关闭,只在私聊;规则面板可给某个群单独打开
内容 主模型 + 面板人设 + 最近 12 条记忆生成,短句优先;方向含「想念 / 关心 / 接旧话题 / 小鱼碎碎念」,不是固定句子
语音 主动那句照样走「想发就发」随机判定
失败 模型空回复 / 报错 → 一个字都不发

面板

AI 配置 → 主动发言:主动找我(随缘)开关(默认开)、冷却间隔滑块(60~1800 秒,默认 180)、群聊也允许。 状态行:主动发言:开启(随缘)· 冷却 180 秒 · 待机 1 个会话 · 已主动 3 · 没心情 12 · 收尾暂停 1。

日志

日志 内容
主动发言 待机 冷却多少秒后开始随缘判定
主动发言 触发 触发时间、空闲多久、p=命中概率 roll=骰子、进入多少秒冷却
主动发言 跳过 / 顺延 / 停表 没心情、正在回复、收尾语句
[PROACTIVE] 主动发出的那句话原文

2.5.0:空闲主动发言(会接话,不会刷屏)

旧版是「每 N 分钟给最近活跃私聊群发一句问候」——那套已经删掉,生硬、会打断人、还容易刷屏。 新版走空闲触发 + 上下文生成:

维度 行为
触发 最后一条消息之后静默满 N 秒才考虑开口(默认 120 秒,面板滑块 30~600)
说什么 把「刚刚聊了什么 + 对方最后一句」交给主模型,按面板人设写成一句短话;不是固定句子
频率 同一段空闲最多一条;你回话后重新计时,进入新一轮
边界 正在生成回复时到点 → 顺延,绝不插话;你说「晚安 / 先不聊了 / 拜拜」→ 直接停表
群聊 默认关闭,只在私聊生效;规则面板可对某个群单独打开
语音 主动那句照样进「想发就发」随机判定,有心情就出声
失败 模型没给出内容 / 报错 → 一个字都不发(宁可不吭声,也不发套话)

面板

AI 配置 → 主动发言:开关(默认开)、空闲等待时长(滑块 30~600 秒,默认 120)、群聊也允许。 状态行会显示:主动发言:开启 · 空闲 120 秒触发 · 计时中 1 个会话(最近 98 秒后)· 已主动 3。

管理 → 规则 新增「主动发言(本会话覆盖)」:跟随全局 / 允许 / 本会话不主动 —— 可以给某个群开、给某个私聊关。

日志

前缀 内容
[napcat] 主动发言 计时中 什么时候排的计时、多久后检查、最后一句是什么
[napcat] 主动发言 已发送 空闲了多久才开口、之后重置计时
[napcat] 主动发言 跳过 / 顺延 / 停表 为什么没说话(未开启、已说过一条、正在回复、收尾语句)
[PROACTIVE] 主动发出的那句话原文(在「消息收发记录」里能看到)

2.4.0:识图改用主模型原生视觉(不再配两套模型)

为什么能这么干:DSH 的模型目录里 deepseek-flash(= DeepSeek-V4.1-Flash)本身就声明了 inputModalities: ["text","image"],视觉是主模型自带能力,没必要再挂一个独立的识图接口。

做法

旧(≤2.3.1) 新(2.4.0)
面板填「识图接口地址 + 识图 API Key + 识图模型名」 三个输入框全部删除,复用 DSH 里已填好的主模型与密钥
先调识图接口拿一句描述,再把描述拼进 prompt 图片和文本同一次请求进主模型:[{type:'text'}, {type:'image', attachment}]
两套模型、两套密钥、两份计费 一套配置,文字和看图同一个模型

技术要点:QQ 图片先下载成字节 → ctx.attachments.saveImage() 存进 DSH 附件库拿 ImageAttachmentRef → 以 {type:'image', attachment} 进 user 消息;宿主的 deepseek 适配器 会自己 readImageRequest() 转成 base64 图片块发出去。直接塞 url 是不行的(适配器会判为 不支持的块,整条请求失败),所以必须走附件引用。

保留的东西

  • 识图总开关(默认开)、识图提示词(默认「简短描述这张图片内容」,可改、持久化)。
  • 规则面板仍可按群/私聊单独关识图、单独调语音概率。
  • 识图回复照常进 TTS 判定(全部 / 关键词 / 性格随机 / 强制语音)。
  • 强制语音仍是绕过 LLM 的预处理拦截:发个语音说你好 直接抠出「你好」合成发送。
  • 识图关闭 / 图片取不到 / 类型不支持 / 附件服务不可用 / 主模型报错 → 一律走人设兜底台词。

日志

前缀 内容
[FORCE] 命中哪个强制语音词、抠出的朗读文本(或「交给模型起稿」)
[VISION] 原生多模态 · 图 N 张 或 fallback(原因);原因含 download / unsupported-type / no-url / no-attachment-service
[MODEL] multimodal · 带图 N 张请求主模型 · 返回 M 字
[TTS] synth-ok(引擎 + 音色 + 耗时)/ fail / skip

同类报错 30 秒只写一条,插件不崩、不刷屏。

验收

  1. 发「发个语音说你好」→ 直接发 QQ 语音,不经过模型,不会出现「鱼鱼不会按按钮」。
  2. 发「一车肥鱼」表情包 → 图片随对话进 V4.1-Flash,小鱼语气回复(日志有 [VISION] 原生多模态 · 图 1 张 + [MODEL] multimodal)。
  3. 不用填任何识图地址 / 识图 Key —— 用的就是 DSH 当前选择的主模型。

2.3.1:专项修复(强制语音只回文字 / 发图不识图)

真因(看日志就能确认)

  1. 强制语音只回文字:两个原因叠加 ——
    • ttsUrl 是空的,plan() 判定「无引擎」→ 只发文字;
    • 指令走的是「先让模型说话,再决定发不发语音」,模型先回了「鱼鱼这小短鳍按不来按钮啦」,推脱话术检查里没覆盖「按不来」,于是这条推脱被当正文发出去了。
  2. 发图不识图:visionEndpoint 是空的 → 直接 no-endpoint 兜底。

修法

① 强制语音改成「消息底层预处理拦截」,绕过 LLM 消息进来先解析指令,命中 发语音 / 发句语音 / 语音说xxx / 说出来 … 就把要念的正文抠出来,直接合成语音发送,完全不调模型(从根上不可能再推脱)。

  • 「发个语音说你好」→ 朗读文本 = 你好 → 直接发语音;
  • 「语音说你今天怎么样」→ 朗读文本 = 你今天怎么样;
  • 只说「发语音」没给内容 → 才让模型起稿,但仍强制语音,并注入「禁止说不会发语音/按不了按钮」+ 回复里出现推脱就重试/清洗;
  • 带图 + 强制语音 → 不走抠文本,走识图 + 人设起稿 + 强制语音(否则会把「这是啥」念出来)。
  • 优先级最高:ttsMode=off、随机 0% 也照发。

② 内置离线语音引擎(真开箱即用) 没填 TTS 接口地址时,自动用 Windows 自带语音(SAPI)合成 WAV → 交 NapCat 发语音。零依赖、无需联网、无需 Key;本机实测 3.16 秒语音合成耗时 ~2.4 秒。填了接口则以接口为准。可在面板关闭(「接口没填时用系统内置语音」)或指定音色(Huihui / Xiaoxiao …)。

③ 识图接口自动预置 默认 visionEndpoint = zhipu(智谱,glm-4v-flash 有免费额度),模型名自动补,提示词默认「简短描述图片内容,不要冗长」,开关默认开 —— 只需要补一个 API Key 就能识图(面板会显示「缺 API Key → 补上即可识图」)。地址/提示词都能在界面改,保存即持久化。

④ 日志(面板「消息收发记录」直接看)

前缀 内容
[FORCE] 是否命中强制语音、命中词、抠出的朗读文本
[VISION] 是否触发识图、ok/fallback、原因(no-endpoint / auth / http-500 / no-url …)、识别到的内容
[TTS] synth-ok(引擎 http/local + 音色 + 耗时)/ fail / skip(含 no-engine 原因)

同类报错 30 秒内只写一条,不刷屏;所有异常都降级为文字,插件不崩。

升级到 2.3.1 后必须完全退出 DSH 再启动。

2.3.0:强制语音指令 + 识图默认开箱即用

一、TTS 强制语音(内置关键词库,不用配)

只要这句话里命中下列任一说法,优先级最高:跳过模式(哪怕模式是「关闭」)、跳过随机概率,直接合成语音发出去。

发语音 发句语音 发条语音 发个语音 发一段语音 来句语音 来条语音 来段语音 语音说 用语音 语音回复 语音回答 语音念 语音读 说出来 念出来 读出来 念一下 读一下 说给我听 念给我听 唱一个

例:发「发个语音说你好」→ 直接发语音。

防推脱三层保险:

  1. 生成前给模型注入定向提示:你能直接发语音,禁止回答「不会发语音 / 按不了按钮 / 没法发」;
  2. 回复里仍出现推脱话术 → 带更硬指令自动重试一次;
  3. 还在推脱 → 清洗掉推脱句,语音照发(messages.log 记一行 TTS retry)。

二、识图默认配置

  • 识图开关默认开启;识图提示词默认「简短描述图片内容,不要冗长」(老配置里的旧默认值会自动迁移)。
  • 接口地址支持预设短名,只填一个词就行,模型名会自动补:
填这个 实际地址 自动模型
zhipu open.bigmodel.cn/api/paas/v4/chat/completions glm-4v-flash(有免费额度)
dashscope dashscope.aliyuncs.com/compatible-mode/v1/chat/completions qwen-vl-max
openai api.openai.com/v1/chat/completions gpt-4o-mini
siliconflow api.siliconflow.cn/v1/chat/completions Qwen/Qwen2.5-VL-72B-Instruct
moonshot api.moonshot.cn/v1/chat/completions moonshot-v1-8k-vision-preview
openrouter openrouter.ai/api/v1/chat/completions google/gemini-2.0-flash-exp:free
ollama 127.0.0.1:11434/v1/chat/completions llava

也可以填任意完整地址(.../chat/completions)或只填 base 地址(https://api.openai.com/v1,会自动补 /chat/completions),自定义完整地址不会被乱拼。

API Key 只能你自己填(这个没法内置)。

三、联动

  • 识图产出的回复若没有命中强制语音,正常走「心情随机语音」掷骰子。
  • 强制语音与随机模式互不干扰:随机 0% 时普通消息不发语音,但「发语音」指令照样发。
  • 规则面板可对单个群/私聊单独关闭识图、单独设语音概率(规则 > 全局)。
  • 面板「人设已生效」下方能看到:语音 已发/跳过/强制/失败、识图 成功/失败 + 上一张图内容。

四、容错

TTS / 识图 超时、401、5xx、返回空 → 一律降级为文字(或人设兜底台词),只写日志,插件不崩;同类报错 30 秒内只记一条,不刷屏。所有配置保存后立即生效,重启自动加载。

升级到 2.3.0 后必须完全退出 DSH 再启动。

2.2.0:识图(看图 → 用人设说话)

面板 AI 配置 → 识图(多模态):

配置项 说明
识图功能总开关 默认开启。关闭后收到图片只回一句人设兜底台词
多模态模型接口地址 OpenAI 兼容的 /chat/completions,如 https://api.openai.com/v1/chat/completions
识图 API Key 以 Bearer 发送;留空则不带鉴权头
多模态模型名 如 gpt-4o-mini / qwen-vl-max / glm-4v-flash(必须支持图片输入)
识图提示词 默认「描述这张图片,简短一点」

链路:QQ 图片 → 从 NapCat 消息里拿直链(只有文件名时用 OneBot get_image 换)→ 多模态接口得到一句描述 → 描述作为上下文交给主模型 → 按面板人设的语气重新组织语言(不是冷冰冰的客观描述)。

例:识图得到「一整车肥鱼表情包」→ 大肥鱼会说「哇!一整车都是肥鱼小伙伴,浩浩荡荡的,也太热闹啦🥳」。

  • 兜底:识图关闭 / 没填接口 / 超时 / 密钥错 / 请求报错 / 返回空 → 直接回人设台词: 「呜…有点糊糊的,鱼鱼眼睛小,得凑很近才看得清一点点。要不你讲讲图里是啥?我慢慢听~」 (这条台词整句一次发出,不拆段、不刷屏;同类报错 30 秒内只写一条日志。)
  • 和随机语音联动:识图回复与兜底台词都走同一条投递管道,所以照样按 TTS 模式掷骰子,可能变成语音。
  • 按会话覆盖:管理 → 规则可对某个私聊/群聊单独「关闭识图」。
  • 纯图片消息也能收到:以前没有文字的消息会被当作空消息丢掉,现在只要带图片就放行。
  • 同一张图 10 分钟内复用识别结果(有缓存,不重复烧 token)。
  • 面板「人设已生效」下方会显示识图状态、成功/失败次数和上一张图识别到的内容。

升级到 2.2.0 后必须完全退出 DSH 再启动。

2.1.0:TTS 语音「性格随机模式(想发就发)」

面板 AI 配置 → 语音(TTS) 区域,模式四选一:

模式 行为
关闭 只发文字
全部消息转语音 每条回复都发语音(回复超过「单条语音最长字数」时自动补发文字,信息不丢)
关键词触发 用户或回复里出现触发词(默认「语音 / 发语音 / 说给我听 / 念一下」)才发语音
性格随机模式(想发就发) 每轮先掷骰子,命中发语音,没命中只发文字

随机模式细节

⚠️ 语音要真的出声,必须填「TTS 接口地址」。接口只需要 POST {text, voice} 返回音频二进制或 {"audio_base64":"..."};OpenAI 兼容的 /v1/audio/speech 也能直接用(此时再填「TTS 模型名」)。没填接口时面板会显示「未填接口地址」,行为等同只发文字。

  • 概率滑动条 0~100%,默认 40%;0 = 永不发,100 = 次次发。是每轮独立掷骰,不是固定间隔,所以出现「有时出声、有时懒得开口」的随性节奏。
  • 人设智能权重(默认开):短句(≤12 字)权重 ×1.3、活泼语气再往上抬;长段解释(>120 字)×0.72、emotion 低落词(难过/累/emo/哭…)再压;人设里带「懒 / 瘫 / 佛系 / 慢悠悠」整体 ×0.9。边界仍然严格:0 还是 0,100 还是 100。
  • 随机音色(默认开):每次从音色池(默认 软萌少女,甜妹,奶音,软糯小助手,可改)里随机挑,每轮都可能不一样;关掉则固定用「固定音色」。
  • 兜底:TTS 接口超时/报错/返回空/发送失败 → 自动降级只发文字,写 messages.log,插件不崩;如果是「只发语音」模式且发送失败,会补发文字保证用户收到内容。
  • 缓存:同文本+同音色命中内存缓存(TTL 10 分钟、上限 60 条,自动淘汰)。音频全程 base64 直发 NapCat,不落盘,所以没有残留文件需要清理。
  • 按会话覆盖:管理 → 规则里可以对某个群/某个私聊单独设 语音模式 / 概率 / 随机音色(留空 = 跟随全局)。
  • 面板「人设已生效」下方会显示:语音模式、概率、已发 / 跳过 / 失败次数、上一句用的音色 —— 用来确认「确实是想发就发」。

升级到 2.1.0 后必须完全退出 DSH 再启动。旧配置里 ttsEnabled=true 会自动迁移成 ttsMode='all',行为不变。

2.0.1(紧急修复):人格不生效 / 固定输出套话

现象:面板里填了「大肥鱼」,问“你叫什么名字”却固定回一句 我觉得这个得看具体情况~你先说说细节,我再给你个靠谱的答复。

两个真实原因(都已修):

  1. 代码里有一段硬编码兜底话术(离线降级时输出)。模型调用一失败就会吐出这句, 看起来就像“人格完全没生效”。现在这句连同整套硬编码兜底已彻底删除: 模型不可用时就用人设推导一句身份回答(如「我是大肥鱼呀~…」),推导不出来就不发消息, 绝不敷衍一句模板话。
  2. system 提示词用错了传参方式。以前把人格塞在 llm.stream({ system }) 这个字段里, 而 DSH 的模型协议要求 system 作为 role:'system' 的消息进入 messages;字段被忽略, 等于人格根本没送到模型。现在 system 以标准消息发送,面板文本一字不改地成为唯一 system。
  3. 顺带修掉「模型返回空内容」→ 之前 maxTokens=800 时,模型的思维链会把预算吃光, 可见文本为空,于是又触发第 1 条的兜底话术。现在 maxTokens=2000,并且空内容会自动 以 reasoningEffort:'off' 重试一次。

现在的规则:

  • 面板填了人格 → 它就是唯一的 system(不加前缀、不追加任何“最高优先级”之类的说明文字,也不放风格模板);
  • 面板留空 → 完全不发 system,不自动补任何文字;
  • 问身份(你叫什么 / 你是谁 / 你会做什么)→ 额外给一条定向指令:直接按人设第一人称回答, 禁止“看具体情况、你先说说细节”式回避;万一模型还是回避,会自动带硬指令重试一次;
  • 上下文(历史 / 摘要 / 知识库)和排版要求全部挪到 user 消息里,不再污染 system。

排障:面板「人设已生效」下方会显示回复引擎、上次生成成功/失败以及具体报错; 明细写入 ~/.dsh/napcat-bot/engine.log,也可通过 GET /napcat-bot/api/napcat/log?source=engine 查看。

升级到 2.0.1 后必须完全退出 DSH 再启动(服务端代码只在启动时加载)。

三步验收

  1. 导入 dsh-napcat-bot-2.8.0.tgz → 完全退出并重启 DSH(服务端代码只在启动时加载)→ 启用插件 → 日志出现「内置 NapCat 已就绪,WebUI 监听 127.0.0.1:6099」。
  2. 设置页左侧【集成】里点「NapCat QQ 机器人」→ 第二个标签页「NapCat WebUI」已自动带上管理 Token,直接扫码登录。
  3. 三个状态灯全绿(NapCat 进程 ✓ / WS 已连接 ✓ / 可以收发消息 ✓)后,私聊发消息、群里 @ 机器人即可收到 AI 回复。

端口分工(1.5.0 起强制纠正,不再可能连错)

用途 地址 说明
NapCat WebUI(管理面) http://127.0.0.1:6099/webui 只用来扫码登录 QQ、看 NapCat 自己的面板。它不是 OneBot 端口
OneBot 正向 WS(收消息) ws://127.0.0.1:3001 插件主通道。连上后 NapCat 的「WS 客户端数」显示 1
OneBot HTTP(发消息) http://127.0.0.1:3000 能开就用;没开也不影响,插件会自动改走 WS 通道发送

历史配置里如果 napcatWsUrl 被写成了 6099(把管理面当成了 OneBot 端口),插件启动时会 自动改回 3001 并落盘,日志里会有一行 已修正 OneBot 端点:WS ws://127.0.0.1:6099 → ws://127.0.0.1:3001。 面板顶部也会把这三个地址并排列出来,排障时一眼能确认连的是哪个口。

⚠️ 关键:QQ 登录 ≠ WS 自动连上

这是本插件踩过的最深的坑,1.4.0 修了配置生效问题,1.5.0 又补了两个:

坑 0:WS 连到 6099 上(1.5.0 修复)

6099 是 NapCat 的管理面(HTTP 页面 + WebUI API),不在上面跑 OneBot 协议。 连它只会被拒或无响应,表现就是「插件 WS 状态未知/断开、NapCat 里 WS 客户端数一直是 0」。 现在 normalizeWsUrl() 会把「空值 / 等于 6099 / 等于 HTTP 端口」的地址一律纠正成 ws://127.0.0.1:3001, 配置层、WS 客户端层、面板展示层三处都过一遍,不可能再漏。

坑 1:改配置文件不生效(1.4.0 修复)

直接改 config/onebot11_<QQ>.json 并不能保证生效。 实测 NapCat 4.18.41 经常不热加载该文件, 甚至在自身保存配置时把 websocketServers 条目丢掉——结果就是「QQ 明明登录了,3001 却没在监听」。

所以插件改用 NapCat 自己的 WebUI HTTP API 来配置 OneBot 网络:

POST /api/auth/login              { hash = sha256(webuiToken + ".napcat") }  → data.Credential
POST /api/OB11Config/SetConfig    { config: "<json 文本>" }   // NapCat 内部 save + reloadNetwork

SetConfig 会让 NapCat 真正重启网络适配器,3001 立刻开始监听,不需要重启 NapCat、不需要重新扫码。 插件在「WebUI 就绪」「QQ 登录成功」「发现 3001 未监听」三个时机自动调用它,失败则每 6 秒重试。

坑 2:OneBot HTTP 必须打到具名路径(1.4.0 修复)

NapCat 的 HTTP 适配器不按 body 里的 action 字段分发。往根路径 POST http://127.0.0.1:3000 只会得到一个桩响应:

{"status":"ok","retcode":0,"data":{},"message":"NapCat4 Is Running"}

于是 get_login_info 永远拿不到 QQ 号(面板显示「未登录」),send_private_msg / send_group_msg 变成空操作(消息永远发不出去却也不报错)。

正确写法是 POST 到 /<action>。1.5.0 更进一步:HTTP 不通时 action 会自动走正向 WS(同一连接支持 echo 应答), 所以「NapCat 只开了 WS、没开 HTTP」也能正常收发。

两处 Token(插件已全自动,这里只是说明区别)

Token 用途 谁生成
WebUI 管理 Token 登录 http://127.0.0.1:6099/webui 这个管理页面 插件启动前写入 config/webui.json,随后从 NapCat 启动日志([WebUi] WebUi Token: xxx)自动读取校验;内嵌页面自动附加,无需手输
OneBot access_token 插件 ↔ NapCat 通信用(HTTP Bearer / WS query),真正收发 QQ 消息 插件首次启用时随机生成,自动写进插件配置与 NapCat 的 onebot11_<QQ>.json

两者完全独立:WebUI Token 只影响能否打开管理页面;OneBot token 只影响插件能否调 NapCat 的 API。

自动写入的 NapCat 配置

插件启动内置 NapCat 前会主动写配置文件(作为兜底),并在运行期用官方 API 校正:

  • config/webui.json:host=127.0.0.1、port=6099、带 token、不禁用 WebUI。
  • config/onebot11_<QQ>.json(对历史登录过的 QQ 号全部预置):
    • 正向 HTTP(默认 3000)—— 插件用它发消息、查登录态;
    • 正向 WS(默认 3001)—— 插件用它收消息;
    • 两者使用同一个自动生成的 token(与插件配置一致,不一致会直接 403 → 全链路静默);
    • reportSelfMessage=false,避免机器人收到自己发的消息形成自问自答。

状态灯与自动重连(1.5.0)

面板顶部固定三个灯,语义与 NapCat 侧对得上:

灯 含义
🟢 NapCat 进程已启动 内置 NapCat 在跑(按 6099 端口实际监听判定,不是看状态文件)
🟡 WS 正在尝试连接 插件正在重连 ws://127.0.0.1:3001;旁边显示第几次重试、下次重试倒计时
🔴 WS 连接失败 重连次数用尽(wsMaxRetries>0 时)或环境不支持 WebSocket
🟢✓ 可以收发消息 WS 已连接 且 3001 在监听 且 QQ 已登录

灯下面一行会直接显示 WS 目标:ws://127.0.0.1:3001(端口 3001),并标注 客户端数 1, 可以和 NapCat WebUI 里的「WS 客户端数」对照。

重连策略(面板可调):

  • 首次重连间隔 wsRetryInterval(默认 2000ms),之后指数退避 2→4→8→15s(上限 wsRetryMaxInterval)。
  • wsMaxRetries 默认 0 = 一直重试;填 N 则连续失败 N 次后停止(按钮可手动重连)。
  • 连接超时 8s 未建立即判失败换下一次(不依赖 onclose——实测 Node 的 WebSocket 在连接被拒时可能只触发 error)。
  • 心跳看门狗:连上后 wsHeartbeatTimeout(默认 100s)内收不到任何数据(含心跳)→ 判定半死连接,主动重连。
  • NapCat 重启 / QQ 掉线重连后,插件检测到 3001 恢复监听会立即重连,不用手动操作。
  • 进程守护:NapCat 进程异常退出(6099 不再监听)→ 状态如实回落成「未运行」,并在 12s 后自动重新拉起; 10 分钟内最多自动拉起 3 次,超过则停在「反复退出」提示,避免把 QQ 拉成重启风暴。

消息应答逻辑(默认即正确)

  • 私聊自动回复;群聊默认仅在被 @ 时回复(面板可关)。
  • 只处理真人聊天消息:post_type=message 且 message_type=private|group; 自动丢弃点赞 / 群成员变动等 notice、心跳 meta_event、加好友请求、机器人自己发的消息(含 sub_type=self)、以及纯图片语音等无文字消息。
  • 回复时把人设 Prompt + 最近 10 条对话记忆一起喂给模型,输出限制为口语化短句(不用 Markdown、不自称 AI)。
  • 回复引擎默认 dsh:走 ctx.get('llm') + ctx.get('agentDefaultModel').currentSelection(),用你在 DSH 里选中的模型与凭据,不需要另填 API Key。
  • 发送失败会被捕获并显示在面板(发送失败:...),同时计入「失败」计数。
  • 面板顶部有连接指示灯:未连接 / 连接中 / 已就绪,并显示 QQ 号、收发计数、最近收到时间、错误信息。

面板结构(固定两个标签页)

  1. AI 配置:AI 配置 内含内置 NapCat 状态卡与启停、连接(全部自动维护,一般不用改)、人格与回复、行为与风控(记忆开关、群内仅 @ 回复、主动发言、冷却与每分钟上限)。
  2. NapCat WebUI:内嵌 WebUI(自动免 Token 登录,这里扫码登录 QQ)、两类 Token 卡片、NapCat 控制台日志预览(实时刷新 / 暂停 / 只看 Token 行 / 复制日志)。

插件详情页里的配置表单同样保留,两个入口共存。

进程管理

  • 启动前先检测:自己的 pid 文件 + 日志里的 Main Process ID + 6099 端口探测 + 短期互斥锁,绝不重复多开 NapCat / QQ。
  • 关闭插件或退出 DSH 时自动停掉由本插件拉起的 NapCat 进程树(面板可关 stopNapCatOnDisable)。
  • 面板提供「启动内置 NapCat / 重新启动 / 停止」。

关于 QQ(唯一的外部前提)

NapCat 的工作原理是注入已安装的官方 QQ NT 客户端(NapCatWinBootMain.exe + NapCatWinBootHook.dll)。 系统上有官方 QQ 即可,不需要再单独安装或启动 NapCat。插件按下列顺序探测 QQ:

  1. 面板里填写的 qqPath;
  2. %ProgramFiles% / %ProgramFiles(x86)% / %LOCALAPPDATA%\Programs 下的 Tencent\QQNT\QQ.exe;
  3. C: – F: 盘下的 Program Files\Tencent\QQNT\QQ.exe、Tencent\QQNT\QQ.exe。

探测不到时面板会明确提示「未检测到 QQ NT」并给出填写入口。

HTTP 面板接口

注意:宿主 webServer 的 prefix 匹配是 pathname === prefix || pathname.startsWith(prefix + '/'), 因此 prefix 路由的 path 结尾不能带斜杠(早期版本 /napcat-bot/api/napcat/ 就是因为这个永远 404)。

方法 路径 说明
GET /napcat-bot/api/status 连接三态、OneBot 登录态、内置进程态、WebUI Token、运行计数、当前回复引擎
GET/POST /napcat-bot/api/config 读取 / 保存配置(保存即热重载)
GET /napcat-bot/api/webui WebUI 地址 + 自动读取的管理 Token + OneBot token
POST /napcat-bot/api/webui/token 重新生成 / 指定 WebUI 管理 Token(并重启生效)
POST /napcat-bot/api/napcat/{start|stop|restart} 内置 NapCat 启停
POST /napcat-bot/api/napcat/reconnect 只重连插件侧 WS(不动 NapCat、不踢 QQ)
POST /napcat-bot/api/napcat/apply-onebot 立即通过 WebUI API 重新应用 OneBot 配置
GET /napcat-bot/api/napcat/log?tail=N 控制台日志尾部(含 Token 行)
POST /napcat-bot/api/client-report 客户端 UI 注册自检回执(排障用)

排障

现象 处理
面板还是旧样子(没有三个灯 / 没有端点行) 服务端代码只在 DSH 启动时加载。装完新版必须完全退出 DSH 再启动,否则跑的还是内存里的旧版本
侧边【集成】没有入口 看 ~/.dsh/napcat-bot/client-report.json,其中 registered 应含 settings.section(1.5.0 已验证会出现)
WS 状态未知 / 断开,NapCat 里客户端数 0 1.5.0 已自动纠正到 3001;确认面板「WS 目标」这一行显示的是 ws://127.0.0.1:3001
状态灯一直「WS 正在尝试连接」 点「重新应用 OneBot 配置」;若仍不行看日志里有没有 WebSocket服务: ... 已启动
显示「未登录」但 QQ 确实在线 1.4.0 已修:OneBot HTTP 必须 POST 到 /<action>,打到根路径只会拿到桩响应
日志面板 404 1.3.0 起已修(路由 path 去掉尾斜杠),确认版本 ≥ 1.5.0
收到消息但发不出去 1.5.0 会自动改走 WS 通道发送,不再依赖 3000;若仍失败看面板 发送失败:... 的具体报错
NapCat 进程显示「未运行」 1.5.0 按端口真实存活判定,会 12s 后自动拉起(10 分钟内最多 3 次);也可点「启动内置 NapCat」
回复走离线兜底 面板连接卡显示「回复引擎:离线兜底应答」→ DSH 未配置模型,或 llm 服务不可用
内嵌 WebUI 空白 个别版本禁止 iframe 嵌套,点「在浏览器打开」,链接已带 Token

v2.0.0:AstrBot 风格消息管道

三个标签页

标签 内容
AI 配置 三个状态灯、端点明细、人设生效提示、消息收发记录、全部开关与字段
NapCat WebUI 内嵌管理页(自动带 token),扫码登录 QQ
管理 人格 / 会话 / 规则 / 知识库 / 任务 五个子页

消息处理顺序

  1. 组件解析(文本/图片/@/语音/文件/卡片)→ 过滤(自身、系统、进退群、红包闪照、空消息、纯表情)
  2. 黑白名单(用户 + 群)→ message_id 去重(跨实例共享,2 分钟窗口;另加 1.5s 内容指纹兜底)
  3. 私聊直达;群聊「@ 唤醒 → 免@ 超时(默认 120s,滑动续期)→ 超时静默」
  4. 会话等待器(多轮流程接管,例如 /idiom 成语接龙,接管期间不过模型)
  5. 指令层(见下)→ 上下文(历史 + 摘要压缩 + 知识库检索)→ 模型流式 → 分段发送

会话隔离

  • 私聊:private:${uid},强制独立,不可关闭。
  • 群聊:开关 groupUniqueSession(面板「会话策略」里)
    • 开:group:${gid}:${uid},每人独立上下文
    • 关:group:${gid},全群共用一套

指令

基础:/help /reset /new /stop /stats /sid /history /ls /rename /del /switch /idiom 管理员:/op /deop /ban /unban /model /provider /groupnew

人格优先级

规则里指定的人格(按会话) > 全局人格(人格库) > 面板单人格字段。 全部走 system prompt 最前面,插件内不再内置任何固定人设(旧版的“小深”会在升级时自动清除)。 人格文本支持变量:{{bot_name}} {{group_id}} {{user_id}} {{user_name}} {{time}}。

知识库

本地 BM25 + 字符 bigram 哈希向量余弦的混合检索(0.65 / 0.35 加权),中文优化; 按规则把知识库绑定到会话,命中后自动加进 system 并在回复末尾标注来源。 支持粘贴文本片段(PDF/DOCX 请先转成文本)。

定时任务

target 形如 group:123456 / private:10001,prompt 以 gen: 开头表示先用模型生成文案再发。 任务持久化在 state.json,支持立即执行/删除,重启后继续按间隔触发。

目录结构

lib/index.js           服务端主入口(配置 schema、路由、生命周期、单例守卫)
lib/client.js          面板 UI(AI 配置 / NapCat WebUI / 管理 三个标签)
lib/pipeline.js        消息流水线:解析 → 过滤 → 去重 → 指令 → 识图 → 搜索 → 推理 → 分段/语音
lib/responder.js       DSH 模型调用(system 只来自面板人格,三级重试,绝不输出套话)
lib/napcat.js          OneBot 连接:HTTP 动作 + 正向 WS + 重连/心跳
lib/napcat-embedded.js 内置 NapCat 进程管理(启动/停止/探活/自动拉起)
lib/vision.js          原生多模态:图片 → 附件引用;两种识图模式
lib/search.js          联网搜索:手动指令 + 自动查证 + 断路器
lib/tts.js             TTS 四种模式 + 强制语音 + 音色池 + 缓存
lib/tts-local.js       内置离线语音(Windows SAPI)
lib/proactive.js       主动发言调度(随缘随机、仅冷却)
lib/state.js           人格/规则/定时任务持久化
lib/sessions.js        会话与上下文
lib/kb.js              知识库混合检索
lib/risk.js            限流 / 黑白名单
tools/                 开发期自测脚本(Node 原生跑,无第三方依赖)

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 26d747f22eb4

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