koishi-plugin-bind-bot 2.2.9 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,6 +38,7 @@ const XLSX = __importStar(require("xlsx"));
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const bind_status_1 = require("./utils/bind-status");
41
+ const helpers_1 = require("./utils/helpers");
41
42
  class GroupExporter {
42
43
  logger;
43
44
  ctx;
@@ -169,7 +170,7 @@ class GroupExporter {
169
170
  MC_UUID: binding?.mcUuid || '',
170
171
  B站UID: binding?.buidUid || '',
171
172
  B站用户名: binding?.buidUsername || '',
172
- 舰长等级: binding?.guardLevelText || '',
173
+ 舰长等级: (0, helpers_1.getGuardLevelText)(binding?.guardLevel),
173
174
  粉丝牌名称: binding?.medalName || '',
174
175
  粉丝牌等级: binding?.medalLevel ? binding.medalLevel.toString() : '',
175
176
  荣耀等级: binding?.wealthMedalLevel ? binding.wealthMedalLevel.toString() : '',
@@ -4,6 +4,7 @@ exports.BindingHandler = void 0;
4
4
  const koishi_1 = require("koishi");
5
5
  const base_handler_1 = require("./base.handler");
6
6
  const bind_status_1 = require("../utils/bind-status");
7
+ const helpers_1 = require("../utils/helpers");
7
8
  /**
8
9
  * 交互式绑定命令处理器
9
10
  * 处理 "绑定" 命令,引导用户完成 MC 和 B站双重绑定
@@ -25,7 +26,7 @@ class BindingHandler extends base_handler_1.BaseHandler {
25
26
  const displayUsername = bind_status_1.BindStatus.getDisplayMcUsername(bind, '未绑定');
26
27
  let bindInfo = `${prefix}\n✅ MC账号: ${displayUsername}\n✅ B站账号: ${bind.buidUsername} (UID: ${bind.buidUid})`;
27
28
  if (bind.guardLevel > 0) {
28
- bindInfo += `\n舰长等级: ${bind.guardLevelText}`;
29
+ bindInfo += `\n舰长等级: ${(0, helpers_1.getGuardLevelText)(bind.guardLevel)}`;
29
30
  }
30
31
  if (bind.medalName) {
31
32
  bindInfo += `\n粉丝牌: ${bind.medalName} Lv.${bind.medalLevel}`;
@@ -8,6 +8,7 @@ const koishi_1 = require("koishi");
8
8
  const base_handler_1 = require("./base.handler");
9
9
  const axios_1 = __importDefault(require("axios"));
10
10
  const bind_status_1 = require("../utils/bind-status");
11
+ const helpers_1 = require("../utils/helpers");
11
12
  /**
12
13
  * BUID 命令处理器
13
14
  * 处理 B站账号相关命令
@@ -78,13 +79,13 @@ class BuidHandler extends base_handler_1.BaseHandler {
78
79
  const userInfo = `${target ? `用户 ${bind.qqId} 的` : '您的'}B站账号信息:\nB站UID: ${bind.buidUid}\n用户名: ${bind.buidUsername}`;
79
80
  let detailInfo = '';
80
81
  if (bind.guardLevel > 0) {
81
- detailInfo += `\n舰长等级: ${bind.guardLevelText} (${bind.guardLevel})`;
82
+ detailInfo += `\n舰长等级: ${(0, helpers_1.getGuardLevelText)(bind.guardLevel)} (${bind.guardLevel})`;
82
83
  if (bind.maxGuardLevel > 0 && bind.maxGuardLevel < bind.guardLevel) {
83
- detailInfo += `\n历史最高: ${bind.maxGuardLevelText} (${bind.maxGuardLevel})`;
84
+ detailInfo += `\n历史最高: ${(0, helpers_1.getGuardLevelText)(bind.maxGuardLevel)} (${bind.maxGuardLevel})`;
84
85
  }
85
86
  }
86
87
  else if (bind.maxGuardLevel > 0) {
87
- detailInfo += `\n历史舰长: ${bind.maxGuardLevelText} (${bind.maxGuardLevel})`;
88
+ detailInfo += `\n历史舰长: ${(0, helpers_1.getGuardLevelText)(bind.maxGuardLevel)} (${bind.maxGuardLevel})`;
88
89
  }
89
90
  detailInfo += `\n粉丝牌: ${bind.medalName || '无'} Lv.${bind.medalLevel || 0}`;
90
91
  detailInfo += `\n荣耀等级: ${bind.wealthMedalLevel || 0}`;
@@ -161,13 +162,13 @@ class BuidHandler extends base_handler_1.BaseHandler {
161
162
  // 构建详细信息
162
163
  let adminInfo = `B站UID"${bind.buidUid}"绑定信息:\nQQ号: ${bind.qqId}\n用户名: ${bind.buidUsername}`;
163
164
  if (bind.guardLevel > 0) {
164
- adminInfo += `\n舰长等级: ${bind.guardLevelText} (${bind.guardLevel})`;
165
+ adminInfo += `\n舰长等级: ${(0, helpers_1.getGuardLevelText)(bind.guardLevel)} (${bind.guardLevel})`;
165
166
  if (bind.maxGuardLevel > 0 && bind.maxGuardLevel < bind.guardLevel) {
166
- adminInfo += `\n历史最高: ${bind.maxGuardLevelText} (${bind.maxGuardLevel})`;
167
+ adminInfo += `\n历史最高: ${(0, helpers_1.getGuardLevelText)(bind.maxGuardLevel)} (${bind.maxGuardLevel})`;
167
168
  }
168
169
  }
169
170
  else if (bind.maxGuardLevel > 0) {
170
- adminInfo += `\n历史舰长: ${bind.maxGuardLevelText} (${bind.maxGuardLevel})`;
171
+ adminInfo += `\n历史舰长: ${(0, helpers_1.getGuardLevelText)(bind.maxGuardLevel)} (${bind.maxGuardLevel})`;
171
172
  }
172
173
  if (bind.medalName) {
173
174
  adminInfo += `\n粉丝牌: ${bind.medalName} Lv.${bind.medalLevel}`;
@@ -374,9 +375,7 @@ class BuidHandler extends base_handler_1.BaseHandler {
374
375
  buidUid: buidUser.uid,
375
376
  buidUsername: buidUser.username,
376
377
  guardLevel: buidUser.guard_level || 0,
377
- guardLevelText: buidUser.guard_level_text || '',
378
378
  maxGuardLevel: buidUser.max_guard_level || 0,
379
- maxGuardLevelText: buidUser.max_guard_level_text || '',
380
379
  medalName: buidUser.medal?.name || '',
381
380
  medalLevel: buidUser.medal?.level || 0,
382
381
  wealthMedalLevel: buidUser.wealthMedalLevel || 0,
@@ -431,9 +430,7 @@ class BuidHandler extends base_handler_1.BaseHandler {
431
430
  const updateData = {
432
431
  buidUsername: buidUser.username,
433
432
  guardLevel: buidUser.guard_level || 0,
434
- guardLevelText: buidUser.guard_level_text || '',
435
433
  maxGuardLevel: buidUser.max_guard_level || 0,
436
- maxGuardLevelText: buidUser.max_guard_level_text || '',
437
434
  medalName: buidUser.medal?.name || '',
438
435
  medalLevel: buidUser.medal?.level || 0,
439
436
  wealthMedalLevel: buidUser.wealthMedalLevel || 0,
@@ -638,9 +638,7 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
638
638
  buidUid: zminfoUser.uid,
639
639
  buidUsername: zminfoUser.username,
640
640
  guardLevel: zminfoUser.guard_level || 0,
641
- guardLevelText: zminfoUser.guard_level_text || '',
642
641
  maxGuardLevel: zminfoUser.guard_level || 0,
643
- maxGuardLevelText: zminfoUser.guard_level_text || '',
644
642
  medalName: zminfoUser.medal?.name || '',
645
643
  medalLevel: zminfoUser.medal?.level || 0,
646
644
  wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
@@ -659,11 +657,7 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
659
657
  buidUid: zminfoUser.uid,
660
658
  buidUsername: zminfoUser.username,
661
659
  guardLevel: zminfoUser.guard_level || 0,
662
- guardLevelText: zminfoUser.guard_level_text || '',
663
660
  maxGuardLevel: Math.max(bind.maxGuardLevel || 0, zminfoUser.guard_level || 0),
664
- maxGuardLevelText: zminfoUser.guard_level > (bind.maxGuardLevel || 0)
665
- ? zminfoUser.guard_level_text
666
- : bind.maxGuardLevelText,
667
661
  medalName: zminfoUser.medal?.name || '',
668
662
  medalLevel: zminfoUser.medal?.level || 0,
669
663
  wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
@@ -4,6 +4,7 @@ exports.McidCommandHandler = void 0;
4
4
  const koishi_1 = require("koishi");
5
5
  const base_handler_1 = require("./base.handler");
6
6
  const bind_status_1 = require("../utils/bind-status");
7
+ const helpers_1 = require("../utils/helpers");
7
8
  class McidCommandHandler extends base_handler_1.BaseHandler {
8
9
  /**
9
10
  * 注册所有MCID命令
@@ -92,14 +93,14 @@ class McidCommandHandler extends base_handler_1.BaseHandler {
92
93
  if (refreshedBind) {
93
94
  let buidInfo = `该用户尚未绑定MC账号\n\nB站账号信息:\nB站UID: ${refreshedBind.buidUid}\n用户名: ${refreshedBind.buidUsername}`;
94
95
  if (refreshedBind.guardLevel > 0) {
95
- buidInfo += `\n舰长等级: ${refreshedBind.guardLevelText} (${refreshedBind.guardLevel})`;
96
+ buidInfo += `\n舰长等级: ${(0, helpers_1.getGuardLevelText)(refreshedBind.guardLevel)} (${refreshedBind.guardLevel})`;
96
97
  if (refreshedBind.maxGuardLevel > 0 &&
97
98
  refreshedBind.maxGuardLevel < refreshedBind.guardLevel) {
98
- buidInfo += `\n历史最高: ${refreshedBind.maxGuardLevelText} (${refreshedBind.maxGuardLevel})`;
99
+ buidInfo += `\n历史最高: ${(0, helpers_1.getGuardLevelText)(refreshedBind.maxGuardLevel)} (${refreshedBind.maxGuardLevel})`;
99
100
  }
100
101
  }
101
102
  else if (refreshedBind.maxGuardLevel > 0) {
102
- buidInfo += `\n历史舰长: ${refreshedBind.maxGuardLevelText} (${refreshedBind.maxGuardLevel})`;
103
+ buidInfo += `\n历史舰长: ${(0, helpers_1.getGuardLevelText)(refreshedBind.maxGuardLevel)} (${refreshedBind.maxGuardLevel})`;
103
104
  }
104
105
  if (refreshedBind.medalName) {
105
106
  buidInfo += `\n粉丝牌: ${refreshedBind.medalName} Lv.${refreshedBind.medalLevel}`;
@@ -132,14 +133,14 @@ class McidCommandHandler extends base_handler_1.BaseHandler {
132
133
  if (refreshedBind) {
133
134
  let buidInfo = `您尚未绑定MC账号\n\nB站账号信息:\nB站UID: ${refreshedBind.buidUid}\n用户名: ${refreshedBind.buidUsername}`;
134
135
  if (refreshedBind.guardLevel > 0) {
135
- buidInfo += `\n舰长等级: ${refreshedBind.guardLevelText} (${refreshedBind.guardLevel})`;
136
+ buidInfo += `\n舰长等级: ${(0, helpers_1.getGuardLevelText)(refreshedBind.guardLevel)} (${refreshedBind.guardLevel})`;
136
137
  if (refreshedBind.maxGuardLevel > 0 &&
137
138
  refreshedBind.maxGuardLevel < refreshedBind.guardLevel) {
138
- buidInfo += `\n历史最高: ${refreshedBind.maxGuardLevelText} (${refreshedBind.maxGuardLevel})`;
139
+ buidInfo += `\n历史最高: ${(0, helpers_1.getGuardLevelText)(refreshedBind.maxGuardLevel)} (${refreshedBind.maxGuardLevel})`;
139
140
  }
140
141
  }
141
142
  else if (refreshedBind.maxGuardLevel > 0) {
142
- buidInfo += `\n历史舰长: ${refreshedBind.maxGuardLevelText} (${refreshedBind.maxGuardLevel})`;
143
+ buidInfo += `\n历史舰长: ${(0, helpers_1.getGuardLevelText)(refreshedBind.maxGuardLevel)} (${refreshedBind.maxGuardLevel})`;
143
144
  }
144
145
  if (refreshedBind.medalName) {
145
146
  buidInfo += `\n粉丝牌: ${refreshedBind.medalName} Lv.${refreshedBind.medalLevel}`;
@@ -217,13 +218,13 @@ class McidCommandHandler extends base_handler_1.BaseHandler {
217
218
  if (bind.buidUid) {
218
219
  buidInfo = `B站账号信息:\nB站UID: ${bind.buidUid}\n用户名: ${bind.buidUsername}`;
219
220
  if (bind.guardLevel > 0) {
220
- buidInfo += `\n舰长等级: ${bind.guardLevelText} (${bind.guardLevel})`;
221
+ buidInfo += `\n舰长等级: ${(0, helpers_1.getGuardLevelText)(bind.guardLevel)} (${bind.guardLevel})`;
221
222
  if (bind.maxGuardLevel > 0 && bind.maxGuardLevel < bind.guardLevel) {
222
- buidInfo += `\n历史最高: ${bind.maxGuardLevelText} (${bind.maxGuardLevel})`;
223
+ buidInfo += `\n历史最高: ${(0, helpers_1.getGuardLevelText)(bind.maxGuardLevel)} (${bind.maxGuardLevel})`;
223
224
  }
224
225
  }
225
226
  else if (bind.maxGuardLevel > 0) {
226
- buidInfo += `\n历史舰长: ${bind.maxGuardLevelText} (${bind.maxGuardLevel})`;
227
+ buidInfo += `\n历史舰长: ${(0, helpers_1.getGuardLevelText)(bind.maxGuardLevel)} (${bind.maxGuardLevel})`;
227
228
  }
228
229
  if (bind.medalName) {
229
230
  buidInfo += `\n粉丝牌: ${bind.medalName} Lv.${bind.medalLevel}`;
package/lib/index.js CHANGED
@@ -11,6 +11,7 @@ const rate_limiter_1 = require("./utils/rate-limiter");
11
11
  const helpers_1 = require("./utils/helpers");
12
12
  const error_utils_1 = require("./utils/error-utils");
13
13
  const mcidbind_repository_1 = require("./repositories/mcidbind.repository");
14
+ const supabase_client_1 = require("./utils/supabase-client");
14
15
  const schedule_mute_repository_1 = require("./repositories/schedule-mute.repository");
15
16
  const handlers_1 = require("./handlers");
16
17
  const service_container_1 = require("./services/service-container");
@@ -53,6 +54,12 @@ exports.Config = koishi_1.Schema.object({
53
54
  forceBindTargetUpUid: koishi_1.Schema.number().description('强制绑定目标UP主UID').default(686127),
54
55
  forceBindTargetRoomId: koishi_1.Schema.number().description('强制绑定目标房间号').default(544853),
55
56
  forceBindTargetMedalName: koishi_1.Schema.string().description('强制绑定目标粉丝牌名称').default('生态'),
57
+ supabaseUrl: koishi_1.Schema.string()
58
+ .description('Supabase项目URL')
59
+ .default(''),
60
+ supabaseKey: koishi_1.Schema.string()
61
+ .description('Supabase API Key (anon/service_role key)')
62
+ .default(''),
56
63
  groupRequestReview: koishi_1.Schema.object({
57
64
  enabled: koishi_1.Schema.boolean().description('是否启用入群申请审批功能').default(false),
58
65
  targetGroupId: koishi_1.Schema.string()
@@ -109,7 +116,8 @@ function apply(ctx, config) {
109
116
  const logger = new koishi_1.Logger('bind-bot');
110
117
  const loggerService = new logger_1.LoggerService(logger, config.debugMode);
111
118
  // 创建数据仓储实例
112
- const mcidbindRepo = new mcidbind_repository_1.MCIDBINDRepository(ctx, loggerService);
119
+ const supabaseClient = new supabase_client_1.SupabaseClient({ url: config.supabaseUrl, key: config.supabaseKey });
120
+ const mcidbindRepo = new mcidbind_repository_1.MCIDBINDRepository(supabaseClient, loggerService);
113
121
  const scheduleMuteRepo = new schedule_mute_repository_1.ScheduleMuteRepository(ctx, loggerService);
114
122
  // 交互型绑定会话管理
115
123
  const bindingSessions = new Map();
@@ -583,299 +591,7 @@ function apply(ctx, config) {
583
591
  content.body = 'Internal Server Error';
584
592
  }
585
593
  });
586
- // 在数据库中创建MCIDBIND表
587
- ctx.model.extend('mcidbind', {
588
- qqId: {
589
- type: 'string'
590
- },
591
- mcUsername: {
592
- type: 'string',
593
- initial: null
594
- },
595
- mcUuid: {
596
- type: 'string',
597
- initial: null
598
- },
599
- lastModified: {
600
- type: 'timestamp',
601
- initial: null
602
- },
603
- isAdmin: {
604
- type: 'boolean',
605
- initial: false
606
- },
607
- whitelist: {
608
- type: 'json',
609
- initial: []
610
- },
611
- tags: {
612
- type: 'json',
613
- initial: []
614
- },
615
- // BUID相关字段
616
- buidUid: {
617
- type: 'string',
618
- initial: ''
619
- },
620
- buidUsername: {
621
- type: 'string',
622
- initial: ''
623
- },
624
- guardLevel: {
625
- type: 'integer',
626
- initial: 0
627
- },
628
- guardLevelText: {
629
- type: 'string',
630
- initial: ''
631
- },
632
- maxGuardLevel: {
633
- type: 'integer',
634
- initial: 0
635
- },
636
- maxGuardLevelText: {
637
- type: 'string',
638
- initial: ''
639
- },
640
- medalName: {
641
- type: 'string',
642
- initial: ''
643
- },
644
- medalLevel: {
645
- type: 'integer',
646
- initial: 0
647
- },
648
- wealthMedalLevel: {
649
- type: 'integer',
650
- initial: 0
651
- },
652
- lastActiveTime: {
653
- type: 'timestamp',
654
- initial: null
655
- },
656
- reminderCount: {
657
- type: 'integer',
658
- initial: 0
659
- },
660
- usernameLastChecked: {
661
- type: 'timestamp',
662
- initial: null
663
- },
664
- usernameCheckFailCount: {
665
- type: 'integer',
666
- initial: 0
667
- },
668
- // 绑定状态标志字段
669
- hasMcBind: {
670
- type: 'boolean',
671
- initial: false
672
- },
673
- hasBuidBind: {
674
- type: 'boolean',
675
- initial: false
676
- }
677
- }, {
678
- // 设置主键为qqId
679
- primary: 'qqId',
680
- // 添加索引
681
- unique: [['mcUsername'], ['buidUid']],
682
- // 添加isAdmin索引,提高查询效率
683
- indexes: [['isAdmin'], ['buidUid']]
684
- });
685
- // 检查表结构是否包含旧字段
686
- const checkTableStructure = async () => {
687
- try {
688
- // 尝试获取一条记录来检查字段
689
- const records = await mcidbindRepo.findAll({ limit: 1 });
690
- // 如果没有记录,不需要迁移
691
- if (!records || records.length === 0)
692
- return false;
693
- // 检查记录中是否包含id或userId字段,或缺少whitelist字段
694
- const record = records[0];
695
- return 'id' in record || 'userId' in record || !('whitelist' in record);
696
- }
697
- catch (error) {
698
- logger.error(`[初始化] 检查表结构失败: ${error.message}`);
699
- return false;
700
- }
701
- };
702
- // 添加缺失字段
703
- const addMissingFields = async () => {
704
- try {
705
- // 获取所有记录
706
- const records = await mcidbindRepo.findAll();
707
- let updatedCount = 0;
708
- // 更新每个缺少字段的记录
709
- for (const record of records) {
710
- let needUpdate = false;
711
- const updateData = {};
712
- const qqId = record.qqId; // 提前提取 qqId,避免类型推断问题
713
- // 检查并添加whitelist字段
714
- if (!record.whitelist) {
715
- updateData.whitelist = [];
716
- needUpdate = true;
717
- }
718
- // 检查并添加tags字段
719
- if (!record.tags) {
720
- updateData.tags = [];
721
- needUpdate = true;
722
- }
723
- // 检查并添加maxGuardLevel字段
724
- if (!('maxGuardLevel' in record)) {
725
- updateData.maxGuardLevel = 0;
726
- needUpdate = true;
727
- }
728
- // 检查并添加maxGuardLevelText字段
729
- if (!('maxGuardLevelText' in record)) {
730
- updateData.maxGuardLevelText = '';
731
- needUpdate = true;
732
- }
733
- // 检查并添加reminderCount字段
734
- if (!('reminderCount' in record)) {
735
- updateData.reminderCount = 0;
736
- needUpdate = true;
737
- }
738
- // 检查并修复hasMcBind字段(数据迁移 + 数据一致性检查)
739
- const currentHasMcBind = record.hasMcBind;
740
- const mcUsername = record.mcUsername;
741
- const hasValidMc = !!(mcUsername && !mcUsername.startsWith('_temp_'));
742
- // 情况1:字段不存在,需要添加
743
- if (currentHasMcBind === undefined || currentHasMcBind === null) {
744
- updateData.hasMcBind = hasValidMc;
745
- needUpdate = true;
746
- logger.debug(`[数据迁移] 添加hasMcBind字段 QQ(${qqId}): ${hasValidMc}`);
747
- }
748
- // 情况2:字段存在但值不正确,需要修复
749
- else if (currentHasMcBind !== hasValidMc) {
750
- updateData.hasMcBind = hasValidMc;
751
- needUpdate = true;
752
- logger.info(`[数据修复] 修正hasMcBind QQ(${qqId}): ${currentHasMcBind} -> ${hasValidMc}`);
753
- }
754
- // 清理临时用户名(无论hasMcBind字段是否存在)
755
- if (!hasValidMc && mcUsername && mcUsername.startsWith('_temp_')) {
756
- updateData.mcUsername = null;
757
- updateData.mcUuid = null;
758
- updateData.whitelist = [];
759
- needUpdate = true;
760
- logger.info(`[数据清理] 清理QQ(${qqId})的临时用户名: ${mcUsername}`);
761
- }
762
- // 检查并修复hasBuidBind字段(数据迁移 + 数据一致性检查)
763
- const currentHasBuidBind = record.hasBuidBind;
764
- const buidUid = record.buidUid;
765
- const hasValidBuid = !!(buidUid && buidUid.length > 0);
766
- // 情况1:字段不存在,需要添加
767
- if (currentHasBuidBind === undefined || currentHasBuidBind === null) {
768
- updateData.hasBuidBind = hasValidBuid;
769
- needUpdate = true;
770
- logger.debug(`[数据迁移] 添加hasBuidBind字段 QQ(${qqId}): ${hasValidBuid}`);
771
- }
772
- // 情况2:字段存在但值不正确,需要修复
773
- else if (currentHasBuidBind !== hasValidBuid) {
774
- updateData.hasBuidBind = hasValidBuid;
775
- needUpdate = true;
776
- logger.info(`[数据修复] 修正hasBuidBind QQ(${qqId}): ${currentHasBuidBind} -> ${hasValidBuid}`);
777
- }
778
- // 如果需要更新,执行更新操作
779
- if (needUpdate) {
780
- await mcidbindRepo.update(qqId, updateData);
781
- updatedCount++;
782
- }
783
- }
784
- if (updatedCount > 0) {
785
- logger.info(`[初始化] 成功为${updatedCount}条记录添加缺失字段`);
786
- }
787
- else {
788
- logger.info('[初始化] 所有记录都包含必要字段,无需更新');
789
- }
790
- return true;
791
- }
792
- catch (error) {
793
- logger.error(`[初始化] 添加缺失字段失败: ${error.message}`);
794
- return false;
795
- }
796
- };
797
- // 在插件启动时执行数据迁移(放在函数定义之后)
798
- ctx.on('ready', async () => {
799
- logger.info('[初始化] 开始数据迁移和一致性检查...');
800
- await addMissingFields();
801
- });
802
- // 重建MCIDBIND表
803
- const rebuildMcidBindTable = async () => {
804
- try {
805
- // 备份现有数据
806
- const oldRecords = await mcidbindRepo.findAll();
807
- logger.info(`[初始化] 成功备份${oldRecords.length}条记录`);
808
- // 创建数据备份(用于恢复)
809
- const backupData = JSON.parse(JSON.stringify(oldRecords));
810
- try {
811
- // 提取有效数据
812
- const validRecords = oldRecords
813
- .map(record => {
814
- // 确保qqId存在
815
- if (!record.qqId) {
816
- // 如果没有qqId但有userId,尝试从userId提取
817
- if ('userId' in record && record.userId) {
818
- record.qqId = normalizeQQId(String(record.userId));
819
- }
820
- else {
821
- // 既没有qqId也没有userId,跳过此记录
822
- return null;
823
- }
824
- }
825
- return {
826
- qqId: record.qqId,
827
- mcUsername: record.mcUsername || '',
828
- mcUuid: record.mcUuid || '',
829
- lastModified: record.lastModified || new Date(),
830
- isAdmin: record.isAdmin || false,
831
- whitelist: record.whitelist || [],
832
- tags: record.tags || []
833
- };
834
- })
835
- .filter(record => record !== null);
836
- // 删除现有表
837
- await mcidbindRepo.deleteAll();
838
- logger.info('[初始化] 成功删除旧表数据');
839
- // 重新创建记录
840
- let successCount = 0;
841
- let errorCount = 0;
842
- for (const record of validRecords) {
843
- try {
844
- await mcidbindRepo.create(record);
845
- successCount++;
846
- }
847
- catch (e) {
848
- errorCount++;
849
- logger.warn(`[初始化] 重建记录失败 (QQ=${record.qqId}): ${e.message}`);
850
- }
851
- }
852
- logger.info(`[初始化] 成功重建了${successCount}条记录,失败${errorCount}条`);
853
- return true;
854
- }
855
- catch (migrationError) {
856
- // 迁移过程出错,尝试恢复
857
- logger.error(`[初始化] 表重建过程失败,尝试恢复数据: ${migrationError.message}`);
858
- try {
859
- // 清空表以避免重复数据
860
- await mcidbindRepo.deleteAll();
861
- // 恢复原始数据
862
- for (const record of backupData) {
863
- await mcidbindRepo.create(record);
864
- }
865
- logger.info(`[初始化] 成功恢复${backupData.length}条原始记录`);
866
- }
867
- catch (recoveryError) {
868
- logger.error(`[初始化] 数据恢复失败,可能导致数据丢失: ${recoveryError.message}`);
869
- throw new Error('数据迁移失败且无法恢复');
870
- }
871
- throw migrationError;
872
- }
873
- }
874
- catch (error) {
875
- logger.error(`[初始化] 重建表失败: ${error.message}`);
876
- throw error;
877
- }
878
- };
594
+ // mcidbind 表已迁移至 Supabase,无需在 Koishi 中注册模型
879
595
  // 处理用户ID,去除平台前缀,只保留QQ号
880
596
  const normalizeQQId = (userId) => {
881
597
  // 处理空值情况
@@ -1,117 +1,35 @@
1
- import { Context } from 'koishi';
2
1
  import { LoggerService } from '../utils/logger';
2
+ import { SupabaseClient } from '../utils/supabase-client';
3
3
  import type { MCIDBIND } from '../types';
4
4
  /**
5
5
  * MCIDBIND 数据仓储类
6
- * 封装所有 MCIDBIND 表的数据库操作
6
+ * 通过 Supabase REST API 操作 user 表
7
7
  */
8
8
  export declare class MCIDBINDRepository {
9
- private ctx;
9
+ private supabase;
10
10
  private logger;
11
- constructor(ctx: Context, logger: LoggerService);
12
- /**
13
- * 根据 QQ 号查询绑定信息
14
- * @param qqId QQ号(已规范化)
15
- * @returns 绑定信息或 null
16
- */
11
+ constructor(supabase: SupabaseClient, logger: LoggerService);
17
12
  findByQQId(qqId: string): Promise<MCIDBIND | null>;
18
- /**
19
- * 根据 MC 用户名查询绑定信息(精确匹配)
20
- * @param mcUsername MC用户名
21
- * @returns 绑定信息或 null
22
- */
23
13
  findByMCUsername(mcUsername: string): Promise<MCIDBIND | null>;
24
- /**
25
- * 根据 MC 用户名查询绑定信息(不区分大小写)
26
- * @param mcUsername MC用户名
27
- * @returns 绑定信息或 null
28
- */
29
14
  findByUsernameIgnoreCase(mcUsername: string): Promise<MCIDBIND | null>;
30
- /**
31
- * 根据 MC UUID 查询绑定信息
32
- * @param mcUuid MC UUID(可带或不带连字符)
33
- * @returns 绑定信息或 null
34
- */
35
15
  findByUuid(mcUuid: string): Promise<MCIDBIND | null>;
36
- /**
37
- * 根据 B站 UID 查询绑定信息
38
- * @param buidUid B站UID
39
- * @returns 绑定信息或 null
40
- */
41
16
  findByBuidUid(buidUid: string): Promise<MCIDBIND | null>;
42
- /**
43
- * 获取所有绑定记录
44
- * @param options 查询选项
45
- * @returns 绑定记录列表
46
- */
47
17
  findAll(options?: {
48
18
  limit?: number;
49
19
  }): Promise<MCIDBIND[]>;
50
- /**
51
- * 根据标签查询绑定记录
52
- * @param tag 标签名称
53
- * @returns 包含该标签的绑定记录列表
54
- */
55
20
  findByTag(tag: string): Promise<MCIDBIND[]>;
56
- /**
57
- * 创建新的绑定记录
58
- * @param data 绑定数据
59
- * @returns 创建的记录
60
- */
61
21
  create(data: Partial<MCIDBIND> & {
62
22
  qqId: string;
63
23
  }): Promise<MCIDBIND>;
64
- /**
65
- * 更新绑定记录(部分字段)
66
- * @param qqId QQ号
67
- * @param data 要更新的字段
68
- */
69
24
  update(qqId: string, data: Partial<MCIDBIND>): Promise<void>;
70
- /**
71
- * 删除绑定记录
72
- * @param qqId QQ号
73
- * @returns 删除的记录数
74
- */
75
25
  delete(qqId: string): Promise<number>;
76
- /**
77
- * 删除所有绑定记录
78
- * @returns 删除的记录数
79
- */
80
26
  deleteAll(): Promise<number>;
81
- /**
82
- * 批量创建绑定记录
83
- * @param records 绑定记录列表
84
- */
85
27
  batchCreate(records: Array<Partial<MCIDBIND> & {
86
28
  qqId: string;
87
29
  }>): Promise<void>;
88
- /**
89
- * 为用户添加标签
90
- * @param qqId QQ号
91
- * @param tag 标签名称
92
- */
93
30
  addTag(qqId: string, tag: string): Promise<void>;
94
- /**
95
- * 为用户移除标签
96
- * @param qqId QQ号
97
- * @param tag 标签名称
98
- */
99
31
  removeTag(qqId: string, tag: string): Promise<void>;
100
- /**
101
- * 为用户添加白名单服务器
102
- * @param qqId QQ号
103
- * @param serverId 服务器ID
104
- */
105
32
  addWhitelist(qqId: string, serverId: string): Promise<void>;
106
- /**
107
- * 为用户移除白名单服务器
108
- * @param qqId QQ号
109
- * @param serverId 服务器ID
110
- */
111
33
  removeWhitelist(qqId: string, serverId: string): Promise<void>;
112
- /**
113
- * 获取所有管理员
114
- * @returns 管理员列表
115
- */
116
34
  findAllAdmins(): Promise<MCIDBIND[]>;
117
35
  }
@@ -2,171 +2,143 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MCIDBINDRepository = void 0;
4
4
  const helpers_1 = require("../utils/helpers");
5
+ const TABLE = 'user';
6
+ const DATE_FIELDS = ['lastModified', 'usernameLastChecked', 'lastActiveTime'];
7
+ function deserialize(row) {
8
+ if (!row)
9
+ return row;
10
+ for (const field of DATE_FIELDS) {
11
+ if (row[field] && typeof row[field] === 'string') {
12
+ row[field] = new Date(row[field]);
13
+ }
14
+ }
15
+ return row;
16
+ }
17
+ function serializeDates(data) {
18
+ const out = { ...data };
19
+ for (const field of DATE_FIELDS) {
20
+ if (out[field] instanceof Date) {
21
+ out[field] = out[field].toISOString();
22
+ }
23
+ }
24
+ return out;
25
+ }
5
26
  /**
6
27
  * MCIDBIND 数据仓储类
7
- * 封装所有 MCIDBIND 表的数据库操作
28
+ * 通过 Supabase REST API 操作 user 表
8
29
  */
9
30
  class MCIDBINDRepository {
10
- ctx;
31
+ supabase;
11
32
  logger;
12
- constructor(ctx, logger) {
13
- this.ctx = ctx;
33
+ constructor(supabase, logger) {
34
+ this.supabase = supabase;
14
35
  this.logger = logger;
15
36
  }
16
- /**
17
- * 根据 QQ 号查询绑定信息
18
- * @param qqId QQ号(已规范化)
19
- * @returns 绑定信息或 null
20
- */
21
37
  async findByQQId(qqId) {
22
38
  try {
23
39
  this.logger.debug('数据库', `查询QQ(${qqId})的绑定信息`);
24
- const binds = await this.ctx.database.get('mcidbind', { qqId });
25
- return binds.length > 0 ? binds[0] : null;
40
+ const rows = await this.supabase.get(TABLE, `qqId=eq.${qqId}&limit=1`);
41
+ return rows.length > 0 ? deserialize(rows[0]) : null;
26
42
  }
27
43
  catch (error) {
28
44
  this.logger.error('数据库', `查询QQ(${qqId})绑定信息失败: ${error.message}`);
29
45
  return null;
30
46
  }
31
47
  }
32
- /**
33
- * 根据 MC 用户名查询绑定信息(精确匹配)
34
- * @param mcUsername MC用户名
35
- * @returns 绑定信息或 null
36
- */
37
48
  async findByMCUsername(mcUsername) {
38
49
  try {
39
50
  this.logger.debug('数据库', `查询MC用户名(${mcUsername})的绑定信息`);
40
- const binds = await this.ctx.database.get('mcidbind', { mcUsername });
41
- return binds.length > 0 ? binds[0] : null;
51
+ const rows = await this.supabase.get(TABLE, `mcUsername=eq.${encodeURIComponent(mcUsername)}&limit=1`);
52
+ return rows.length > 0 ? deserialize(rows[0]) : null;
42
53
  }
43
54
  catch (error) {
44
55
  this.logger.error('数据库', `查询MC用户名(${mcUsername})绑定信息失败: ${error.message}`);
45
56
  return null;
46
57
  }
47
58
  }
48
- /**
49
- * 根据 MC 用户名查询绑定信息(不区分大小写)
50
- * @param mcUsername MC用户名
51
- * @returns 绑定信息或 null
52
- */
53
59
  async findByUsernameIgnoreCase(mcUsername) {
54
60
  try {
55
61
  const normalizedInput = (0, helpers_1.normalizeUsername)(mcUsername, this.logger.getRawLogger());
56
62
  this.logger.debug('数据库', `查询MC用户名(${mcUsername} -> ${normalizedInput})的绑定信息(不区分大小写)`);
57
- // 获取所有绑定记录,然后在应用层过滤(因为 Koishi 数据库不支持不区分大小写查询)
58
- const allBinds = await this.ctx.database.get('mcidbind', {});
59
- const match = allBinds.find(bind => bind.mcUsername && (0, helpers_1.normalizeUsername)(bind.mcUsername) === normalizedInput);
60
- return match || null;
63
+ const rows = await this.supabase.get(TABLE, `mcUsername=ilike.${encodeURIComponent(mcUsername)}`);
64
+ const match = rows.find(bind => bind.mcUsername && (0, helpers_1.normalizeUsername)(bind.mcUsername) === normalizedInput);
65
+ return match ? deserialize(match) : null;
61
66
  }
62
67
  catch (error) {
63
68
  this.logger.error('数据库', `查询MC用户名(${mcUsername})绑定信息失败(不区分大小写): ${error.message}`);
64
69
  return null;
65
70
  }
66
71
  }
67
- /**
68
- * 根据 MC UUID 查询绑定信息
69
- * @param mcUuid MC UUID(可带或不带连字符)
70
- * @returns 绑定信息或 null
71
- */
72
72
  async findByUuid(mcUuid) {
73
73
  try {
74
- // 规范化 UUID(移除连字符)
75
74
  const cleanUuid = mcUuid.replace(/-/g, '');
76
75
  this.logger.debug('数据库', `查询MC UUID(${cleanUuid})的绑定信息`);
77
- // 先尝试精确匹配
78
- let binds = await this.ctx.database.get('mcidbind', { mcUuid: cleanUuid });
79
- if (binds.length > 0)
80
- return binds[0];
81
- // 尝试带连字符的格式
82
76
  const formattedUuid = `${cleanUuid.substring(0, 8)}-${cleanUuid.substring(8, 12)}-${cleanUuid.substring(12, 16)}-${cleanUuid.substring(16, 20)}-${cleanUuid.substring(20)}`;
83
- binds = await this.ctx.database.get('mcidbind', { mcUuid: formattedUuid });
84
- if (binds.length > 0)
85
- return binds[0];
86
- // 如果都没有,在应用层过滤(去除连字符后比较)
87
- const allBinds = await this.ctx.database.get('mcidbind', {});
88
- const match = allBinds.find(bind => bind.mcUuid && bind.mcUuid.replace(/-/g, '') === cleanUuid);
89
- return match || null;
77
+ // or 一次查两种格式
78
+ const rows = await this.supabase.get(TABLE, `or=(mcUuid.eq.${cleanUuid},mcUuid.eq.${formattedUuid})&limit=1`);
79
+ if (rows.length > 0)
80
+ return deserialize(rows[0]);
81
+ // 兜底:用 ilike 模糊匹配
82
+ const fallback = await this.supabase.get(TABLE, `mcUuid=ilike.*${cleanUuid}*&limit=1`);
83
+ if (fallback.length > 0)
84
+ return deserialize(fallback[0]);
85
+ return null;
90
86
  }
91
87
  catch (error) {
92
88
  this.logger.error('数据库', `查询MC UUID(${mcUuid})绑定信息失败: ${error.message}`);
93
89
  return null;
94
90
  }
95
91
  }
96
- /**
97
- * 根据 B站 UID 查询绑定信息
98
- * @param buidUid B站UID
99
- * @returns 绑定信息或 null
100
- */
101
92
  async findByBuidUid(buidUid) {
102
93
  try {
103
94
  this.logger.debug('数据库', `查询B站UID(${buidUid})的绑定信息`);
104
- const binds = await this.ctx.database.get('mcidbind', { buidUid });
105
- return binds.length > 0 ? binds[0] : null;
95
+ const rows = await this.supabase.get(TABLE, `buidUid=eq.${buidUid}&limit=1`);
96
+ return rows.length > 0 ? deserialize(rows[0]) : null;
106
97
  }
107
98
  catch (error) {
108
99
  this.logger.error('数据库', `查询B站UID(${buidUid})绑定信息失败: ${error.message}`);
109
100
  return null;
110
101
  }
111
102
  }
112
- /**
113
- * 获取所有绑定记录
114
- * @param options 查询选项
115
- * @returns 绑定记录列表
116
- */
117
103
  async findAll(options) {
118
104
  try {
119
105
  this.logger.debug('数据库', `获取所有绑定记录${options?.limit ? ` (limit: ${options.limit})` : ''}`);
120
- const records = await this.ctx.database.get('mcidbind', {}, options);
121
- return records;
106
+ const query = options?.limit ? `limit=${options.limit}` : '';
107
+ const rows = await this.supabase.get(TABLE, query);
108
+ return rows.map(deserialize);
122
109
  }
123
110
  catch (error) {
124
111
  this.logger.error('数据库', `获取所有绑定记录失败: ${error.message}`);
125
112
  return [];
126
113
  }
127
114
  }
128
- /**
129
- * 根据标签查询绑定记录
130
- * @param tag 标签名称
131
- * @returns 包含该标签的绑定记录列表
132
- */
133
115
  async findByTag(tag) {
134
116
  try {
135
117
  this.logger.debug('数据库', `查询包含标签"${tag}"的绑定记录`);
136
- const allRecords = await this.ctx.database.get('mcidbind', {});
137
- return allRecords.filter(record => record.tags && record.tags.includes(tag));
118
+ const rows = await this.supabase.get(TABLE, `tags=cs.${encodeURIComponent(JSON.stringify([tag]))}`);
119
+ return rows.map(deserialize);
138
120
  }
139
121
  catch (error) {
140
122
  this.logger.error('数据库', `查询标签"${tag}"的绑定记录失败: ${error.message}`);
141
123
  return [];
142
124
  }
143
125
  }
144
- /**
145
- * 创建新的绑定记录
146
- * @param data 绑定数据
147
- * @returns 创建的记录
148
- */
149
126
  async create(data) {
150
127
  try {
151
128
  this.logger.debug('数据库', `创建QQ(${data.qqId})的绑定记录`);
152
- const created = await this.ctx.database.create('mcidbind', data);
129
+ const created = await this.supabase.post(TABLE, serializeDates(data));
153
130
  this.logger.info('数据库', `成功创建QQ(${data.qqId})的绑定记录`, true);
154
- return created;
131
+ return deserialize(created);
155
132
  }
156
133
  catch (error) {
157
134
  this.logger.error('数据库', `创建QQ(${data.qqId})绑定记录失败: ${error.message}`);
158
135
  throw error;
159
136
  }
160
137
  }
161
- /**
162
- * 更新绑定记录(部分字段)
163
- * @param qqId QQ号
164
- * @param data 要更新的字段
165
- */
166
138
  async update(qqId, data) {
167
139
  try {
168
140
  this.logger.debug('数据库', `更新QQ(${qqId})的绑定记录`);
169
- await this.ctx.database.set('mcidbind', { qqId }, data);
141
+ await this.supabase.patch(TABLE, `qqId=eq.${qqId}`, serializeDates(data));
170
142
  this.logger.info('数据库', `成功更新QQ(${qqId})的绑定记录`, true);
171
143
  }
172
144
  catch (error) {
@@ -174,49 +146,35 @@ class MCIDBINDRepository {
174
146
  throw error;
175
147
  }
176
148
  }
177
- /**
178
- * 删除绑定记录
179
- * @param qqId QQ号
180
- * @returns 删除的记录数
181
- */
182
149
  async delete(qqId) {
183
150
  try {
184
151
  this.logger.debug('数据库', `删除QQ(${qqId})的绑定记录`);
185
- const result = await this.ctx.database.remove('mcidbind', { qqId });
186
- this.logger.info('数据库', `成功删除QQ(${qqId})的绑定记录(删除${result.removed}条)`, true);
187
- return result.removed;
152
+ const removed = await this.supabase.del(TABLE, `qqId=eq.${qqId}`);
153
+ this.logger.info('数据库', `成功删除QQ(${qqId})的绑定记录(删除${removed}条)`, true);
154
+ return removed;
188
155
  }
189
156
  catch (error) {
190
157
  this.logger.error('数据库', `删除QQ(${qqId})绑定记录失败: ${error.message}`);
191
158
  throw error;
192
159
  }
193
160
  }
194
- /**
195
- * 删除所有绑定记录
196
- * @returns 删除的记录数
197
- */
198
161
  async deleteAll() {
199
162
  try {
200
163
  this.logger.debug('数据库', '删除所有绑定记录');
201
- const result = await this.ctx.database.remove('mcidbind', {});
202
- this.logger.info('数据库', `成功删除所有绑定记录(删除${result.removed}条)`, true);
203
- return result.removed;
164
+ const removed = await this.supabase.del(TABLE, 'qqId=not.is.null');
165
+ this.logger.info('数据库', `成功删除所有绑定记录(删除${removed}条)`, true);
166
+ return removed;
204
167
  }
205
168
  catch (error) {
206
169
  this.logger.error('数据库', `删除所有绑定记录失败: ${error.message}`);
207
170
  throw error;
208
171
  }
209
172
  }
210
- /**
211
- * 批量创建绑定记录
212
- * @param records 绑定记录列表
213
- */
214
173
  async batchCreate(records) {
215
174
  try {
216
175
  this.logger.debug('数据库', `批量创建${records.length}条绑定记录`);
217
- for (const record of records) {
218
- await this.ctx.database.create('mcidbind', record);
219
- }
176
+ const serialized = records.map(r => serializeDates(r));
177
+ await this.supabase.postMany(TABLE, serialized);
220
178
  this.logger.info('数据库', `成功批量创建${records.length}条绑定记录`, true);
221
179
  }
222
180
  catch (error) {
@@ -224,11 +182,6 @@ class MCIDBINDRepository {
224
182
  throw error;
225
183
  }
226
184
  }
227
- /**
228
- * 为用户添加标签
229
- * @param qqId QQ号
230
- * @param tag 标签名称
231
- */
232
185
  async addTag(qqId, tag) {
233
186
  try {
234
187
  const bind = await this.findByQQId(qqId);
@@ -247,11 +200,6 @@ class MCIDBINDRepository {
247
200
  throw error;
248
201
  }
249
202
  }
250
- /**
251
- * 为用户移除标签
252
- * @param qqId QQ号
253
- * @param tag 标签名称
254
- */
255
203
  async removeTag(qqId, tag) {
256
204
  try {
257
205
  const bind = await this.findByQQId(qqId);
@@ -271,11 +219,6 @@ class MCIDBINDRepository {
271
219
  throw error;
272
220
  }
273
221
  }
274
- /**
275
- * 为用户添加白名单服务器
276
- * @param qqId QQ号
277
- * @param serverId 服务器ID
278
- */
279
222
  async addWhitelist(qqId, serverId) {
280
223
  try {
281
224
  const bind = await this.findByQQId(qqId);
@@ -294,11 +237,6 @@ class MCIDBINDRepository {
294
237
  throw error;
295
238
  }
296
239
  }
297
- /**
298
- * 为用户移除白名单服务器
299
- * @param qqId QQ号
300
- * @param serverId 服务器ID
301
- */
302
240
  async removeWhitelist(qqId, serverId) {
303
241
  try {
304
242
  const bind = await this.findByQQId(qqId);
@@ -318,15 +256,11 @@ class MCIDBINDRepository {
318
256
  throw error;
319
257
  }
320
258
  }
321
- /**
322
- * 获取所有管理员
323
- * @returns 管理员列表
324
- */
325
259
  async findAllAdmins() {
326
260
  try {
327
261
  this.logger.debug('数据库', '获取所有管理员');
328
- const allRecords = await this.ctx.database.get('mcidbind', {});
329
- return allRecords.filter(record => record.isAdmin);
262
+ const rows = await this.supabase.get(TABLE, 'isAdmin=eq.true');
263
+ return rows.map(deserialize);
330
264
  }
331
265
  catch (error) {
332
266
  this.logger.error('数据库', `获取所有管理员失败: ${error.message}`);
@@ -274,9 +274,7 @@ class DatabaseService {
274
274
  buidUid: buidUser.uid.toString(), // 转换为字符串存储
275
275
  buidUsername: buidUser.username,
276
276
  guardLevel: buidUser.guard_level || 0,
277
- guardLevelText: buidUser.guard_level_text || '',
278
277
  maxGuardLevel: buidUser.max_guard_level || 0,
279
- maxGuardLevelText: buidUser.max_guard_level_text || '',
280
278
  medalName: buidUser.medal?.name || '',
281
279
  medalLevel: buidUser.medal?.level || 0,
282
280
  wealthMedalLevel: buidUser.wealthMedalLevel || 0,
@@ -334,9 +332,7 @@ class DatabaseService {
334
332
  const updateData = {
335
333
  buidUsername: buidUser.username,
336
334
  guardLevel: buidUser.guard_level || 0,
337
- guardLevelText: buidUser.guard_level_text || '',
338
335
  maxGuardLevel: buidUser.max_guard_level || 0,
339
- maxGuardLevelText: buidUser.max_guard_level_text || '',
340
336
  medalName: buidUser.medal?.name || '',
341
337
  medalLevel: buidUser.medal?.level || 0,
342
338
  wealthMedalLevel: buidUser.wealthMedalLevel || 0,
@@ -386,9 +382,7 @@ class DatabaseService {
386
382
  buidUid: null,
387
383
  buidUsername: null,
388
384
  guardLevel: 0,
389
- guardLevelText: '',
390
385
  maxGuardLevel: 0,
391
- maxGuardLevelText: '',
392
386
  medalName: '',
393
387
  medalLevel: 0,
394
388
  wealthMedalLevel: 0,
@@ -53,6 +53,10 @@ export interface Config {
53
53
  forceBindTargetRoomId: number;
54
54
  /** 强制绑定目标粉丝牌名称 */
55
55
  forceBindTargetMedalName: string;
56
+ /** Supabase项目URL */
57
+ supabaseUrl: string;
58
+ /** Supabase API Key */
59
+ supabaseKey: string;
56
60
  /** 入群申请审批功能配置 */
57
61
  groupRequestReview?: GroupRequestReviewConfig;
58
62
  }
@@ -24,9 +24,7 @@
24
24
  * buidUid: '87654321',
25
25
  * buidUsername: 'B站用户名',
26
26
  * guardLevel: 3,
27
- * guardLevelText: '舰长',
28
27
  * maxGuardLevel: 3,
29
- * maxGuardLevelText: '舰长',
30
28
  * medalName: '粉丝牌',
31
29
  * medalLevel: 20,
32
30
  * wealthMedalLevel: 15,
@@ -60,12 +58,8 @@ export interface MCIDBIND {
60
58
  buidUsername: string | null;
61
59
  /** 当前舰长等级 (0=无, 1=总督, 2=提督, 3=舰长) */
62
60
  guardLevel: number;
63
- /** 当前舰长等级文本 (例: '舰长', '提督', '总督') */
64
- guardLevelText: string;
65
61
  /** 历史最高舰长等级 */
66
62
  maxGuardLevel: number;
67
- /** 历史最高舰长等级文本 */
68
- maxGuardLevelText: string;
69
63
  /** 粉丝牌名称 */
70
64
  medalName: string;
71
65
  /** 粉丝牌等级 */
@@ -54,9 +54,7 @@ export interface UpdateMcBindData {
54
54
  * buidUid: '87654321',
55
55
  * buidUsername: 'B站用户名',
56
56
  * guardLevel: 3,
57
- * guardLevelText: '舰长',
58
57
  * maxGuardLevel: 3,
59
- * maxGuardLevelText: '舰长',
60
58
  * medalName: '粉丝牌',
61
59
  * medalLevel: 20,
62
60
  * wealthMedalLevel: 15,
@@ -72,12 +70,8 @@ export interface UpdateBuidBindData {
72
70
  buidUsername?: string | null;
73
71
  /** 当前舰长等级 */
74
72
  guardLevel?: number;
75
- /** 当前舰长等级文本 */
76
- guardLevelText?: string;
77
73
  /** 历史最高舰长等级 */
78
74
  maxGuardLevel?: number;
79
- /** 历史最高舰长等级文本 */
80
- maxGuardLevelText?: string;
81
75
  /** 粉丝牌名称 */
82
76
  medalName?: string;
83
77
  /** 粉丝牌等级 */
@@ -104,7 +98,6 @@ export interface UpdateBuidBindData {
104
98
  * {
105
99
  * buidUsername: '新用户名',
106
100
  * guardLevel: 3,
107
- * guardLevelText: '舰长',
108
101
  * medalLevel: 21
109
102
  * }
110
103
  * ```
@@ -114,12 +107,8 @@ export interface UpdateBuidInfoData {
114
107
  buidUsername?: string;
115
108
  /** 当前舰长等级 */
116
109
  guardLevel?: number;
117
- /** 当前舰长等级文本 */
118
- guardLevelText?: string;
119
110
  /** 历史最高舰长等级 */
120
111
  maxGuardLevel?: number;
121
- /** 历史最高舰长等级文本 */
122
- maxGuardLevelText?: string;
123
112
  /** 粉丝牌名称 */
124
113
  medalName?: string;
125
114
  /** 粉丝牌等级 */
@@ -89,15 +89,7 @@ export declare function levenshteinDistance(str1: string, str2: string): number;
89
89
  * @returns 相似度值(0到1之间,1表示完全相同)
90
90
  */
91
91
  export declare function calculateSimilarity(str1: string, str2: string): number;
92
- /**
93
- * 规范化 Minecraft 用户名(统一小写,用于存储和比较)
94
- * Minecraft 用户名不区分大小写,但 Mojang 返回的是规范大小写
95
- * 为避免 "Notch" 和 "notch" 被视为不同用户,统一转小写存储
96
- *
97
- * @param username MC 用户名
98
- * @param logger Koishi Logger实例(用于日志)
99
- * @returns 规范化后的用户名(小写)
100
- */
92
+ export declare function getGuardLevelText(level: number): string;
101
93
  export declare function normalizeUsername(username: string, logger?: Logger): string;
102
94
  /**
103
95
  * 比较两个 Minecraft 用户名是否相同(不区分大小写)
@@ -11,6 +11,7 @@ exports.escapeRegExp = escapeRegExp;
11
11
  exports.cleanUserInput = cleanUserInput;
12
12
  exports.levenshteinDistance = levenshteinDistance;
13
13
  exports.calculateSimilarity = calculateSimilarity;
14
+ exports.getGuardLevelText = getGuardLevelText;
14
15
  exports.normalizeUsername = normalizeUsername;
15
16
  exports.isSameUsername = isSameUsername;
16
17
  exports.extractBuidUsernameFromNickname = extractBuidUsernameFromNickname;
@@ -369,6 +370,10 @@ function calculateSimilarity(str1, str2) {
369
370
  * @param logger Koishi Logger实例(用于日志)
370
371
  * @returns 规范化后的用户名(小写)
371
372
  */
373
+ const GUARD_LEVEL_TEXT = { 1: '总督', 2: '提督', 3: '舰长' };
374
+ function getGuardLevelText(level) {
375
+ return GUARD_LEVEL_TEXT[level] || '';
376
+ }
372
377
  function normalizeUsername(username, logger) {
373
378
  if (!username) {
374
379
  logger?.warn('[用户名规范化] 收到空用户名');
@@ -0,0 +1,14 @@
1
+ export interface SupabaseConfig {
2
+ url: string;
3
+ key: string;
4
+ }
5
+ export declare class SupabaseClient {
6
+ private config;
7
+ private client;
8
+ constructor(config: SupabaseConfig);
9
+ get<T>(table: string, query?: string): Promise<T[]>;
10
+ post<T>(table: string, body: object | object[]): Promise<T>;
11
+ postMany<T>(table: string, body: object[]): Promise<T[]>;
12
+ patch(table: string, query: string, body: object): Promise<void>;
13
+ del(table: string, query: string): Promise<number>;
14
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SupabaseClient = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ class SupabaseClient {
9
+ config;
10
+ client;
11
+ constructor(config) {
12
+ this.config = config;
13
+ this.client = axios_1.default.create({
14
+ baseURL: `${config.url}/rest/v1`,
15
+ headers: {
16
+ apikey: config.key,
17
+ Authorization: `Bearer ${config.key}`,
18
+ 'Content-Type': 'application/json',
19
+ Prefer: 'return=representation',
20
+ },
21
+ });
22
+ }
23
+ async get(table, query = '') {
24
+ const url = query ? `/${table}?${query}` : `/${table}`;
25
+ const { data } = await this.client.get(url);
26
+ return data;
27
+ }
28
+ async post(table, body) {
29
+ const { data } = await this.client.post(`/${table}`, body);
30
+ return Array.isArray(data) ? data[0] : data;
31
+ }
32
+ async postMany(table, body) {
33
+ const { data } = await this.client.post(`/${table}`, body);
34
+ return data;
35
+ }
36
+ async patch(table, query, body) {
37
+ await this.client.patch(`/${table}?${query}`, body);
38
+ }
39
+ async del(table, query) {
40
+ const { data } = await this.client.delete(`/${table}?${query}`);
41
+ return Array.isArray(data) ? data.length : 0;
42
+ }
43
+ }
44
+ exports.SupabaseClient = SupabaseClient;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-bind-bot",
3
3
  "description": "[WittF自用] BIND-BOT - 账号绑定管理机器人,支持Minecraft账号和B站账号绑定与管理。",
4
- "version": "2.2.9",
4
+ "version": "2.3.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [