koishi-plugin-bind-bot 2.1.0 → 2.1.2
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/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 +260 -142
- package/lib/repositories/mcidbind.repository.js +2 -2
- package/lib/repositories/schedule-mute.repository.js +2 -2
- package/lib/services/api.service.js +28 -22
- package/lib/services/database.service.js +16 -14
- package/lib/services/nickname.service.js +10 -10
- package/lib/types/api.d.ts +90 -0
- package/lib/types/config.d.ts +61 -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,20 @@ 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
|
-
forceBindTargetRoomId: koishi_1.Schema.number()
|
|
70
|
-
.description('强制绑定目标房间号')
|
|
71
|
-
.default(544853),
|
|
72
|
-
forceBindTargetMedalName: koishi_1.Schema.string()
|
|
73
|
-
.description('强制绑定目标粉丝牌名称')
|
|
74
|
-
.default('生态'),
|
|
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('生态'),
|
|
75
55
|
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),
|
|
56
|
+
id: koishi_1.Schema.string().description('服务器唯一ID(不允许重复)').required(),
|
|
57
|
+
name: koishi_1.Schema.string().description('服务器名称(用于指令显示)').required(),
|
|
58
|
+
enabled: koishi_1.Schema.boolean().description('服务器是否启用').default(true),
|
|
85
59
|
displayAddress: koishi_1.Schema.string()
|
|
86
60
|
.description('服务器展示地址(显示给用户的连接地址)')
|
|
87
61
|
.default(''),
|
|
@@ -91,9 +65,7 @@ exports.Config = koishi_1.Schema.object({
|
|
|
91
65
|
rconAddress: koishi_1.Schema.string()
|
|
92
66
|
.description('RCON地址,格式为 IP:端口,例如 127.0.0.1:25575')
|
|
93
67
|
.required(),
|
|
94
|
-
rconPassword: koishi_1.Schema.string()
|
|
95
|
-
.description('RCON密码')
|
|
96
|
-
.default(''),
|
|
68
|
+
rconPassword: koishi_1.Schema.string().description('RCON密码').default(''),
|
|
97
69
|
addCommand: koishi_1.Schema.string()
|
|
98
70
|
.description('添加白名单命令模板,使用${MCID}作为替换符')
|
|
99
71
|
.default('whitelist add ${MCID}'),
|
|
@@ -103,14 +75,16 @@ exports.Config = koishi_1.Schema.object({
|
|
|
103
75
|
idType: koishi_1.Schema.union([
|
|
104
76
|
koishi_1.Schema.const('username').description('使用用户名'),
|
|
105
77
|
koishi_1.Schema.const('uuid').description('使用UUID')
|
|
106
|
-
])
|
|
107
|
-
|
|
108
|
-
.description('
|
|
109
|
-
|
|
78
|
+
])
|
|
79
|
+
.default('username')
|
|
80
|
+
.description('白名单添加时使用的ID类型'),
|
|
81
|
+
allowSelfApply: koishi_1.Schema.boolean().description('是否允许用户自行申请白名单').default(false),
|
|
110
82
|
acceptEmptyResponse: koishi_1.Schema.boolean()
|
|
111
83
|
.description('是否将命令的空响应视为成功(某些服务器成功执行命令后不返回内容,仅对本服务器生效)')
|
|
112
|
-
.default(false)
|
|
113
|
-
}))
|
|
84
|
+
.default(false)
|
|
85
|
+
}))
|
|
86
|
+
.description('Minecraft服务器配置列表')
|
|
87
|
+
.default([])
|
|
114
88
|
});
|
|
115
89
|
function apply(ctx, config) {
|
|
116
90
|
// 创建日志服务
|
|
@@ -153,7 +127,7 @@ function apply(ctx, config) {
|
|
|
153
127
|
const lastReminder = reminderCooldown.get(userId);
|
|
154
128
|
if (!lastReminder)
|
|
155
129
|
return false;
|
|
156
|
-
return
|
|
130
|
+
return Date.now() - lastReminder < REMINDER_COOLDOWN_TIME;
|
|
157
131
|
};
|
|
158
132
|
// 设置用户提醒冷却
|
|
159
133
|
const setReminderCooldown = (userId) => {
|
|
@@ -219,7 +193,12 @@ function apply(ctx, config) {
|
|
|
219
193
|
// 发送超时消息,@用户
|
|
220
194
|
const normalizedUser = normalizeQQId(userId);
|
|
221
195
|
ctx.bots.forEach(bot => {
|
|
222
|
-
bot
|
|
196
|
+
bot
|
|
197
|
+
.sendMessage(channelId, [
|
|
198
|
+
koishi_1.h.at(normalizedUser),
|
|
199
|
+
koishi_1.h.text(' 绑定会话已超时,请重新开始绑定流程\n\n⚠️ 温馨提醒:若在管理员多次提醒后仍不配合绑定账号信息,将按群规进行相应处理。')
|
|
200
|
+
])
|
|
201
|
+
.catch(() => { });
|
|
223
202
|
});
|
|
224
203
|
logger.info(`[交互绑定] QQ(${normalizedUser})的绑定会话因超时被清理`);
|
|
225
204
|
}, BINDING_SESSION_TIMEOUT);
|
|
@@ -259,7 +238,39 @@ function apply(ctx, config) {
|
|
|
259
238
|
if (!content)
|
|
260
239
|
return false;
|
|
261
240
|
// 常见的聊天用语或明显无关的内容
|
|
262
|
-
const chatKeywords = [
|
|
241
|
+
const chatKeywords = [
|
|
242
|
+
'你好',
|
|
243
|
+
'hello',
|
|
244
|
+
'hi',
|
|
245
|
+
'在吗',
|
|
246
|
+
'在不在',
|
|
247
|
+
'怎么样',
|
|
248
|
+
'什么',
|
|
249
|
+
'为什么',
|
|
250
|
+
'好的',
|
|
251
|
+
'谢谢',
|
|
252
|
+
'哈哈',
|
|
253
|
+
'呵呵',
|
|
254
|
+
'早上好',
|
|
255
|
+
'晚上好',
|
|
256
|
+
'晚安',
|
|
257
|
+
'再见',
|
|
258
|
+
'拜拜',
|
|
259
|
+
'666',
|
|
260
|
+
'牛',
|
|
261
|
+
'厉害',
|
|
262
|
+
'真的吗',
|
|
263
|
+
'不是吧',
|
|
264
|
+
'哇',
|
|
265
|
+
'哦',
|
|
266
|
+
'嗯',
|
|
267
|
+
'好吧',
|
|
268
|
+
'行',
|
|
269
|
+
'可以',
|
|
270
|
+
'没事',
|
|
271
|
+
'没问题',
|
|
272
|
+
'没关系'
|
|
273
|
+
];
|
|
263
274
|
const lowercaseContent = content.toLowerCase();
|
|
264
275
|
// 检查是否包含明显的聊天用语
|
|
265
276
|
if (chatKeywords.some(keyword => lowercaseContent.includes(keyword))) {
|
|
@@ -285,7 +296,10 @@ function apply(ctx, config) {
|
|
|
285
296
|
return true;
|
|
286
297
|
}
|
|
287
298
|
// 如果是明显的指令格式
|
|
288
|
-
if (content.startsWith('.') ||
|
|
299
|
+
if (content.startsWith('.') ||
|
|
300
|
+
content.startsWith('/') ||
|
|
301
|
+
content.startsWith('mcid') ||
|
|
302
|
+
content.startsWith('buid')) {
|
|
289
303
|
return true;
|
|
290
304
|
}
|
|
291
305
|
}
|
|
@@ -303,7 +317,10 @@ function apply(ctx, config) {
|
|
|
303
317
|
return true;
|
|
304
318
|
}
|
|
305
319
|
// 如果是明显的指令格式
|
|
306
|
-
if (content.startsWith('.') ||
|
|
320
|
+
if (content.startsWith('.') ||
|
|
321
|
+
content.startsWith('/') ||
|
|
322
|
+
content.startsWith('mcid') ||
|
|
323
|
+
content.startsWith('buid')) {
|
|
307
324
|
return true;
|
|
308
325
|
}
|
|
309
326
|
}
|
|
@@ -314,9 +331,9 @@ function apply(ctx, config) {
|
|
|
314
331
|
const getCommandPrefix = () => {
|
|
315
332
|
if (config.allowTextPrefix && config.botNickname) {
|
|
316
333
|
// 检查botNickname是否已经包含@符号,避免重复添加
|
|
317
|
-
const nickname = config.botNickname.startsWith('@')
|
|
318
|
-
config.botNickname
|
|
319
|
-
`@${config.botNickname}`;
|
|
334
|
+
const nickname = config.botNickname.startsWith('@')
|
|
335
|
+
? config.botNickname
|
|
336
|
+
: `@${config.botNickname}`;
|
|
320
337
|
return `${nickname} `;
|
|
321
338
|
}
|
|
322
339
|
return '';
|
|
@@ -413,7 +430,8 @@ function apply(ctx, config) {
|
|
|
413
430
|
}
|
|
414
431
|
else {
|
|
415
432
|
// 不在禁言时间,自动启动交互式绑定
|
|
416
|
-
welcomeMessage +=
|
|
433
|
+
welcomeMessage +=
|
|
434
|
+
'📋 请选择绑定方式:\n1️⃣ 发送您的B站UID进行B站绑定\n2️⃣ 发送"跳过"仅绑定MC账号';
|
|
417
435
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
418
436
|
logger.info(`[新人绑定] 为新成员QQ(${normalizedUserId})自动启动交互式绑定流程`);
|
|
419
437
|
// 创建绑定会话并发送初始提示
|
|
@@ -428,7 +446,7 @@ function apply(ctx, config) {
|
|
|
428
446
|
if (existingBind.mcUsername.startsWith('_temp_')) {
|
|
429
447
|
// 临时用户名,实际上应该是只绑定了B站但MC是临时的,不应该进入这个分支
|
|
430
448
|
// 这种情况应该按照"只绑定了B站"处理
|
|
431
|
-
welcomeMessage +=
|
|
449
|
+
welcomeMessage += '📋 检测到您已绑定B站账号,但尚未绑定MC账号\n';
|
|
432
450
|
welcomeMessage += `🎮 可使用 ${formatCommand('mcid bind <MC用户名>')} 绑定MC账号`;
|
|
433
451
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
434
452
|
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})实际只绑定了B站(MC为临时用户名),已发送绑定提醒`);
|
|
@@ -445,7 +463,7 @@ function apply(ctx, config) {
|
|
|
445
463
|
}
|
|
446
464
|
else {
|
|
447
465
|
// 不在禁言时间,自动启动B站绑定
|
|
448
|
-
welcomeMessage +=
|
|
466
|
+
welcomeMessage += '📋 请发送您的B站UID进行绑定';
|
|
449
467
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
450
468
|
logger.info(`[新人绑定] 为新成员QQ(${normalizedUserId})自动启动B站绑定流程`);
|
|
451
469
|
// 创建绑定会话,直接进入B站绑定步骤
|
|
@@ -458,14 +476,16 @@ function apply(ctx, config) {
|
|
|
458
476
|
}
|
|
459
477
|
else if (!existingBind.mcUsername && existingBind.buidUid) {
|
|
460
478
|
// 只绑定了B站,未绑定MC - 仅发送提醒
|
|
461
|
-
welcomeMessage +=
|
|
479
|
+
welcomeMessage += '📋 检测到您已绑定B站账号,但尚未绑定MC账号\n';
|
|
462
480
|
welcomeMessage += `🎮 可使用 ${formatCommand('mcid bind <MC用户名>')} 绑定MC账号`;
|
|
463
481
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
464
482
|
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})已绑定B站但未绑定MC,已发送绑定提醒`);
|
|
465
483
|
}
|
|
466
|
-
else if (existingBind.mcUsername &&
|
|
484
|
+
else if (existingBind.mcUsername &&
|
|
485
|
+
existingBind.mcUsername.startsWith('_temp_') &&
|
|
486
|
+
existingBind.buidUid) {
|
|
467
487
|
// MC是临时用户名但已绑定B站 - 也按照"只绑定了B站"处理
|
|
468
|
-
welcomeMessage +=
|
|
488
|
+
welcomeMessage += '📋 检测到您已绑定B站账号,但尚未绑定MC账号\n';
|
|
469
489
|
welcomeMessage += `🎮 可使用 ${formatCommand('mcid bind <MC用户名>')} 绑定MC账号`;
|
|
470
490
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
471
491
|
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})已绑定B站但MC为临时用户名,已发送绑定提醒`);
|
|
@@ -479,10 +499,10 @@ function apply(ctx, config) {
|
|
|
479
499
|
// 注册天选开奖 Webhook
|
|
480
500
|
ctx.server.post('/lottery', async (content) => {
|
|
481
501
|
try {
|
|
482
|
-
logger.info(
|
|
502
|
+
logger.info('[天选开奖] 收到天选开奖webhook请求');
|
|
483
503
|
// 检查天选播报开关
|
|
484
504
|
if (!config?.enableLotteryBroadcast) {
|
|
485
|
-
logger.info(
|
|
505
|
+
logger.info('[天选开奖] 天选播报功能已禁用,忽略webhook请求');
|
|
486
506
|
content.status = 200;
|
|
487
507
|
content.body = 'Lottery broadcast disabled';
|
|
488
508
|
return;
|
|
@@ -520,7 +540,7 @@ function apply(ctx, config) {
|
|
|
520
540
|
return;
|
|
521
541
|
}
|
|
522
542
|
if (!lotteryData.lottery_id || !lotteryData.winners || !Array.isArray(lotteryData.winners)) {
|
|
523
|
-
logger.warn(
|
|
543
|
+
logger.warn('[天选开奖] 数据格式不完整');
|
|
524
544
|
content.status = 400;
|
|
525
545
|
content.body = 'Incomplete data format';
|
|
526
546
|
return;
|
|
@@ -549,92 +569,92 @@ function apply(ctx, config) {
|
|
|
549
569
|
// 在数据库中创建MCIDBIND表
|
|
550
570
|
ctx.model.extend('mcidbind', {
|
|
551
571
|
qqId: {
|
|
552
|
-
type: 'string'
|
|
572
|
+
type: 'string'
|
|
553
573
|
},
|
|
554
574
|
mcUsername: {
|
|
555
575
|
type: 'string',
|
|
556
|
-
initial: ''
|
|
576
|
+
initial: ''
|
|
557
577
|
},
|
|
558
578
|
mcUuid: {
|
|
559
579
|
type: 'string',
|
|
560
|
-
initial: ''
|
|
580
|
+
initial: ''
|
|
561
581
|
},
|
|
562
582
|
lastModified: {
|
|
563
583
|
type: 'timestamp',
|
|
564
|
-
initial: null
|
|
584
|
+
initial: null
|
|
565
585
|
},
|
|
566
586
|
isAdmin: {
|
|
567
587
|
type: 'boolean',
|
|
568
|
-
initial: false
|
|
588
|
+
initial: false
|
|
569
589
|
},
|
|
570
590
|
whitelist: {
|
|
571
591
|
type: 'json',
|
|
572
|
-
initial: []
|
|
592
|
+
initial: []
|
|
573
593
|
},
|
|
574
594
|
tags: {
|
|
575
595
|
type: 'json',
|
|
576
|
-
initial: []
|
|
596
|
+
initial: []
|
|
577
597
|
},
|
|
578
598
|
// BUID相关字段
|
|
579
599
|
buidUid: {
|
|
580
600
|
type: 'string',
|
|
581
|
-
initial: ''
|
|
601
|
+
initial: ''
|
|
582
602
|
},
|
|
583
603
|
buidUsername: {
|
|
584
604
|
type: 'string',
|
|
585
|
-
initial: ''
|
|
605
|
+
initial: ''
|
|
586
606
|
},
|
|
587
607
|
guardLevel: {
|
|
588
608
|
type: 'integer',
|
|
589
|
-
initial: 0
|
|
609
|
+
initial: 0
|
|
590
610
|
},
|
|
591
611
|
guardLevelText: {
|
|
592
612
|
type: 'string',
|
|
593
|
-
initial: ''
|
|
613
|
+
initial: ''
|
|
594
614
|
},
|
|
595
615
|
maxGuardLevel: {
|
|
596
616
|
type: 'integer',
|
|
597
|
-
initial: 0
|
|
617
|
+
initial: 0
|
|
598
618
|
},
|
|
599
619
|
maxGuardLevelText: {
|
|
600
620
|
type: 'string',
|
|
601
|
-
initial: ''
|
|
621
|
+
initial: ''
|
|
602
622
|
},
|
|
603
623
|
medalName: {
|
|
604
624
|
type: 'string',
|
|
605
|
-
initial: ''
|
|
625
|
+
initial: ''
|
|
606
626
|
},
|
|
607
627
|
medalLevel: {
|
|
608
628
|
type: 'integer',
|
|
609
|
-
initial: 0
|
|
629
|
+
initial: 0
|
|
610
630
|
},
|
|
611
631
|
wealthMedalLevel: {
|
|
612
632
|
type: 'integer',
|
|
613
|
-
initial: 0
|
|
633
|
+
initial: 0
|
|
614
634
|
},
|
|
615
635
|
lastActiveTime: {
|
|
616
636
|
type: 'timestamp',
|
|
617
|
-
initial: null
|
|
637
|
+
initial: null
|
|
618
638
|
},
|
|
619
639
|
reminderCount: {
|
|
620
640
|
type: 'integer',
|
|
621
|
-
initial: 0
|
|
641
|
+
initial: 0
|
|
622
642
|
},
|
|
623
643
|
usernameLastChecked: {
|
|
624
644
|
type: 'timestamp',
|
|
625
|
-
initial: null
|
|
645
|
+
initial: null
|
|
626
646
|
},
|
|
627
647
|
usernameCheckFailCount: {
|
|
628
648
|
type: 'integer',
|
|
629
|
-
initial: 0
|
|
630
|
-
}
|
|
649
|
+
initial: 0
|
|
650
|
+
}
|
|
631
651
|
}, {
|
|
632
652
|
// 设置主键为qqId
|
|
633
653
|
primary: 'qqId',
|
|
634
654
|
// 添加索引
|
|
635
655
|
unique: [['mcUsername'], ['buidUid']],
|
|
636
656
|
// 添加isAdmin索引,提高查询效率
|
|
637
|
-
indexes: [['isAdmin'], ['buidUid']]
|
|
657
|
+
indexes: [['isAdmin'], ['buidUid']]
|
|
638
658
|
});
|
|
639
659
|
// 检查表结构是否包含旧字段
|
|
640
660
|
const checkTableStructure = async () => {
|
|
@@ -698,7 +718,7 @@ function apply(ctx, config) {
|
|
|
698
718
|
logger.info(`[初始化] 成功为${updatedCount}条记录添加缺失字段`);
|
|
699
719
|
}
|
|
700
720
|
else {
|
|
701
|
-
logger.info(
|
|
721
|
+
logger.info('[初始化] 所有记录都包含必要字段,无需更新');
|
|
702
722
|
}
|
|
703
723
|
return true;
|
|
704
724
|
}
|
|
@@ -717,7 +737,8 @@ function apply(ctx, config) {
|
|
|
717
737
|
const backupData = JSON.parse(JSON.stringify(oldRecords));
|
|
718
738
|
try {
|
|
719
739
|
// 提取有效数据
|
|
720
|
-
const validRecords = oldRecords
|
|
740
|
+
const validRecords = oldRecords
|
|
741
|
+
.map(record => {
|
|
721
742
|
// 确保qqId存在
|
|
722
743
|
if (!record.qqId) {
|
|
723
744
|
// 如果没有qqId但有userId,尝试从userId提取
|
|
@@ -738,7 +759,8 @@ function apply(ctx, config) {
|
|
|
738
759
|
whitelist: record.whitelist || [],
|
|
739
760
|
tags: record.tags || []
|
|
740
761
|
};
|
|
741
|
-
})
|
|
762
|
+
})
|
|
763
|
+
.filter(record => record !== null);
|
|
742
764
|
// 删除现有表
|
|
743
765
|
await mcidbindRepo.deleteAll();
|
|
744
766
|
logger.info('[初始化] 成功删除旧表数据');
|
|
@@ -786,7 +808,7 @@ function apply(ctx, config) {
|
|
|
786
808
|
const normalizeQQId = (userId) => {
|
|
787
809
|
// 处理空值情况
|
|
788
810
|
if (!userId) {
|
|
789
|
-
logger.warn(
|
|
811
|
+
logger.warn('[用户ID] 收到空用户ID');
|
|
790
812
|
return '';
|
|
791
813
|
}
|
|
792
814
|
let extractedId = '';
|
|
@@ -840,8 +862,12 @@ function apply(ctx, config) {
|
|
|
840
862
|
// 处理私聊和群聊的消息格式
|
|
841
863
|
// 主动消息不引用原消息
|
|
842
864
|
const promptMessage = session.channelId?.startsWith('private:')
|
|
843
|
-
?
|
|
844
|
-
|
|
865
|
+
? isProactiveMessage
|
|
866
|
+
? content
|
|
867
|
+
: [koishi_1.h.quote(session.messageId), ...content]
|
|
868
|
+
: isProactiveMessage
|
|
869
|
+
? [koishi_1.h.at(normalizedQQId), '\n', ...content]
|
|
870
|
+
: [koishi_1.h.quote(session.messageId), koishi_1.h.at(normalizedQQId), '\n', ...content];
|
|
845
871
|
// 发送消息并获取返回的消息ID
|
|
846
872
|
const messageResult = await session.send(promptMessage);
|
|
847
873
|
if (config.debugMode) {
|
|
@@ -853,11 +879,18 @@ function apply(ctx, config) {
|
|
|
853
879
|
// 但如果用户在绑定会话中发送聊天消息(不包括指令),不撤回
|
|
854
880
|
// 主动消息不撤回用户消息
|
|
855
881
|
const bindingSession = getBindingSession(session.userId, session.channelId);
|
|
856
|
-
const isBindingCommand = session.content &&
|
|
857
|
-
session.content.
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
882
|
+
const isBindingCommand = session.content &&
|
|
883
|
+
(session.content.trim() === '绑定' ||
|
|
884
|
+
(session.content.includes('@') && session.content.includes('绑定')));
|
|
885
|
+
const shouldNotRecallUserMessage = bindingSession &&
|
|
886
|
+
session.content &&
|
|
887
|
+
!isBindingCommand &&
|
|
888
|
+
checkIrrelevantInput(bindingSession, session.content.trim());
|
|
889
|
+
if (config.recallUserMessage &&
|
|
890
|
+
isGroupMessage &&
|
|
891
|
+
session.messageId &&
|
|
892
|
+
!shouldNotRecallUserMessage &&
|
|
893
|
+
!isProactiveMessage) {
|
|
861
894
|
setTimeout(async () => {
|
|
862
895
|
try {
|
|
863
896
|
await session.bot.deleteMessage(session.channelId, session.messageId);
|
|
@@ -877,7 +910,7 @@ function apply(ctx, config) {
|
|
|
877
910
|
logDebug('消息', `QQ(${normalizedQQId})在绑定会话中发送聊天消息,跳过撤回用户消息`);
|
|
878
911
|
}
|
|
879
912
|
else if (isProactiveMessage && config.debugMode) {
|
|
880
|
-
logDebug('消息',
|
|
913
|
+
logDebug('消息', '主动发送的消息,跳过撤回用户消息');
|
|
881
914
|
}
|
|
882
915
|
// 处理撤回机器人消息 - 只在群聊中撤回机器人自己的消息
|
|
883
916
|
// 检查是否为不应撤回的重要提示消息(只有绑定会话超时提醒)
|
|
@@ -904,9 +937,10 @@ function apply(ctx, config) {
|
|
|
904
937
|
}
|
|
905
938
|
else if (messageResult && typeof messageResult === 'object') {
|
|
906
939
|
// 尝试提取各种可能的消息ID格式
|
|
907
|
-
messageId =
|
|
908
|
-
messageResult.
|
|
909
|
-
|
|
940
|
+
messageId =
|
|
941
|
+
messageResult.messageId ||
|
|
942
|
+
messageResult.id ||
|
|
943
|
+
messageResult.message_id;
|
|
910
944
|
}
|
|
911
945
|
if (messageId) {
|
|
912
946
|
// 设置定时器延迟撤回
|
|
@@ -926,11 +960,11 @@ function apply(ctx, config) {
|
|
|
926
960
|
}
|
|
927
961
|
}
|
|
928
962
|
else if (config.debugMode) {
|
|
929
|
-
logWarn('消息',
|
|
963
|
+
logWarn('消息', '无法获取消息ID,自动撤回功能无法生效');
|
|
930
964
|
}
|
|
931
965
|
}
|
|
932
966
|
else if (config.debugMode) {
|
|
933
|
-
logDebug('消息',
|
|
967
|
+
logDebug('消息', '检测到私聊消息,不撤回机器人回复');
|
|
934
968
|
}
|
|
935
969
|
}
|
|
936
970
|
}
|
|
@@ -964,7 +998,7 @@ function apply(ctx, config) {
|
|
|
964
998
|
const getServerConfigById = (serverId) => {
|
|
965
999
|
if (!config.servers || !Array.isArray(config.servers))
|
|
966
1000
|
return null;
|
|
967
|
-
return config.servers.find(server => server.id === serverId &&
|
|
1001
|
+
return config.servers.find(server => server.id === serverId && server.enabled !== false) || null;
|
|
968
1002
|
};
|
|
969
1003
|
// 根据服务器名称获取服务器配置
|
|
970
1004
|
const getServerConfigByName = (serverName) => {
|
|
@@ -1118,9 +1152,9 @@ function apply(ctx, config) {
|
|
|
1118
1152
|
const regularPrefixRegex = new RegExp(`^${escapeRegExp(botNickname)}\\s+((mcid|buid|绑定|bind)\\s*.*)$`, 'i');
|
|
1119
1153
|
const regularMatch = content.match(regularPrefixRegex);
|
|
1120
1154
|
// 2. 如果botNickname不包含@,也尝试匹配带@的版本
|
|
1121
|
-
const atPrefixRegex = !botNickname.startsWith('@')
|
|
1122
|
-
new RegExp(`^@${escapeRegExp(botNickname)}\\s+((mcid|buid|绑定|bind)\\s*.*)$`, 'i')
|
|
1123
|
-
null;
|
|
1155
|
+
const atPrefixRegex = !botNickname.startsWith('@')
|
|
1156
|
+
? new RegExp(`^@${escapeRegExp(botNickname)}\\s+((mcid|buid|绑定|bind)\\s*.*)$`, 'i')
|
|
1157
|
+
: null;
|
|
1124
1158
|
if (regularMatch && regularMatch[1]) {
|
|
1125
1159
|
matchedCommand = regularMatch[1].trim();
|
|
1126
1160
|
}
|
|
@@ -1165,8 +1199,12 @@ function apply(ctx, config) {
|
|
|
1165
1199
|
return next();
|
|
1166
1200
|
}
|
|
1167
1201
|
// 跳过空消息或命令消息
|
|
1168
|
-
if (!session.content ||
|
|
1169
|
-
session.content.
|
|
1202
|
+
if (!session.content ||
|
|
1203
|
+
session.content.startsWith('.') ||
|
|
1204
|
+
session.content.startsWith('/') ||
|
|
1205
|
+
session.content.includes('mcid') ||
|
|
1206
|
+
session.content.includes('buid') ||
|
|
1207
|
+
session.content.includes('绑定')) {
|
|
1170
1208
|
return next();
|
|
1171
1209
|
}
|
|
1172
1210
|
// 检查当前时间是否在群组禁言时间段内
|
|
@@ -1182,7 +1220,7 @@ function apply(ctx, config) {
|
|
|
1182
1220
|
}
|
|
1183
1221
|
// 随机触发概率:管理员 1%,普通用户 80%,避免过于频繁
|
|
1184
1222
|
const isUserAdmin = await isAdmin(session.userId);
|
|
1185
|
-
const triggerRate = isUserAdmin ? 0.01 : 0.
|
|
1223
|
+
const triggerRate = isUserAdmin ? 0.01 : 0.8;
|
|
1186
1224
|
if (Math.random() > triggerRate) {
|
|
1187
1225
|
return next();
|
|
1188
1226
|
}
|
|
@@ -1242,7 +1280,9 @@ function apply(ctx, config) {
|
|
|
1242
1280
|
return next();
|
|
1243
1281
|
}
|
|
1244
1282
|
// 情况2:只绑定了B站,未绑定MC
|
|
1245
|
-
if (bind.buidUid &&
|
|
1283
|
+
if (bind.buidUid &&
|
|
1284
|
+
bind.buidUsername &&
|
|
1285
|
+
(!bind.mcUsername || bind.mcUsername.startsWith('_temp_'))) {
|
|
1246
1286
|
const mcInfo = null;
|
|
1247
1287
|
const isNicknameCorrect = services.nickname.checkNicknameFormat(currentNickname, bind.buidUsername, mcInfo);
|
|
1248
1288
|
if (!isNicknameCorrect) {
|
|
@@ -1263,7 +1303,10 @@ function apply(ctx, config) {
|
|
|
1263
1303
|
return next();
|
|
1264
1304
|
}
|
|
1265
1305
|
// 情况3:都已绑定,但群昵称格式不正确
|
|
1266
|
-
if (bind.buidUid &&
|
|
1306
|
+
if (bind.buidUid &&
|
|
1307
|
+
bind.buidUsername &&
|
|
1308
|
+
bind.mcUsername &&
|
|
1309
|
+
!bind.mcUsername.startsWith('_temp_')) {
|
|
1267
1310
|
const isNicknameCorrect = services.nickname.checkNicknameFormat(currentNickname, bind.buidUsername, bind.mcUsername);
|
|
1268
1311
|
if (!isNicknameCorrect) {
|
|
1269
1312
|
// 更新提醒次数
|
|
@@ -1305,19 +1348,26 @@ function apply(ctx, config) {
|
|
|
1305
1348
|
if (content === '取消' || content === 'cancel') {
|
|
1306
1349
|
removeBindingSession(session.userId, session.channelId);
|
|
1307
1350
|
logger.info(`[交互绑定] QQ(${normalizedUserId})手动取消了绑定会话`);
|
|
1308
|
-
await sendMessage(session, [
|
|
1351
|
+
await sendMessage(session, [
|
|
1352
|
+
koishi_1.h.text('❌ 绑定会话已取消\n\n📋 温馨提醒:请按群规设置合适的群昵称。若在管理员多次提醒后仍不配合绑定账号信息或按规修改群昵称,将按群规进行相应处理。')
|
|
1353
|
+
]);
|
|
1309
1354
|
return;
|
|
1310
1355
|
}
|
|
1311
1356
|
// 检查是否在绑定过程中使用了其他绑定相关命令(排除跳过选项)
|
|
1312
1357
|
// 这里使用原始内容检测命令,避免误判@Bot发送的正常输入
|
|
1313
|
-
if (rawContent &&
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
rawContent.includes('
|
|
1317
|
-
|
|
1318
|
-
|
|
1358
|
+
if (rawContent &&
|
|
1359
|
+
content !== '跳过' &&
|
|
1360
|
+
content !== 'skip' &&
|
|
1361
|
+
(rawContent.includes('绑定') ||
|
|
1362
|
+
rawContent.includes('bind') ||
|
|
1363
|
+
rawContent.includes('mcid') ||
|
|
1364
|
+
rawContent.includes('buid') ||
|
|
1365
|
+
rawContent.startsWith('.') ||
|
|
1366
|
+
rawContent.startsWith('/'))) {
|
|
1319
1367
|
const currentState = bindingSession.state === 'waiting_mc_username' ? 'MC用户名' : 'B站UID';
|
|
1320
|
-
await sendMessage(session, [
|
|
1368
|
+
await sendMessage(session, [
|
|
1369
|
+
koishi_1.h.text(`🔄 您正在进行交互式绑定,请继续输入${currentState}\n\n如需取消当前绑定,请发送"取消"`)
|
|
1370
|
+
]);
|
|
1321
1371
|
return;
|
|
1322
1372
|
}
|
|
1323
1373
|
// 检查是否为明显无关的输入
|
|
@@ -1329,7 +1379,39 @@ function apply(ctx, config) {
|
|
|
1329
1379
|
invalidInputCount: newCount
|
|
1330
1380
|
});
|
|
1331
1381
|
// 检查是否为明显的聊天内容(使用清理后的内容)
|
|
1332
|
-
const chatKeywords = [
|
|
1382
|
+
const chatKeywords = [
|
|
1383
|
+
'你好',
|
|
1384
|
+
'hello',
|
|
1385
|
+
'hi',
|
|
1386
|
+
'在吗',
|
|
1387
|
+
'在不在',
|
|
1388
|
+
'怎么样',
|
|
1389
|
+
'什么',
|
|
1390
|
+
'为什么',
|
|
1391
|
+
'好的',
|
|
1392
|
+
'谢谢',
|
|
1393
|
+
'哈哈',
|
|
1394
|
+
'呵呵',
|
|
1395
|
+
'早上好',
|
|
1396
|
+
'晚上好',
|
|
1397
|
+
'晚安',
|
|
1398
|
+
'再见',
|
|
1399
|
+
'拜拜',
|
|
1400
|
+
'666',
|
|
1401
|
+
'牛',
|
|
1402
|
+
'厉害',
|
|
1403
|
+
'真的吗',
|
|
1404
|
+
'不是吧',
|
|
1405
|
+
'哇',
|
|
1406
|
+
'哦',
|
|
1407
|
+
'嗯',
|
|
1408
|
+
'好吧',
|
|
1409
|
+
'行',
|
|
1410
|
+
'可以',
|
|
1411
|
+
'没事',
|
|
1412
|
+
'没问题',
|
|
1413
|
+
'没关系'
|
|
1414
|
+
];
|
|
1333
1415
|
const isChatMessage = chatKeywords.some(keyword => content.toLowerCase().includes(keyword)) ||
|
|
1334
1416
|
/[!?。,;:""''()【】〈〉《》「」『』〔〕〖〗〘〙〚〛]{2,}/.test(content) ||
|
|
1335
1417
|
/[!?.,;:"'()[\]<>{}]{3,}/.test(content);
|
|
@@ -1339,13 +1421,17 @@ function apply(ctx, config) {
|
|
|
1339
1421
|
removeBindingSession(session.userId, session.channelId);
|
|
1340
1422
|
logger.info(`[交互绑定] QQ(${normalizedUserId})持续发送聊天消息,自动取消绑定会话避免打扰`);
|
|
1341
1423
|
// 对于聊天取消,给一个更温和的提示,同时提醒群规
|
|
1342
|
-
await sendMessage(session, [
|
|
1424
|
+
await sendMessage(session, [
|
|
1425
|
+
koishi_1.h.text(`💬 看起来您在聊天,绑定流程已自动取消\n\n📋 温馨提醒:请按群规设置合适的群昵称。若在管理员多次提醒后仍不配合绑定账号信息或按规修改群昵称,将按群规进行相应处理。\n\n如需绑定账号,请随时使用 ${formatCommand('绑定')} 命令重新开始`)
|
|
1426
|
+
]);
|
|
1343
1427
|
return;
|
|
1344
1428
|
}
|
|
1345
1429
|
else {
|
|
1346
1430
|
// 第一次聊天消息,给温和提醒
|
|
1347
1431
|
const expectedInput = bindingSession.state === 'waiting_mc_username' ? 'MC用户名' : 'B站UID';
|
|
1348
|
-
await sendMessage(session, [
|
|
1432
|
+
await sendMessage(session, [
|
|
1433
|
+
koishi_1.h.text(`💭 您当前正在进行账号绑定,需要输入${expectedInput}\n\n如不需要绑定,请发送"取消",或继续聊天我们会自动取消绑定流程`)
|
|
1434
|
+
]);
|
|
1349
1435
|
return;
|
|
1350
1436
|
}
|
|
1351
1437
|
}
|
|
@@ -1354,14 +1440,20 @@ function apply(ctx, config) {
|
|
|
1354
1440
|
if (newCount === 1) {
|
|
1355
1441
|
// 第1次无关输入,提醒检查
|
|
1356
1442
|
const expectedInput = bindingSession.state === 'waiting_mc_username' ? 'MC用户名' : 'B站UID';
|
|
1357
|
-
await sendMessage(session, [
|
|
1443
|
+
await sendMessage(session, [
|
|
1444
|
+
koishi_1.h.text(`🤔 您当前正在进行绑定流程,需要输入${expectedInput}\n\n如果您想取消绑定,请发送"取消"`)
|
|
1445
|
+
]);
|
|
1358
1446
|
return;
|
|
1359
1447
|
}
|
|
1360
1448
|
else if (newCount >= 2) {
|
|
1361
1449
|
// 第2次无关输入,建议取消
|
|
1362
1450
|
removeBindingSession(session.userId, session.channelId);
|
|
1363
1451
|
logger.info(`[交互绑定] QQ(${normalizedUserId})因多次无关输入自动取消绑定会话`);
|
|
1364
|
-
await sendMessage(session, [
|
|
1452
|
+
await sendMessage(session, [
|
|
1453
|
+
koishi_1.h.text('🔄 检测到您可能不想继续绑定流程,已自动取消绑定会话\n\n📋 温馨提醒:请按群规设置合适的群昵称。若在管理员多次提醒后仍不配合绑定账号信息或按规修改群昵称,将按群规进行相应处理。\n\n如需重新绑定,请使用 ' +
|
|
1454
|
+
formatCommand('绑定') +
|
|
1455
|
+
' 命令')
|
|
1456
|
+
]);
|
|
1365
1457
|
return;
|
|
1366
1458
|
}
|
|
1367
1459
|
}
|
|
@@ -1409,7 +1501,9 @@ function apply(ctx, config) {
|
|
|
1409
1501
|
}
|
|
1410
1502
|
await sendMessage(session, [
|
|
1411
1503
|
koishi_1.h.text(`🎉 绑定完成!\nMC: 未绑定\nB站: ${existingBind.buidUsername}\n\n💡 您可以随时使用 ${formatCommand('mcid bind <用户名>')} 绑定MC账号`),
|
|
1412
|
-
...(config?.showAvatar
|
|
1504
|
+
...(config?.showAvatar
|
|
1505
|
+
? [koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${existingBind.buidUid}?size=160`)]
|
|
1506
|
+
: [])
|
|
1413
1507
|
]);
|
|
1414
1508
|
return;
|
|
1415
1509
|
}
|
|
@@ -1446,7 +1540,9 @@ function apply(ctx, config) {
|
|
|
1446
1540
|
const profile = await services.api.validateUsername(content);
|
|
1447
1541
|
if (!profile) {
|
|
1448
1542
|
logger.warn(`[交互绑定] QQ(${normalizedUserId})输入的MC用户名"${content}"不存在`);
|
|
1449
|
-
await sendMessage(session, [
|
|
1543
|
+
await sendMessage(session, [
|
|
1544
|
+
koishi_1.h.text(`❌ 用户名 ${content} 不存在\n请重新输入或发送"跳过"完成绑定`)
|
|
1545
|
+
]);
|
|
1450
1546
|
return;
|
|
1451
1547
|
}
|
|
1452
1548
|
const username = profile.name;
|
|
@@ -1455,22 +1551,28 @@ function apply(ctx, config) {
|
|
|
1455
1551
|
const existingBind = await services.database.getMcBindByQQId(normalizedUserId);
|
|
1456
1552
|
if (existingBind && existingBind.mcUsername && !existingBind.mcUsername.startsWith('_temp_')) {
|
|
1457
1553
|
// 检查冷却时间
|
|
1458
|
-
if (!await isAdmin(session.userId) && !checkCooldown(existingBind.lastModified)) {
|
|
1554
|
+
if (!(await isAdmin(session.userId)) && !checkCooldown(existingBind.lastModified)) {
|
|
1459
1555
|
const days = config.cooldownDays;
|
|
1460
1556
|
const now = new Date();
|
|
1461
1557
|
const diffTime = now.getTime() - existingBind.lastModified.getTime();
|
|
1462
1558
|
const passedDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
1463
1559
|
const remainingDays = days - passedDays;
|
|
1464
1560
|
removeBindingSession(session.userId, session.channelId);
|
|
1465
|
-
const displayUsername = existingBind.mcUsername && !existingBind.mcUsername.startsWith('_temp_')
|
|
1466
|
-
|
|
1561
|
+
const displayUsername = existingBind.mcUsername && !existingBind.mcUsername.startsWith('_temp_')
|
|
1562
|
+
? existingBind.mcUsername
|
|
1563
|
+
: '未绑定';
|
|
1564
|
+
await sendMessage(session, [
|
|
1565
|
+
koishi_1.h.text(`❌ 您已绑定MC账号: ${displayUsername}\n\n如需修改,请在冷却期结束后(还需${remainingDays}天)使用 ${formatCommand('mcid change')} 命令或联系管理员`)
|
|
1566
|
+
]);
|
|
1467
1567
|
return;
|
|
1468
1568
|
}
|
|
1469
1569
|
}
|
|
1470
1570
|
// 检查用户名是否已被其他人绑定
|
|
1471
1571
|
if (await services.database.checkUsernameExists(username, session.userId, uuid)) {
|
|
1472
1572
|
logger.warn(`[交互绑定] MC用户名"${username}"已被其他用户绑定`);
|
|
1473
|
-
await sendMessage(session, [
|
|
1573
|
+
await sendMessage(session, [
|
|
1574
|
+
koishi_1.h.text(`❌ 用户名 ${username} 已被其他用户绑定\n\n请输入其他MC用户名或发送"跳过"完成绑定`)
|
|
1575
|
+
]);
|
|
1474
1576
|
return;
|
|
1475
1577
|
}
|
|
1476
1578
|
// 绑定MC账号
|
|
@@ -1578,20 +1680,26 @@ function apply(ctx, config) {
|
|
|
1578
1680
|
// 验证UID格式
|
|
1579
1681
|
if (!actualUid || !/^\d+$/.test(actualUid)) {
|
|
1580
1682
|
logger.warn(`[交互绑定] QQ(${normalizedUserId})输入的B站UID"${content}"格式无效`);
|
|
1581
|
-
await sendMessage(session, [
|
|
1683
|
+
await sendMessage(session, [
|
|
1684
|
+
koishi_1.h.text('❌ UID格式无效,请重新输入\n支持格式:纯数字、UID:数字、空间链接\n或发送"跳过"仅绑定MC账号')
|
|
1685
|
+
]);
|
|
1582
1686
|
return;
|
|
1583
1687
|
}
|
|
1584
1688
|
// 检查UID是否已被绑定
|
|
1585
1689
|
if (await services.database.checkBuidExists(actualUid, session.userId)) {
|
|
1586
1690
|
logger.warn(`[交互绑定] B站UID"${actualUid}"已被其他用户绑定`);
|
|
1587
|
-
await sendMessage(session, [
|
|
1691
|
+
await sendMessage(session, [
|
|
1692
|
+
koishi_1.h.text(`❌ UID ${actualUid} 已被其他用户绑定\n\n请输入其他B站UID\n或发送"跳过"仅绑定MC账号`)
|
|
1693
|
+
]);
|
|
1588
1694
|
return;
|
|
1589
1695
|
}
|
|
1590
1696
|
// 验证UID是否存在
|
|
1591
1697
|
const buidUser = await services.api.validateBUID(actualUid);
|
|
1592
1698
|
if (!buidUser) {
|
|
1593
1699
|
logger.warn(`[交互绑定] QQ(${normalizedUserId})输入的B站UID"${actualUid}"不存在`);
|
|
1594
|
-
await sendMessage(session, [
|
|
1700
|
+
await sendMessage(session, [
|
|
1701
|
+
koishi_1.h.text(`❌ 无法验证UID: ${actualUid}\n\n该用户可能不存在或未被发现\n可以去直播间发个弹幕后重试绑定\n或发送"跳过"仅绑定MC账号`)
|
|
1702
|
+
]);
|
|
1595
1703
|
return;
|
|
1596
1704
|
}
|
|
1597
1705
|
// 绑定B站账号
|
|
@@ -1600,9 +1708,13 @@ function apply(ctx, config) {
|
|
|
1600
1708
|
logger.error(`[交互绑定] QQ(${normalizedUserId})绑定B站账号失败`);
|
|
1601
1709
|
removeBindingSession(session.userId, session.channelId);
|
|
1602
1710
|
// 根据是否有MC绑定提供不同的提示
|
|
1603
|
-
const displayMcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1711
|
+
const displayMcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1712
|
+
? bindingSession.mcUsername
|
|
1713
|
+
: null;
|
|
1604
1714
|
const mcStatus = displayMcName ? `您的MC账号${displayMcName}已成功绑定\n` : '';
|
|
1605
|
-
await sendMessage(session, [
|
|
1715
|
+
await sendMessage(session, [
|
|
1716
|
+
koishi_1.h.text(`❌ B站账号绑定失败,数据库操作出错\n\n${mcStatus}可稍后使用 ${formatCommand('buid bind <UID>')} 命令单独绑定B站账号`)
|
|
1717
|
+
]);
|
|
1606
1718
|
return;
|
|
1607
1719
|
}
|
|
1608
1720
|
logger.info(`[交互绑定] QQ(${normalizedUserId})成功绑定B站UID: ${actualUid}`);
|
|
@@ -1611,7 +1723,9 @@ function apply(ctx, config) {
|
|
|
1611
1723
|
// 自动群昵称设置功能 - 使用新的autoSetGroupNickname函数
|
|
1612
1724
|
try {
|
|
1613
1725
|
// 检查是否有有效的MC用户名(不是临时用户名)
|
|
1614
|
-
const mcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1726
|
+
const mcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1727
|
+
? bindingSession.mcUsername
|
|
1728
|
+
: null;
|
|
1615
1729
|
await services.nickname.autoSetGroupNickname(session, mcName, buidUser.username, String(buidUser.uid));
|
|
1616
1730
|
logger.info(`[交互绑定] QQ(${normalizedUserId})绑定完成,已设置群昵称`);
|
|
1617
1731
|
}
|
|
@@ -1632,7 +1746,9 @@ function apply(ctx, config) {
|
|
|
1632
1746
|
extraInfo += `\n荣耀等级: ${buidUser.wealthMedalLevel}`;
|
|
1633
1747
|
}
|
|
1634
1748
|
// 准备完成消息
|
|
1635
|
-
const displayMcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1749
|
+
const displayMcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_')
|
|
1750
|
+
? bindingSession.mcUsername
|
|
1751
|
+
: null;
|
|
1636
1752
|
const mcInfo = displayMcName ? `MC: ${displayMcName}` : 'MC: 未绑定';
|
|
1637
1753
|
let extraTip = '';
|
|
1638
1754
|
// 如果用户跳过了MC绑定或MC账号是temp,提供后续绑定的指引
|
|
@@ -1641,7 +1757,9 @@ function apply(ctx, config) {
|
|
|
1641
1757
|
}
|
|
1642
1758
|
await sendMessage(session, [
|
|
1643
1759
|
koishi_1.h.text(`🎉 绑定完成!\n${mcInfo}\nB站: ${buidUser.username}${extraInfo}${extraTip}`),
|
|
1644
|
-
...(config?.showAvatar
|
|
1760
|
+
...(config?.showAvatar
|
|
1761
|
+
? [koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${buidUser.uid}?size=160`)]
|
|
1762
|
+
: [])
|
|
1645
1763
|
]);
|
|
1646
1764
|
};
|
|
1647
1765
|
// 帮助函数:转义正则表达式中的特殊字符
|
|
@@ -1661,7 +1779,7 @@ function apply(ctx, config) {
|
|
|
1661
1779
|
// @Bot昵称 格式(如果配置了botNickname)
|
|
1662
1780
|
config.botNickname ? new RegExp(`^@${escapeRegExp(config.botNickname)}\\s+`, 'i') : null,
|
|
1663
1781
|
// @botUserId 格式
|
|
1664
|
-
new RegExp(`^@${escapeRegExp(botUserId)}\\s+`, 'i')
|
|
1782
|
+
new RegExp(`^@${escapeRegExp(botUserId)}\\s+`, 'i')
|
|
1665
1783
|
].filter(Boolean);
|
|
1666
1784
|
let cleanedContent = content.trim();
|
|
1667
1785
|
// 尝试匹配并移除@Bot前缀
|