🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

QQ 通道 DandanLLab/dsh-channel-qq

DeepSeek Harnessv0.1.0
DandanLLab2 天前有提交已提供中文文档扫描:含敏感能力

dshDeepSeek Harness的 QQ 群聊/私聊通道插件,基于 OneBot v11NapCat。 把 QQ…

36.3/100
综合分
5
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查有告警:npm 同名包「dsh-channel-qq」归属 aegink/dsh-channel-qq,装到的可能不是本插件。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add DandanLLab/dsh-channel-qq
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · platform
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 2 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意

npm 同名包「dsh-channel-qq」归属 aegink/dsh-channel-qq,装到的可能不是本插件

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-channel-qq @ 0.1.0
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/3 07:16:39

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 245 处,展示前 122 条)
  • 读取密钥环境变量
    lib/index.js:144
    const bridgeKey = process.env.CHANNEL_QQ_BRIDGE_KEY ?? '';
  • 发起网络请求
    lib/index.js:234
    const res = await fetch(url);
  • 发起网络请求
    lib/meme/client.js:232
    return (await fetch('/dsh-memes-api?' + qs.toString())).json()
  • 发起网络请求
    lib/meme/client.js:267
    const res = await (await fetch('/dsh-memes-api', {
  • 发起网络请求
    lib/meme/client.js:604
    const response = await fetch('/dsh-memes-export?packId=' + encodeURIComponent(id))
  • 发起网络请求
    lib/meme/client.js:671
    const res = await fetch('/dsh-memes-api?packId=' + encodeURIComponent(row.packId)).then((r) => r.json())
  • 发起网络请求
    lib/meme/client.js:825
    const response = await fetch('/dsh-memes-export?packId=' + encodeURIComponent(pack.id))
  • 发起网络请求
    lib/meme/client.js:2182
    fetch('/dsh-memes-api?packId=all')
  • 写入本地文件
    lib/life/kb.cjs:69
    try { mkdirSync(sc.dir, { recursive: true }) } catch { }
  • 写入本地文件
    lib/life/kb.cjs:70
    appendFileSync(sc.hitlog, JSON.stringify(obj) + '\n')
  • 读取本地文件
    lib/life/kb.cjs:76
    try { return JSON.parse(readFileSync(kbScope(KB_AGENT).dir + '/.kb-recent.json', 'utf8')) } catch { return {} }
  • 写入本地文件
    lib/life/kb.cjs:91
    writeFileSync(kbScope(KB_AGENT).dir + '/.kb-recent.json', JSON.stringify(m))
  • 写入本地文件
    lib/life/kb.cjs:98
    try { appendFileSync(INTERACTIONS, JSON.stringify({ at: new Date().toISOString(), ...ev }) + '\n') } catch { }
  • 读取本地文件
    lib/life/kb.cjs:104
    if (!existsSync(gp)) return []
  • 读取本地文件
    lib/life/kb.cjs:108
    for (const l of readFileSync(gp, 'utf8').split('\n')) {
  • 读取本地文件
    lib/life/kb.cjs:144
    function memStateLoad() { try { return JSON.parse(readFileSync(MEMSTATE, 'utf8')) } catch { return { archived: [], forgotten: [] } } }
  • 写入本地文件
    lib/life/kb.cjs:145
    function memStateSave(m) { try { writeFileSync(MEMSTATE, JSON.stringify(m, null, 1)) } catch { } }
  • 读取本地文件
    lib/life/kb.cjs:149
    if (!existsSync(target)) { console.log('[forget] 不存在: ' + file); return }
  • 写入本地文件
    lib/life/kb.cjs:154
    try { mkdirSync(trashDir, { recursive: true }); renameSync(target, trashDir + '/' + file.split('/').pop()) } catch { }
  • 读取本地文件
    lib/life/kb.cjs:184
    if (existsSync(SES_INDEX)) {
  • 删除本地文件
    lib/life/kb.cjs:339
    if (man) for (const s of man.shards) { const p = shardPaths(s.n, ''); try { unlinkSync(p.jsonl) } catch { } try { unlinkSync(p.bin) } catch { } }
  • 删除本地文件
    lib/life/kb.cjs:340
    try { unlinkSync(S.shards) } catch { }
  • 发起网络请求
    lib/life/kb.cjs:384
    const res = await fetch(EMBED_URL, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ model: 'bge-small-zh', in
  • 删除本地文件
    lib/life/kb.cjs:871
    try { unlinkSync(SES_LOCK) } catch { }
  • 写入本地文件
    lib/outbound.js:142
    fs.mkdirSync(this.pendingDir, { recursive: true });
  • 写入本地文件
    lib/outbound.js:144
    fs.writeFileSync(file, JSON.stringify({ action, payload }));
  • 读取本地文件
    lib/outbound.js:157
    files = fs.readdirSync(this.pendingDir).filter((f) => f.endsWith('.json'));
  • 读取本地文件
    lib/outbound.js:168
    const { action, payload } = JSON.parse(fs.readFileSync(full, 'utf8'));
  • 删除本地文件
    lib/outbound.js:169
    fs.unlinkSync(full);
  • 写入本地文件
    lib/outbound.js:226
    fs.appendFileSync(file, new Date().toISOString() + '\n' + text + '\n---\n');
  • 发起网络请求
    lib/bridge.js:287
    const res = await fetch(file.url);
  • 发起网络请求
    lib/bridge.js:299
    const res = await fetch(data.file);
  • 发起网络请求
    lib/bridge.js:304
    const res = await fetch(data.url);
  • 发起网络请求
    lib/bridge.js:1036
    const res = await fetch(url);
  • 请求外部主机ss.xingzhige.com
    lib/tools.js:26
    const xz = (payload) => () => fetch('https://ss.xingzhige.com/music_card/card', {
  • 发起网络请求
    lib/tools.js:26
    const xz = (payload) => () => fetch('https://ss.xingzhige.com/music_card/card', {
  • 请求外部主机ss.xingzhige.com
    lib/tools.js:38
    return fetch('https://ss.xingzhige.com/music_card/card', {
  • 发起网络请求
    lib/tools.js:38
    return fetch('https://ss.xingzhige.com/music_card/card', {
  • 请求外部主机106.55.0.102
    lib/tools.js:52
    return fetch('http://106.55.0.102:10087/', {
  • 发起网络请求
    lib/tools.js:52
    return fetch('http://106.55.0.102:10087/', {
  • 发起网络请求
    lib/tools.js:214
    const audio = await fetch(resolved.url);
  • 发起网络请求
    lib/tools.js:964
    const cdpUp = await fetch('http://127.0.0.1:9334/json', { signal: AbortSignal.timeout(3000) }).then((r) => r.ok).catch(() => false);
  • 发起网络请求
    lib/tools.js:1016
    const cdpUp = await fetch('http://127.0.0.1:9777/json', { signal: AbortSignal.timeout(3000) }).then((r) => r.ok).catch(() => false);
  • 请求外部主机api.qijieya.cn
    lib/tools.js:1923
    const res = await fetch(`https://api.qijieya.cn/meting/?server=netease&type=lrc&id=${encodeURIComponent(String(args.songId))}`);
  • 读取本地文件
    lib/meme/index.js:29
    const buf = await readFile(filePath);
  • 写入本地文件
    lib/meme/index.js:37
    await writeFile(tmpPath, buf);
  • 删除本地文件
    lib/meme/index.js:39
    setTimeout(() => { rm(tmpPath).catch(() => { }); }, 30_000);
  • 读取本地文件
    lib/meme/index.js:41
    const b64 = (await readFile(sendPath)).toString('base64');
  • 发起网络请求
    lib/emoji.js:36
    const res = await fetch(url);
  • 读取本地文件
    lib/affection.js:96
    const content = await fsp.readFile(path.join(dir, `${safe}.md`), 'utf-8');
  • 读取本地文件
    lib/affection.js:102
    for (const f of await fsp.readdir(dir)) {
  • 读取本地文件
    lib/affection.js:105
    const st = await fsp.stat(path.join(dir, f));
  • 读取本地文件
    lib/affection.js:121
    const names = await fsp.readdir(sentFile).catch(() => []);
  • 读取本地文件
    lib/affection.js:125
    const entries = JSON.parse(await fsp.readFile(path.join(sentFile, n), 'utf-8'));
  • 读取本地文件
    lib/affection.js:200
    custom = readFileSync(this.customHtmlPath, 'utf-8');
  • 读取本地文件
    lib/emotion.js:72
    const raw = JSON.parse(readFileSync(this.file, 'utf-8'));
  • 读取本地文件
    lib/emotion.js:141
    const raw = JSON.parse(readFileSync(this.file, 'utf-8'));
  • 发起网络请求
    lib/stt.js:88
    const res = await fetch(url);
  • 请求外部主机music.163.com
    lib/netease-vip.js:32
    const res = await fetch(`https://music.163.com${path}`, {
  • 发起网络请求
    lib/netease-vip.js:32
    const res = await fetch(`https://music.163.com${path}`, {
  • 请求外部主机music.163.com
    lib/netease-vip.js:52
    const res = await fetch(`https://music.163.com/api/song/detail?ids=%5B${encodeURIComponent(String(Number(songId)))}%5D`, {
  • 发起网络请求
    lib/netease-vip.js:52
    const res = await fetch(`https://music.163.com/api/song/detail?ids=%5B${encodeURIComponent(String(Number(songId)))}%5D`, {
  • 请求外部主机music.163.com
    lib/netease-vip.js:110
    const res = await fetch('https://music.163.com/api/login/qrcode/client/login', {
  • 发起网络请求
    lib/netease-vip.js:110
    const res = await fetch('https://music.163.com/api/login/qrcode/client/login', {
  • 发起网络请求
    lib/kugou-api.js:35
    const res = await fetch(`${BASE}/register/dev`);
  • 发起网络请求
    lib/kugou-api.js:56
    const res = await fetch(`${BASE}${path}?${search.toString()}`);
  • 读取本地文件
    lib/audio-convert.js:18
    const buf = fs.readFileSync(filePath);
  • 读取本地文件
    lib/audio-convert.js:56
    if (!fs.existsSync(inputPath)) {
  • 读取本地文件
    lib/audio-convert.js:59
    const fileBuf = fs.readFileSync(inputPath);
  • 读取本地文件
    lib/audio-convert.js:69
    if (!fs.existsSync(dir)) {
  • 写入本地文件
    lib/audio-convert.js:70
    fs.mkdirSync(dir, { recursive: true });
  • 写入本地文件
    lib/audio-convert.js:74
    fs.writeFileSync(wavPath, wavBuffer);
  • 发起网络请求
    lib/audio-convert.js:176
    const resp = await fetch(url, {
  • 写入本地文件
    lib/audio-convert.js:197
    fs.writeFileSync(tmpMp3, rawBuffer);
  • 删除本地文件
    lib/audio-convert.js:213
    fs.unlinkSync(tmpMp3);
  • 删除本地文件
    lib/audio-convert.js:214
    fs.rmdirSync(tmpDir);
  • 读取本地文件
    lib/audio-convert.js:240
    if (!fs.existsSync(outputDir))
  • 写入本地文件
    lib/audio-convert.js:241
    fs.mkdirSync(outputDir, { recursive: true });
  • 写入本地文件
    lib/audio-convert.js:243
    fs.writeFileSync(silkPath, silkBuffer);
  • 读取本地文件
    lib/audio-convert.js:248
    if (!fs.existsSync(filePath))
  • 执行外部命令来自配置/变量
    lib/audio-convert.js:354
    execFile(ffmpegCmd, args, {
  • 执行外部命令固定命令
    lib/life/life-engine.js:919
    const r = execFileSync('python', [TAILMSG, ...args], { timeout, windowsHide: true, encoding: 'utf8', cwd: '~/.tailmsg' })
  • 执行外部命令来自配置/变量
    lib/life/life-engine.js:1077
    try { const c = sleepExec(process.execPath, [KB_CLI, ...argv], { timeout: 600000, windowsHide: true, env: { ...process.env, KB_WORKSPACE: workspaceOf(
  • 读取本地文件
    lib/meme/meme-engine.js:472
    try { return JSON.parse(readFileSync(settingsFile, 'utf8')) } catch (e) { return {} }
  • 写入本地文件
    lib/meme/meme-engine.js:476
    mkdirSync(join(dshHome(), '.dsh'), { recursive: true })
  • 写入本地文件
    lib/meme/meme-engine.js:477
    writeFileSync(settingsFile, JSON.stringify({ ...readSettings(), ...patch }, null, 2))
  • 读取本地文件
    lib/meme/meme-engine.js:637
    const bytes = readFileSync(file)
  • 读取本地文件
    lib/meme/meme-engine.js:904
    if (existsSync(join(bundledRoot, 'index.db'))) {
  • 写入本地文件
    lib/meme/meme-engine.js:959
    mkdirSync(dir, { recursive: true })
  • 读取本地文件
    lib/meme/meme-engine.js:961
    if (!existsSync(indexPath)) {
  • 发起网络请求
    lib/meme/meme-engine.js:1063
    const res = await fetch(imageUrl, { signal: AbortSignal.timeout(15000), redirect: 'follow' })
  • 写入本地文件
    lib/meme/meme-engine.js:1098
    mkdirSync(join(targetRoot, 'memes', tag), { recursive: true })
  • 写入本地文件
    lib/meme/meme-engine.js:1099
    writeFileSync(join(targetRoot, rel), buf)
  • 读取本地文件
    lib/meme/meme-engine.js:1360
    const raw = readFileSync(catalogCacheFile, 'utf8')
  • 写入本地文件
    lib/meme/meme-engine.js:1369
    mkdirSync(dirname(catalogCacheFile), { recursive: true })
  • 发起网络请求
    lib/meme/meme-engine.js:1388
    const res = await fetch(withCacheBust(u), {
  • 请求外部主机api.github.com
    lib/meme/meme-engine.js:1432
    const res = await fetch('https://api.github.com/repos/' + repo + '/releases?per_page=100', {
  • 发起网络请求
    lib/meme/meme-engine.js:1432
    const res = await fetch('https://api.github.com/repos/' + repo + '/releases?per_page=100', {
  • 发起网络请求
    lib/meme/meme-engine.js:1497
    res = await fetch(u, { signal: AbortSignal.timeout(15000), redirect: 'follow' })
  • 发起网络请求
    lib/meme/meme-engine.js:1539
    res = await fetch(u, { signal: AbortSignal.timeout(180000), redirect: 'follow' })
  • 发起网络请求
    lib/meme/meme-engine.js:1592
    const res = await fetch(u, { signal: AbortSignal.timeout(20000), redirect: 'follow' })
  • 删除本地文件递归
    lib/meme/meme-engine.js:1695
    rmSync(stageAbs, { recursive: true, force: true })
  • 删除本地文件递归
    lib/meme/meme-engine.js:1696
    rmSync(backupAbs, { recursive: true, force: true })
  • 读取本地文件
    lib/meme/meme-engine.js:1724
    if (existsSync(targetAbs)) {
  • 删除本地文件递归
    lib/meme/meme-engine.js:1734
    rmSync(backupAbs, { recursive: true, force: true })
  • 删除本地文件递归
    lib/meme/meme-engine.js:1739
    rmSync(stageAbs, { recursive: true, force: true })
  • 删除本地文件递归
    lib/meme/meme-engine.js:1835
    rmSync(workDir, { recursive: true, force: true })
  • 删除本地文件
    lib/meme/meme-engine.js:1861
    try { unlinkSync(join(workDir, known.path)) } catch { }
  • 读取本地文件
    lib/meme/memes.js:109
    return statSync(dir).isDirectory() && existsSync(join(dir, 'index.db'))
  • 读取本地文件
    lib/meme/memes.js:109
    return statSync(dir).isDirectory() && existsSync(join(dir, 'index.db'))
  • 读取本地文件
    lib/meme/memes.js:121
    const m = JSON.parse(readFileSync(join(dir, 'manifest.json'), 'utf8'))
  • 读取本地文件
    lib/meme/memes.js:158
    if (!parent || !existsSync(parent)) return
  • 读取本地文件
    lib/meme/memes.js:161
    try { names = readdirSync(parent) } catch { return }
  • 读取本地文件
    lib/meme/memes.js:167
    try { subs = readdirSync(dir) } catch { continue }
  • 写入本地文件
    lib/meme/memes.js:707
    mkdirSync(dir, { recursive: true })
  • 写入本地文件
    lib/meme/memes.js:716
    writeFileSync(manifestPath, JSON.stringify({ id: meta.id, name: meta.name, description: String(meta.description || ''), version: '1.0.0' }, null, 2))
  • 写入本地文件
    lib/meme/memes.js:746
    mkdirSync(dirname(to), { recursive: true })
  • 写入本地文件
    lib/meme/memes.js:747
    try { renameSync(from, to) } catch {
  • 写入本地文件
    lib/meme/memes.js:748
    copyFileSync(from, to)
  • 删除本地文件
    lib/meme/memes.js:749
    unlinkSync(from)
  • 发起网络请求
    lib/meme/qq-settings.client.js:27
    fetch('/channel-qq-api').then((r) => r.json()).then((j) => setState(j.settings ?? {})).catch(() => setState({}))
  • 发起网络请求
    lib/meme/qq-settings.client.js:33
    const r = await fetch('/channel-qq-api', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(state ?? {}) })
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
涉及外部主机:ss.xingzhige.com、106.55.0.102、api.qijieya.cn、music.163.com、api.github.com

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-tools@deepseek-ai/cordis@deepseek-ai/dsh-llm
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 接入QQ群聊私聊
  • 收发消息与文件
  • 基于OneBotv11

README

由 DeepSeek 最新模型翻译生成
dsh-channel-qq dsh(DeepSeek Harness)的 QQ 群聊/私聊通道插件,基于 OneBot v11(NapCat)。 把 QQ 的世界映射进智能体上下文,把行动权交给智能体本身——插件不做任何代答。 功能总览 - 消息通道:群聊+私聊,注入模式(inject/steer)或攒批模式可选 - 视觉:入站图片自动附件化给多模态模型;表情包偷取+情绪标签库(MD5 去重+语义搜索) - 语音:语音条转文字(STT);TTS 语音回复(多服务商) - 文件:私聊/群文件收发、自动落盘 inbox - 音乐:网易云(含 VIP 扫码登录)+ 酷狗(KuGouMusicApi)点歌/歌单 - 社交:戳一戳裁决制、表情包反应、关键词触发、情感引擎(好感冒泡) - QQ 客户端全能力:个性签名/资料/头像/在线状态/群打卡/合并转发/收藏/群荣誉(NapCat 扩展 action;其他 OneBot 客户端不支持时明确报错) - 表情包图库组件:内置 dsh-meme——send_meme/learn_meme、情绪+关键词搜图、AI 学图、管理面板(详见下文) - 日程:主动出击、每日群日报脚本
展开完整文档(README 剩余 1 节)
dsh-channel-qq

dsh(DeepSeek Harness)的 QQ 群聊/私聊通道插件,基于 OneBot v11(NapCat)。
把 QQ 的世界映射进智能体上下文,把行动权交给智能体本身——插件不做任何代答。

功能总览

- 消息通道:群聊+私聊,注入模式(inject/steer)或攒批模式可选
- 视觉:入站图片自动附件化给多模态模型;表情包偷取+情绪标签库(MD5 去重+语义搜索)
- 语音:语音条转文字(STT);TTS 语音回复(多服务商)
- 文件:私聊/群文件收发、自动落盘 inbox
- 音乐:网易云(含 VIP 扫码登录)+ 酷狗(KuGouMusicApi)点歌/歌单
- 社交:戳一戳裁决制、表情包反应、关键词触发、情感引擎(好感冒泡)
- QQ 客户端全能力:个性签名/资料/头像/在线状态/群打卡/合并转发/收藏/群荣誉(NapCat 扩展 action;其他 OneBot 客户端不支持时明确报错)
- 表情包图库组件:内置 dsh-meme——send_meme/learn_meme、情绪+关键词搜图、AI 学图、管理面板(详见下文)
- 日程:主动出击、每日群日报脚本
- 生活引擎组件:内置 dsh-life-engine——记忆可塑性(状态机+睡眠巩固)、内心独白、好奇心引擎、反思回路、每日自画像、KB 语义/考古双路检索(详见下文)
- 运维:网关重试、僵尸连接看门狗、下发队列限速、发送日志

安装

1. 克隆到任意目录
git clone <repo> dsh-channel-qq
cd dsh-channel-qq
npm install # 或 pnpm install

2. 编译
npx tsc -p tsconfig.json

在 dsh 配置档的 cordis.patch.yml 里挂载:

- insert:
- id: channel-qq
name: dsh-channel-qq
config:
connection:
mode: reverse # 或 forward url: ws://127.0.0.1:8082 selfId: <机器人QQ号>
admins: [<管理员QQ号>]
primaryGroup: <主力群号或null> workspacePath: <agent工作区路径> agentPreset: <agent预设名> persona: # ★ 背景设定:全部可自定义 selfName: MyBot # 角色名 masterName: 主人 # 对主人的称呼 peerName: 朋友 # 对群友的称呼 pronoun: 咱 # 自称 injectMode: enabled: true # 注入模式(推荐) wakeIdle: true

人格背景完全自定义:selfName/masterName/peerName/pronoun 四个字段决定插件注入提示里的称谓。角色性格本身写在你的智能体预设的 persona 提示词里,与插件解耦——换角色只改两处文本,不动代码。

音乐点歌(可选)

网易云:
- 免登录可搜可发免费歌;VIP 歌需要登录
- 让智能体调 qq_netease_login(action=start 拿二维码 → 你用网易云App扫码 → action=poll)
- 会员 Cookie 持久化在 <dshHome>/channel-qq/netease-cookie.txt

酷狗:
- 先部署 KuGouMusicApi(PORT=4000 node app.js),或设置环境变量 KUGOU_API_BASE 指向你的服务地址
- qq_kugou_login 扫码登录后搜索/VIP 歌解锁
- Cookie 持久化在 <dshHome>/channel-qq/kugou-cookie.txt

群日报(可选)

node scripts/daily-report.mjs --date 2026-09-26
报告生成在 <dshHome>/channel-qq/daily-report-<date>.md。

情感引擎与好感度

- affection.json:好感冒泡值,随互动实时演化(有 token 保护的悬浮窗可视化)
- emotion.json:多维情绪状态(心情/能量/最近事件),影响回复语气
- 悬浮窗端口默认 8789,可配置或关闭

表情包图库组件(Meme Engine)

本插件从 0.2.1 起把 dsh-meme(MIT, © Selfloom contributors)作为内置组件打包:图库管理(情绪/关键词双维搜图、跨图库开关、ZIP 导入导出)、 send_meme(智能体按情绪主动发图)、learn_meme(用户说"收藏这张"时 AI 自动识别分类/描述/关键词后收录)、自包含管理面板(/memes-panel)与 /dsh-memes 图片路由。

- 默认开启;config.memeEngine.enabled: false 一键关闭
- 零配置 QQ 发送:组件不再依赖独立的 dsh-companion——channel-qq 包装器直接把发送桥接给自己的 OneBot 客户端(沿用群白名单/私聊 admins 窗口纪律与魔数后缀校验)
- 随包内置图库(dafeiyu-001,24 张)开箱即用;用户图库默认扫 ~/.dsh/meme-packs
- 独立安装的 dsh-meme 不受影响(适配层自动回落 companionQq 通道)

生活引擎组件(Life Engine)

本插件从 0.2.0 起把 dsh-life-engine(MIT)作为内置组件打包:记忆状态机(NEW→INDEXED→ACTIVE→DECAYING→ARCHIVED→FORGOTTEN)、睡眠巩固、内心独白、好奇心引擎、反思回路、每日自画像,以及 KB 语义检索(kb_search / kb_deep_search / brain_search)与自主闹钟、tailmsg 通信等 30+ 工具。

- 默认开启;config.lifeEngine.enabled: false 一键关闭
- 身份与路径全部外置:组件从 <dshHome>/tongling-life/config.json 热读配置(目录名沿自组件历史,可在 config 内自定义 agentName)—— agentName(KB 专属库子目录名)、personaName、familyPeers / familyMap (家族神经网节点)、kbCli、embedUrl / embedModel(本地向量端点,可选)
- kb.cjs 会话层根目录可用 KB_SESSION_ROOTS(分号分隔)指定;agent 集合用 KB_AGENTS 指定
- 可选的本地向量端点(默认 http://127.0.0.1:17861,bge-small-zh 512 维):装了就是全量语义检索,不装自动降级 bigram+Jaccard

陌生人私聊门控(主人审计)

私聊不是谁都能跟 agent 聊起来的(config.dmGate,默认开启):

- admins 永远直通;dmGate.allowList 里的号直通
- QQ 好友自动放行(好友列表本地校验,30 秒缓存)
- 陌生人进待审:收到一条统一说明(不进 agent、不耗模型),同时给主人发待审通知(同一陌生人 10 分钟只通知一次 + 全局 60 秒连发保护)
- 主人三种放行方式:qq_dm_gate 工具(action=approve/deny/list,agent 侧一句话审计)、配置把号写进 allowList、或直接加好友(自动放行)
- deny 进冷却:该号再发消息不回复不通知,静默丢弃
- 状态持久化在 <dshHome>/channel-qq/dm-gate.json

依赖

- dsh harness(cordis 框架)
- NapCat(OneBot v11 实现)
- 可选:ffmpeg(语音转码)、KuGouMusicApi(酷狗点歌)

目录结构

src/
index.ts 插件入口,服务生命周期 config.ts 配置模型(含 persona 背景设定) onebot.ts OneBot v11 WS 传输(forward/reverse + 心跳看门狗) bridge.ts 会话桥:消息→上下文,注入/攒批双模式 segments.ts 消息分段解析(文本/图片/引用/文件/语音) tools.ts 48+ qq_* 工具注册 emoji-library.ts 表情库:MD5去重+视觉打标+语义搜索 emotion.ts 多维情感引擎 affection.ts 好感度+悬浮窗 scheduler.ts 主动出击调度 netease-vip.ts 网易云(含 VIP 扫码登录) kugou-api.ts 酷狗(KuGouMusicApi 客户端) stt.ts 语音转文字 audio-convert.ts TTS 语音合成 life/ 生活引擎组件(dsh-life-engine 并入:index.ts 薄包装 + life-engine.js / kb.cjs 原样组件) meme/ 表情包图库组件(dsh-meme 并入:index.ts 适配层 + index/memes/anim/panel 原样组件 + 内置图库) scripts/ daily-report.mjs 群日报生成 copy-life-assets.mjs 构建时把 life 组件资产拷进 lib/ docs/ emoji-pipeline.md 表情包完整方案(入库三闸/严格后缀/MISS 兜底) upstream-fusion-ledger.md 上游二改融合对账(吸收/不吸收/为什么)

License

MIT(含 src/life/ 组件,源自 DandanLLab/dsh-life-engine,同 MIT)
预览分享卡片长什么样 ↓DandanLLab/dsh-channel-qq
分享 · DeepSeek Harness Hub
DandanLLab/dsh-channel-qq

dshDeepSeek Harness的 QQ 群聊/私聊通道插件,基于 OneBot v11NapCat。 把 QQ…

回到顶部