koishi-plugin-bind-bot 2.1.8 → 2.2.1
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 +4 -3
- package/lib/handlers/binding.handler.js +9 -19
- package/lib/handlers/buid.handler.js +19 -26
- package/lib/handlers/group-request-review.handler.js +12 -24
- package/lib/handlers/lottery.handler.js +4 -4
- package/lib/handlers/mcid.handler.js +31 -37
- package/lib/handlers/tag.handler.js +10 -7
- package/lib/handlers/whitelist.handler.js +5 -13
- package/lib/index.js +96 -64
- package/lib/services/database.service.d.ts +5 -1
- package/lib/services/database.service.js +110 -28
- package/lib/services/nickname.service.js +2 -2
- package/lib/types/database.d.ts +4 -0
- package/lib/types/update-data.d.ts +4 -0
- package/lib/utils/bind-status.d.ts +111 -0
- package/lib/utils/bind-status.js +156 -0
- package/lib/utils/helpers.js +0 -4
- package/lib/utils/index.d.ts +7 -0
- package/lib/utils/index.js +27 -0
- package/lib/utils/message-utils.js +2 -2
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ exports.WhitelistHandler = void 0;
|
|
|
4
4
|
const koishi_1 = require("koishi");
|
|
5
5
|
const base_handler_1 = require("./base.handler");
|
|
6
6
|
const helpers_1 = require("../utils/helpers");
|
|
7
|
+
const bind_status_1 = require("../utils/bind-status");
|
|
7
8
|
/**
|
|
8
9
|
* 白名单命令处理器
|
|
9
10
|
* 处理所有 mcid.whitelist 子命令
|
|
@@ -105,7 +106,7 @@ class WhitelistHandler extends base_handler_1.BaseHandler {
|
|
|
105
106
|
})
|
|
106
107
|
.join('\n\n'); // 使用双换行分隔不同服务器,增强可读性
|
|
107
108
|
this.logger.info('白名单', `成功: QQ(${normalizedUserId})获取了服务器列表,共${enabledServers.length}个服务器`);
|
|
108
|
-
const displayUsername =
|
|
109
|
+
const displayUsername = bind_status_1.BindStatus.hasValidMcBind(userBind)
|
|
109
110
|
? userBind.mcUsername
|
|
110
111
|
: '未绑定MC账号';
|
|
111
112
|
return this.deps.sendMessage(session, [
|
|
@@ -157,10 +158,7 @@ class WhitelistHandler extends base_handler_1.BaseHandler {
|
|
|
157
158
|
const targetValue = targets[0];
|
|
158
159
|
// 首先检查是否存在该标签名
|
|
159
160
|
const allBinds = await this.repos.mcidbind.findAll();
|
|
160
|
-
const usersWithTag = allBinds.filter(bind => bind.tags &&
|
|
161
|
-
bind.tags.includes(targetValue) &&
|
|
162
|
-
bind.mcUsername &&
|
|
163
|
-
!bind.mcUsername.startsWith('_temp_'));
|
|
161
|
+
const usersWithTag = allBinds.filter(bind => bind.tags && bind.tags.includes(targetValue) && bind_status_1.BindStatus.hasValidMcBind(bind));
|
|
164
162
|
if (usersWithTag.length > 0) {
|
|
165
163
|
// 作为标签处理
|
|
166
164
|
const tagName = targetValue;
|
|
@@ -363,10 +361,7 @@ class WhitelistHandler extends base_handler_1.BaseHandler {
|
|
|
363
361
|
const targetValue = targets[0];
|
|
364
362
|
// 首先检查是否存在该标签名
|
|
365
363
|
const allBinds = await this.repos.mcidbind.findAll();
|
|
366
|
-
const usersWithTag = allBinds.filter(bind => bind.tags &&
|
|
367
|
-
bind.tags.includes(targetValue) &&
|
|
368
|
-
bind.mcUsername &&
|
|
369
|
-
!bind.mcUsername.startsWith('_temp_'));
|
|
364
|
+
const usersWithTag = allBinds.filter(bind => bind.tags && bind.tags.includes(targetValue) && bind_status_1.BindStatus.hasValidMcBind(bind));
|
|
370
365
|
if (usersWithTag.length > 0) {
|
|
371
366
|
// 作为标签处理
|
|
372
367
|
const tagName = targetValue;
|
|
@@ -685,10 +680,7 @@ class WhitelistHandler extends base_handler_1.BaseHandler {
|
|
|
685
680
|
// 查询所有已绑定MC账号的用户
|
|
686
681
|
const allBinds = await this.repos.mcidbind.findAll();
|
|
687
682
|
// 过滤掉无效的绑定:没有用户名或UUID的记录
|
|
688
|
-
const validBinds = allBinds.filter(bind => (bind.
|
|
689
|
-
bind.mcUsername.trim() !== '' &&
|
|
690
|
-
!bind.mcUsername.startsWith('_temp_')) ||
|
|
691
|
-
(bind.mcUuid && bind.mcUuid.trim() !== ''));
|
|
683
|
+
const validBinds = allBinds.filter(bind => bind_status_1.BindStatus.hasValidMcBind(bind) || (bind.mcUuid && bind.mcUuid.trim() !== ''));
|
|
692
684
|
// 按绑定时间排序,早绑定的用户优先处理
|
|
693
685
|
validBinds.sort((a, b) => {
|
|
694
686
|
const timeA = a.lastModified ? new Date(a.lastModified).getTime() : 0;
|
package/lib/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const mcidbind_repository_1 = require("./repositories/mcidbind.repository");
|
|
|
14
14
|
const schedule_mute_repository_1 = require("./repositories/schedule-mute.repository");
|
|
15
15
|
const handlers_1 = require("./handlers");
|
|
16
16
|
const service_container_1 = require("./services/service-container");
|
|
17
|
+
const bind_status_1 = require("./utils/bind-status");
|
|
17
18
|
exports.name = 'bind-bot';
|
|
18
19
|
// 声明插件依赖
|
|
19
20
|
exports.inject = ['database', 'server'];
|
|
@@ -428,7 +429,7 @@ function apply(ctx, config) {
|
|
|
428
429
|
// 检查用户是否已有绑定记录
|
|
429
430
|
const existingBind = await services.database.getMcBindByQQId(normalizedUserId);
|
|
430
431
|
// 如果用户已完成全部绑定,不需要提醒
|
|
431
|
-
if (
|
|
432
|
+
if (bind_status_1.BindStatus.hasCompletedAllBinds(existingBind)) {
|
|
432
433
|
logger.info(`[新人绑定] 用户QQ(${normalizedUserId})已完成全部绑定,跳过提醒`);
|
|
433
434
|
return;
|
|
434
435
|
}
|
|
@@ -436,7 +437,7 @@ function apply(ctx, config) {
|
|
|
436
437
|
const inMuteTime = await isInMuteTime(session.channelId);
|
|
437
438
|
// 发送欢迎消息
|
|
438
439
|
let welcomeMessage = `🎉 欢迎新成员 ${koishi_1.h.at(session.userId)} 加入群聊!\n\n`;
|
|
439
|
-
if (!existingBind || (!existingBind
|
|
440
|
+
if (!existingBind || (!bind_status_1.BindStatus.hasValidMcBind(existingBind) && !bind_status_1.BindStatus.hasValidBuidBind(existingBind))) {
|
|
440
441
|
// 完全未绑定
|
|
441
442
|
if (inMuteTime) {
|
|
442
443
|
// 在禁言时间内,只发送欢迎消息和基本提醒
|
|
@@ -457,49 +458,36 @@ function apply(ctx, config) {
|
|
|
457
458
|
bindingSession.state = 'waiting_buid';
|
|
458
459
|
}
|
|
459
460
|
}
|
|
460
|
-
else if (existingBind
|
|
461
|
-
// 只绑定了MC
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
//
|
|
466
|
-
welcomeMessage += '
|
|
467
|
-
welcomeMessage += `🎮 可使用 ${formatCommand('mcid bind <MC用户名>')} 绑定MC账号`;
|
|
461
|
+
else if (bind_status_1.BindStatus.hasValidMcBind(existingBind) && !existingBind.buidUid) {
|
|
462
|
+
// 只绑定了MC(非临时用户名),未绑定B站
|
|
463
|
+
const displayUsername = existingBind.mcUsername;
|
|
464
|
+
welcomeMessage += `🎮 已绑定MC: ${displayUsername}\n`;
|
|
465
|
+
if (inMuteTime) {
|
|
466
|
+
// 在禁言时间内,只发送状态信息
|
|
467
|
+
welcomeMessage += `📋 请在非禁言时间段使用 ${formatCommand('buid bind <B站UID>')} 绑定B站账号`;
|
|
468
468
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
469
|
-
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})
|
|
469
|
+
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})在禁言时间内,仅发送绑定状态提醒`);
|
|
470
470
|
}
|
|
471
471
|
else {
|
|
472
|
-
//
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
else {
|
|
482
|
-
// 不在禁言时间,自动启动B站绑定
|
|
483
|
-
welcomeMessage += '📋 请发送您的B站UID进行绑定';
|
|
484
|
-
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
485
|
-
logger.info(`[新人绑定] 为新成员QQ(${normalizedUserId})自动启动B站绑定流程`);
|
|
486
|
-
// 创建绑定会话,直接进入B站绑定步骤
|
|
487
|
-
createBindingSession(session.userId, session.channelId);
|
|
488
|
-
const bindingSession = getBindingSession(session.userId, session.channelId);
|
|
489
|
-
bindingSession.state = 'waiting_buid';
|
|
490
|
-
bindingSession.mcUsername = existingBind.mcUsername;
|
|
491
|
-
}
|
|
472
|
+
// 不在禁言时间,自动启动B站绑定
|
|
473
|
+
welcomeMessage += '📋 请发送您的B站UID进行绑定';
|
|
474
|
+
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
475
|
+
logger.info(`[新人绑定] 为新成员QQ(${normalizedUserId})自动启动B站绑定流程`);
|
|
476
|
+
// 创建绑定会话,直接进入B站绑定步骤
|
|
477
|
+
createBindingSession(session.userId, session.channelId);
|
|
478
|
+
const bindingSession = getBindingSession(session.userId, session.channelId);
|
|
479
|
+
bindingSession.state = 'waiting_buid';
|
|
480
|
+
bindingSession.mcUsername = existingBind.mcUsername;
|
|
492
481
|
}
|
|
493
482
|
}
|
|
494
|
-
else if (!existingBind
|
|
483
|
+
else if (!bind_status_1.BindStatus.hasValidMcBind(existingBind) && bind_status_1.BindStatus.hasValidBuidBind(existingBind)) {
|
|
495
484
|
// 只绑定了B站,未绑定MC - 仅发送提醒
|
|
496
485
|
welcomeMessage += '📋 检测到您已绑定B站账号,但尚未绑定MC账号\n';
|
|
497
486
|
welcomeMessage += `🎮 可使用 ${formatCommand('mcid bind <MC用户名>')} 绑定MC账号`;
|
|
498
487
|
await session.bot.sendMessage(session.channelId, welcomeMessage);
|
|
499
488
|
logger.info(`[新人绑定] 新成员QQ(${normalizedUserId})已绑定B站但未绑定MC,已发送绑定提醒`);
|
|
500
489
|
}
|
|
501
|
-
else if (existingBind
|
|
502
|
-
existingBind.mcUsername.startsWith('_temp_') &&
|
|
490
|
+
else if (!bind_status_1.BindStatus.hasValidMcBind(existingBind) &&
|
|
503
491
|
existingBind.buidUid) {
|
|
504
492
|
// MC是临时用户名但已绑定B站 - 也按照"只绑定了B站"处理
|
|
505
493
|
welcomeMessage += '📋 检测到您已绑定B站账号,但尚未绑定MC账号\n';
|
|
@@ -664,6 +652,15 @@ function apply(ctx, config) {
|
|
|
664
652
|
usernameCheckFailCount: {
|
|
665
653
|
type: 'integer',
|
|
666
654
|
initial: 0
|
|
655
|
+
},
|
|
656
|
+
// 绑定状态标志字段
|
|
657
|
+
hasMcBind: {
|
|
658
|
+
type: 'boolean',
|
|
659
|
+
initial: false
|
|
660
|
+
},
|
|
661
|
+
hasBuidBind: {
|
|
662
|
+
type: 'boolean',
|
|
663
|
+
initial: false
|
|
667
664
|
}
|
|
668
665
|
}, {
|
|
669
666
|
// 设置主键为qqId
|
|
@@ -673,6 +670,11 @@ function apply(ctx, config) {
|
|
|
673
670
|
// 添加isAdmin索引,提高查询效率
|
|
674
671
|
indexes: [['isAdmin'], ['buidUid']]
|
|
675
672
|
});
|
|
673
|
+
// 在插件启动时执行数据迁移
|
|
674
|
+
ctx.on('ready', async () => {
|
|
675
|
+
logger.info('[初始化] 开始数据迁移和一致性检查...');
|
|
676
|
+
await addMissingFields();
|
|
677
|
+
});
|
|
676
678
|
// 检查表结构是否包含旧字段
|
|
677
679
|
const checkTableStructure = async () => {
|
|
678
680
|
try {
|
|
@@ -700,6 +702,7 @@ function apply(ctx, config) {
|
|
|
700
702
|
for (const record of records) {
|
|
701
703
|
let needUpdate = false;
|
|
702
704
|
const updateData = {};
|
|
705
|
+
const qqId = record.qqId; // 提前提取 qqId,避免类型推断问题
|
|
703
706
|
// 检查并添加whitelist字段
|
|
704
707
|
if (!record.whitelist) {
|
|
705
708
|
updateData.whitelist = [];
|
|
@@ -725,9 +728,49 @@ function apply(ctx, config) {
|
|
|
725
728
|
updateData.reminderCount = 0;
|
|
726
729
|
needUpdate = true;
|
|
727
730
|
}
|
|
731
|
+
// 检查并修复hasMcBind字段(数据迁移 + 数据一致性检查)
|
|
732
|
+
const currentHasMcBind = record.hasMcBind;
|
|
733
|
+
const mcUsername = record.mcUsername;
|
|
734
|
+
const hasValidMc = !!(mcUsername && !mcUsername.startsWith('_temp_'));
|
|
735
|
+
// 情况1:字段不存在,需要添加
|
|
736
|
+
if (currentHasMcBind === undefined || currentHasMcBind === null) {
|
|
737
|
+
updateData.hasMcBind = hasValidMc;
|
|
738
|
+
needUpdate = true;
|
|
739
|
+
logger.debug(`[数据迁移] 添加hasMcBind字段 QQ(${qqId}): ${hasValidMc}`);
|
|
740
|
+
}
|
|
741
|
+
// 情况2:字段存在但值不正确,需要修复
|
|
742
|
+
else if (currentHasMcBind !== hasValidMc) {
|
|
743
|
+
updateData.hasMcBind = hasValidMc;
|
|
744
|
+
needUpdate = true;
|
|
745
|
+
logger.info(`[数据修复] 修正hasMcBind QQ(${qqId}): ${currentHasMcBind} -> ${hasValidMc}`);
|
|
746
|
+
}
|
|
747
|
+
// 清理临时用户名(无论hasMcBind字段是否存在)
|
|
748
|
+
if (!hasValidMc && mcUsername && mcUsername.startsWith('_temp_')) {
|
|
749
|
+
updateData.mcUsername = '';
|
|
750
|
+
updateData.mcUuid = '';
|
|
751
|
+
updateData.whitelist = [];
|
|
752
|
+
needUpdate = true;
|
|
753
|
+
logger.info(`[数据清理] 清理QQ(${qqId})的临时用户名: ${mcUsername}`);
|
|
754
|
+
}
|
|
755
|
+
// 检查并修复hasBuidBind字段(数据迁移 + 数据一致性检查)
|
|
756
|
+
const currentHasBuidBind = record.hasBuidBind;
|
|
757
|
+
const buidUid = record.buidUid;
|
|
758
|
+
const hasValidBuid = !!(buidUid && buidUid.length > 0);
|
|
759
|
+
// 情况1:字段不存在,需要添加
|
|
760
|
+
if (currentHasBuidBind === undefined || currentHasBuidBind === null) {
|
|
761
|
+
updateData.hasBuidBind = hasValidBuid;
|
|
762
|
+
needUpdate = true;
|
|
763
|
+
logger.debug(`[数据迁移] 添加hasBuidBind字段 QQ(${qqId}): ${hasValidBuid}`);
|
|
764
|
+
}
|
|
765
|
+
// 情况2:字段存在但值不正确,需要修复
|
|
766
|
+
else if (currentHasBuidBind !== hasValidBuid) {
|
|
767
|
+
updateData.hasBuidBind = hasValidBuid;
|
|
768
|
+
needUpdate = true;
|
|
769
|
+
logger.info(`[数据修复] 修正hasBuidBind QQ(${qqId}): ${currentHasBuidBind} -> ${hasValidBuid}`);
|
|
770
|
+
}
|
|
728
771
|
// 如果需要更新,执行更新操作
|
|
729
772
|
if (needUpdate) {
|
|
730
|
-
await mcidbindRepo.update(
|
|
773
|
+
await mcidbindRepo.update(qqId, updateData);
|
|
731
774
|
updatedCount++;
|
|
732
775
|
}
|
|
733
776
|
}
|
|
@@ -1266,21 +1309,22 @@ function apply(ctx, config) {
|
|
|
1266
1309
|
return next();
|
|
1267
1310
|
}
|
|
1268
1311
|
// 情况1:完全未绑定
|
|
1269
|
-
if (!bind || (!bind
|
|
1312
|
+
if (!bind || (!bind_status_1.BindStatus.hasValidMcBind(bind) && !bind_status_1.BindStatus.hasValidBuidBind(bind))) {
|
|
1270
1313
|
// 创建新记录或获取提醒次数
|
|
1271
1314
|
let reminderCount = 0;
|
|
1272
1315
|
if (!bind) {
|
|
1273
1316
|
// 创建新记录
|
|
1274
|
-
const tempUsername = `_temp_${normalizedUserId}`;
|
|
1275
1317
|
await mcidbindRepo.create({
|
|
1276
1318
|
qqId: normalizedUserId,
|
|
1277
|
-
mcUsername:
|
|
1319
|
+
mcUsername: '',
|
|
1278
1320
|
mcUuid: '',
|
|
1279
1321
|
lastModified: new Date(),
|
|
1280
1322
|
isAdmin: false,
|
|
1281
1323
|
whitelist: [],
|
|
1282
1324
|
tags: [],
|
|
1283
|
-
reminderCount: 1
|
|
1325
|
+
reminderCount: 1,
|
|
1326
|
+
hasMcBind: false,
|
|
1327
|
+
hasBuidBind: false
|
|
1284
1328
|
});
|
|
1285
1329
|
reminderCount = 1;
|
|
1286
1330
|
}
|
|
@@ -1302,7 +1346,7 @@ function apply(ctx, config) {
|
|
|
1302
1346
|
// 情况2:只绑定了B站,未绑定MC
|
|
1303
1347
|
if (bind.buidUid &&
|
|
1304
1348
|
bind.buidUsername &&
|
|
1305
|
-
|
|
1349
|
+
!bind_status_1.BindStatus.hasValidMcBind(bind)) {
|
|
1306
1350
|
const mcInfo = null;
|
|
1307
1351
|
const isNicknameCorrect = services.nickname.checkNicknameFormat(currentNickname, bind.buidUsername, mcInfo);
|
|
1308
1352
|
if (!isNicknameCorrect) {
|
|
@@ -1325,8 +1369,7 @@ function apply(ctx, config) {
|
|
|
1325
1369
|
// 情况3:都已绑定,但群昵称格式不正确
|
|
1326
1370
|
if (bind.buidUid &&
|
|
1327
1371
|
bind.buidUsername &&
|
|
1328
|
-
bind
|
|
1329
|
-
!bind.mcUsername.startsWith('_temp_')) {
|
|
1372
|
+
bind_status_1.BindStatus.hasValidMcBind(bind)) {
|
|
1330
1373
|
const isNicknameCorrect = services.nickname.checkNicknameFormat(currentNickname, bind.buidUsername, bind.mcUsername);
|
|
1331
1374
|
if (!isNicknameCorrect) {
|
|
1332
1375
|
// 更新提醒次数
|
|
@@ -1530,20 +1573,17 @@ function apply(ctx, config) {
|
|
|
1530
1573
|
else {
|
|
1531
1574
|
// 用户未绑定B站账号,需要完成B站绑定
|
|
1532
1575
|
logger.info(`[交互绑定] QQ(${normalizedUserId})跳过了MC账号绑定,需要完成B站绑定`);
|
|
1533
|
-
//
|
|
1534
|
-
const
|
|
1535
|
-
const tempMcUsername = `_temp_skip_${timestamp}`;
|
|
1536
|
-
// 创建临时MC绑定
|
|
1537
|
-
const tempBindResult = await services.database.createOrUpdateMcBind(session.userId, tempMcUsername, '', false);
|
|
1576
|
+
// 创建绑定记录(不使用临时MC用户名)
|
|
1577
|
+
const tempBindResult = await services.database.createOrUpdateMcBind(session.userId, '', '', false);
|
|
1538
1578
|
if (!tempBindResult) {
|
|
1539
|
-
logger.error(`[交互绑定] QQ(${normalizedUserId})
|
|
1540
|
-
await sendMessage(session, [koishi_1.h.text('❌
|
|
1579
|
+
logger.error(`[交互绑定] QQ(${normalizedUserId})创建MC绑定记录失败`);
|
|
1580
|
+
await sendMessage(session, [koishi_1.h.text('❌ 创建绑定记录失败,请稍后重试')]);
|
|
1541
1581
|
return;
|
|
1542
1582
|
}
|
|
1543
1583
|
// 跳转到B站绑定流程
|
|
1544
1584
|
updateBindingSession(session.userId, session.channelId, {
|
|
1545
1585
|
state: 'waiting_buid',
|
|
1546
|
-
mcUsername:
|
|
1586
|
+
mcUsername: '',
|
|
1547
1587
|
mcUuid: ''
|
|
1548
1588
|
});
|
|
1549
1589
|
await sendMessage(session, [koishi_1.h.text('✅ 已跳过MC绑定\n📋 请发送您的B站UID')]);
|
|
@@ -1569,7 +1609,7 @@ function apply(ctx, config) {
|
|
|
1569
1609
|
const uuid = profile.id;
|
|
1570
1610
|
// 检查用户是否已绑定MC账号
|
|
1571
1611
|
const existingBind = await services.database.getMcBindByQQId(normalizedUserId);
|
|
1572
|
-
if (
|
|
1612
|
+
if (bind_status_1.BindStatus.hasValidMcBind(existingBind)) {
|
|
1573
1613
|
// 检查冷却时间
|
|
1574
1614
|
if (!(await isAdmin(session.userId)) && !checkCooldown(existingBind.lastModified)) {
|
|
1575
1615
|
const days = config.cooldownDays;
|
|
@@ -1578,9 +1618,7 @@ function apply(ctx, config) {
|
|
|
1578
1618
|
const passedDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
1579
1619
|
const remainingDays = days - passedDays;
|
|
1580
1620
|
removeBindingSession(session.userId, session.channelId);
|
|
1581
|
-
const displayUsername =
|
|
1582
|
-
? existingBind.mcUsername
|
|
1583
|
-
: '未绑定';
|
|
1621
|
+
const displayUsername = bind_status_1.BindStatus.getDisplayMcUsername(existingBind, '未绑定');
|
|
1584
1622
|
await sendMessage(session, [
|
|
1585
1623
|
koishi_1.h.text(`❌ 您已绑定MC账号: ${displayUsername}\n\n如需修改,请在冷却期结束后(还需${remainingDays}天)使用 ${formatCommand('mcid change')} 命令或联系管理员`)
|
|
1586
1624
|
]);
|
|
@@ -1728,9 +1766,7 @@ function apply(ctx, config) {
|
|
|
1728
1766
|
logger.error(`[交互绑定] QQ(${normalizedUserId})绑定B站账号失败`);
|
|
1729
1767
|
removeBindingSession(session.userId, session.channelId);
|
|
1730
1768
|
// 根据是否有MC绑定提供不同的提示
|
|
1731
|
-
const displayMcName = bindingSession.mcUsername
|
|
1732
|
-
? bindingSession.mcUsername
|
|
1733
|
-
: null;
|
|
1769
|
+
const displayMcName = bindingSession.mcUsername || null;
|
|
1734
1770
|
const mcStatus = displayMcName ? `您的MC账号${displayMcName}已成功绑定\n` : '';
|
|
1735
1771
|
await sendMessage(session, [
|
|
1736
1772
|
koishi_1.h.text(`❌ B站账号绑定失败,数据库操作出错\n\n${mcStatus}可稍后使用 ${formatCommand('buid bind <UID>')} 命令单独绑定B站账号`)
|
|
@@ -1742,10 +1778,8 @@ function apply(ctx, config) {
|
|
|
1742
1778
|
removeBindingSession(session.userId, session.channelId);
|
|
1743
1779
|
// 自动群昵称设置功能 - 使用新的autoSetGroupNickname函数
|
|
1744
1780
|
try {
|
|
1745
|
-
// 检查是否有有效的MC
|
|
1746
|
-
const mcName = bindingSession.mcUsername
|
|
1747
|
-
? bindingSession.mcUsername
|
|
1748
|
-
: null;
|
|
1781
|
+
// 检查是否有有效的MC用户名
|
|
1782
|
+
const mcName = bindingSession.mcUsername || null;
|
|
1749
1783
|
await services.nickname.autoSetGroupNickname(session, mcName, buidUser.username, String(buidUser.uid));
|
|
1750
1784
|
logger.info(`[交互绑定] QQ(${normalizedUserId})绑定完成,已设置群昵称`);
|
|
1751
1785
|
}
|
|
@@ -1766,9 +1800,7 @@ function apply(ctx, config) {
|
|
|
1766
1800
|
extraInfo += `\n荣耀等级: ${buidUser.wealthMedalLevel}`;
|
|
1767
1801
|
}
|
|
1768
1802
|
// 准备完成消息
|
|
1769
|
-
const displayMcName = bindingSession.mcUsername
|
|
1770
|
-
? bindingSession.mcUsername
|
|
1771
|
-
: null;
|
|
1803
|
+
const displayMcName = bindingSession.mcUsername || null;
|
|
1772
1804
|
const mcInfo = displayMcName ? `MC: ${displayMcName}` : 'MC: 未绑定';
|
|
1773
1805
|
let extraTip = '';
|
|
1774
1806
|
// 如果用户跳过了MC绑定或MC账号是temp,提供后续绑定的指引
|
|
@@ -26,7 +26,7 @@ export declare class DatabaseService {
|
|
|
26
26
|
*/
|
|
27
27
|
createOrUpdateMcBind(userId: string, mcUsername: string, mcUuid: string, isAdmin?: boolean): Promise<boolean>;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* 解绑 MC 账号(只清空 MC 字段,保留 B 站绑定)
|
|
30
30
|
*/
|
|
31
31
|
deleteMcBind(userId: string): Promise<boolean>;
|
|
32
32
|
/**
|
|
@@ -49,6 +49,10 @@ export declare class DatabaseService {
|
|
|
49
49
|
* 仅更新 B 站信息,不更新绑定时间(用于查询时刷新数据)
|
|
50
50
|
*/
|
|
51
51
|
updateBuidInfoOnly(userId: string, buidUser: ZminfoUser): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* 解绑 B 站账号(只清空 B 站字段,保留 MC 绑定)
|
|
54
|
+
*/
|
|
55
|
+
deleteBuidBind(userId: string): Promise<boolean>;
|
|
52
56
|
/**
|
|
53
57
|
* 检查并更新用户名(如果与当前数据库中的不同)
|
|
54
58
|
*/
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DatabaseService = void 0;
|
|
4
4
|
const helpers_1 = require("../utils/helpers");
|
|
5
|
+
const bind_status_1 = require("../utils/bind-status");
|
|
5
6
|
/**
|
|
6
7
|
* 数据库服务层
|
|
7
8
|
* 统一管理数据库操作,包括 MC 绑定和 BUID 绑定的 CRUD
|
|
@@ -77,7 +78,8 @@ class DatabaseService {
|
|
|
77
78
|
const updateData = {
|
|
78
79
|
mcUsername,
|
|
79
80
|
mcUuid,
|
|
80
|
-
lastModified: new Date()
|
|
81
|
+
lastModified: new Date(),
|
|
82
|
+
hasMcBind: true
|
|
81
83
|
};
|
|
82
84
|
// 仅当指定了isAdmin参数时更新管理员状态
|
|
83
85
|
if (typeof isAdmin !== 'undefined') {
|
|
@@ -95,7 +97,9 @@ class DatabaseService {
|
|
|
95
97
|
mcUsername,
|
|
96
98
|
mcUuid,
|
|
97
99
|
lastModified: new Date(),
|
|
98
|
-
isAdmin: isAdmin || false
|
|
100
|
+
isAdmin: isAdmin || false,
|
|
101
|
+
hasMcBind: true,
|
|
102
|
+
hasBuidBind: false
|
|
99
103
|
});
|
|
100
104
|
this.logger.info('MCIDBIND', `创建绑定: QQ=${normalizedQQId}, MC用户名=${mcUsername}, UUID=${mcUuid}`, true);
|
|
101
105
|
return true;
|
|
@@ -112,45 +116,59 @@ class DatabaseService {
|
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
118
|
/**
|
|
115
|
-
*
|
|
119
|
+
* 解绑 MC 账号(只清空 MC 字段,保留 B 站绑定)
|
|
116
120
|
*/
|
|
117
121
|
async deleteMcBind(userId) {
|
|
118
122
|
try {
|
|
119
123
|
// 验证输入参数
|
|
120
124
|
if (!userId) {
|
|
121
|
-
this.logger.error('MCIDBIND', '
|
|
125
|
+
this.logger.error('MCIDBIND', '解绑MC失败: 无效的用户ID');
|
|
122
126
|
return false;
|
|
123
127
|
}
|
|
124
128
|
const normalizedQQId = this.normalizeQQId(userId);
|
|
125
129
|
if (!normalizedQQId) {
|
|
126
|
-
this.logger.error('MCIDBIND', '
|
|
130
|
+
this.logger.error('MCIDBIND', '解绑MC失败: 无法提取有效的QQ号');
|
|
127
131
|
return false;
|
|
128
132
|
}
|
|
129
133
|
// 查询是否存在绑定记录
|
|
130
134
|
const bind = await this.getMcBindByQQId(normalizedQQId);
|
|
131
|
-
if (bind) {
|
|
132
|
-
|
|
135
|
+
if (!bind) {
|
|
136
|
+
this.logger.warn('MCIDBIND', `解绑MC失败: QQ=${normalizedQQId}不存在绑定记录`);
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
// 如果没有MC绑定,返回失败
|
|
140
|
+
if (!bind_status_1.BindStatus.hasValidMcBind(bind)) {
|
|
141
|
+
this.logger.warn('MCIDBIND', `解绑MC失败: QQ=${normalizedQQId}未绑定MC账号`);
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const oldUsername = bind.mcUsername;
|
|
145
|
+
// 检查是否有B站绑定
|
|
146
|
+
if (bind_status_1.BindStatus.hasValidBuidBind(bind)) {
|
|
147
|
+
// 如果有B站绑定,只清空MC字段,保留记录
|
|
148
|
+
await this.mcidbindRepo.update(normalizedQQId, {
|
|
149
|
+
mcUsername: '',
|
|
150
|
+
mcUuid: '',
|
|
151
|
+
hasMcBind: false,
|
|
152
|
+
whitelist: [],
|
|
153
|
+
lastModified: new Date()
|
|
154
|
+
});
|
|
155
|
+
this.logger.info('MCIDBIND', `解绑MC: QQ=${normalizedQQId}, MC用户名=${oldUsername}, 保留B站绑定`, true);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// 如果没有B站绑定,删除整条记录
|
|
133
159
|
const removedCount = await this.mcidbindRepo.delete(normalizedQQId);
|
|
134
|
-
// 检查是否真正删除成功
|
|
135
160
|
if (removedCount > 0) {
|
|
136
|
-
|
|
137
|
-
if (bind.mcUsername)
|
|
138
|
-
logMessage += `, MC用户名=${bind.mcUsername}`;
|
|
139
|
-
if (bind.buidUid)
|
|
140
|
-
logMessage += `, B站UID=${bind.buidUid}(${bind.buidUsername})`;
|
|
141
|
-
this.logger.info('MCIDBIND', logMessage, true);
|
|
142
|
-
return true;
|
|
161
|
+
this.logger.info('MCIDBIND', `解绑MC并删除记录: QQ=${normalizedQQId}, MC用户名=${oldUsername}`, true);
|
|
143
162
|
}
|
|
144
163
|
else {
|
|
145
|
-
this.logger.warn('MCIDBIND',
|
|
164
|
+
this.logger.warn('MCIDBIND', `解绑MC异常: QQ=${normalizedQQId}, 可能未实际删除`);
|
|
146
165
|
return false;
|
|
147
166
|
}
|
|
148
167
|
}
|
|
149
|
-
|
|
150
|
-
return false;
|
|
168
|
+
return true;
|
|
151
169
|
}
|
|
152
170
|
catch (error) {
|
|
153
|
-
this.logger.error('MCIDBIND',
|
|
171
|
+
this.logger.error('MCIDBIND', `解绑MC失败: 错误=${error.message}`);
|
|
154
172
|
return false;
|
|
155
173
|
}
|
|
156
174
|
}
|
|
@@ -164,17 +182,13 @@ class DatabaseService {
|
|
|
164
182
|
this.logger.warn('绑定检查', '尝试检查空MC用户名');
|
|
165
183
|
return false;
|
|
166
184
|
}
|
|
167
|
-
// 跳过临时用户名的检查
|
|
168
|
-
if (username.startsWith('_temp_')) {
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
185
|
// 使用不区分大小写的查询
|
|
172
186
|
const bind = await this.mcidbindRepo.findByUsernameIgnoreCase(username);
|
|
173
187
|
// 如果没有绑定,返回false
|
|
174
188
|
if (!bind)
|
|
175
189
|
return false;
|
|
176
190
|
// 如果绑定的用户名是临时用户名,视为未绑定
|
|
177
|
-
if (
|
|
191
|
+
if (!bind_status_1.BindStatus.hasValidMcBind(bind)) {
|
|
178
192
|
return false;
|
|
179
193
|
}
|
|
180
194
|
// 如果提供了 UUID,检查是否为同一个 MC 账号(用户改名场景)
|
|
@@ -272,23 +286,26 @@ class DatabaseService {
|
|
|
272
286
|
lastModified: new Date()
|
|
273
287
|
};
|
|
274
288
|
if (bind) {
|
|
289
|
+
// 添加 hasBuidBind 标志
|
|
290
|
+
updateData.hasBuidBind = true;
|
|
275
291
|
await this.mcidbindRepo.update(normalizedQQId, updateData);
|
|
276
292
|
this.logger.info('B站账号绑定', `更新绑定: QQ=${normalizedQQId}, B站UID=${buidUser.uid}, 用户名=${buidUser.username}`, true);
|
|
277
293
|
}
|
|
278
294
|
else {
|
|
279
|
-
//
|
|
280
|
-
const tempMcUsername = `_temp_skip_${normalizedQQId}_${Date.now()}`;
|
|
295
|
+
// 创建新绑定记录(不使用临时用户名)
|
|
281
296
|
const newBind = {
|
|
282
297
|
qqId: normalizedQQId,
|
|
283
|
-
mcUsername:
|
|
298
|
+
mcUsername: '',
|
|
284
299
|
mcUuid: '',
|
|
285
300
|
isAdmin: false,
|
|
286
301
|
whitelist: [],
|
|
287
302
|
tags: [],
|
|
303
|
+
hasMcBind: false,
|
|
304
|
+
hasBuidBind: true,
|
|
288
305
|
...updateData
|
|
289
306
|
};
|
|
290
307
|
await this.mcidbindRepo.create(newBind);
|
|
291
|
-
this.logger.info('B站账号绑定', `创建绑定(跳过MC): QQ=${normalizedQQId}, B站UID=${buidUser.uid}, 用户名=${buidUser.username}
|
|
308
|
+
this.logger.info('B站账号绑定', `创建绑定(跳过MC): QQ=${normalizedQQId}, B站UID=${buidUser.uid}, 用户名=${buidUser.username}`, true);
|
|
292
309
|
}
|
|
293
310
|
return true;
|
|
294
311
|
}
|
|
@@ -334,6 +351,71 @@ class DatabaseService {
|
|
|
334
351
|
return false;
|
|
335
352
|
}
|
|
336
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* 解绑 B 站账号(只清空 B 站字段,保留 MC 绑定)
|
|
356
|
+
*/
|
|
357
|
+
async deleteBuidBind(userId) {
|
|
358
|
+
try {
|
|
359
|
+
// 验证输入参数
|
|
360
|
+
if (!userId) {
|
|
361
|
+
this.logger.error('B站账号解绑', '解绑失败: 无效的用户ID');
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
const normalizedQQId = this.normalizeQQId(userId);
|
|
365
|
+
if (!normalizedQQId) {
|
|
366
|
+
this.logger.error('B站账号解绑', '解绑失败: 无法提取有效的QQ号');
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
// 查询是否存在绑定记录
|
|
370
|
+
const bind = await this.getMcBindByQQId(normalizedQQId);
|
|
371
|
+
if (!bind) {
|
|
372
|
+
this.logger.warn('B站账号解绑', `解绑失败: QQ=${normalizedQQId}不存在绑定记录`);
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
// 如果没有B站绑定,返回失败
|
|
376
|
+
if (!bind_status_1.BindStatus.hasValidBuidBind(bind)) {
|
|
377
|
+
this.logger.warn('B站账号解绑', `解绑失败: QQ=${normalizedQQId}未绑定B站账号`);
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
const oldBuidUid = bind.buidUid;
|
|
381
|
+
const oldBuidUsername = bind.buidUsername;
|
|
382
|
+
// 检查是否有MC绑定
|
|
383
|
+
if (bind_status_1.BindStatus.hasValidMcBind(bind)) {
|
|
384
|
+
// 如果有MC绑定,只清空B站字段,保留记录
|
|
385
|
+
await this.mcidbindRepo.update(normalizedQQId, {
|
|
386
|
+
buidUid: '',
|
|
387
|
+
buidUsername: '',
|
|
388
|
+
guardLevel: 0,
|
|
389
|
+
guardLevelText: '',
|
|
390
|
+
maxGuardLevel: 0,
|
|
391
|
+
maxGuardLevelText: '',
|
|
392
|
+
medalName: '',
|
|
393
|
+
medalLevel: 0,
|
|
394
|
+
wealthMedalLevel: 0,
|
|
395
|
+
lastActiveTime: null,
|
|
396
|
+
hasBuidBind: false,
|
|
397
|
+
lastModified: new Date()
|
|
398
|
+
});
|
|
399
|
+
this.logger.info('B站账号解绑', `解绑B站: QQ=${normalizedQQId}, B站UID=${oldBuidUid}, 用户名=${oldBuidUsername}, 保留MC绑定`, true);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
// 如果没有MC绑定,删除整条记录
|
|
403
|
+
const removedCount = await this.mcidbindRepo.delete(normalizedQQId);
|
|
404
|
+
if (removedCount > 0) {
|
|
405
|
+
this.logger.info('B站账号解绑', `解绑B站并删除记录: QQ=${normalizedQQId}, B站UID=${oldBuidUid}, 用户名=${oldBuidUsername}`, true);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
this.logger.warn('B站账号解绑', `解绑B站异常: QQ=${normalizedQQId}, 可能未实际删除`);
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
this.logger.error('B站账号解绑', `解绑B站失败: 错误=${error.message}`);
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
337
419
|
// =========== 用户名更新检查 ===========
|
|
338
420
|
/**
|
|
339
421
|
* 检查并更新用户名(如果与当前数据库中的不同)
|
|
@@ -28,7 +28,7 @@ class NicknameService {
|
|
|
28
28
|
if (!nickname || !buidUsername)
|
|
29
29
|
return false;
|
|
30
30
|
// 期望格式:B站名称(ID:MC用户名)或 B站名称(ID:未绑定)
|
|
31
|
-
const mcInfo = mcUsername
|
|
31
|
+
const mcInfo = mcUsername || '未绑定';
|
|
32
32
|
const expectedFormat = `${buidUsername}(ID:${mcInfo})`;
|
|
33
33
|
return nickname === expectedFormat;
|
|
34
34
|
}
|
|
@@ -159,7 +159,7 @@ class NicknameService {
|
|
|
159
159
|
const actualUserId = targetUserId || session.userId;
|
|
160
160
|
const normalizedUserId = this.normalizeQQId(actualUserId);
|
|
161
161
|
const targetGroupId = specifiedGroupId || this.config.autoNicknameGroupId;
|
|
162
|
-
const mcInfo = mcUsername
|
|
162
|
+
const mcInfo = mcUsername || '未绑定';
|
|
163
163
|
this.logger.debug('群昵称设置', `开始处理QQ(${normalizedUserId})的群昵称设置,目标群: ${targetGroupId}`);
|
|
164
164
|
// 检查前置条件
|
|
165
165
|
if (!session.bot.internal) {
|
package/lib/types/database.d.ts
CHANGED
|
@@ -76,6 +76,10 @@ export interface MCIDBIND {
|
|
|
76
76
|
lastActiveTime: Date;
|
|
77
77
|
/** 随机提醒次数 (用于天选时刻等功能) */
|
|
78
78
|
reminderCount: number;
|
|
79
|
+
/** 是否已绑定 Minecraft 账号 (true=已绑定有效MC账号, false=未绑定或临时状态) */
|
|
80
|
+
hasMcBind: boolean;
|
|
81
|
+
/** 是否已绑定 B站账号 (true=已绑定有效B站账号, false=未绑定) */
|
|
82
|
+
hasBuidBind: boolean;
|
|
79
83
|
}
|
|
80
84
|
/**
|
|
81
85
|
* SCHEDULE_MUTE_TASKS表结构 - 定时禁言任务配置
|