@fengye_nobug/dsh-qq-bot 0.0.0-stage → 0.2.4
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/CHANGELOG.md +297 -0
- package/DESIGN.md +184 -0
- package/LICENSE +26 -0
- package/README.md +624 -2
- package/assets/market-cover.jpg +0 -0
- package/client/client.js +1203 -0
- package/cordis.patch.yml +68 -0
- package/faces.js +285 -0
- package/index.js +4256 -0
- package/napcat.js +485 -0
- package/onebot.js +388 -0
- package/package.json +73 -4
- package/screenshots.json +3 -0
- package/stickers.js +237 -0
- package/updates.js +272 -0
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# dsh-qq-bot bundle patch: one insert row carrying the whole plugin config.
|
|
2
|
+
# Cordis 插件 id 仍是 qq-bridge(与上游兼容)。显示名是 dsh-qq-bot。
|
|
3
|
+
# 用户在 profile 的 cordis.patch.yml 里按 id 覆盖这一行;patch 会替换整份 config,
|
|
4
|
+
# 所以要保留的键请全部重述。
|
|
5
|
+
- insert:
|
|
6
|
+
- id: qq-bridge
|
|
7
|
+
name: dsh-qq-bot
|
|
8
|
+
config:
|
|
9
|
+
# 固定目标会话:QQ 的一切都注入这一个会话(保留其上下文)。
|
|
10
|
+
# 留空表示不驱动任何会话。
|
|
11
|
+
targetSessionId: ''
|
|
12
|
+
# 可选:resume 目标会话时挂载的 agent preset(留空则沿用会话 header 里记录的)。
|
|
13
|
+
agentPreset: ''
|
|
14
|
+
# 状态文件(最近聊天缓冲 + 唤醒日志 + 出站目的地)。留空 = $DSH_HOME/qq-bridge-state.json
|
|
15
|
+
statePath: ''
|
|
16
|
+
# 注册 qq_bridge_status / qq_bridge_send / qq_bridge_transport / qq_bridge_simulate
|
|
17
|
+
# 四个调试工具(排障用,正式环境可关)。
|
|
18
|
+
debugTools: true
|
|
19
|
+
|
|
20
|
+
# ── M2:OneBot 传输(NapCat / go-cqhttp)─────────────────────────────
|
|
21
|
+
# NapCat 的“正向 WebSocket”地址;**留空 = 完全不启用传输**(默认安全)。
|
|
22
|
+
onebotUrl: ''
|
|
23
|
+
# 存放 access_token 的**凭据引用名**(就是一个环境变量名),不要明文写 token。
|
|
24
|
+
# 它会经 ctx.credentials 解析(进程环境 → 凭据存储 → .env),每次建连重新解析。
|
|
25
|
+
# 设置方式见 README“配置 access_token”。
|
|
26
|
+
accessTokenEnv: ''
|
|
27
|
+
# 机器人自己的 QQ 号;留空则连上后自动调 get_login_info 获取。
|
|
28
|
+
selfId: ''
|
|
29
|
+
# 回复时引用触发的那条消息([CQ:reply])。
|
|
30
|
+
replyWithQuote: false
|
|
31
|
+
# 群里只有被 @ 才处理(比概率唤醒更严的一道闸门)。
|
|
32
|
+
atOnlyInGroup: false
|
|
33
|
+
# 出站前去掉 Markdown 标记(QQ 不渲染,留着只是噪音)。
|
|
34
|
+
stripMarkdown: true
|
|
35
|
+
# 多久没有任何 WS 流量就判定连接已死并重连。
|
|
36
|
+
heartbeatTimeoutMs: 90000
|
|
37
|
+
|
|
38
|
+
# ── M5:NapCat 自助托管(“一键包”体验,但不真打包)─────────────────────
|
|
39
|
+
# 注意:这些只是**参数**。插件加载时绝不会去下载或执行任何东西,
|
|
40
|
+
# 一切动作都要靠显式调用 qq_bridge_napcat 工具触发。
|
|
41
|
+
# NapCat 安装目录;留空 = $DSH_HOME/napcat
|
|
42
|
+
napcatInstallDir: ''
|
|
43
|
+
# 对外可打开的 NapCat WebUI。留空 = 读 webui.json(通常是 127.0.0.1,只在跑 NapCat 的机器上打得开)。
|
|
44
|
+
napcatWebuiUrl: ''
|
|
45
|
+
# 要装的版本 tag(如 v4.18.28);留空 = 最新
|
|
46
|
+
napcatVersion: ''
|
|
47
|
+
# 下载用的 HTTP 代理。**本机直连 GitHub 不通**,走代理才行,例如
|
|
48
|
+
# http://127.0.0.1:7890;留空 = 读 HTTPS_PROXY/HTTP_PROXY/ALL_PROXY 环境变量。
|
|
49
|
+
downloadProxy: ''
|
|
50
|
+
# 写进 NapCat 配置、并用来连的正向 WS 端口
|
|
51
|
+
onebotPort: 3001
|
|
52
|
+
# 快速登录用的 QQ 号(需先用 QQ 手动成功登录过一次)
|
|
53
|
+
qqNumber: ''
|
|
54
|
+
|
|
55
|
+
# 会话怎么分:single = 所有群共用 targetSessionId;perGroup = 每个群/私聊各一条。
|
|
56
|
+
sessionScope: single
|
|
57
|
+
# 每个会话最多保留的最近轮数。0 = 不限制。
|
|
58
|
+
maxRecentTurns: 40
|
|
59
|
+
# ↓ 以下几项是 settings 命名空间 qq-bridge 的默认值(设置界面可覆盖)
|
|
60
|
+
nicknames: ['244', '小助手']
|
|
61
|
+
# 非昵称消息的唤醒概率(5%)
|
|
62
|
+
wakeProbability: 0.05
|
|
63
|
+
# ⚠️ 白名单 fail-closed:留空表示任何来源都不能唤醒会话。
|
|
64
|
+
# 正式使用时填入实际的 QQ openid。
|
|
65
|
+
whitelist: []
|
|
66
|
+
groupWhitelist: []
|
|
67
|
+
# 启动时向 GitHub 检查一次有没有新版本。默认关。只写日志,不自动安装。
|
|
68
|
+
checkUpdateOnStart: false
|
package/faces.js
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// faces.js — 出站把「[笑哭]」这类表情名拆成 OneBot face 段。
|
|
2
|
+
//
|
|
3
|
+
// id 对齐 OneBot / CoolQ 的 face 表,以及 QQ 客户端里的系统表情:
|
|
4
|
+
// 笑哭 = 182,挥手 = 129。对不上的方括号保持文字,不猜。
|
|
5
|
+
// 纯数据 + 纯函数,方便测试;发送仍由 sendToQQ 负责。
|
|
6
|
+
|
|
7
|
+
/** @type {Array<[string, number]>} */
|
|
8
|
+
const FACE_ENTRIES = [
|
|
9
|
+
// OneBot / CoolQ face id。笑哭、挥手与 QQ 系统表情编号一致。
|
|
10
|
+
['惊讶', 0],
|
|
11
|
+
['撇嘴', 1],
|
|
12
|
+
['色', 2],
|
|
13
|
+
['发呆', 3],
|
|
14
|
+
['得意', 4],
|
|
15
|
+
['流泪', 5],
|
|
16
|
+
['害羞', 6],
|
|
17
|
+
['闭嘴', 7],
|
|
18
|
+
['睡', 8],
|
|
19
|
+
['大哭', 9],
|
|
20
|
+
['尴尬', 10],
|
|
21
|
+
['发怒', 11],
|
|
22
|
+
['调皮', 12],
|
|
23
|
+
['呲牙', 13],
|
|
24
|
+
['微笑', 14],
|
|
25
|
+
['难过', 15],
|
|
26
|
+
['酷', 16],
|
|
27
|
+
['抓狂', 18],
|
|
28
|
+
['吐', 19],
|
|
29
|
+
['偷笑', 20],
|
|
30
|
+
['可爱', 21],
|
|
31
|
+
['白眼', 22],
|
|
32
|
+
['傲慢', 23],
|
|
33
|
+
['饥饿', 24],
|
|
34
|
+
['困', 25],
|
|
35
|
+
['惊恐', 26],
|
|
36
|
+
['流汗', 27],
|
|
37
|
+
['憨笑', 28],
|
|
38
|
+
['大兵', 29],
|
|
39
|
+
['奋斗', 30],
|
|
40
|
+
['咒骂', 31],
|
|
41
|
+
['疑问', 32],
|
|
42
|
+
['嘘', 33],
|
|
43
|
+
['晕', 34],
|
|
44
|
+
['折磨', 35],
|
|
45
|
+
['衰', 36],
|
|
46
|
+
['骷髅', 37],
|
|
47
|
+
['敲打', 38],
|
|
48
|
+
['再见', 39],
|
|
49
|
+
['发抖', 41],
|
|
50
|
+
['爱情', 42],
|
|
51
|
+
['跳跳', 43],
|
|
52
|
+
['猪头', 46],
|
|
53
|
+
['拥抱', 49],
|
|
54
|
+
['蛋糕', 53],
|
|
55
|
+
['闪电', 54],
|
|
56
|
+
['炸弹', 55],
|
|
57
|
+
['刀', 56],
|
|
58
|
+
['足球', 57],
|
|
59
|
+
['便便', 59],
|
|
60
|
+
['咖啡', 60],
|
|
61
|
+
['饭', 61],
|
|
62
|
+
['玫瑰', 63],
|
|
63
|
+
['凋谢', 64],
|
|
64
|
+
['爱心', 66],
|
|
65
|
+
['心碎', 67],
|
|
66
|
+
['礼物', 69],
|
|
67
|
+
['太阳', 74],
|
|
68
|
+
['月亮', 75],
|
|
69
|
+
['强', 76],
|
|
70
|
+
['弱', 77],
|
|
71
|
+
['握手', 78],
|
|
72
|
+
['胜利', 79],
|
|
73
|
+
['飞吻', 85],
|
|
74
|
+
['怄火', 86],
|
|
75
|
+
['西瓜', 89],
|
|
76
|
+
['冷汗', 96],
|
|
77
|
+
['擦汗', 97],
|
|
78
|
+
['抠鼻', 98],
|
|
79
|
+
['鼓掌', 99],
|
|
80
|
+
['糗大了', 100],
|
|
81
|
+
['坏笑', 101],
|
|
82
|
+
['左哼哼', 102],
|
|
83
|
+
['右哼哼', 103],
|
|
84
|
+
['哈欠', 104],
|
|
85
|
+
['鄙视', 105],
|
|
86
|
+
['委屈', 106],
|
|
87
|
+
['快哭了', 107],
|
|
88
|
+
['阴险', 108],
|
|
89
|
+
['亲亲', 109],
|
|
90
|
+
['吓', 110],
|
|
91
|
+
['可怜', 111],
|
|
92
|
+
['菜刀', 112],
|
|
93
|
+
['啤酒', 113],
|
|
94
|
+
['篮球', 114],
|
|
95
|
+
['乒乓', 115],
|
|
96
|
+
['示爱', 116],
|
|
97
|
+
['瓢虫', 117],
|
|
98
|
+
['抱拳', 118],
|
|
99
|
+
['勾引', 119],
|
|
100
|
+
['拳头', 120],
|
|
101
|
+
['差劲', 121],
|
|
102
|
+
['爱你', 122],
|
|
103
|
+
['NO', 123],
|
|
104
|
+
['OK', 124],
|
|
105
|
+
['转圈', 125],
|
|
106
|
+
['磕头', 126],
|
|
107
|
+
['回头', 127],
|
|
108
|
+
['跳绳', 128],
|
|
109
|
+
['挥手', 129],
|
|
110
|
+
['激动', 130],
|
|
111
|
+
['街舞', 131],
|
|
112
|
+
['献吻', 132],
|
|
113
|
+
['左太极', 133],
|
|
114
|
+
['右太极', 134],
|
|
115
|
+
['双喜', 136],
|
|
116
|
+
['鞭炮', 137],
|
|
117
|
+
['灯笼', 138],
|
|
118
|
+
['K歌', 140],
|
|
119
|
+
['喝彩', 144],
|
|
120
|
+
['祈祷', 145],
|
|
121
|
+
['爆筋', 146],
|
|
122
|
+
['棒棒糖', 147],
|
|
123
|
+
['喝奶', 148],
|
|
124
|
+
['飞机', 151],
|
|
125
|
+
['钞票', 158],
|
|
126
|
+
['药', 168],
|
|
127
|
+
['手枪', 169],
|
|
128
|
+
['茶', 171],
|
|
129
|
+
['眨眼睛', 172],
|
|
130
|
+
['泪奔', 173],
|
|
131
|
+
['无奈', 174],
|
|
132
|
+
['卖萌', 175],
|
|
133
|
+
['小纠结', 176],
|
|
134
|
+
['喷血', 177],
|
|
135
|
+
['斜眼笑', 178],
|
|
136
|
+
['doge', 179],
|
|
137
|
+
['惊喜', 180],
|
|
138
|
+
['骚扰', 181],
|
|
139
|
+
['笑哭', 182],
|
|
140
|
+
['我最美', 183],
|
|
141
|
+
['河蟹', 184],
|
|
142
|
+
['羊驼', 185],
|
|
143
|
+
['幽灵', 187],
|
|
144
|
+
['蛋', 188],
|
|
145
|
+
['菊花', 190],
|
|
146
|
+
['红包', 192],
|
|
147
|
+
['大笑', 193],
|
|
148
|
+
['不开心', 194],
|
|
149
|
+
['冷漠', 197],
|
|
150
|
+
['呃', 198],
|
|
151
|
+
['好棒', 199],
|
|
152
|
+
['拜托', 200],
|
|
153
|
+
['点赞', 201],
|
|
154
|
+
['无聊', 202],
|
|
155
|
+
['托脸', 203],
|
|
156
|
+
['吃', 204],
|
|
157
|
+
['送花', 205],
|
|
158
|
+
['害怕', 206],
|
|
159
|
+
['花痴', 207],
|
|
160
|
+
['小样儿', 208],
|
|
161
|
+
['飙泪', 210],
|
|
162
|
+
['我不看', 211],
|
|
163
|
+
['托腮', 212],
|
|
164
|
+
// 客户端面板上的叫法和 CoolQ 旧表不一样,两个都认。
|
|
165
|
+
['悠闲', 29],
|
|
166
|
+
['赞', 76],
|
|
167
|
+
['踩', 77],
|
|
168
|
+
['左亲亲', 109],
|
|
169
|
+
// QQ 系统表情里后来常用、旧表没列的。
|
|
170
|
+
['啵啵', 214],
|
|
171
|
+
['蹭一蹭', 219],
|
|
172
|
+
['抱抱', 222],
|
|
173
|
+
['拍手', 227],
|
|
174
|
+
['佛系', 232],
|
|
175
|
+
['喷脸', 240],
|
|
176
|
+
['甩头', 243],
|
|
177
|
+
['加油抱抱', 246],
|
|
178
|
+
['脑阔疼', 262],
|
|
179
|
+
['捂脸', 264],
|
|
180
|
+
['辣眼睛', 265],
|
|
181
|
+
['哦哟', 266],
|
|
182
|
+
['头秃', 267],
|
|
183
|
+
['问号脸', 268],
|
|
184
|
+
['暗中观察', 269],
|
|
185
|
+
['emm', 270],
|
|
186
|
+
['吃瓜', 271],
|
|
187
|
+
['呵呵哒', 272],
|
|
188
|
+
['我酸了', 273],
|
|
189
|
+
['汪汪', 277],
|
|
190
|
+
['汗', 278],
|
|
191
|
+
['无眼笑', 281],
|
|
192
|
+
['敬礼', 282],
|
|
193
|
+
['面无表情', 284],
|
|
194
|
+
['摸鱼', 285],
|
|
195
|
+
['哦', 287],
|
|
196
|
+
['睁眼', 289],
|
|
197
|
+
['敲开心', 290],
|
|
198
|
+
['摸锦鲤', 293],
|
|
199
|
+
['期待', 294],
|
|
200
|
+
['拜谢', 297],
|
|
201
|
+
['元宝', 298],
|
|
202
|
+
['牛啊', 299],
|
|
203
|
+
['右亲亲', 305],
|
|
204
|
+
['牛气冲天', 306],
|
|
205
|
+
['喵喵', 307],
|
|
206
|
+
['仔细分析', 314],
|
|
207
|
+
['加油', 315],
|
|
208
|
+
['崇拜', 318],
|
|
209
|
+
['比心', 319],
|
|
210
|
+
['庆祝', 320],
|
|
211
|
+
['拒绝', 322],
|
|
212
|
+
['吃糖', 324],
|
|
213
|
+
['生气', 326],
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
/** @type {Record<string, number>} */
|
|
217
|
+
export const FACE_NAME_TO_ID = Object.create(null)
|
|
218
|
+
/** @type {Map<string, number>} */
|
|
219
|
+
const FACE_LOOKUP = new Map()
|
|
220
|
+
|
|
221
|
+
for (const [name, id] of FACE_ENTRIES) {
|
|
222
|
+
const key = String(name).trim().toLowerCase()
|
|
223
|
+
if (!key || FACE_LOOKUP.has(key)) throw new Error(`faces.js:表情名重复(${name})`)
|
|
224
|
+
if (!Number.isInteger(id) || id < 0) throw new Error(`faces.js:表情 id 不合法(${name}=${id})`)
|
|
225
|
+
FACE_LOOKUP.set(key, id)
|
|
226
|
+
FACE_NAME_TO_ID[name] = id
|
|
227
|
+
}
|
|
228
|
+
Object.freeze(FACE_NAME_TO_ID)
|
|
229
|
+
|
|
230
|
+
/** 把数字串收成十进制 id。空、非数字、过长都返回 null。 */
|
|
231
|
+
function canonicalFaceId(digits) {
|
|
232
|
+
if (!/^\d{1,10}$/.test(digits)) return null
|
|
233
|
+
const n = Number(digits)
|
|
234
|
+
if (!Number.isSafeInteger(n)) return null
|
|
235
|
+
return String(n)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 方括号里的内容是不是一个系统表情。
|
|
240
|
+
* 认三种写法:表情名、`表情182`(入站占位符)、`CQ:face,id=182`。
|
|
241
|
+
* @param {string} raw
|
|
242
|
+
* @returns {string | null}
|
|
243
|
+
*/
|
|
244
|
+
function faceIdFromBracket(raw) {
|
|
245
|
+
const inner = String(raw).trim()
|
|
246
|
+
if (inner === '') return null
|
|
247
|
+
const byLabel = /^表情\s*(\d+)$/.exec(inner)
|
|
248
|
+
if (byLabel) return canonicalFaceId(byLabel[1])
|
|
249
|
+
const cq = /^cq:face\b([\s\S]*)$/i.exec(inner)
|
|
250
|
+
if (cq) {
|
|
251
|
+
const id = /(?:^|,)\s*id\s*=\s*(\d+)\s*(?:,|$)/i.exec(cq[1])
|
|
252
|
+
return id ? canonicalFaceId(id[1]) : null
|
|
253
|
+
}
|
|
254
|
+
const named = FACE_LOOKUP.get(inner.toLowerCase())
|
|
255
|
+
return named === undefined ? null : String(named)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 把出站正文拆成有序的 OneBot 消息段。
|
|
260
|
+
*
|
|
261
|
+
* - `[笑哭]` → `{ type:'face', data:{ id:'182' } }`
|
|
262
|
+
* - `[表情182]`、`[CQ:face,id=182]` 同样是 face 段
|
|
263
|
+
* - 对不上的 `[…]` 留在 text 段里,不另起一段
|
|
264
|
+
* - 纯文本返回恰好一段 text;空串返回 []
|
|
265
|
+
*
|
|
266
|
+
* id 用字符串:NapCat 的 face 段把 id 定义成 string。
|
|
267
|
+
* @param {string} body
|
|
268
|
+
* @returns {Array<{ type: string, data: Record<string, string> }>}
|
|
269
|
+
*/
|
|
270
|
+
export function textToOneBotSegments(body) {
|
|
271
|
+
const text = String(body ?? '')
|
|
272
|
+
if (text === '') return []
|
|
273
|
+
const segments = []
|
|
274
|
+
let cursor = 0
|
|
275
|
+
for (const match of text.matchAll(/\[([^\[\]]*)\]/g)) {
|
|
276
|
+
const id = faceIdFromBracket(match[1])
|
|
277
|
+
if (id === null) continue
|
|
278
|
+
const start = match.index ?? 0
|
|
279
|
+
if (start > cursor) segments.push({ type: 'text', data: { text: text.slice(cursor, start) } })
|
|
280
|
+
segments.push({ type: 'face', data: { id } })
|
|
281
|
+
cursor = start + match[0].length
|
|
282
|
+
}
|
|
283
|
+
if (cursor < text.length) segments.push({ type: 'text', data: { text: text.slice(cursor) } })
|
|
284
|
+
return segments
|
|
285
|
+
}
|