@karinjs/adapter-qqbot 2.1.0 → 3.0.0
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 +10 -0
- package/dist/apps/cleanup.d.ts +6 -2
- package/dist/apps/cleanup.js +5 -10
- package/dist/apps/login.js +2 -2
- package/dist/config-BUuU6FnQ.js +293 -0
- package/dist/index.d.ts +426 -217
- package/dist/index.js +470 -288
- package/dist/message-BTGL3TSn.js +1843 -0
- package/dist/{onboard-DPWgX39H.js → onboard-vIEoe9dB.js} +1 -1
- package/dist/utils-N9PNiHjE.js +4 -0
- package/dist/web.config.d.ts +24 -19
- package/dist/web.config.js +262 -91
- package/package.json +8 -6
- package/dist/config-DKa7596f.js +0 -170
- package/dist/message-O73AeL_P.js +0 -1739
- package/dist/utils-BPbnNAix.js +0 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as getDefaultConfig, m as log, n as config, o as writeConfig } from "./config-BUuU6FnQ.js";
|
|
2
2
|
import { a as sleep } from "./common-by1GpV0b.js";
|
|
3
3
|
import qrcode from "qrcode";
|
|
4
4
|
import axios from "node-karin/axios";
|
package/dist/web.config.d.ts
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
//#region src/web.config.d.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
2
|
+
type WebQQBotInput = {
|
|
3
|
+
name: unknown;
|
|
4
|
+
appId: unknown;
|
|
5
|
+
secret: unknown;
|
|
6
|
+
'proxy:api': unknown;
|
|
7
|
+
'proxy:ws': unknown;
|
|
8
|
+
sandbox: unknown;
|
|
9
|
+
qqEnable: unknown;
|
|
10
|
+
guildEnable: unknown;
|
|
11
|
+
guildMode: unknown;
|
|
12
|
+
regex: unknown;
|
|
13
|
+
'keyboard:enable': unknown;
|
|
14
|
+
'markdown:enable': unknown;
|
|
15
|
+
'messageCache:enable': unknown;
|
|
16
|
+
'messageCache:self': unknown;
|
|
17
|
+
'messageCache:level': unknown;
|
|
18
|
+
'messageCache:ttlHours': unknown;
|
|
19
|
+
'messageCache:maxRows': unknown;
|
|
20
|
+
'event:type': unknown;
|
|
21
|
+
};
|
|
22
|
+
type WebConfigInput = {
|
|
23
|
+
qqbot: WebQQBotInput[];
|
|
24
|
+
};
|
|
25
|
+
declare const _default: import("node-karin").DefineComponentConfig<WebConfigInput>;
|
|
21
26
|
//#endregion
|
|
22
27
|
export { _default as default };
|
package/dist/web.config.js
CHANGED
|
@@ -1,8 +1,92 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import "./utils-
|
|
1
|
+
import { a as readConfigFile, f as pkg, i as getDefaultConfig, n as config, o as writeConfig, s as buildFallbackWsUrlFromApi, u as normalizeProxyConfig } from "./config-BUuU6FnQ.js";
|
|
2
|
+
import "./utils-N9PNiHjE.js";
|
|
3
3
|
import { components, defineConfig } from "node-karin";
|
|
4
4
|
|
|
5
5
|
//#region src/web.config.ts
|
|
6
|
+
const defaultProxy = () => getDefaultConfig()[0].proxy;
|
|
7
|
+
const unwrapValue = (value) => {
|
|
8
|
+
if (value && typeof value === "object" && "value" in value) return value.value;
|
|
9
|
+
return value;
|
|
10
|
+
};
|
|
11
|
+
const toBool = (value, fallback) => {
|
|
12
|
+
const raw = unwrapValue(value);
|
|
13
|
+
if (typeof raw === "boolean") return raw;
|
|
14
|
+
if (typeof raw === "number") return raw !== 0;
|
|
15
|
+
if (typeof raw === "string") {
|
|
16
|
+
const normalized = raw.trim().toLowerCase();
|
|
17
|
+
if ([
|
|
18
|
+
"false",
|
|
19
|
+
"0",
|
|
20
|
+
"off",
|
|
21
|
+
"no",
|
|
22
|
+
""
|
|
23
|
+
].includes(normalized)) return false;
|
|
24
|
+
if ([
|
|
25
|
+
"true",
|
|
26
|
+
"1",
|
|
27
|
+
"on",
|
|
28
|
+
"yes"
|
|
29
|
+
].includes(normalized)) return true;
|
|
30
|
+
}
|
|
31
|
+
if (raw === null || raw === void 0) return fallback;
|
|
32
|
+
return fallback;
|
|
33
|
+
};
|
|
34
|
+
const toStringValue = (value, fallback = "") => {
|
|
35
|
+
const raw = unwrapValue(value);
|
|
36
|
+
if (typeof raw === "string") return raw;
|
|
37
|
+
if (raw === null || raw === void 0) return fallback;
|
|
38
|
+
return String(raw);
|
|
39
|
+
};
|
|
40
|
+
const toStringList = (value) => {
|
|
41
|
+
const raw = unwrapValue(value);
|
|
42
|
+
if (!Array.isArray(raw)) return [];
|
|
43
|
+
return raw.map((item) => toStringValue(item)).filter(Boolean);
|
|
44
|
+
};
|
|
45
|
+
const proxyKeys = (sandbox) => sandbox ? {
|
|
46
|
+
api: "sandboxApi",
|
|
47
|
+
ws: "sandboxWs"
|
|
48
|
+
} : {
|
|
49
|
+
api: "prodApi",
|
|
50
|
+
ws: "prodWs"
|
|
51
|
+
};
|
|
52
|
+
const getBotProxyInput = (item) => {
|
|
53
|
+
const keys = proxyKeys(!!item.sandbox);
|
|
54
|
+
const proxy = item.proxy || defaultProxy();
|
|
55
|
+
return {
|
|
56
|
+
"proxy:api": proxy[keys.api] || defaultProxy()[keys.api],
|
|
57
|
+
"proxy:ws": proxy[keys.ws] || defaultProxy()[keys.ws]
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
const proxyFromInput = (item, previous) => {
|
|
61
|
+
const def = defaultProxy();
|
|
62
|
+
const sandbox = toBool(item.sandbox, previous?.sandbox ?? false);
|
|
63
|
+
const previousSandbox = previous?.sandbox ?? sandbox;
|
|
64
|
+
const keys = proxyKeys(sandbox);
|
|
65
|
+
const previousVisibleKeys = proxyKeys(previousSandbox);
|
|
66
|
+
const previousProxy = previous?.proxy;
|
|
67
|
+
const base = {
|
|
68
|
+
...def,
|
|
69
|
+
...previousProxy
|
|
70
|
+
};
|
|
71
|
+
const envChanged = !!previous && previousSandbox !== sandbox;
|
|
72
|
+
const rawApi = toStringValue(item["proxy:api"]).trim();
|
|
73
|
+
const rawWs = toStringValue(item["proxy:ws"]).trim();
|
|
74
|
+
const previousVisibleApi = previousProxy?.[previousVisibleKeys.api] || def[previousVisibleKeys.api];
|
|
75
|
+
const previousVisibleWs = previousProxy?.[previousVisibleKeys.ws] || def[previousVisibleKeys.ws];
|
|
76
|
+
const api = envChanged && rawApi === previousVisibleApi ? base[keys.api] || def[keys.api] : rawApi || base[keys.api] || def[keys.api];
|
|
77
|
+
const previousApi = previousProxy?.[keys.api] || def[keys.api];
|
|
78
|
+
const previousWs = previousProxy?.[keys.ws] || def[keys.ws];
|
|
79
|
+
const wsInput = envChanged && rawWs === previousVisibleWs ? base[keys.ws] || def[keys.ws] : rawWs;
|
|
80
|
+
let ws = wsInput || previousWs || def[keys.ws];
|
|
81
|
+
const previousAutoWs = buildFallbackWsUrlFromApi(previousApi);
|
|
82
|
+
if (!wsInput || wsInput === previousWs && previousWs === previousAutoWs && api !== previousApi) ws = buildFallbackWsUrlFromApi(api);
|
|
83
|
+
return normalizeProxyConfig({
|
|
84
|
+
...base,
|
|
85
|
+
[keys.api]: api,
|
|
86
|
+
tokenApi: base.tokenApi || def.tokenApi,
|
|
87
|
+
[keys.ws]: ws
|
|
88
|
+
});
|
|
89
|
+
};
|
|
6
90
|
var web_config_default = defineConfig({
|
|
7
91
|
info: {
|
|
8
92
|
id: pkg().name,
|
|
@@ -23,120 +107,173 @@ var web_config_default = defineConfig({
|
|
|
23
107
|
name: item.name,
|
|
24
108
|
appId: item.appId,
|
|
25
109
|
secret: item.secret,
|
|
26
|
-
|
|
27
|
-
sandboxApi: item.sandboxApi,
|
|
28
|
-
tokenApi: item.tokenApi,
|
|
110
|
+
"event:type": String(item.event?.type ?? 2),
|
|
29
111
|
sandbox: item.sandbox,
|
|
30
112
|
qqEnable: item.qqEnable,
|
|
31
113
|
guildEnable: item.guildEnable,
|
|
32
114
|
guildMode: item.guildMode === 1,
|
|
33
115
|
regex: item.regex.map((r) => `${r.reg} ${r.rep}`),
|
|
34
116
|
"keyboard:enable": item.keyboard?.enable !== false,
|
|
117
|
+
"markdown:enable": item.markdown?.enable !== false,
|
|
35
118
|
"messageCache:enable": item.messageCache?.enable === true,
|
|
36
119
|
"messageCache:self": item.messageCache?.self === true,
|
|
37
|
-
"
|
|
120
|
+
"messageCache:level": item.messageCache?.level || "standard",
|
|
121
|
+
"messageCache:ttlHours": String(item.messageCache?.ttlHours ?? 24),
|
|
122
|
+
"messageCache:maxRows": String(item.messageCache?.maxRows ?? 2e5),
|
|
123
|
+
...getBotProxyInput(item)
|
|
38
124
|
});
|
|
39
125
|
});
|
|
40
|
-
return [components.
|
|
41
|
-
|
|
42
|
-
|
|
126
|
+
return [components.divider.horizontal("bot-section", {
|
|
127
|
+
description: "机器人账号",
|
|
128
|
+
descPosition: 5
|
|
129
|
+
}), components.accordionPro.create("qqbot", data, {
|
|
130
|
+
label: "QQ 官方机器人列表",
|
|
131
|
+
description: "每一项对应一个 QQ 官方机器人账号。新增机器人时先填 AppID 和 Secret,再按需调整接收方式、场景能力和发送能力。",
|
|
43
132
|
children: {
|
|
44
133
|
key: "qqbotConfig",
|
|
45
134
|
children: [
|
|
135
|
+
components.radio.group("event:type", {
|
|
136
|
+
label: "事件接收方式",
|
|
137
|
+
description: "QQ 官方机器人只有 Webhook 推送和 WebSocket 主动连接两种接收方式。不确定时选择 WebSocket;如果 QQ 后台已启用 Webhook,可在下方高级设置填写第三方 Webhook 转 WebSocket 服务地址。",
|
|
138
|
+
defaultValue: "2",
|
|
139
|
+
radio: [
|
|
140
|
+
components.radio.create("0", {
|
|
141
|
+
label: "关闭接收",
|
|
142
|
+
value: "0",
|
|
143
|
+
description: "停用这个机器人,不连接 WebSocket,也不接收 Webhook 事件。"
|
|
144
|
+
}),
|
|
145
|
+
components.radio.create("1", {
|
|
146
|
+
label: "Webhook 推送",
|
|
147
|
+
value: "1",
|
|
148
|
+
description: "由 QQ 官方把事件推送到 Karin 的 /qqbot/webhook 路由。通常需要公网地址,并在 QQ 后台配置回调地址。"
|
|
149
|
+
}),
|
|
150
|
+
components.radio.create("2", {
|
|
151
|
+
label: "WebSocket 主动连接",
|
|
152
|
+
value: "2",
|
|
153
|
+
description: "适配器主动连接 WebSocket 地址接收事件。默认连接 QQ 官方网关;如果 QQ 后台已开 Webhook,可改为第三方 Webhook 转 WebSocket 服务地址。"
|
|
154
|
+
})
|
|
155
|
+
]
|
|
156
|
+
}),
|
|
157
|
+
components.divider.horizontal("bot-basic-section", {
|
|
158
|
+
description: "基础信息",
|
|
159
|
+
descPosition: 5
|
|
160
|
+
}),
|
|
46
161
|
components.input.create("name", {
|
|
47
|
-
label: "
|
|
48
|
-
description: "
|
|
162
|
+
label: "机器人显示名称",
|
|
163
|
+
description: "只用于 Karin 配置页和日志里识别这个机器人,不影响 QQ 平台资料。扫码登录写入配置时通常会自动填充。"
|
|
49
164
|
}),
|
|
50
165
|
components.input.create("appId", {
|
|
51
|
-
label: "AppID",
|
|
52
|
-
description: "
|
|
166
|
+
label: "机器人 AppID",
|
|
167
|
+
description: "QQ 开放平台机器人应用的 AppID,是识别机器人账号的唯一编号。填错会导致鉴权和收发消息失败。",
|
|
53
168
|
isRequired: true
|
|
54
169
|
}),
|
|
55
170
|
components.input.create("secret", {
|
|
56
|
-
label: "Secret",
|
|
57
|
-
description: "
|
|
171
|
+
label: "机器人 Secret",
|
|
172
|
+
description: "QQ 开放平台机器人应用的 Secret,用于获取 access token。请不要泄露;扫码授权会刷新 Secret,旧 Secret 会立即失效。",
|
|
58
173
|
isRequired: true
|
|
59
174
|
}),
|
|
60
|
-
components.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
isRequired: true
|
|
64
|
-
}),
|
|
65
|
-
components.input.create("sandboxApi", {
|
|
66
|
-
label: "沙盒环境 API",
|
|
67
|
-
defaultValue: "https://sandbox.api.sgroup.qq.com",
|
|
68
|
-
isRequired: true
|
|
69
|
-
}),
|
|
70
|
-
components.input.create("tokenApi", {
|
|
71
|
-
label: "AccessToken 接口",
|
|
72
|
-
defaultValue: "https://bots.qq.com/app/getAppAccessToken",
|
|
73
|
-
isRequired: true
|
|
175
|
+
components.divider.horizontal("bot-scene-section", {
|
|
176
|
+
description: "场景能力",
|
|
177
|
+
descPosition: 5
|
|
74
178
|
}),
|
|
75
179
|
components.switch.create("sandbox", {
|
|
76
|
-
label: "
|
|
77
|
-
description: "
|
|
180
|
+
label: "使用沙盒环境",
|
|
181
|
+
description: "开启后这个机器人会使用沙盒环境的 OpenAPI 和 WebSocket 地址;关闭时使用正式环境地址。正式上线机器人请关闭。",
|
|
78
182
|
defaultSelected: false
|
|
79
183
|
}),
|
|
80
184
|
components.switch.create("qqEnable", {
|
|
81
|
-
label: "QQ
|
|
82
|
-
description: "
|
|
185
|
+
label: "启用 QQ 私聊 / 群聊",
|
|
186
|
+
description: "开启后会处理 QQ 单聊和群聊里的消息事件;如果这个机器人只用于频道,可以关闭。",
|
|
83
187
|
defaultSelected: true
|
|
84
188
|
}),
|
|
85
189
|
components.switch.create("guildEnable", {
|
|
86
|
-
label: "
|
|
87
|
-
description: "
|
|
190
|
+
label: "启用频道消息",
|
|
191
|
+
description: "开启后会处理 QQ 频道消息事件;如果这个机器人只用于私聊或群聊,可以关闭。",
|
|
88
192
|
defaultSelected: true
|
|
89
193
|
}),
|
|
90
194
|
components.switch.create("guildMode", {
|
|
91
|
-
label: "
|
|
92
|
-
description: "
|
|
195
|
+
label: "频道私域机器人模式",
|
|
196
|
+
description: "关闭时按公域机器人处理,通常只接收 @ 机器人相关消息;开启时按私域机器人处理,可接收更多频道消息,需确认机器人权限匹配。",
|
|
93
197
|
defaultSelected: false
|
|
94
198
|
}),
|
|
199
|
+
components.divider.horizontal("bot-message-section", {
|
|
200
|
+
description: "消息发送",
|
|
201
|
+
descPosition: 5
|
|
202
|
+
}),
|
|
95
203
|
components.input.group("regex", {
|
|
96
204
|
data: [],
|
|
97
205
|
label: "消息正则替换",
|
|
98
|
-
description: "
|
|
206
|
+
description: "收到文本消息后,先按这里的规则替换内容,再交给 Karin 插件处理。每行格式是“正则表达式 替换内容”,中间用空格分隔;例如“^/ #”会把“/菜单”改成“#菜单”。不懂正则可以保留默认或清空。",
|
|
99
207
|
template: components.input.create("regex", {
|
|
100
|
-
label: "
|
|
208
|
+
label: "一条替换规则",
|
|
101
209
|
placeholder: "^/ #"
|
|
102
210
|
})
|
|
103
211
|
}),
|
|
104
212
|
components.switch.create("keyboard:enable", {
|
|
105
|
-
label: "
|
|
106
|
-
description: "
|
|
213
|
+
label: "把链接自动转成按钮",
|
|
214
|
+
description: "开启后,发送文本里出现的 http(s) 链接会尽量转成 QQ keyboard 按钮,用户可以直接点按钮打开链接。该能力依赖 Markdown 通道。",
|
|
107
215
|
defaultSelected: true
|
|
108
216
|
}),
|
|
217
|
+
components.switch.create("markdown:enable", {
|
|
218
|
+
label: "启用 Markdown 发送通道",
|
|
219
|
+
description: "开启后普通文本和图片优先合并为 QQ Markdown 消息,Markdown 里的图片会以内嵌图片形式发送;关闭后普通图片会按官方富媒体单独上传发送。插件显式传入的 segment.markdown 不受此开关影响。",
|
|
220
|
+
defaultSelected: true
|
|
221
|
+
}),
|
|
222
|
+
components.divider.horizontal("bot-cache-section", {
|
|
223
|
+
description: "消息缓存",
|
|
224
|
+
descPosition: 5
|
|
225
|
+
}),
|
|
109
226
|
components.switch.create("messageCache:enable", {
|
|
110
|
-
label: "
|
|
111
|
-
description: "
|
|
227
|
+
label: "缓存收到的消息到数据库",
|
|
228
|
+
description: "开启后会把最近一天收到的消息写入本地数据库,供 bot.getMsg 等查询使用;收到的图片、语音、视频等富媒体也会保存到本地。消息量大时会明显占用磁盘空间。",
|
|
112
229
|
defaultSelected: false
|
|
113
230
|
}),
|
|
114
231
|
components.switch.create("messageCache:self", {
|
|
115
|
-
label: "
|
|
116
|
-
description: "
|
|
232
|
+
label: "同时缓存机器人自己发出的消息",
|
|
233
|
+
description: "只有开启“缓存收到的消息到数据库”后才生效。开启后,机器人发送成功的消息也会写入缓存,方便之后通过消息 ID 查询。",
|
|
117
234
|
defaultSelected: false
|
|
118
235
|
}),
|
|
119
|
-
components.radio.group("
|
|
120
|
-
label: "
|
|
121
|
-
description: "
|
|
122
|
-
defaultValue: "
|
|
236
|
+
components.radio.group("messageCache:level", {
|
|
237
|
+
label: "缓存存储分级",
|
|
238
|
+
description: "控制每条消息落库的内容多少,直接影响数据库体积。撤回和引用解析在任何分级下都可用。",
|
|
239
|
+
defaultValue: "standard",
|
|
123
240
|
radio: [
|
|
124
|
-
components.radio.create("
|
|
125
|
-
label: "
|
|
126
|
-
value: "
|
|
127
|
-
description: "
|
|
241
|
+
components.radio.create("minimal", {
|
|
242
|
+
label: "最小",
|
|
243
|
+
value: "minimal",
|
|
244
|
+
description: "只记录消息 ID 映射与引用关系,不保存正文和媒体,体积最小。"
|
|
128
245
|
}),
|
|
129
|
-
components.radio.create("
|
|
130
|
-
label: "
|
|
131
|
-
value: "
|
|
132
|
-
description: "
|
|
246
|
+
components.radio.create("standard", {
|
|
247
|
+
label: "标准(推荐)",
|
|
248
|
+
value: "standard",
|
|
249
|
+
description: "保存文本、表情和媒体本地文件,不保存 markdown 原文。"
|
|
133
250
|
}),
|
|
134
|
-
components.radio.create("
|
|
135
|
-
label: "
|
|
136
|
-
value: "
|
|
137
|
-
description: "
|
|
251
|
+
components.radio.create("full", {
|
|
252
|
+
label: "完整",
|
|
253
|
+
value: "full",
|
|
254
|
+
description: "所有支持的消息段原样保存,包含 markdown 原文,体积最大。"
|
|
138
255
|
})
|
|
139
256
|
]
|
|
257
|
+
}),
|
|
258
|
+
components.input.create("messageCache:ttlHours", {
|
|
259
|
+
label: "缓存保留小时数",
|
|
260
|
+
description: "超过该时长的消息会被清理任务删除。范围 1~720 小时,默认 24。多个机器人同时开启缓存时按最大值生效。"
|
|
261
|
+
}),
|
|
262
|
+
components.input.create("messageCache:maxRows", {
|
|
263
|
+
label: "缓存最大消息条数",
|
|
264
|
+
description: "数据库消息行数硬上限,超出后从最旧的消息开始删除,保证磁盘占用可预期。范围 1000~5000000,默认 200000。"
|
|
265
|
+
}),
|
|
266
|
+
components.divider.horizontal("bot-proxy-section", {
|
|
267
|
+
description: "连接代理(高级设置:通常只在使用 Webhook 转 WebSocket 服务时填写,除非你知道你在做什么,否则不建议更改)",
|
|
268
|
+
descPosition: 5
|
|
269
|
+
}),
|
|
270
|
+
components.input.create("proxy:ws", {
|
|
271
|
+
label: "WebSocket 接入地址",
|
|
272
|
+
description: "事件接收方式为 WebSocket 时连接的地址。常见用途是填写第三方 Webhook 转 WebSocket 服务地址,因为 QQ 后台开启 Webhook 后通常无法再直连官方 WebSocket。不填会使用当前环境的官方默认网关。"
|
|
273
|
+
}),
|
|
274
|
+
components.input.create("proxy:api", {
|
|
275
|
+
label: "OpenAPI 反代地址",
|
|
276
|
+
description: "用于发送消息、上传媒体、查询网关和获取机器人资料的 QQ OpenAPI 根地址。多数 Webhook 转 WebSocket 场景不用改这里;只有 OpenAPI 请求也需要走反代时才填写。"
|
|
140
277
|
})
|
|
141
278
|
]
|
|
142
279
|
}
|
|
@@ -147,41 +284,75 @@ var web_config_default = defineConfig({
|
|
|
147
284
|
success: false,
|
|
148
285
|
message: "保存失败:配置格式错误"
|
|
149
286
|
};
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
287
|
+
const prevConfig = readConfigFile();
|
|
288
|
+
let data;
|
|
289
|
+
try {
|
|
290
|
+
data = input.qqbot.map((item) => {
|
|
291
|
+
const previous = prevConfig.find((cfg) => cfg.appId === toStringValue(item.appId));
|
|
292
|
+
const sandbox = toBool(item.sandbox, previous?.sandbox ?? false);
|
|
293
|
+
const qqEnable = toBool(item.qqEnable, previous?.qqEnable ?? true);
|
|
294
|
+
const guildEnable = toBool(item.guildEnable, previous?.guildEnable ?? true);
|
|
295
|
+
const guildMode = toBool(item.guildMode, previous?.guildMode === 1) ? 1 : 0;
|
|
296
|
+
const eventType = Number(toStringValue(item["event:type"], String(previous?.event?.type ?? 0)));
|
|
297
|
+
const keyboardEnable = toBool(item["keyboard:enable"], previous?.keyboard?.enable ?? true);
|
|
298
|
+
const markdownEnable = toBool(item["markdown:enable"], previous?.markdown?.enable ?? true);
|
|
299
|
+
const messageCacheEnable = toBool(item["messageCache:enable"], previous?.messageCache?.enable ?? false);
|
|
300
|
+
const messageCacheSelf = toBool(item["messageCache:self"], previous?.messageCache?.self ?? false);
|
|
301
|
+
/** 枚举与数值范围由 writeConfig 内的 formatConfig 统一校验回落。 */
|
|
302
|
+
const messageCacheLevel = toStringValue(item["messageCache:level"], previous?.messageCache?.level ?? "standard");
|
|
303
|
+
/** 输入被清空时回落上次值/默认值,避免 Number('') = 0 被夹到范围下限。 */
|
|
304
|
+
const ttlHoursRaw = toStringValue(item["messageCache:ttlHours"]).trim();
|
|
305
|
+
const messageCacheTtlHours = ttlHoursRaw ? Number(ttlHoursRaw) : previous?.messageCache?.ttlHours ?? 24;
|
|
306
|
+
const maxRowsRaw = toStringValue(item["messageCache:maxRows"]).trim();
|
|
307
|
+
const messageCacheMaxRows = maxRowsRaw ? Number(maxRowsRaw) : previous?.messageCache?.maxRows ?? 2e5;
|
|
308
|
+
const regex = toStringList(item.regex).map((str) => {
|
|
309
|
+
const parts = str.split(" ");
|
|
310
|
+
return {
|
|
311
|
+
reg: parts[0]?.replace(/^<|>$/g, "") || "",
|
|
312
|
+
rep: parts[1]?.replace(/^<|>$/g, "") || ""
|
|
313
|
+
};
|
|
314
|
+
});
|
|
315
|
+
const proxy = proxyFromInput(item, previous);
|
|
155
316
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
317
|
+
name: toStringValue(item.name),
|
|
318
|
+
appId: toStringValue(item.appId),
|
|
319
|
+
secret: toStringValue(item.secret),
|
|
320
|
+
proxy,
|
|
321
|
+
sandbox,
|
|
322
|
+
qqEnable,
|
|
323
|
+
guildEnable,
|
|
324
|
+
guildMode,
|
|
325
|
+
regex,
|
|
326
|
+
keyboard: { enable: keyboardEnable },
|
|
327
|
+
markdown: { enable: markdownEnable },
|
|
328
|
+
messageCache: {
|
|
329
|
+
enable: messageCacheEnable,
|
|
330
|
+
self: messageCacheSelf,
|
|
331
|
+
level: messageCacheLevel,
|
|
332
|
+
ttlHours: messageCacheTtlHours,
|
|
333
|
+
maxRows: messageCacheMaxRows
|
|
334
|
+
},
|
|
335
|
+
event: { type: eventType }
|
|
158
336
|
};
|
|
159
337
|
});
|
|
338
|
+
} catch (err) {
|
|
160
339
|
return {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
secret: item.secret || "",
|
|
164
|
-
prodApi: item.prodApi || "https://api.sgroup.qq.com",
|
|
165
|
-
sandboxApi: item.sandboxApi || "https://sandbox.api.sgroup.qq.com",
|
|
166
|
-
tokenApi: item.tokenApi || "https://bots.qq.com/app/getAppAccessToken",
|
|
167
|
-
sandbox: !!item.sandbox,
|
|
168
|
-
qqEnable: !!item.qqEnable,
|
|
169
|
-
guildEnable: !!item.guildEnable,
|
|
170
|
-
guildMode,
|
|
171
|
-
regex,
|
|
172
|
-
keyboard: { enable: item["keyboard:enable"] !== false },
|
|
173
|
-
messageCache: {
|
|
174
|
-
enable: !!item["messageCache:enable"],
|
|
175
|
-
self: !!item["messageCache:self"]
|
|
176
|
-
},
|
|
177
|
-
event: { type: eventType }
|
|
340
|
+
success: false,
|
|
341
|
+
message: `保存失败:代理地址格式错误:${err?.message || "unknown"}`
|
|
178
342
|
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
writeConfig(data);
|
|
346
|
+
return {
|
|
347
|
+
success: true,
|
|
348
|
+
message: "保存成功"
|
|
349
|
+
};
|
|
350
|
+
} catch (err) {
|
|
351
|
+
return {
|
|
352
|
+
success: false,
|
|
353
|
+
message: `保存失败:${err?.message || "unknown"}`
|
|
354
|
+
};
|
|
355
|
+
}
|
|
185
356
|
}
|
|
186
357
|
});
|
|
187
358
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karinjs/adapter-qqbot",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "karin adapter for QQ Official Bot (2.0)",
|
|
5
5
|
"homepage": "https://github.com/KarinJS/karin-plugin-adapter-qqbot",
|
|
6
6
|
"bugs": {
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"lint": "eslint src",
|
|
25
25
|
"lint:fix": "eslint src --fix",
|
|
26
26
|
"pub": "npm publish --access public",
|
|
27
|
-
"sort": "npx sort-package-json"
|
|
27
|
+
"sort": "npx sort-package-json",
|
|
28
|
+
"test": "node tests/schema-sql.test.mjs && node tests/codec.test.mjs && node tests/store.test.mjs"
|
|
28
29
|
},
|
|
29
30
|
"dependencies": {
|
|
30
31
|
"@karinjs/geturls": "^1.0.2",
|
|
@@ -35,14 +36,15 @@
|
|
|
35
36
|
"tweetnacl": "^1.0.3"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
39
|
+
"@tencent-connect/qqbot-nodejs": "^1.0.4",
|
|
38
40
|
"@types/express": "5.0.6",
|
|
39
|
-
"@types/node": "^24.13.
|
|
41
|
+
"@types/node": "^24.13.3",
|
|
40
42
|
"@types/qrcode": "^1.5.6",
|
|
41
43
|
"eslint": "^9.39.4",
|
|
42
44
|
"neostandard": "^0.13.0",
|
|
43
|
-
"node-karin": "
|
|
44
|
-
"tsdown": "^0.22.
|
|
45
|
-
"tsx": "^4.
|
|
45
|
+
"node-karin": "1.16.3",
|
|
46
|
+
"tsdown": "^0.22.13",
|
|
47
|
+
"tsx": "^4.23.1",
|
|
46
48
|
"typescript": "^6.0.3"
|
|
47
49
|
},
|
|
48
50
|
"publishConfig": {
|