koishi-plugin-bind-bot 2.1.1 → 2.1.3
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/lib/export-utils.js +13 -6
- package/lib/force-bind-utils.js +12 -8
- package/lib/handlers/binding.handler.js +63 -27
- package/lib/handlers/buid.handler.js +102 -45
- package/lib/handlers/group-request-review.handler.d.ts +97 -0
- package/lib/handlers/group-request-review.handler.js +582 -0
- package/lib/handlers/index.d.ts +1 -0
- package/lib/handlers/index.js +1 -0
- package/lib/handlers/lottery.handler.js +11 -9
- package/lib/handlers/mcid.handler.js +197 -86
- package/lib/handlers/tag.handler.js +86 -31
- package/lib/handlers/whitelist.handler.js +252 -77
- package/lib/index.js +282 -142
- package/lib/repositories/mcidbind.repository.js +2 -2
- package/lib/repositories/schedule-mute.repository.js +2 -2
- package/lib/services/api.service.js +27 -23
- package/lib/services/database.service.js +16 -14
- package/lib/services/nickname.service.js +12 -16
- package/lib/types/api.d.ts +90 -0
- package/lib/types/common.d.ts +45 -0
- package/lib/types/config.d.ts +88 -0
- package/lib/types/database.d.ts +50 -0
- package/lib/types/update-data.d.ts +83 -0
- package/lib/utils/error-utils.js +7 -8
- package/lib/utils/helpers.js +45 -7
- package/lib/utils/message-utils.js +36 -23
- package/lib/utils/session-manager.js +6 -1
- package/package.json +12 -2
package/lib/index.js
CHANGED
|
@@ -20,12 +20,8 @@ exports.inject = ['database', 'server'];
|
|
|
20
20
|
// 注意:Config 作为 Schema 常量导出,类型使用 IConfig 或从 './types' 导入
|
|
21
21
|
// 创建配置Schema
|
|
22
22
|
exports.Config = koishi_1.Schema.object({
|
|
23
|
-
cooldownDays: koishi_1.Schema.number()
|
|
24
|
-
|
|
25
|
-
.default(15),
|
|
26
|
-
masterId: koishi_1.Schema.string()
|
|
27
|
-
.description('主人QQ号,拥有管理员管理权限')
|
|
28
|
-
.default(''),
|
|
23
|
+
cooldownDays: koishi_1.Schema.number().description('操作冷却时间(天)').default(15),
|
|
24
|
+
masterId: koishi_1.Schema.string().description('主人QQ号,拥有管理员管理权限').default(''),
|
|
29
25
|
allowTextPrefix: koishi_1.Schema.boolean()
|
|
30
26
|
.description('是否允许通过文本前缀触发指令(如"@机器人 mcid bind xxx")')
|
|
31
27
|
.default(false),
|
|
@@ -35,12 +31,8 @@ exports.Config = koishi_1.Schema.object({
|
|
|
35
31
|
autoRecallTime: koishi_1.Schema.number()
|
|
36
32
|
.description('消息自动撤回时间(秒),同时控制机器人和用户消息,设置为0表示不自动撤回')
|
|
37
33
|
.default(0),
|
|
38
|
-
recallUserMessage: koishi_1.Schema.boolean()
|
|
39
|
-
|
|
40
|
-
.default(false),
|
|
41
|
-
debugMode: koishi_1.Schema.boolean()
|
|
42
|
-
.description('调试模式,启用详细日志输出')
|
|
43
|
-
.default(false),
|
|
34
|
+
recallUserMessage: koishi_1.Schema.boolean().description('是否撤回用户发送的指令消息').default(false),
|
|
35
|
+
debugMode: koishi_1.Schema.boolean().description('调试模式,启用详细日志输出').default(false),
|
|
44
36
|
showAvatar: koishi_1.Schema.boolean()
|
|
45
37
|
.description('是否显示头像图片(MC用头图,B站用头像)')
|
|
46
38
|
.default(false),
|
|
@@ -50,38 +42,39 @@ exports.Config = koishi_1.Schema.object({
|
|
|
50
42
|
zminfoApiUrl: koishi_1.Schema.string()
|
|
51
43
|
.description('ZMINFO API地址')
|
|
52
44
|
.default('https://zminfo-api.wittf.com'),
|
|
53
|
-
enableLotteryBroadcast: koishi_1.Schema.boolean()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
.description('天选开奖播报目标群ID'),
|
|
58
|
-
lotteryTargetPrivateId: koishi_1.Schema.string()
|
|
59
|
-
.description('天选开奖播报私聊目标ID(格式:private:QQ号)'),
|
|
60
|
-
autoNicknameGroupId: koishi_1.Schema.string()
|
|
61
|
-
.description('自动群昵称设置目标群ID')
|
|
62
|
-
.default('123456789'),
|
|
45
|
+
enableLotteryBroadcast: koishi_1.Schema.boolean().description('是否启用天选开奖播报功能').default(false),
|
|
46
|
+
lotteryTargetGroupId: koishi_1.Schema.string().description('天选开奖播报目标群ID'),
|
|
47
|
+
lotteryTargetPrivateId: koishi_1.Schema.string().description('天选开奖播报私聊目标ID(格式:private:QQ号)'),
|
|
48
|
+
autoNicknameGroupId: koishi_1.Schema.string().description('自动群昵称设置目标群ID').default('123456789'),
|
|
63
49
|
forceBindSessdata: koishi_1.Schema.string()
|
|
64
50
|
.description('B站Cookie信息,用于强制绑定时获取粉丝牌信息(支持完整Cookie或单独SESSDATA)')
|
|
65
51
|
.default(''),
|
|
66
|
-
forceBindTargetUpUid: koishi_1.Schema.number()
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.description('
|
|
71
|
-
.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.
|
|
52
|
+
forceBindTargetUpUid: koishi_1.Schema.number().description('强制绑定目标UP主UID').default(686127),
|
|
53
|
+
forceBindTargetRoomId: koishi_1.Schema.number().description('强制绑定目标房间号').default(544853),
|
|
54
|
+
forceBindTargetMedalName: koishi_1.Schema.string().description('强制绑定目标粉丝牌名称').default('生态'),
|
|
55
|
+
groupRequestReview: koishi_1.Schema.object({
|
|
56
|
+
enabled: koishi_1.Schema.boolean().description('是否启用入群申请审批功能').default(false),
|
|
57
|
+
targetGroupId: koishi_1.Schema.string()
|
|
58
|
+
.description('需要审批的目标群ID(入群申请来源群)')
|
|
59
|
+
.default('931805503'),
|
|
60
|
+
reviewGroupId: koishi_1.Schema.string()
|
|
61
|
+
.description('管理员审批操作所在的群ID(播报群)')
|
|
62
|
+
.default('290238092'),
|
|
63
|
+
approveAutoBindEmoji: koishi_1.Schema.string()
|
|
64
|
+
.description('批准并自动绑定的表情ID(/太赞了)')
|
|
65
|
+
.default('389'),
|
|
66
|
+
approveInteractiveBindEmoji: koishi_1.Schema.string()
|
|
67
|
+
.description('批准并交互式绑定的表情ID(/偷感)')
|
|
68
|
+
.default('427'),
|
|
69
|
+
rejectEmoji: koishi_1.Schema.string().description('拒绝申请的表情ID(/NO)').default('123'),
|
|
70
|
+
autoCleanupHours: koishi_1.Schema.number()
|
|
71
|
+
.description('待审批记录自动清理时间(小时)')
|
|
72
|
+
.default(24)
|
|
73
|
+
}).description('入群申请审批功能配置'),
|
|
75
74
|
servers: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
76
|
-
id: koishi_1.Schema.string()
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
name: koishi_1.Schema.string()
|
|
80
|
-
.description('服务器名称(用于指令显示)')
|
|
81
|
-
.required(),
|
|
82
|
-
enabled: koishi_1.Schema.boolean()
|
|
83
|
-
.description('服务器是否启用')
|
|
84
|
-
.default(true),
|
|
75
|
+
id: koishi_1.Schema.string().description('服务器唯一ID(不允许重复)').required(),
|
|
76
|
+
name: koishi_1.Schema.string().description('服务器名称(用于指令显示)').required(),
|
|
77
|
+
enabled: koishi_1.Schema.boolean().description('服务器是否启用').default(true),
|
|
85
78
|
displayAddress: koishi_1.Schema.string()
|
|
86
79
|
.description('服务器展示地址(显示给用户的连接地址)')
|
|
87
80
|
.default(''),
|
|
@@ -91,9 +84,7 @@ exports.Config = koishi_1.Schema.object({
|
|
|
91
84
|
rconAddress: koishi_1.Schema.string()
|
|
92
85
|
.description('RCON地址,格式为 IP:端口,例如 127.0.0.1:25575')
|
|
93
86
|
.required(),
|
|
94
|
-
rconPassword: koishi_1.Schema.string()
|
|
95
|
-
.description('RCON密码')
|
|
96
|
-
.default(''),
|
|
87
|
+
rconPassword: koishi_1.Schema.string().description('RCON密码').default(''),
|
|
97
88
|
addCommand: koishi_1.Schema.string()
|
|
98
89
|
.description('添加白名单命令模板,使用${MCID}作为替换符')
|
|
99
90
|
.default('whitelist add ${MCID}'),
|
|
@@ -103,14 +94,16 @@ exports.Config = koishi_1.Schema.object({
|
|
|
103
94
|
idType: koishi_1.Schema.union([
|
|
104
95
|
koishi_1.Schema.const('username').description('使用用户名'),
|
|
105
96
|
koishi_1.Schema.const('uuid').description('使用UUID')
|
|
106
|
-
])
|
|
107
|
-
|
|
108
|
-
.description('
|
|
109
|
-
|
|
97
|
+
])
|
|
98
|
+
.default('username')
|
|
99
|
+
.description('白名单添加时使用的ID类型'),
|
|
100
|
+
allowSelfApply: koishi_1.Schema.boolean().description('是否允许用户自行申请白名单').default(false),
|
|
110
101
|
acceptEmptyResponse: koishi_1.Schema.boolean()
|
|
111
102
|
.description('是否将命令的空响应视为成功(某些服务器成功执行命令后不返回内容,仅对本服务器生效)')
|
|
112
|
-
.default(false)
|
|
113
|
-
}))
|
|
103
|
+
.default(false)
|
|
104
|
+
}))
|
|
105
|
+
.description('Minecraft服务器配置列表')
|
|
106
|
+
.default([])
|
|
114
107
|
});
|
|
115
108
|
function apply(ctx, config) {
|
|
116
109
|
// 创建日志服务
|
|
@@ -153,7 +146,7 @@ function apply(ctx, config) {
|
|
|
153
146
|
const lastReminder = reminderCooldown.get(userId);
|
|
154
147
|
if (!lastReminder)
|
|
155
148
|
return false;
|
|
156
|
-
return
|
|
149
|
+
return Date.now() - lastReminder < REMINDER_COOLDOWN_TIME;
|
|
157
150
|
};
|
|
158
151
|
// 设置用户提醒冷却
|
|
159
152
|
const setReminderCooldown = (userId) => {
|
|
@@ -219,7 +212,12 @@ function apply(ctx, config) {
|
|
|
219
212
|
// 发送超时消息,@用户
|
|
220
213
|
const normalizedUser = normalizeQQId(userId);
|
|
221
214
|
ctx.bots.forEach(bot => {
|
|
222
|
-
bot
|
|
215
|
+
bot
|
|
216
|
+
.sendMessage(channelId, [
|
|
217
|
+
koishi_1.h.at(normalizedUser),
|
|
218
|
+
koishi_1.h.text(' 绑定会话已超时,请重新开始绑定流程\n\n⚠️ 温馨提醒:若在管理员多次提醒后仍不配合绑定账号信息,将按群规进行相应处理。')
|
|
219
|
+
])
|
|
220
|
+
.catch(() => { });
|
|
223
221
|
});
|
|
224
222
|
logger.info(`[交互绑定] QQ(${normalizedUser})的绑定会话因超时被清理`);
|
|
225
223
|
}, BINDING_SESSION_TIMEOUT);
|
|
@@ -259,7 +257,39 @@ function apply(ctx, config) {
|
|
|
259
257
|
if (!content)
|
|
260
258
|
return false;
|
|
261
259
|
// 常见的聊天用语或明显无关的内容
|
|
262
|
-
const chatKeywords = [
|
|
260
|
+
const chatKeywords = [
|
|
261
|
+
'你好',
|
|
262
|
+
'hello',
|
|
263
|
+
'hi',
|
|
264
|
+
'在吗',
|
|
265
|
+
'在不在',
|
|
266
|
+
'怎么样',
|
|
267
|
+
'什么',
|
|
268
|
+
'为什么',
|
|
269
|
+
'好的',
|
|
270
|
+
'谢谢',
|
|
271
|
+
'哈哈',
|
|
272
|
+
'呵呵',
|
|
273
|
+
'早上好',
|
|
274
|
+
'晚上好',
|
|
275
|
+
'晚安',
|
|
276
|
+
'再见',
|
|
277
|
+
'拜拜',
|
|
278
|
+
'666',
|
|
279
|
+
'牛',
|
|
280
|
+
'厉害',
|
|
281
|
+
'真的吗',
|
|
282
|
+
'不是吧',
|
|
283
|
+
'哇',
|
|
284
|
+
'哦',
|
|
285
|
+
'嗯',
|
|
286
|
+
'好吧',
|
|
287
|
+
'行',
|
|
288
|
+
'可以',
|
|
289
|
+
'没事',
|
|
290
|
+
'没问题',
|
|
291
|
+
'没关系'
|
|
292
|
+
];
|
|
263
293
|
const lowercaseContent = content.toLowerCase();
|
|
264
294
|
// 检查是否包含明显的聊天用语
|
|
265
295
|
if (chatKeywords.some(keyword => lowercaseContent.includes(keyword))) {
|
|
@@ -285,7 +315,10 @@ function apply(ctx, config) {
|
|
|
285
315
|
return true;
|
|
286
316
|
}
|
|
287
317
|
// 如果是明显的指令格式
|
|
288
|
-
if (content.startsWith('.') ||
|
|
318
|
+
if (content.startsWith('.') ||
|
|
319
|
+
content.startsWith('/') ||
|
|
320
|
+
content.startsWith('mcid') ||
|
|
321
|
+
content.startsWith('buid')) {
|
|
289
322
|
return true;
|
|
290
323
|
}
|
|
291
324
|
}
|
|
@@ -303,7 +336,10 @@ function apply(ctx, config) {
|
|
|
303
336
|
return true;
|
|
304
337
|
}
|
|
305
338
|
// 如果是明显的指令格式
|
|
306
|
-
if (content.startsWith('.') ||
|
|
339
|
+
if (content.startsWith('.') ||
|
|
340
|
+
content.startsWith('/') ||
|
|
341
|
+
content.startsWith('mcid') ||
|
|
342
|
+
content.startsWith('buid')) {
|
|
307
343
|
return true;
|
|
308
344
|
}
|
|
309
345
|
}
|
|
@@ -314,9 +350,9 @@ function apply(ctx, config) {
|
|
|
314
350
|
const getCommandPrefix = () => {
|
|
315
351
|
if (config.allowTextPrefix && config.botNickname) {
|
|
316
352
|
// 检查botNickname是否已经包含@符号,避免重复添加
|
|
317
|
-
const nickname = config.botNickname.startsWith('@')
|
|
318
|
-
config.botNickname
|
|
319
|
-
`@${config.botNickname}`;
|
|
353
|
+
const nickname = config.botNickname.startsWith('@')
|
|
354
|
+
? config.botNickname
|
|
355
|
+
: `@${config.botNickname}`;
|
|
320
356
|
return `${nickname} `;
|
|
321
357
|
}
|
|
322
358
|
return '';
|
|
@@ -413,7 +449,8 @@ function apply(ctx, config) {
|
|
|
413
449
|
}
|
|
414
450
|
else {
|
|
415
451
|
// 不在禁言时间,自动启动交互式绑定
|
|
416
|
-
welcomeMessage +=
|
|
452
|
+
welcomeMessage +=
|
|
453
|
+
'📋 请选择绑定方式:\n1️⃣ 发送您的B站UID进行B站绑定\n2️⃣ 发送"跳过"仅绑定MC账号';
|
|
417
454
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
418
455
|
logger.info(`[新人绑定] 为新成员QQ(${normalizedUserId})自动启动交互式绑定流程`);
|
|
419
456
|
// 创建绑定会话并发送初始提示
|
|
@@ -428,7 +465,7 @@ function apply(ctx, config) {
|
|
|
428
465
|
if (existingBind.mcUsername.startsWith('_temp_')) {
|
|
429
466
|
// 临时用户名,实际上应该是只绑定了B站但MC是临时的,不应该进入这个分支
|
|
430
467
|
// 这种情况应该按照"只绑定了B站"处理
|
|
431
|
-
welcomeMessage +=
|
|
468
|
+
welcomeMessage += '📋 检测到您已绑定B站账号,但尚未绑定MC账号\n';
|
|
432
469
|
welcomeMessage += `🎮 可使用 ${formatCommand('mcid bind <MC用户名>')} 绑定MC账号`;
|
|
433
470
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
434
471
|
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})实际只绑定了B站(MC为临时用户名),已发送绑定提醒`);
|
|
@@ -445,7 +482,7 @@ function apply(ctx, config) {
|
|
|
445
482
|
}
|
|
446
483
|
else {
|
|
447
484
|
// 不在禁言时间,自动启动B站绑定
|
|
448
|
-
welcomeMessage +=
|
|
485
|
+
welcomeMessage += '📋 请发送您的B站UID进行绑定';
|
|
449
486
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
450
487
|
logger.info(`[新人绑定] 为新成员QQ(${normalizedUserId})自动启动B站绑定流程`);
|
|
451
488
|
// 创建绑定会话,直接进入B站绑定步骤
|
|
@@ -458,14 +495,16 @@ function apply(ctx, config) {
|
|
|
458
495
|
}
|
|
459
496
|
else if (!existingBind.mcUsername && existingBind.buidUid) {
|
|
460
497
|
// 只绑定了B站,未绑定MC - 仅发送提醒
|
|
461
|
-
welcomeMessage +=
|
|
498
|
+
welcomeMessage += '📋 检测到您已绑定B站账号,但尚未绑定MC账号\n';
|
|
462
499
|
welcomeMessage += `🎮 可使用 ${formatCommand('mcid bind <MC用户名>')} 绑定MC账号`;
|
|
463
500
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
464
501
|
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})已绑定B站但未绑定MC,已发送绑定提醒`);
|
|
465
502
|
}
|
|
466
|
-
else if (existingBind.mcUsername &&
|
|
503
|
+
else if (existingBind.mcUsername &&
|
|
504
|
+
existingBind.mcUsername.startsWith('_temp_') &&
|
|
505
|
+
existingBind.buidUid) {
|
|
467
506
|
// MC是临时用户名但已绑定B站 - 也按照"只绑定了B站"处理
|
|
468
|
-
welcomeMessage +=
|
|
507
|
+
welcomeMessage += '📋 检测到您已绑定B站账号,但尚未绑定MC账号\n';
|
|
469
508
|
welcomeMessage += `🎮 可使用 ${formatCommand('mcid bind <MC用户名>')} 绑定MC账号`;
|
|
470
509
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
471
510
|
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})已绑定B站但MC为临时用户名,已发送绑定提醒`);
|
|
@@ -479,10 +518,10 @@ function apply(ctx, config) {
|
|
|
479
518
|
// 注册天选开奖 Webhook
|
|
480
519
|
ctx.server.post('/lottery', async (content) => {
|
|
481
520
|
try {
|
|
482
|
-
logger.info(
|
|
521
|
+
logger.info('[天选开奖] 收到天选开奖webhook请求');
|
|
483
522
|
// 检查天选播报开关
|
|
484
523
|
if (!config?.enableLotteryBroadcast) {
|
|
485
|
-
logger.info(
|
|
524
|
+
logger.info('[天选开奖] 天选播报功能已禁用,忽略webhook请求');
|
|
486
525
|
content.status = 200;
|
|
487
526
|
content.body = 'Lottery broadcast disabled';
|
|
488
527
|
return;
|
|
@@ -520,7 +559,7 @@ function apply(ctx, config) {
|
|
|
520
559
|
return;
|
|
521
560
|
}
|
|
522
561
|
if (!lotteryData.lottery_id || !lotteryData.winners || !Array.isArray(lotteryData.winners)) {
|
|
523
|
-
logger.warn(
|
|
562
|
+
logger.warn('[天选开奖] 数据格式不完整');
|
|
524
563
|
content.status = 400;
|
|
525
564
|
content.body = 'Incomplete data format';
|
|
526
565
|
return;
|
|
@@ -549,92 +588,92 @@ function apply(ctx, config) {
|
|
|
549
588
|
// 在数据库中创建MCIDBIND表
|
|
550
589
|
ctx.model.extend('mcidbind', {
|
|
551
590
|
qqId: {
|
|
552
|
-
type: 'string'
|
|
591
|
+
type: 'string'
|
|
553
592
|
},
|
|
554
593
|
mcUsername: {
|
|
555
594
|
type: 'string',
|
|
556
|
-
initial: ''
|
|
595
|
+
initial: ''
|
|
557
596
|
},
|
|
558
597
|
mcUuid: {
|
|
559
598
|
type: 'string',
|
|
560
|
-
initial: ''
|
|
599
|
+
initial: ''
|
|
561
600
|
},
|
|
562
601
|
lastModified: {
|
|
563
602
|
type: 'timestamp',
|
|
564
|
-
initial: null
|
|
603
|
+
initial: null
|
|
565
604
|
},
|
|
566
605
|
isAdmin: {
|
|
567
606
|
type: 'boolean',
|
|
568
|
-
initial: false
|
|
607
|
+
initial: false
|
|
569
608
|
},
|
|
570
609
|
whitelist: {
|
|
571
610
|
type: 'json',
|
|
572
|
-
initial: []
|
|
611
|
+
initial: []
|
|
573
612
|
},
|
|
574
613
|
tags: {
|
|
575
614
|
type: 'json',
|
|
576
|
-
initial: []
|
|
615
|
+
initial: []
|
|
577
616
|
},
|
|
578
617
|
// BUID相关字段
|
|
579
618
|
buidUid: {
|
|
580
619
|
type: 'string',
|
|
581
|
-
initial: ''
|
|
620
|
+
initial: ''
|
|
582
621
|
},
|
|
583
622
|
buidUsername: {
|
|
584
623
|
type: 'string',
|
|
585
|
-
initial: ''
|
|
624
|
+
initial: ''
|
|
586
625
|
},
|
|
587
626
|
guardLevel: {
|
|
588
627
|
type: 'integer',
|
|
589
|
-
initial: 0
|
|
628
|
+
initial: 0
|
|
590
629
|
},
|
|
591
630
|
guardLevelText: {
|
|
592
631
|
type: 'string',
|
|
593
|
-
initial: ''
|
|
632
|
+
initial: ''
|
|
594
633
|
},
|
|
595
634
|
maxGuardLevel: {
|
|
596
635
|
type: 'integer',
|
|
597
|
-
initial: 0
|
|
636
|
+
initial: 0
|
|
598
637
|
},
|
|
599
638
|
maxGuardLevelText: {
|
|
600
639
|
type: 'string',
|
|
601
|
-
initial: ''
|
|
640
|
+
initial: ''
|
|
602
641
|
},
|
|
603
642
|
medalName: {
|
|
604
643
|
type: 'string',
|
|
605
|
-
initial: ''
|
|
644
|
+
initial: ''
|
|
606
645
|
},
|
|
607
646
|
medalLevel: {
|
|
608
647
|
type: 'integer',
|
|
609
|
-
initial: 0
|
|
648
|
+
initial: 0
|
|
610
649
|
},
|
|
611
650
|
wealthMedalLevel: {
|
|
612
651
|
type: 'integer',
|
|
613
|
-
initial: 0
|
|
652
|
+
initial: 0
|
|
614
653
|
},
|
|
615
654
|
lastActiveTime: {
|
|
616
655
|
type: 'timestamp',
|
|
617
|
-
initial: null
|
|
656
|
+
initial: null
|
|
618
657
|
},
|
|
619
658
|
reminderCount: {
|
|
620
659
|
type: 'integer',
|
|
621
|
-
initial: 0
|
|
660
|
+
initial: 0
|
|
622
661
|
},
|
|
623
662
|
usernameLastChecked: {
|
|
624
663
|
type: 'timestamp',
|
|
625
|
-
initial: null
|
|
664
|
+
initial: null
|
|
626
665
|
},
|
|
627
666
|
usernameCheckFailCount: {
|
|
628
667
|
type: 'integer',
|
|
629
|
-
initial: 0
|
|
630
|
-
}
|
|
668
|
+
initial: 0
|
|
669
|
+
}
|
|
631
670
|
}, {
|
|
632
671
|
// 设置主键为qqId
|
|
633
672
|
primary: 'qqId',
|
|
634
673
|
// 添加索引
|
|
635
674
|
unique: [['mcUsername'], ['buidUid']],
|
|
636
675
|
// 添加isAdmin索引,提高查询效率
|
|
637
|
-
indexes: [['isAdmin'], ['buidUid']]
|
|
676
|
+
indexes: [['isAdmin'], ['buidUid']]
|
|
638
677
|
});
|
|
639
678
|
// 检查表结构是否包含旧字段
|
|
640
679
|
const checkTableStructure = async () => {
|
|
@@ -698,7 +737,7 @@ function apply(ctx, config) {
|
|
|
698
737
|
logger.info(`[初始化] 成功为${updatedCount}条记录添加缺失字段`);
|
|
699
738
|
}
|
|
700
739
|
else {
|
|
701
|
-
logger.info(
|
|
740
|
+
logger.info('[初始化] 所有记录都包含必要字段,无需更新');
|
|
702
741
|
}
|
|
703
742
|
return true;
|
|
704
743
|
}
|
|
@@ -717,7 +756,8 @@ function apply(ctx, config) {
|
|
|
717
756
|
const backupData = JSON.parse(JSON.stringify(oldRecords));
|
|
718
757
|
try {
|
|
719
758
|
// 提取有效数据
|
|
720
|
-
const validRecords = oldRecords
|
|
759
|
+
const validRecords = oldRecords
|
|
760
|
+
.map(record => {
|
|
721
761
|
// 确保qqId存在
|
|
722
762
|
if (!record.qqId) {
|
|
723
763
|
// 如果没有qqId但有userId,尝试从userId提取
|
|
@@ -738,7 +778,8 @@ function apply(ctx, config) {
|
|
|
738
778
|
whitelist: record.whitelist || [],
|
|
739
779
|
tags: record.tags || []
|
|
740
780
|
};
|
|
741
|
-
})
|
|
781
|
+
})
|
|
782
|
+
.filter(record => record !== null);
|
|
742
783
|
// 删除现有表
|
|
743
784
|
await mcidbindRepo.deleteAll();
|
|
744
785
|
logger.info('[初始化] 成功删除旧表数据');
|
|
@@ -786,7 +827,7 @@ function apply(ctx, config) {
|
|
|
786
827
|
const normalizeQQId = (userId) => {
|
|
787
828
|
// 处理空值情况
|
|
788
829
|
if (!userId) {
|
|
789
|
-
logger.warn(
|
|
830
|
+
logger.warn('[用户ID] 收到空用户ID');
|
|
790
831
|
return '';
|
|
791
832
|
}
|
|
792
833
|
let extractedId = '';
|
|
@@ -840,8 +881,12 @@ function apply(ctx, config) {
|
|
|
840
881
|
// 处理私聊和群聊的消息格式
|
|
841
882
|
// 主动消息不引用原消息
|
|
842
883
|
const promptMessage = session.channelId?.startsWith('private:')
|
|
843
|
-
?
|
|
844
|
-
|
|
884
|
+
? isProactiveMessage
|
|
885
|
+
? content
|
|
886
|
+
: [koishi_1.h.quote(session.messageId), ...content]
|
|
887
|
+
: isProactiveMessage
|
|
888
|
+
? [koishi_1.h.at(normalizedQQId), '\n', ...content]
|
|
889
|
+
: [koishi_1.h.quote(session.messageId), koishi_1.h.at(normalizedQQId), '\n', ...content];
|
|
845
890
|
// 发送消息并获取返回的消息ID
|
|
846
891
|
const messageResult = await session.send(promptMessage);
|
|
847
892
|
if (config.debugMode) {
|
|
@@ -853,11 +898,18 @@ function apply(ctx, config) {
|
|
|
853
898
|
// 但如果用户在绑定会话中发送聊天消息(不包括指令),不撤回
|
|
854
899
|
// 主动消息不撤回用户消息
|
|
855
900
|
const bindingSession = getBindingSession(session.userId, session.channelId);
|
|
856
|
-
const isBindingCommand = session.content &&
|
|
857
|
-
session.content.
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
901
|
+
const isBindingCommand = session.content &&
|
|
902
|
+
(session.content.trim() === '绑定' ||
|
|
903
|
+
(session.content.includes('@') && session.content.includes('绑定')));
|
|
904
|
+
const shouldNotRecallUserMessage = bindingSession &&
|
|
905
|
+
session.content &&
|
|
906
|
+
!isBindingCommand &&
|
|
907
|
+
checkIrrelevantInput(bindingSession, session.content.trim());
|
|
908
|
+
if (config.recallUserMessage &&
|
|
909
|
+
isGroupMessage &&
|
|
910
|
+
session.messageId &&
|
|
911
|
+
!shouldNotRecallUserMessage &&
|
|
912
|
+
!isProactiveMessage) {
|
|
861
913
|
setTimeout(async () => {
|
|
862
914
|
try {
|
|
863
915
|
await session.bot.deleteMessage(session.channelId, session.messageId);
|
|
@@ -877,7 +929,7 @@ function apply(ctx, config) {
|
|
|
877
929
|
logDebug('消息', `QQ(${normalizedQQId})在绑定会话中发送聊天消息,跳过撤回用户消息`);
|
|
878
930
|
}
|
|
879
931
|
else if (isProactiveMessage && config.debugMode) {
|
|
880
|
-
logDebug('消息',
|
|
932
|
+
logDebug('消息', '主动发送的消息,跳过撤回用户消息');
|
|
881
933
|
}
|
|
882
934
|
// 处理撤回机器人消息 - 只在群聊中撤回机器人自己的消息
|
|
883
935
|
// 检查是否为不应撤回的重要提示消息(只有绑定会话超时提醒)
|
|
@@ -904,9 +956,10 @@ function apply(ctx, config) {
|
|
|
904
956
|
}
|
|
905
957
|
else if (messageResult && typeof messageResult === 'object') {
|
|
906
958
|
// 尝试提取各种可能的消息ID格式
|
|
907
|
-
messageId =
|
|
908
|
-
messageResult.
|
|
909
|
-
|
|
959
|
+
messageId =
|
|
960
|
+
messageResult.messageId ||
|
|
961
|
+
messageResult.id ||
|
|
962
|
+
messageResult.message_id;
|
|
910
963
|
}
|
|
911
964
|
if (messageId) {
|
|
912
965
|
// 设置定时器延迟撤回
|
|
@@ -926,11 +979,11 @@ function apply(ctx, config) {
|
|
|
926
979
|
}
|
|
927
980
|
}
|
|
928
981
|
else if (config.debugMode) {
|
|
929
|
-
logWarn('消息',
|
|
982
|
+
logWarn('消息', '无法获取消息ID,自动撤回功能无法生效');
|
|
930
983
|
}
|
|
931
984
|
}
|
|
932
985
|
else if (config.debugMode) {
|
|
933
|
-
logDebug('消息',
|
|
986
|
+
logDebug('消息', '检测到私聊消息,不撤回机器人回复');
|
|
934
987
|
}
|
|
935
988
|
}
|
|
936
989
|
}
|
|
@@ -964,7 +1017,7 @@ function apply(ctx, config) {
|
|
|
964
1017
|
const getServerConfigById = (serverId) => {
|
|
965
1018
|
if (!config.servers || !Array.isArray(config.servers))
|
|
966
1019
|
return null;
|
|
967
|
-
return config.servers.find(server => server.id === serverId &&
|
|
1020
|
+
return config.servers.find(server => server.id === serverId && server.enabled !== false) || null;
|
|
968
1021
|
};
|
|
969
1022
|
// 根据服务器名称获取服务器配置
|
|
970
1023
|
const getServerConfigByName = (serverName) => {
|
|
@@ -1097,6 +1150,9 @@ function apply(ctx, config) {
|
|
|
1097
1150
|
// 实例化McidCommandHandler并注册命令
|
|
1098
1151
|
const mcidHandler = new handlers_1.McidCommandHandler(ctx, config, loggerService, repositories, handlerDependencies);
|
|
1099
1152
|
mcidHandler.register();
|
|
1153
|
+
// 实例化并注册入群申请审批Handler
|
|
1154
|
+
const groupRequestReviewHandler = new handlers_1.GroupRequestReviewHandler(ctx, config, loggerService, repositories, handlerDependencies);
|
|
1155
|
+
groupRequestReviewHandler.register();
|
|
1100
1156
|
// 自定义文本前缀匹配
|
|
1101
1157
|
if (config.allowTextPrefix && config.botNickname) {
|
|
1102
1158
|
// 创建一个前缀匹配器
|
|
@@ -1118,9 +1174,9 @@ function apply(ctx, config) {
|
|
|
1118
1174
|
const regularPrefixRegex = new RegExp(`^${escapeRegExp(botNickname)}\\s+((mcid|buid|绑定|bind)\\s*.*)$`, 'i');
|
|
1119
1175
|
const regularMatch = content.match(regularPrefixRegex);
|
|
1120
1176
|
// 2. 如果botNickname不包含@,也尝试匹配带@的版本
|
|
1121
|
-
const atPrefixRegex = !botNickname.startsWith('@')
|
|
1122
|
-
new RegExp(`^@${escapeRegExp(botNickname)}\\s+((mcid|buid|绑定|bind)\\s*.*)$`, 'i')
|
|
1123
|
-
null;
|
|
1177
|
+
const atPrefixRegex = !botNickname.startsWith('@')
|
|
1178
|
+
? new RegExp(`^@${escapeRegExp(botNickname)}\\s+((mcid|buid|绑定|bind)\\s*.*)$`, 'i')
|
|
1179
|
+
: null;
|
|
1124
1180
|
if (regularMatch && regularMatch[1]) {
|
|
1125
1181
|
matchedCommand = regularMatch[1].trim();
|
|
1126
1182
|
}
|
|
@@ -1165,8 +1221,12 @@ function apply(ctx, config) {
|
|
|
1165
1221
|
return next();
|
|
1166
1222
|
}
|
|
1167
1223
|
// 跳过空消息或命令消息
|
|
1168
|
-
if (!session.content ||
|
|
1169
|
-
session.content.
|
|
1224
|
+
if (!session.content ||
|
|
1225
|
+
session.content.startsWith('.') ||
|
|
1226
|
+
session.content.startsWith('/') ||
|
|
1227
|
+
session.content.includes('mcid') ||
|
|
1228
|
+
session.content.includes('buid') ||
|
|
1229
|
+
session.content.includes('绑定')) {
|
|
1170
1230
|
return next();
|
|
1171
1231
|
}
|
|
1172
1232
|
// 检查当前时间是否在群组禁言时间段内
|
|
@@ -1182,7 +1242,7 @@ function apply(ctx, config) {
|
|
|
1182
1242
|
}
|
|
1183
1243
|
// 随机触发概率:管理员 1%,普通用户 80%,避免过于频繁
|
|
1184
1244
|
const isUserAdmin = await isAdmin(session.userId);
|
|
1185
|
-
const triggerRate = isUserAdmin ? 0.01 : 0.
|
|
1245
|
+
const triggerRate = isUserAdmin ? 0.01 : 0.8;
|
|
1186
1246
|
if (Math.random() > triggerRate) {
|
|
1187
1247
|
return next();
|
|
1188
1248
|
}
|
|
@@ -1242,7 +1302,9 @@ function apply(ctx, config) {
|
|
|
1242
1302
|
return next();
|
|
1243
1303
|
}
|
|
1244
1304
|
// 情况2:只绑定了B站,未绑定MC
|
|
1245
|
-
if (bind.buidUid &&
|
|
1305
|
+
if (bind.buidUid &&
|
|
1306
|
+
bind.buidUsername &&
|
|
1307
|
+
(!bind.mcUsername || bind.mcUsername.startsWith('_temp_'))) {
|
|
1246
1308
|
const mcInfo = null;
|
|
1247
1309
|
const isNicknameCorrect = services.nickname.checkNicknameFormat(currentNickname, bind.buidUsername, mcInfo);
|
|
1248
1310
|
if (!isNicknameCorrect) {
|
|
@@ -1263,7 +1325,10 @@ function apply(ctx, config) {
|
|
|
1263
1325
|
return next();
|
|
1264
1326
|
}
|
|
1265
1327
|
// 情况3:都已绑定,但群昵称格式不正确
|
|
1266
|
-
if (bind.buidUid &&
|
|
1328
|
+
if (bind.buidUid &&
|
|
1329
|
+
bind.buidUsername &&
|
|
1330
|
+
bind.mcUsername &&
|
|
1331
|
+
!bind.mcUsername.startsWith('_temp_')) {
|
|
1267
1332
|
const isNicknameCorrect = services.nickname.checkNicknameFormat(currentNickname, bind.buidUsername, bind.mcUsername);
|
|
1268
1333
|
if (!isNicknameCorrect) {
|
|
1269
1334
|
// 更新提醒次数
|
|
@@ -1305,19 +1370,26 @@ function apply(ctx, config) {
|
|
|
1305
1370
|
if (content === '取消' || content === 'cancel') {
|
|
1306
1371
|
removeBindingSession(session.userId, session.channelId);
|
|
1307
1372
|
logger.info(`[交互绑定] QQ(${normalizedUserId})手动取消了绑定会话`);
|
|
1308
|
-
await sendMessage(session, [
|
|
1373
|
+
await sendMessage(session, [
|
|
1374
|
+
koishi_1.h.text('❌ 绑定会话已取消\n\n📋 温馨提醒:请按群规设置合适的群昵称。若在管理员多次提醒后仍不配合绑定账号信息或按规修改群昵称,将按群规进行相应处理。')
|
|
1375
|
+
]);
|
|
1309
1376
|
return;
|
|
1310
1377
|
}
|
|
1311
1378
|
// 检查是否在绑定过程中使用了其他绑定相关命令(排除跳过选项)
|
|
1312
1379
|
// 这里使用原始内容检测命令,避免误判@Bot发送的正常输入
|
|
1313
|
-
if (rawContent &&
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
rawContent.includes('
|
|
1317
|
-
|
|
1318
|
-
|
|
1380
|
+
if (rawContent &&
|
|
1381
|
+
content !== '跳过' &&
|
|
1382
|
+
content !== 'skip' &&
|
|
1383
|
+
(rawContent.includes('绑定') ||
|
|
1384
|
+
rawContent.includes('bind') ||
|
|
1385
|
+
rawContent.includes('mcid') ||
|
|
1386
|
+
rawContent.includes('buid') ||
|
|
1387
|
+
rawContent.startsWith('.') ||
|
|
1388
|
+
rawContent.startsWith('/'))) {
|
|
1319
1389
|
const currentState = bindingSession.state === 'waiting_mc_username' ? 'MC用户名' : 'B站UID';
|
|
1320
|
-
await sendMessage(session, [
|
|
1390
|
+
await sendMessage(session, [
|
|
1391
|
+
koishi_1.h.text(`🔄 您正在进行交互式绑定,请继续输入${currentState}\n\n如需取消当前绑定,请发送"取消"`)
|
|
1392
|
+
]);
|
|
1321
1393
|
return;
|
|
1322
1394
|
}
|
|
1323
1395
|
// 检查是否为明显无关的输入
|
|
@@ -1329,7 +1401,39 @@ function apply(ctx, config) {
|
|
|
1329
1401
|
invalidInputCount: newCount
|
|
1330
1402
|
});
|
|
1331
1403
|
// 检查是否为明显的聊天内容(使用清理后的内容)
|
|
1332
|
-
const chatKeywords = [
|
|
1404
|
+
const chatKeywords = [
|
|
1405
|
+
'你好',
|
|
1406
|
+
'hello',
|
|
1407
|
+
'hi',
|
|
1408
|
+
'在吗',
|
|
1409
|
+
'在不在',
|
|
1410
|
+
'怎么样',
|
|
1411
|
+
'什么',
|
|
1412
|
+
'为什么',
|
|
1413
|
+
'好的',
|
|
1414
|
+
'谢谢',
|
|
1415
|
+
'哈哈',
|
|
1416
|
+
'呵呵',
|
|
1417
|
+
'早上好',
|
|
1418
|
+
'晚上好',
|
|
1419
|
+
'晚安',
|
|
1420
|
+
'再见',
|
|
1421
|
+
'拜拜',
|
|
1422
|
+
'666',
|
|
1423
|
+
'牛',
|
|
1424
|
+
'厉害',
|
|
1425
|
+
'真的吗',
|
|
1426
|
+
'不是吧',
|
|
1427
|
+
'哇',
|
|
1428
|
+
'哦',
|
|
1429
|
+
'嗯',
|
|
1430
|
+
'好吧',
|
|
1431
|
+
'行',
|
|
1432
|
+
'可以',
|
|
1433
|
+
'没事',
|
|
1434
|
+
'没问题',
|
|
1435
|
+
'没关系'
|
|
1436
|
+
];
|
|
1333
1437
|
const isChatMessage = chatKeywords.some(keyword => content.toLowerCase().includes(keyword)) ||
|
|
1334
1438
|
/[!?。,;:""''()【】〈〉《》「」『』〔〕〖〗〘〙〚〛]{2,}/.test(content) ||
|
|
1335
1439
|
/[!?.,;:"'()[\]<>{}]{3,}/.test(content);
|
|
@@ -1339,13 +1443,17 @@ function apply(ctx, config) {
|
|
|
1339
1443
|
removeBindingSession(session.userId, session.channelId);
|
|
1340
1444
|
logger.info(`[交互绑定] QQ(${normalizedUserId})持续发送聊天消息,自动取消绑定会话避免打扰`);
|
|
1341
1445
|
// 对于聊天取消,给一个更温和的提示,同时提醒群规
|
|
1342
|
-
await sendMessage(session, [
|
|
1446
|
+
await sendMessage(session, [
|
|
1447
|
+
koishi_1.h.text(`💬 看起来您在聊天,绑定流程已自动取消\n\n📋 温馨提醒:请按群规设置合适的群昵称。若在管理员多次提醒后仍不配合绑定账号信息或按规修改群昵称,将按群规进行相应处理。\n\n如需绑定账号,请随时使用 ${formatCommand('绑定')} 命令重新开始`)
|
|
1448
|
+
]);
|
|
1343
1449
|
return;
|
|
1344
1450
|
}
|
|
1345
1451
|
else {
|
|
1346
1452
|
// 第一次聊天消息,给温和提醒
|
|
1347
1453
|
const expectedInput = bindingSession.state === 'waiting_mc_username' ? 'MC用户名' : 'B站UID';
|
|
1348
|
-
await sendMessage(session, [
|
|
1454
|
+
await sendMessage(session, [
|
|
1455
|
+
koishi_1.h.text(`💭 您当前正在进行账号绑定,需要输入${expectedInput}\n\n如不需要绑定,请发送"取消",或继续聊天我们会自动取消绑定流程`)
|
|
1456
|
+
]);
|
|
1349
1457
|
return;
|
|
1350
1458
|
}
|
|
1351
1459
|
}
|
|
@@ -1354,14 +1462,20 @@ function apply(ctx, config) {
|
|
|
1354
1462
|
if (newCount === 1) {
|
|
1355
1463
|
// 第1次无关输入,提醒检查
|
|
1356
1464
|
const expectedInput = bindingSession.state === 'waiting_mc_username' ? 'MC用户名' : 'B站UID';
|
|
1357
|
-
await sendMessage(session, [
|
|
1465
|
+
await sendMessage(session, [
|
|
1466
|
+
koishi_1.h.text(`🤔 您当前正在进行绑定流程,需要输入${expectedInput}\n\n如果您想取消绑定,请发送"取消"`)
|
|
1467
|
+
]);
|
|
1358
1468
|
return;
|
|
1359
1469
|
}
|
|
1360
1470
|
else if (newCount >= 2) {
|
|
1361
1471
|
// 第2次无关输入,建议取消
|
|
1362
1472
|
removeBindingSession(session.userId, session.channelId);
|
|
1363
1473
|
logger.info(`[交互绑定] QQ(${normalizedUserId})因多次无关输入自动取消绑定会话`);
|
|
1364
|
-
await sendMessage(session, [
|
|
1474
|
+
await sendMessage(session, [
|
|
1475
|
+
koishi_1.h.text('🔄 检测到您可能不想继续绑定流程,已自动取消绑定会话\n\n📋 温馨提醒:请按群规设置合适的群昵称。若在管理员多次提醒后仍不配合绑定账号信息或按规修改群昵称,将按群规进行相应处理。\n\n如需重新绑定,请使用 ' +
|
|
1476
|
+
formatCommand('绑定') +
|
|
1477
|
+
' 命令')
|
|
1478
|
+
]);
|
|
1365
1479
|
return;
|
|
1366
1480
|
}
|
|
1367
1481
|
}
|
|
@@ -1409,7 +1523,9 @@ function apply(ctx, config) {
|
|
|
1409
1523
|
}
|
|
1410
1524
|
await sendMessage(session, [
|
|
1411
1525
|
koishi_1.h.text(`🎉 绑定完成!\nMC: 未绑定\nB站: ${existingBind.buidUsername}\n\n💡 您可以随时使用 ${formatCommand('mcid bind <用户名>')} 绑定MC账号`),
|
|
1412
|
-
...(config?.showAvatar
|
|
1526
|
+
...(config?.showAvatar
|
|
1527
|
+
? [koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${existingBind.buidUid}?size=160`)]
|
|
1528
|
+
: [])
|
|
1413
1529
|
]);
|
|
1414
1530
|
return;
|
|
1415
1531
|
}
|
|
@@ -1446,7 +1562,9 @@ function apply(ctx, config) {
|
|
|
1446
1562
|
const profile = await services.api.validateUsername(content);
|
|
1447
1563
|
if (!profile) {
|
|
1448
1564
|
logger.warn(`[交互绑定] QQ(${normalizedUserId})输入的MC用户名"${content}"不存在`);
|
|
1449
|
-
await sendMessage(session, [
|
|
1565
|
+
await sendMessage(session, [
|
|
1566
|
+
koishi_1.h.text(`❌ 用户名 ${content} 不存在\n请重新输入或发送"跳过"完成绑定`)
|
|
1567
|
+
]);
|
|
1450
1568
|
return;
|
|
1451
1569
|
}
|
|
1452
1570
|
const username = profile.name;
|
|
@@ -1455,22 +1573,28 @@ function apply(ctx, config) {
|
|
|
1455
1573
|
const existingBind = await services.database.getMcBindByQQId(normalizedUserId);
|
|
1456
1574
|
if (existingBind && existingBind.mcUsername && !existingBind.mcUsername.startsWith('_temp_')) {
|
|
1457
1575
|
// 检查冷却时间
|
|
1458
|
-
if (!await isAdmin(session.userId) && !checkCooldown(existingBind.lastModified)) {
|
|
1576
|
+
if (!(await isAdmin(session.userId)) && !checkCooldown(existingBind.lastModified)) {
|
|
1459
1577
|
const days = config.cooldownDays;
|
|
1460
1578
|
const now = new Date();
|
|
1461
1579
|
const diffTime = now.getTime() - existingBind.lastModified.getTime();
|
|
1462
1580
|
const passedDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
1463
1581
|
const remainingDays = days - passedDays;
|
|
1464
1582
|
removeBindingSession(session.userId, session.channelId);
|
|
1465
|
-
const displayUsername = existingBind.mcUsername && !existingBind.mcUsername.startsWith('_temp_')
|
|
1466
|
-
|
|
1583
|
+
const displayUsername = existingBind.mcUsername && !existingBind.mcUsername.startsWith('_temp_')
|
|
1584
|
+
? existingBind.mcUsername
|
|
1585
|
+
: '未绑定';
|
|
1586
|
+
await sendMessage(session, [
|
|
1587
|
+
koishi_1.h.text(`❌ 您已绑定MC账号: ${displayUsername}\n\n如需修改,请在冷却期结束后(还需${remainingDays}天)使用 ${formatCommand('mcid change')} 命令或联系管理员`)
|
|
1588
|
+
]);
|
|
1467
1589
|
return;
|
|
1468
1590
|
}
|
|
1469
1591
|
}
|
|
1470
1592
|
// 检查用户名是否已被其他人绑定
|
|
1471
1593
|
if (await services.database.checkUsernameExists(username, session.userId, uuid)) {
|
|
1472
1594
|
logger.warn(`[交互绑定] MC用户名"${username}"已被其他用户绑定`);
|
|
1473
|
-
await sendMessage(session, [
|
|
1595
|
+
await sendMessage(session, [
|
|
1596
|
+
koishi_1.h.text(`❌ 用户名 ${username} 已被其他用户绑定\n\n请输入其他MC用户名或发送"跳过"完成绑定`)
|
|
1597
|
+
]);
|
|
1474
1598
|
return;
|
|
1475
1599
|
}
|
|
1476
1600
|
// 绑定MC账号
|
|
@@ -1578,20 +1702,26 @@ function apply(ctx, config) {
|
|
|
1578
1702
|
// 验证UID格式
|
|
1579
1703
|
if (!actualUid || !/^\d+$/.test(actualUid)) {
|
|
1580
1704
|
logger.warn(`[交互绑定] QQ(${normalizedUserId})输入的B站UID"${content}"格式无效`);
|
|
1581
|
-
await sendMessage(session, [
|
|
1705
|
+
await sendMessage(session, [
|
|
1706
|
+
koishi_1.h.text('❌ UID格式无效,请重新输入\n支持格式:纯数字、UID:数字、空间链接\n或发送"跳过"仅绑定MC账号')
|
|
1707
|
+
]);
|
|
1582
1708
|
return;
|
|
1583
1709
|
}
|
|
1584
1710
|
// 检查UID是否已被绑定
|
|
1585
1711
|
if (await services.database.checkBuidExists(actualUid, session.userId)) {
|
|
1586
1712
|
logger.warn(`[交互绑定] B站UID"${actualUid}"已被其他用户绑定`);
|
|
1587
|
-
await sendMessage(session, [
|
|
1713
|
+
await sendMessage(session, [
|
|
1714
|
+
koishi_1.h.text(`❌ UID ${actualUid} 已被其他用户绑定\n\n请输入其他B站UID\n或发送"跳过"仅绑定MC账号`)
|
|
1715
|
+
]);
|
|
1588
1716
|
return;
|
|
1589
1717
|
}
|
|
1590
1718
|
// 验证UID是否存在
|
|
1591
1719
|
const buidUser = await services.api.validateBUID(actualUid);
|
|
1592
1720
|
if (!buidUser) {
|
|
1593
1721
|
logger.warn(`[交互绑定] QQ(${normalizedUserId})输入的B站UID"${actualUid}"不存在`);
|
|
1594
|
-
await sendMessage(session, [
|
|
1722
|
+
await sendMessage(session, [
|
|
1723
|
+
koishi_1.h.text(`❌ 无法验证UID: ${actualUid}\n\n该用户可能不存在或未被发现\n可以去直播间发个弹幕后重试绑定\n或发送"跳过"仅绑定MC账号`)
|
|
1724
|
+
]);
|
|
1595
1725
|
return;
|
|
1596
1726
|
}
|
|
1597
1727
|
// 绑定B站账号
|
|
@@ -1600,9 +1730,13 @@ function apply(ctx, config) {
|
|
|
1600
1730
|
logger.error(`[交互绑定] QQ(${normalizedUserId})绑定B站账号失败`);
|
|
1601
1731
|
removeBindingSession(session.userId, session.channelId);
|
|
1602
1732
|
// 根据是否有MC绑定提供不同的提示
|
|
1603
|
-
const displayMcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1733
|
+
const displayMcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1734
|
+
? bindingSession.mcUsername
|
|
1735
|
+
: null;
|
|
1604
1736
|
const mcStatus = displayMcName ? `您的MC账号${displayMcName}已成功绑定\n` : '';
|
|
1605
|
-
await sendMessage(session, [
|
|
1737
|
+
await sendMessage(session, [
|
|
1738
|
+
koishi_1.h.text(`❌ B站账号绑定失败,数据库操作出错\n\n${mcStatus}可稍后使用 ${formatCommand('buid bind <UID>')} 命令单独绑定B站账号`)
|
|
1739
|
+
]);
|
|
1606
1740
|
return;
|
|
1607
1741
|
}
|
|
1608
1742
|
logger.info(`[交互绑定] QQ(${normalizedUserId})成功绑定B站UID: ${actualUid}`);
|
|
@@ -1611,7 +1745,9 @@ function apply(ctx, config) {
|
|
|
1611
1745
|
// 自动群昵称设置功能 - 使用新的autoSetGroupNickname函数
|
|
1612
1746
|
try {
|
|
1613
1747
|
// 检查是否有有效的MC用户名(不是临时用户名)
|
|
1614
|
-
const mcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1748
|
+
const mcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1749
|
+
? bindingSession.mcUsername
|
|
1750
|
+
: null;
|
|
1615
1751
|
await services.nickname.autoSetGroupNickname(session, mcName, buidUser.username, String(buidUser.uid));
|
|
1616
1752
|
logger.info(`[交互绑定] QQ(${normalizedUserId})绑定完成,已设置群昵称`);
|
|
1617
1753
|
}
|
|
@@ -1632,7 +1768,9 @@ function apply(ctx, config) {
|
|
|
1632
1768
|
extraInfo += `\n荣耀等级: ${buidUser.wealthMedalLevel}`;
|
|
1633
1769
|
}
|
|
1634
1770
|
// 准备完成消息
|
|
1635
|
-
const displayMcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1771
|
+
const displayMcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1772
|
+
? bindingSession.mcUsername
|
|
1773
|
+
: null;
|
|
1636
1774
|
const mcInfo = displayMcName ? `MC: ${displayMcName}` : 'MC: 未绑定';
|
|
1637
1775
|
let extraTip = '';
|
|
1638
1776
|
// 如果用户跳过了MC绑定或MC账号是temp,提供后续绑定的指引
|
|
@@ -1641,7 +1779,9 @@ function apply(ctx, config) {
|
|
|
1641
1779
|
}
|
|
1642
1780
|
await sendMessage(session, [
|
|
1643
1781
|
koishi_1.h.text(`🎉 绑定完成!\n${mcInfo}\nB站: ${buidUser.username}${extraInfo}${extraTip}`),
|
|
1644
|
-
...(config?.showAvatar
|
|
1782
|
+
...(config?.showAvatar
|
|
1783
|
+
? [koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${buidUser.uid}?size=160`)]
|
|
1784
|
+
: [])
|
|
1645
1785
|
]);
|
|
1646
1786
|
};
|
|
1647
1787
|
// 帮助函数:转义正则表达式中的特殊字符
|
|
@@ -1661,7 +1801,7 @@ function apply(ctx, config) {
|
|
|
1661
1801
|
// @Bot昵称 格式(如果配置了botNickname)
|
|
1662
1802
|
config.botNickname ? new RegExp(`^@${escapeRegExp(config.botNickname)}\\s+`, 'i') : null,
|
|
1663
1803
|
// @botUserId 格式
|
|
1664
|
-
new RegExp(`^@${escapeRegExp(botUserId)}\\s+`, 'i')
|
|
1804
|
+
new RegExp(`^@${escapeRegExp(botUserId)}\\s+`, 'i')
|
|
1665
1805
|
].filter(Boolean);
|
|
1666
1806
|
let cleanedContent = content.trim();
|
|
1667
1807
|
// 尝试匹配并移除@Bot前缀
|