@zaofan/dsh-qqbot 1.4.6 → 1.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -535
- package/README_EN.md +6 -0
- package/client/qqbot-settings.js +152 -5
- package/dist/channel-tools.d.ts.map +1 -1
- package/dist/channel-tools.js +106 -1
- package/dist/channel-tools.js.map +1 -1
- package/dist/features/agg-score.d.ts +73 -0
- package/dist/features/agg-score.d.ts.map +1 -0
- package/dist/features/agg-score.js +63 -0
- package/dist/features/agg-score.js.map +1 -0
- package/dist/features/attitude.d.ts +132 -0
- package/dist/features/attitude.d.ts.map +1 -0
- package/dist/features/attitude.js +318 -0
- package/dist/features/attitude.js.map +1 -0
- package/dist/features/bot-reply-memo.d.ts +8 -0
- package/dist/features/bot-reply-memo.d.ts.map +1 -0
- package/dist/features/bot-reply-memo.js +34 -0
- package/dist/features/bot-reply-memo.js.map +1 -0
- package/dist/features/emo-samples.d.ts +27 -0
- package/dist/features/emo-samples.d.ts.map +1 -0
- package/dist/features/emo-samples.js +116 -0
- package/dist/features/emo-samples.js.map +1 -0
- package/dist/features/four-source.d.ts +114 -0
- package/dist/features/four-source.d.ts.map +1 -0
- package/dist/features/four-source.js +260 -0
- package/dist/features/four-source.js.map +1 -0
- package/dist/features/image-url-ledger.d.ts +2 -0
- package/dist/features/image-url-ledger.d.ts.map +1 -1
- package/dist/features/image-url-ledger.js +23 -0
- package/dist/features/image-url-ledger.js.map +1 -1
- package/dist/features/intimacy-ledger.d.ts +31 -0
- package/dist/features/intimacy-ledger.d.ts.map +1 -0
- package/dist/features/intimacy-ledger.js +113 -0
- package/dist/features/intimacy-ledger.js.map +1 -0
- package/dist/features/local-signals.d.ts +130 -0
- package/dist/features/local-signals.d.ts.map +1 -0
- package/dist/features/local-signals.js +445 -0
- package/dist/features/local-signals.js.map +1 -0
- package/dist/features/people-memo.d.ts +52 -0
- package/dist/features/people-memo.d.ts.map +1 -0
- package/dist/features/people-memo.js +239 -0
- package/dist/features/people-memo.js.map +1 -0
- package/dist/features/quote-cache.d.ts +19 -0
- package/dist/features/quote-cache.d.ts.map +1 -0
- package/dist/features/quote-cache.js +67 -0
- package/dist/features/quote-cache.js.map +1 -0
- package/dist/features/self-disclosure.d.ts +28 -0
- package/dist/features/self-disclosure.d.ts.map +1 -0
- package/dist/features/self-disclosure.js +60 -0
- package/dist/features/self-disclosure.js.map +1 -0
- package/dist/features/tendency-samples.d.ts +22 -0
- package/dist/features/tendency-samples.d.ts.map +1 -0
- package/dist/features/tendency-samples.js +194 -0
- package/dist/features/tendency-samples.js.map +1 -0
- package/dist/features/thinking-log.d.ts +22 -0
- package/dist/features/thinking-log.d.ts.map +1 -0
- package/dist/features/thinking-log.js +70 -0
- package/dist/features/thinking-log.js.map +1 -0
- package/dist/features/xlsx.d.ts +28 -0
- package/dist/features/xlsx.d.ts.map +1 -0
- package/dist/features/xlsx.js +159 -0
- package/dist/features/xlsx.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +25 -3
- package/dist/session/session-manager.js.map +1 -1
- package/dist/transport/events.d.ts +12 -0
- package/dist/transport/events.d.ts.map +1 -1
- package/dist/transport/events.js +15 -2
- package/dist/transport/events.js.map +1 -1
- package/dist/transport/inbound.d.ts.map +1 -1
- package/dist/transport/inbound.js +344 -20
- package/dist/transport/inbound.js.map +1 -1
- package/dist/transport/outbound-buffer.d.ts.map +1 -1
- package/dist/transport/outbound-buffer.js +8 -0
- package/dist/transport/outbound-buffer.js.map +1 -1
- package/dist/transport/outbound.d.ts +12 -0
- package/dist/transport/outbound.d.ts.map +1 -1
- package/dist/transport/outbound.js +170 -5
- package/dist/transport/outbound.js.map +1 -1
- package/docs/USER-GUIDE.md +429 -0
- package/package.json +2 -1
- package/settings-host.js +154 -0
package/README_EN.md
CHANGED
|
@@ -4,6 +4,8 @@ An **enhanced fork** of the QQ Bot IM plugin for [deepseek-harness](https://gith
|
|
|
4
4
|
|
|
5
5
|
📦 Repo: [gcry13067381632-jpg/dsh-qqbot](https://github.com/gcry13067381632-jpg/dsh-qqbot) (forked from [tencent-connect/dsh-qqbot](https://github.com/tencent-connect/dsh-qqbot))
|
|
6
6
|
|
|
7
|
+
> ⭐ **If it works well for you, please star the repo** — it is the only visible signal that someone is using it, and it keeps the project going.
|
|
8
|
+
|
|
7
9
|
[中文文档](./README.md) | English
|
|
8
10
|
|
|
9
11
|
## 🐋 This fork (enhanced edition)
|
|
@@ -408,6 +410,10 @@ export QQBOT_APPID="xxx" QQBOT_SECRET="xxx"
|
|
|
408
410
|
npx @deepseek-ai/dsh web --patch /path/to/dsh-qqbot/cordis.dev.yml
|
|
409
411
|
```
|
|
410
412
|
|
|
413
|
+
## Support
|
|
414
|
+
|
|
415
|
+
If this plugin saved you some tokens — or made your QQ bot feel alive — a **⭐ Star** is the most concrete way to say thanks. Bugs and feature requests: [Issues](https://github.com/gcry13067381632-jpg/dsh-qqbot/issues).
|
|
416
|
+
|
|
411
417
|
## License
|
|
412
418
|
|
|
413
419
|
[MIT](./LICENSE)
|
package/client/qqbot-settings.js
CHANGED
|
@@ -183,7 +183,10 @@ window.__ModuleLoader__.load({
|
|
|
183
183
|
h('label', { style: labelStyle }, ' 按固定写法匹配文字(高手用,留空=不用): ',
|
|
184
184
|
h('input', { className: 'qqs-inp', style: wideStyle, value: sv(r.conditions.contentRegex), placeholder: '如 ^早安', onChange: (e) => condUpd({ contentRegex: e.target.value }) })),
|
|
185
185
|
h('label', { style: labelStyle }, ' 出现这些词就触发(逗号分隔): ',
|
|
186
|
-
|
|
186
|
+
// ⚠️ 2026-09-14 修(主人:"我输入不了逗号"):原来是**受控** input(value = 数组 join(',')),
|
|
187
|
+
// 输入"看不到,"时末尾空项被 filter(Boolean) 丢掉、文本又被重建成没逗号的样子
|
|
188
|
+
// → 逗号永远打不进去。改成**非受控**(defaultValue):输入过程自由,onChange 只把解析结果写进配置。
|
|
189
|
+
h('input', { className: 'qqs-inp', style: wideStyle, defaultValue: (r.conditions.contentKeywords || []).join(','), onChange: (e) => condUpd({ contentKeywords: e.target.value.split(/[,,]/).map(function (s) { return s.trim() }).filter(Boolean) }) })),
|
|
187
190
|
h('label', { style: labelStyle }, ' 多个条件怎么算: ',
|
|
188
191
|
h('select', { style: inputStyle, value: r.conditions.matchScope || 'any', onChange: (e) => condUpd({ matchScope: e.target.value }) },
|
|
189
192
|
h('option', { value: 'any' }, '满足一个就行'),
|
|
@@ -2005,11 +2008,32 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
2005
2008
|
var col = it.worth ? '#2f9e44' : '#c23131'
|
|
2006
2009
|
out += '<div style="padding:4px 8px;border-bottom:1px solid #f5f5f5;font-size:12px">'
|
|
2007
2010
|
+ '<span style="color:#999">' + hh + '</span> '
|
|
2008
|
-
+ '<b style="color:' + col + '">' + (typeof it.score === 'number' ? it.score.toFixed(2) : (it.img ? '📷' : '-')) + '</b>'
|
|
2011
|
+
+ '<b style="color:' + col + '">' + (typeof it.score === 'number' ? Number(typeof it.scoreAdj === 'number' ? it.scoreAdj : it.score).toFixed(2) : (it.img ? '📷' : '-')) + '</b>'
|
|
2012
|
+
// 好感偏移标记(2026-09-14:**偏移加在分数上,门槛不动**)—— 文案给普通用户看,写成"基础评分+好感偏移"
|
|
2013
|
+
+ (it.aggWeighted && Array.isArray(it.aggParts) && it.aggParts.length
|
|
2014
|
+
// 聚合加权(2026-09-14 主人定):判定分是**加权平均**,这里把每一票摊开给人看,
|
|
2015
|
+
// 不然"1.06 是哪来的"没法核对。格式:昵称 价值分×权重(好感占比)
|
|
2016
|
+
? ' <span style="color:#e8590c;cursor:help" title="' + esc(
|
|
2017
|
+
'综合分 ' + Number(it.scoreAdj).toFixed(2) + ' = 窗口里 ' + it.aggParts.length + ' 条消息的加权平均'
|
|
2018
|
+
+ '(价值越高、好感越高,权重越大)\n'
|
|
2019
|
+
+ it.aggParts.map(function (p, i) {
|
|
2020
|
+
return (i + 1) + '. ' + p.n + ':价值 ' + Number(p.s).toFixed(2)
|
|
2021
|
+
+ ' × 权重 ' + Number(p.w).toFixed(2)
|
|
2022
|
+
+ '(好感占比 ' + (Number(p.r) > 0 ? '+' : '') + Number(p.r).toFixed(2) + ')'
|
|
2023
|
+
}).join('\n')
|
|
2024
|
+
+ '\n记在谁头上:权重最大的那位(当前是 ' + String(it.sender || '?') + ')'
|
|
2025
|
+
) + '">[合' + it.aggParts.length + '·加权]</span>'
|
|
2026
|
+
: (it.attOff && typeof it.score === 'number'
|
|
2027
|
+
? ' <span style="color:#e8590c;cursor:help" title="' + esc('基础评分 ' + Number(it.score).toFixed(2) + ' + 好感偏移 ' + (it.attOff > 0 ? '+' : '−') + Math.abs(Number(it.attOff)).toFixed(2) + ' = 有效分 ' + Number(it.score + it.attOff).toFixed(2) + '(好感档位:' + (it.attTier || '?') + ')') + '">[' + (it.attOff > 0 ? '+' : '−') + Math.abs(Number(it.attOff)).toFixed(2) + ']</span>'
|
|
2028
|
+
: ''))
|
|
2029
|
+
// 门槛(用户设置的基础评分门槛,好感度不改它)
|
|
2030
|
+
+ (typeof it.min === 'number'
|
|
2031
|
+
? ' <span style="color:#888" title="' + esc('基础评分门槛 ' + Number(it.min).toFixed(2) + '(你设置的判定线);好感度以「好感偏移」加减在消息分数上(亲近加分、冷淡减分),不改动门槛') + '">门槛' + Number(it.min).toFixed(2) + '</span>'
|
|
2032
|
+
: '')
|
|
2009
2033
|
+ (it.img ? ' <span style="color:#999" title="图片消息: ' + (it.lib ? '已在库 → 借它的标签当文字评分' : '无文字, 不可评分(默认不拦)') + '">' + (it.lib ? '[库内]' : '[无文字]') + '</span>' : '')
|
|
2010
|
-
+ (it.mention ? ' <span style="color:#1c7ed6" title="被@, 必回">[@]</span>' : '')
|
|
2034
|
+
+ (it.mention ? ' <span style="color:#1c7ed6" title="' + esc(it.mentionForced && !it.worth ? '被@ → 必回(分数没到门槛也回)' : '被@, 必回') + '">[@]</span>' : '')
|
|
2011
2035
|
+ (it.conf !== undefined && it.conf < 0.5 ? ' <span style="color:#999" title="低置信: 最近邻居相似度只有 ' + it.conf + ', 地图上没这类样本 —— 仅作补样本提示, 不再影响拦截">[?]</span>' : '')
|
|
2012
|
-
+ (it.agg ? ' <span style="color:#e8590c" title="聚合了 ' + it.agg + ' 条消息, 取其中最高分">[合' + it.agg + ']</span>' : '')
|
|
2036
|
+
+ (it.agg ? ' <span style="color:#e8590c" title="' + esc(it.aggWeighted ? ('聚合了 ' + it.agg + ' 条消息,**加权平均**综合判断(明细见左边的[合·加权]标记)') : ('聚合了 ' + it.agg + ' 条消息, 取其中最高分')) + '">[合' + it.agg + ']</span>' : '')
|
|
2013
2037
|
+ ' <span style="color:#555">' + esc(String(it.sender || '?')) + ':</span> '
|
|
2014
2038
|
+ esc(String(it.text || '').slice(0, 56))
|
|
2015
2039
|
+ '<div style="color:#bbb;font-size:11px;margin-left:14px">近邻: ' + esc((it.top || []).join(' · ')) + '</div>'
|
|
@@ -2020,6 +2044,89 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
2020
2044
|
return out
|
|
2021
2045
|
}
|
|
2022
2046
|
// 局部刷新评分列表(不调 paintBody → 不闪、不打断滚动、不丢输入框焦点)
|
|
2047
|
+
// 熟识度 + 好感度(2026-09-14 主人"直接推进"后分两列展示, 两个维度别混)
|
|
2048
|
+
// ── 分列 + 图标(2026-09-14 主人:"好感度的文字太多了, 改成有分列, 图标显示") ──
|
|
2049
|
+
// 列: # | 群友 | 熟识度 | 好感度 | 这次 | 活跃
|
|
2050
|
+
// 原来一行塞着"(内心亲近 +0.5 / 两好相凑 ×1.2)(来过2天/消息65/点名35/接话3 · 3分钟前)"两串中文,
|
|
2051
|
+
// 现在压成图标 + 数字,**全文退进 title 悬浮提示** —— 想细看还看得到, 只是不再堵眼睛。
|
|
2052
|
+
var AFF_TIER_ICON = { '陌生人': '👤', '眼熟': '👋', '熟人': '🤝' };
|
|
2053
|
+
var AFF_TIER_COLOR = { '陌生人': '#868e96', '眼熟': '#1c7ed6', '熟人': '#2f9e44' };
|
|
2054
|
+
var ATT_TIER_ICON = { '很亲近': '💖', '亲近': '💗', '中立': '😐', '冷淡': '🧊', '疏远': '❄️' };
|
|
2055
|
+
var ATT_TIER_COLOR = { '很亲近': '#e64980', '亲近': '#f06595', '中立': '#868e96', '冷淡': '#4c6ef5', '疏远': '#364fc7' };
|
|
2056
|
+
var DELTA_ICON = {
|
|
2057
|
+
'near': ['💗', '内心亲近 +0.5'],
|
|
2058
|
+
'refuse': ['🚫', '内心拒绝 −0.5'],
|
|
2059
|
+
'yield': ['🕊️', '让步(心里不肯仍照顾) +0.8'],
|
|
2060
|
+
'harsh': ['🥶', '又烦又冷(重罚) −0.5'],
|
|
2061
|
+
'amp:cold-praise': ['❗×1.5', '对方夸她还掉好感(不领情) ×1.5'],
|
|
2062
|
+
'amp:flatter': ['🎁×1.5', '对方冷她还涨好感(讨好) ×1.5'],
|
|
2063
|
+
'amp:both-warm': ['➕×1.2', '两好相凑 ×1.2'],
|
|
2064
|
+
'amp:both-cold': ['❄️×1.2', '两冷相叠 ×1.2']
|
|
2065
|
+
};
|
|
2066
|
+
var AFF_COLS = { n: 20, aff: 76, att: 92, why: 104, act: 150 };
|
|
2067
|
+
function affinityHeadHtml() {
|
|
2068
|
+
var s = function (w, t) { return '<span style="flex:0 0 ' + w + 'px">' + t + '</span>' }
|
|
2069
|
+
return '<div style="display:flex;gap:8px;align-items:center;font-size:11px;color:#adb5bd;padding-bottom:3px;border-bottom:1px solid #e9ecef">'
|
|
2070
|
+
+ '<span style="flex:0 0 ' + AFF_COLS.n + 'px;text-align:right">#</span>'
|
|
2071
|
+
+ '<span style="flex:1 1 auto;min-width:0">群友</span>'
|
|
2072
|
+
+ s(AFF_COLS.aff, '熟识度') + s(AFF_COLS.att, '好感度') + s(AFF_COLS.why, '这次') + s(AFF_COLS.act, '活跃')
|
|
2073
|
+
+ '</div>'
|
|
2074
|
+
}
|
|
2075
|
+
function affinityRowHtml(i, x, a) {
|
|
2076
|
+
var age = x.lastAt ? Math.round((Date.now() - x.lastAt) / 60000) : 0
|
|
2077
|
+
var tier = x.tier || '陌生人'
|
|
2078
|
+
var color = AFF_TIER_COLOR[tier] || '#868e96'
|
|
2079
|
+
var affTip = '熟识度 ' + (x.score != null ? x.score : '-') + '/100 · 档位: ' + tier
|
|
2080
|
+
+ '(来过 ' + (x.reviews || 0) + ' 天 / 消息 ' + x.msgs + ' / 被点名 ' + x.mentions + ' / 接话 ' + x.replies + ')'
|
|
2081
|
+
var affCell = '<span style="flex:0 0 ' + AFF_COLS.aff + 'px" title="' + esc(affTip) + '">'
|
|
2082
|
+
+ '<b style="color:' + color + '">' + (x.score != null ? x.score : '-') + '</b> <span>' + (AFF_TIER_ICON[tier] || '👤') + '</span></span>'
|
|
2083
|
+
var at = (a && a.tier) || ''
|
|
2084
|
+
var aNum = a ? (a.a >= 0 ? '+' : '') + Number(a.a).toFixed(2) : '—'
|
|
2085
|
+
var aColor = !a ? '#adb5bd' : (a.a > 0.0001 ? '#2f9e44' : (a.a < -0.0001 ? '#e03131' : '#868e96'))
|
|
2086
|
+
var aTip = a
|
|
2087
|
+
? ('好感度 ' + a.a + '(占范围 ' + (a.ratio != null ? (a.ratio * 100).toFixed(1) + '%' : '?') + ') · 档位: ' + (at || '中立')
|
|
2088
|
+
+ (a.why ? ';最近一次: ' + a.why : ''))
|
|
2089
|
+
: '还没有好感度记录(她还没对他形成态度)'
|
|
2090
|
+
var aCell = '<span style="flex:0 0 ' + AFF_COLS.att + 'px" title="' + esc(aTip) + '">'
|
|
2091
|
+
+ '<b style="color:' + aColor + '">' + aNum + '</b> <span style="color:' + (ATT_TIER_COLOR[at] || '#adb5bd') + '">' + (ATT_TIER_ICON[at] || '⚪') + '</span></span>'
|
|
2092
|
+
var codes = (a && a.codes && a.codes.length) ? a.codes : []
|
|
2093
|
+
var marks = codes.map(function (c) {
|
|
2094
|
+
var d = DELTA_ICON[c]
|
|
2095
|
+
return d ? '<span title="' + esc(d[1]) + '">' + d[0] + '</span>' : ''
|
|
2096
|
+
}).filter(Boolean)
|
|
2097
|
+
var whyCell = '<span style="flex:0 0 ' + AFF_COLS.why + 'px" title="' + esc((a && a.why) || '暂无拆解') + '">'
|
|
2098
|
+
+ (marks.length ? marks.join(' ') : '<span style="color:#ced4da">—</span>') + '</span>'
|
|
2099
|
+
var actTip = '来过 ' + (x.reviews || 0) + ' 天 · 消息 ' + x.msgs + ' · 被点名 ' + x.mentions + ' 次 · 接她的话 ' + x.replies + ' 次'
|
|
2100
|
+
+ (age < 120 ? ' · 最近 ' + age + ' 分钟前' : '')
|
|
2101
|
+
var actCell = '<span style="flex:0 0 ' + AFF_COLS.act + 'px;color:#adb5bd;font-size:11px" title="' + esc(actTip) + '">'
|
|
2102
|
+
+ '📅' + (x.reviews || 0) + ' 💬' + x.msgs + ' 📣' + x.mentions + ' ↩' + x.replies
|
|
2103
|
+
+ (age < 120 ? ' <span style="color:#868e96">🕐' + age + '分</span>' : '') + '</span>'
|
|
2104
|
+
return '<div style="display:flex;gap:8px;align-items:center;font-size:12px;color:#495057;padding:2px 0;border-bottom:1px dashed #f1f3f5">'
|
|
2105
|
+
+ '<span style="flex:0 0 ' + AFF_COLS.n + 'px;text-align:right;color:#adb5bd">' + (i + 1) + '</span>'
|
|
2106
|
+
+ '<span style="flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#343a40" title="' + esc(x.key) + '">' + esc(x.name || x.key) + '</span>'
|
|
2107
|
+
+ affCell + aCell + whyCell + actCell + '</div>'
|
|
2108
|
+
}
|
|
2109
|
+
function loadAffinity() {
|
|
2110
|
+
if (state.tab !== 'session') return
|
|
2111
|
+
var box = panel ? panel.querySelector('#dk-aff-box') : null
|
|
2112
|
+
var q = state.ns ? '?' + outNsQ() : ''
|
|
2113
|
+
Promise.all([
|
|
2114
|
+
fetch('/api/qqbot-settings/affinity' + q).then(function (r) { return r.json() }).catch(function () { return {} }),
|
|
2115
|
+
fetch('/api/qqbot-settings/attitude' + q).then(function (r) { return r.json() }).catch(function () { return {} })
|
|
2116
|
+
]).then(function (rs) {
|
|
2117
|
+
var aff = (rs[0] && rs[0].items) || []
|
|
2118
|
+
var att = (rs[1] && rs[1].items) || []
|
|
2119
|
+
var attBy = {}
|
|
2120
|
+
att.forEach(function (x) { attBy[x.key] = x })
|
|
2121
|
+
if (aff.length === 0) { state.affHtml = '还没有数据 —— 群里聊几句就出来了'; }
|
|
2122
|
+
else {
|
|
2123
|
+
state.affHtml = affinityHeadHtml() + aff.map(function (x, i) {
|
|
2124
|
+
return affinityRowHtml(i, x, attBy[x.key])
|
|
2125
|
+
}).join('')
|
|
2126
|
+
}
|
|
2127
|
+
if (box) box.innerHTML = state.affHtml
|
|
2128
|
+
}).catch(function () { if (box) box.textContent = '熟识度/好感度读取失败' })
|
|
2129
|
+
}
|
|
2023
2130
|
function refreshScoreList() {
|
|
2024
2131
|
if (state.tab !== 'session') return
|
|
2025
2132
|
fetch('/api/qqbot-settings/value-scores' + scoreQuery())
|
|
@@ -2106,6 +2213,7 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
2106
2213
|
.then(function (r) { return r.json() })
|
|
2107
2214
|
.then(function (d) { state.valueScores = d && d.ok ? d : { items: [], total: 0 }; paintBody() })
|
|
2108
2215
|
.catch(function () { state.valueScores = { items: [], total: 0 }; paintBody() })
|
|
2216
|
+
.then(function () { loadAffinity() })
|
|
2109
2217
|
.then(function () { state.lmBusy = false })
|
|
2110
2218
|
// 每 10 秒局部刷新一次评分列表(只换 #dk-vs-box 内容, 不整块重绘 → 不闪)
|
|
2111
2219
|
if (state.lmTimer) clearInterval(state.lmTimer)
|
|
@@ -2222,6 +2330,35 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
2222
2330
|
vsToggle.textContent = state.vsOpen ? '收起' : '展开编辑'
|
|
2223
2331
|
if (state.vsOpen) loadValueSamplesEditor()
|
|
2224
2332
|
}
|
|
2333
|
+
// 📊 最近评分 折叠(2026-09-14 主人要求:这两块默认收起,别占地方)
|
|
2334
|
+
var vsViewToggle = panel.querySelector('#dk-vs-view-toggle')
|
|
2335
|
+
if (vsViewToggle) vsViewToggle.onclick = function () {
|
|
2336
|
+
state.vsViewOpen = !state.vsViewOpen
|
|
2337
|
+
var b = panel.querySelector('#dk-vs-box')
|
|
2338
|
+
if (b) b.style.display = state.vsViewOpen ? 'block' : 'none'
|
|
2339
|
+
vsViewToggle.textContent = state.vsViewOpen ? '收起' : '展开'
|
|
2340
|
+
}
|
|
2341
|
+
// 🧠 熟识度 / 💗 好感度 折叠
|
|
2342
|
+
var affToggle = panel.querySelector('#dk-aff-toggle')
|
|
2343
|
+
if (affToggle) affToggle.onclick = function () {
|
|
2344
|
+
state.affOpen = !state.affOpen
|
|
2345
|
+
var b = panel.querySelector('#dk-aff-box')
|
|
2346
|
+
if (b) b.style.display = state.affOpen ? 'block' : 'none'
|
|
2347
|
+
affToggle.textContent = state.affOpen ? '收起' : '展开'
|
|
2348
|
+
}
|
|
2349
|
+
// 📊 导出 Excel(2026-09-14 主人要"一键导出分享")—— 直接走 GET,让浏览器下载
|
|
2350
|
+
var affExport = panel.querySelector('#dk-aff-export')
|
|
2351
|
+
if (affExport) affExport.onclick = function () {
|
|
2352
|
+
var url = '/api/qqbot-settings/export.xlsx' + (state.ns ? '?' + outNsQ() : '')
|
|
2353
|
+
var a = document.createElement('a')
|
|
2354
|
+
a.href = url
|
|
2355
|
+
a.download = ''
|
|
2356
|
+
document.body.appendChild(a)
|
|
2357
|
+
a.click()
|
|
2358
|
+
document.body.removeChild(a)
|
|
2359
|
+
affExport.textContent = '📊 已导出'
|
|
2360
|
+
setTimeout(function () { affExport.textContent = '📊 导出 Excel' }, 2500)
|
|
2361
|
+
}
|
|
2225
2362
|
var vsSaveBtn = panel.querySelector('#dk-vs-save')
|
|
2226
2363
|
if (vsSaveBtn) vsSaveBtn.onclick = function () { saveValueSamplesEditor() }
|
|
2227
2364
|
// 🤖 让ai写(2026-09-13 主人要的): 模拟一条用户消息唤醒她 → 她自己读聊天记录写样例
|
|
@@ -3415,11 +3552,21 @@ var QQS_CSS = ".qqs-btn{font:inherit;color:#333;background:linear-gradient(180de
|
|
|
3415
3552
|
// (只换这个 div 的内容, 不整块 paintBody) → 既不闪, 又能自动更新。
|
|
3416
3553
|
body += '<div class="dk-row" style="font-weight:700;font-size:13px;margin:10px 0 2px;gap:8px;align-items:center;flex-wrap:wrap">'
|
|
3417
3554
|
+ '<span>📊 最近评分(观察期)</span>'
|
|
3555
|
+
+ '<button class="dk-btn" id="dk-vs-view-toggle" style="font-size:12px;padding:1px 8px">' + (state.vsViewOpen ? '收起' : '展开') + '</button>'
|
|
3418
3556
|
+ '<span class="dk-msg" style="font-size:11px;color:#7c6bd6" id="dk-vs-scope">' + esc(curHitLabel()) + '</span>'
|
|
3419
3557
|
+ '<button class="dk-btn" id="dk-vs-reload" style="font-size:12px;padding:1px 8px">🔄 刷新</button>'
|
|
3420
3558
|
+ '<span class="dk-msg" style="font-size:11px;color:#999">每 10 秒自动更新</span>'
|
|
3421
3559
|
+ '</div>'
|
|
3422
|
-
body += '<div id="dk-vs-box">' + scoreListHtml(state.valueScores) + '</div>'
|
|
3560
|
+
body += '<div id="dk-vs-box" style="display:' + (state.vsViewOpen ? 'block' : 'none') + '">' + scoreListHtml(state.valueScores) + '</div>'
|
|
3561
|
+
// 💗 熟识度(她记你多牢) + 好感度(她对你什么态度) —— 两个维度分两列, 别混
|
|
3562
|
+
// 熟识度: {dataRoot}/.qqbot/affinity.json | 好感度: {dataRoot}/.qqbot/attitude.json
|
|
3563
|
+
body += '<div class="dk-row" style="font-weight:700;font-size:13px;margin:10px 0 2px;gap:8px;align-items:center;flex-wrap:wrap">'
|
|
3564
|
+
+ '<span>🧠 熟识度(记你多牢) | 💗 好感度(对你什么态度)(观察期·只统计)</span>'
|
|
3565
|
+
+ '<button class="dk-btn" id="dk-aff-export" style="font-size:12px;padding:1px 8px" title="导成 Excel(三张表:熟识度 / 好感度 / 口径说明),可以直接转发给别人">📊 导出 Excel</button>'
|
|
3566
|
+
+ '<button class="dk-btn" id="dk-aff-toggle" style="font-size:12px;padding:1px 8px">' + (state.affOpen ? '收起' : '展开') + '</button>'
|
|
3567
|
+
+ '</div>'
|
|
3568
|
+
// 分列 + 图标(2026-09-14): 注意这里**不能**再加 white-space:pre-wrap —— 现在是逐行的 flex 布局 HTML
|
|
3569
|
+
body += '<div class="dk-msg" id="dk-aff-box" style="font-size:12px;color:#666;display:' + (state.affOpen ? 'block' : 'none') + '">' + (state.affHtml || '加载中…') + '</div>'
|
|
3423
3570
|
// ✍️ 样例库编辑(2026-09-13 主人问"哪里写样本"): 直接改 jsonl, 保存后下一条消息生效
|
|
3424
3571
|
body += '<div class="dk-row" style="font-weight:700;font-size:13px;margin:12px 0 2px;gap:8px;align-items:center;flex-wrap:wrap">'
|
|
3425
3572
|
+ '<span>✍️ 样例库(决定她的开口标准)</span>'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"channel-tools.d.ts","sourceRoot":"","sources":["../src/channel-tools.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AA2ElE,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,eAAO,MAAM,IAAI,wBAAwB,CAAC;AAC1C,eAAO,MAAM,MAAM,UAAY,CAAC;AAShC,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAI/D;AAkFD,mDAAmD;AACnD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAk1DvD;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,GAAG,KAAK,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCvC"}
|
package/dist/channel-tools.js
CHANGED
|
@@ -22,6 +22,8 @@ import * as broadcastQueue from './features/broadcast.js';
|
|
|
22
22
|
import { wakeSessionAgent, safeAppendUserMessage } from './features/group-hub.js';
|
|
23
23
|
import { managersOf, findManagerByPeer, findManagerBySessionId } from './features/session-registry.js';
|
|
24
24
|
import { handleInbound } from './transport/inbound.js';
|
|
25
|
+
import { getQuote } from './features/quote-cache.js';
|
|
26
|
+
import { appendMemoLine, deleteMemo, listMemos, readMemo } from './features/people-memo.js';
|
|
25
27
|
/** 诊断日志路径: 默认关闭; 需要排查时设环境变量 QQBOT_DIAG_FILE 指向日志文件 */
|
|
26
28
|
const DIAG_FILE = process.env.QQBOT_DIAG_FILE || '';
|
|
27
29
|
/** 大文件异步发送阈值: 本地文件 >= 5MB 时走后台任务(SDK 分片上传耗时, 避免阻塞 LLM 回合) */
|
|
@@ -770,7 +772,8 @@ export async function apply(ctx) {
|
|
|
770
772
|
// ⚠️ 描述只讲两个分支的效果, 不给场景规则——选哪个由模型按当前对话氛围自行权衡(写死规则会导致静默率过高)。
|
|
771
773
|
const replyGateTool = defineTool({
|
|
772
774
|
name: 'reply_gate',
|
|
773
|
-
description: '回复闸门: 开始回复前先调用它, 决定本回合开口还是静默。传 reply:true → 正常继续, 按当前对话决定说什么; 传 reply:false → 本回合立即静默终止, 你不会再输出任何内容(这条消息就像没发生过)。选 true 还是 false 由你根据当前对话自己判断: 觉得该回应就 true, 觉得安静待着更好就 false。'
|
|
775
|
+
description: '回复闸门: 开始回复前先调用它, 决定本回合开口还是静默。传 reply:true → 正常继续, 按当前对话决定说什么; 传 reply:false → 本回合立即静默终止, 你不会再输出任何内容(这条消息就像没发生过)。选 true 还是 false 由你根据当前对话自己判断: 觉得该回应就 true, 觉得安静待着更好就 false。' +
|
|
776
|
+
'\n⚠️ 传 reply:false 后**不要再调用任何工具**(包括记笔记、记喜好、发表情包): 本回合已经关闭, 那些调用会被直接中止 —— 想记录的话, 留到下一次开口的回合里做。',
|
|
774
777
|
parameters: {
|
|
775
778
|
reply: { type: 'boolean', required: true, description: 'true=正常开口回复; false=静默终止本回合(不再输出任何内容)' },
|
|
776
779
|
reason: { type: 'string', required: true, description: '简短判定理由, 如"未@不插嘴" / "被@需回复" / "私聊"' },
|
|
@@ -2018,11 +2021,113 @@ export async function apply(ctx) {
|
|
|
2018
2021
|
}
|
|
2019
2022
|
},
|
|
2020
2023
|
});
|
|
2024
|
+
// quote_view: 查看引用内容(2026-09-13 主人定, 省 token) ——
|
|
2025
|
+
// 引用原文超过 60 字时, 入站上下文只给**前 60 字**, 全文落在
|
|
2026
|
+
// `{dataRoot}/.qqbot/quote-cache.json`(**每会话最多 10 条**), 需要时用本工具取完整原文。
|
|
2027
|
+
// ⚠️ 工具描述**刻意写四个字**(主人要求): 描述进 system prompt, 越短越省。
|
|
2028
|
+
const quoteViewTool = defineTool({
|
|
2029
|
+
name: 'quote_view',
|
|
2030
|
+
description: '查看引用内容',
|
|
2031
|
+
parameters: {
|
|
2032
|
+
idx: { type: 'integer', description: '引用编号(默认最新一条)' },
|
|
2033
|
+
},
|
|
2034
|
+
output: {
|
|
2035
|
+
schema: {
|
|
2036
|
+
type: 'object', additionalProperties: false,
|
|
2037
|
+
properties: {
|
|
2038
|
+
ok: { type: 'boolean', required: true },
|
|
2039
|
+
msg: { type: 'string', required: true },
|
|
2040
|
+
text: { type: 'string' },
|
|
2041
|
+
},
|
|
2042
|
+
},
|
|
2043
|
+
render: (_a, v) => [
|
|
2044
|
+
{ type: 'text', text: v.ok ? (v.text || '') : `查询失败: ${v.msg}` },
|
|
2045
|
+
],
|
|
2046
|
+
},
|
|
2047
|
+
async execute(args, exec) {
|
|
2048
|
+
try {
|
|
2049
|
+
const s = findSessionRec(channelOf(exec), exec);
|
|
2050
|
+
const mgr = s?.ch?.manager;
|
|
2051
|
+
const rec = s?.rec;
|
|
2052
|
+
const dataRoot = String(mgr?.dataRoot || '');
|
|
2053
|
+
if (!dataRoot || !rec?.scope || !rec?.peerId)
|
|
2054
|
+
return { ok: false, msg: '未找到当前 QQ 会话' };
|
|
2055
|
+
const rawIdx = args.idx;
|
|
2056
|
+
const idx = typeof rawIdx === 'number' && Number.isFinite(rawIdx) ? rawIdx : undefined;
|
|
2057
|
+
const e = getQuote(dataRoot, `${rec.scope}:${rec.peerId}`, idx);
|
|
2058
|
+
if (!e)
|
|
2059
|
+
return { ok: false, msg: '没有缓存的引用(短引用不缓存; 或这条引用是更早的、已被 10 条上限挤掉)' };
|
|
2060
|
+
return { ok: true, msg: `引用#${e.id}`, text: `引用#${e.id}${e.sender ? `(${e.sender})` : ''}: ${e.text}` };
|
|
2061
|
+
}
|
|
2062
|
+
catch (err) {
|
|
2063
|
+
return { ok: false, msg: err instanceof Error ? err.message : String(err) };
|
|
2064
|
+
}
|
|
2065
|
+
},
|
|
2066
|
+
});
|
|
2067
|
+
// people_memo: 群友小传(2026-09-13 主人定)—— 文字版好感度的本体。
|
|
2068
|
+
// 记一行(同一人一天最多一条) / 查看 / 删除(群里一句"别记人家"就删)。描述刻意写短。
|
|
2069
|
+
const peopleMemoTool = defineTool({
|
|
2070
|
+
name: 'people_memo',
|
|
2071
|
+
description: '群友小传: 记一行/查看/删除。当群友说出自己的喜好/经历/身份等"旁人能复述的事实"时, 顺手记一条(同一人一天最多 3 条); 想归到八栏就带前缀, 如"喜好:最近在玩XX"(不带前缀则进记事区); 只写事实, 不写评价与猜测',
|
|
2072
|
+
parameters: {
|
|
2073
|
+
who: { type: 'string', required: true, description: '群友昵称或 openid' },
|
|
2074
|
+
line: { type: 'string', description: '要记的一行; 不传=只看' },
|
|
2075
|
+
del: { type: 'boolean', description: 'true=删掉这个人的小传' },
|
|
2076
|
+
},
|
|
2077
|
+
output: {
|
|
2078
|
+
schema: {
|
|
2079
|
+
type: 'object', additionalProperties: false,
|
|
2080
|
+
properties: { ok: { type: 'boolean', required: true }, msg: { type: 'string', required: true } },
|
|
2081
|
+
},
|
|
2082
|
+
render: (_a, v) => [{ type: 'text', text: v.msg }],
|
|
2083
|
+
},
|
|
2084
|
+
async execute(args, exec) {
|
|
2085
|
+
try {
|
|
2086
|
+
const a = args;
|
|
2087
|
+
const s = findSessionRec(channelOf(exec), exec);
|
|
2088
|
+
const mgr = s?.ch?.manager;
|
|
2089
|
+
const rec = s?.rec;
|
|
2090
|
+
const dataRoot = String(mgr?.dataRoot || '');
|
|
2091
|
+
if (!dataRoot)
|
|
2092
|
+
return { ok: false, msg: '未找到当前会话的数据根' };
|
|
2093
|
+
// who → openid: 优先在群成员台账里按昵称找
|
|
2094
|
+
let key = String(a.who || '').trim();
|
|
2095
|
+
if (!/^[0-9A-Fa-f]{16,}$/.test(key) && rec?.scope === 'group' && rec.peerId) {
|
|
2096
|
+
try {
|
|
2097
|
+
const dataDir = join(dataRoot, '表情包');
|
|
2098
|
+
const mems = readGroupMembers(dataDir, rec.peerId) || [];
|
|
2099
|
+
const hit = mems.find((m) => m && m.name === key) || mems.find((m) => m && typeof m.name === 'string' && m.name.includes(key));
|
|
2100
|
+
if (hit && hit.mid)
|
|
2101
|
+
key = hit.mid;
|
|
2102
|
+
}
|
|
2103
|
+
catch { /* 用昵称兜底当 key */ }
|
|
2104
|
+
}
|
|
2105
|
+
if (a.del) {
|
|
2106
|
+
const r = deleteMemo(dataRoot, key);
|
|
2107
|
+
return { ok: r.ok, msg: r.ok ? '好, 已经把 ' + a.who + ' 那份小传删掉了, 以后也不记他' : r.msg };
|
|
2108
|
+
}
|
|
2109
|
+
if (a.line) {
|
|
2110
|
+
const r = appendMemoLine(dataRoot, key, a.line, { name: a.who, force: false });
|
|
2111
|
+
return { ok: r.ok, msg: r.ok ? '记下了: ' + a.line.slice(0, 60) : r.msg };
|
|
2112
|
+
}
|
|
2113
|
+
const cur = readMemo(dataRoot, key);
|
|
2114
|
+
if (cur)
|
|
2115
|
+
return { ok: true, msg: cur.slice(0, 1200) };
|
|
2116
|
+
const all = listMemos(dataRoot);
|
|
2117
|
+
return { ok: true, msg: '还没有 ' + a.who + ' 的小传。现有 ' + all.length + ' 份: ' + all.slice(0, 8).map((x) => x.key).join(', ') };
|
|
2118
|
+
}
|
|
2119
|
+
catch (e) {
|
|
2120
|
+
return { ok: false, msg: e instanceof Error ? e.message : String(e) };
|
|
2121
|
+
}
|
|
2122
|
+
},
|
|
2123
|
+
});
|
|
2021
2124
|
// 逐个注册并记录结果(便于线上定位是哪个工具失败)
|
|
2022
2125
|
const toolDefs = [
|
|
2023
2126
|
{ name: 'send_media', tool: sendMediaTool },
|
|
2024
2127
|
{ name: 'recall_message', tool: recallTool },
|
|
2025
2128
|
{ name: 'text_break', tool: textBreakTool },
|
|
2129
|
+
{ name: 'quote_view', tool: quoteViewTool },
|
|
2130
|
+
{ name: 'people_memo', tool: peopleMemoTool },
|
|
2026
2131
|
{ name: 'tools_reload', tool: toolsReloadTool },
|
|
2027
2132
|
{ name: 'reply_gate', tool: replyGateTool },
|
|
2028
2133
|
{ name: 'outbound_mode', tool: outboundModeTool },
|