koishi-plugin-bind-bot 2.1.6 → 2.1.7

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.
@@ -98,37 +98,25 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
98
98
  */
99
99
  async handleNotice(session) {
100
100
  try {
101
- // 调试日志:记录所有notice事件
102
- this.logger.info('入群审批', `[DEBUG] 收到notice事件 - type: ${session.type}, subtype: ${session.subtype}, guildId: ${session.guildId}`, true);
103
101
  // 只处理群表情回应事件
104
102
  if (session.subtype !== 'group-msg-emoji-like') {
105
- this.logger.info('入群审批', `[DEBUG] 跳过: subtype不匹配 (${session.subtype})`, true);
106
- return;
107
- }
108
- // 只处理管理群的表情
109
- if (session.guildId !== this.reviewConfig.reviewGroupId) {
110
- this.logger.info('入群审批', `[DEBUG] 跳过: guildId不匹配 (收到: ${session.guildId}, 需要: ${this.reviewConfig.reviewGroupId})`, true);
111
103
  return;
112
104
  }
113
105
  // 获取原始事件数据(直接访问 session.onebot,参考luckydraw实现)
114
106
  const data = session.onebot;
115
- this.logger.info('入群审批', `[DEBUG] onebot数据: ${JSON.stringify(data)}`, true);
116
107
  const messageId = data?.message_id;
117
108
  const userId = data?.user_id?.toString();
118
109
  const likes = data?.likes || [];
119
110
  if (!messageId || !userId || likes.length === 0) {
120
- this.logger.info('入群审批', '[DEBUG] 跳过: 缺少必要数据或likes为空', true);
121
111
  return;
122
112
  }
123
113
  const msgId = messageId.toString();
124
114
  const emojiData = likes;
125
115
  const operatorId = this.deps.normalizeQQId(userId);
126
- this.logger.info('入群审批', `收到表情回应 - 消息: ${msgId}, 操作者: ${operatorId}, 表情数: ${emojiData.length}`, true);
116
+ this.logger.debug('入群审批', `收到表情回应 - 消息: ${msgId}, 操作者: ${operatorId}, 表情数: ${emojiData.length}`);
127
117
  // 检查是否是待审批的消息
128
118
  const pendingReq = this.pendingRequests.get(msgId);
129
119
  if (!pendingReq) {
130
- this.logger.info('入群审批', `[DEBUG] 跳过: 消息${msgId}不在待审批列表中`, true);
131
- this.logger.info('入群审批', `[DEBUG] 当前待审批列表: ${Array.from(this.pendingRequests.keys()).join(', ')}`, true);
132
120
  return;
133
121
  }
134
122
  // 检查是否已处理
@@ -589,20 +577,35 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
589
577
  * 执行自动绑定
590
578
  */
591
579
  async performAutoBind(qq, uid, bot) {
592
- const axios = require('axios');
593
580
  try {
594
- // 1. 验证 UID
595
- this.logger.debug('入群审批', `验证 B站 UID: ${uid}`);
596
- const response = await axios.get(`${this.config.zminfoApiUrl}/api/user/${uid}`, {
597
- timeout: 10000,
598
- headers: {
599
- 'User-Agent': 'Koishi-MCID-Bot/1.0'
581
+ // 1. 使用双API数据源获取最新用户信息(优先B站官方API)
582
+ this.logger.debug('入群审批', `开始获取 B站 UID ${uid} 的信息`);
583
+ // 尝试获取B站官方API的用户信息(最权威)
584
+ let officialUsername = null;
585
+ try {
586
+ this.logger.debug('入群审批', '正在查询B站官方API...');
587
+ const officialInfo = await this.deps.apiService.getBilibiliOfficialUserInfo(uid);
588
+ if (officialInfo && officialInfo.name) {
589
+ officialUsername = officialInfo.name;
590
+ this.logger.info('入群审批', `[官方API] ✅ 获取到用户名: "${officialUsername}"`, true);
600
591
  }
601
- });
602
- if (response.status !== 200 || !response.data || !response.data.uid) {
592
+ else {
593
+ this.logger.warn('入群审批', '[官方API] 查询失败');
594
+ }
595
+ }
596
+ catch (officialError) {
597
+ this.logger.warn('入群审批', `[官方API] ❌ 查询出错: ${officialError.message}`);
598
+ }
599
+ // 获取ZMINFO API的完整用户信息(包含粉丝牌、大航海等数据)
600
+ this.logger.debug('入群审批', '正在查询ZMINFO API...');
601
+ const zminfoUser = await this.deps.apiService.validateBUID(uid);
602
+ if (!zminfoUser) {
603
603
  throw new Error(`无法验证B站UID: ${uid}`);
604
604
  }
605
- const buidUser = response.data;
605
+ this.logger.debug('入群审批', `[ZMINFO] 获取到用户名: "${zminfoUser.username}"`);
606
+ // 使用官方API的用户名(如果可用),否则使用ZMINFO的
607
+ const finalUsername = officialUsername || zminfoUser.username;
608
+ this.logger.info('入群审批', `🎯 最终采用用户名: "${finalUsername}"`, true);
606
609
  // 2. 检查是否已被其他人绑定
607
610
  const existingBind = await this.repos.mcidbind.findByBuidUid(uid);
608
611
  if (existingBind && existingBind.qqId !== qq) {
@@ -617,15 +620,15 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
617
620
  qqId: qq,
618
621
  mcUsername: tempMcUsername,
619
622
  mcUuid: '',
620
- buidUid: buidUser.uid,
621
- buidUsername: buidUser.username,
622
- guardLevel: buidUser.guard_level || 0,
623
- guardLevelText: buidUser.guard_level_text || '',
624
- maxGuardLevel: buidUser.guard_level || 0,
625
- maxGuardLevelText: buidUser.guard_level_text || '',
626
- medalName: buidUser.medal?.name || '',
627
- medalLevel: buidUser.medal?.level || 0,
628
- wealthMedalLevel: buidUser.wealth_medal_level || 0,
623
+ buidUid: zminfoUser.uid,
624
+ buidUsername: finalUsername,
625
+ guardLevel: zminfoUser.guard_level || 0,
626
+ guardLevelText: zminfoUser.guard_level_text || '',
627
+ maxGuardLevel: zminfoUser.guard_level || 0,
628
+ maxGuardLevelText: zminfoUser.guard_level_text || '',
629
+ medalName: zminfoUser.medal?.name || '',
630
+ medalLevel: zminfoUser.medal?.level || 0,
631
+ wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
629
632
  lastActiveTime: new Date(),
630
633
  lastModified: new Date()
631
634
  });
@@ -634,26 +637,29 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
634
637
  else {
635
638
  // 更新现有绑定
636
639
  await this.repos.mcidbind.update(qq, {
637
- buidUid: buidUser.uid,
638
- buidUsername: buidUser.username,
639
- guardLevel: buidUser.guard_level || 0,
640
- guardLevelText: buidUser.guard_level_text || '',
641
- maxGuardLevel: Math.max(bind.maxGuardLevel || 0, buidUser.guard_level || 0),
642
- maxGuardLevelText: buidUser.guard_level > (bind.maxGuardLevel || 0)
643
- ? buidUser.guard_level_text
640
+ buidUid: zminfoUser.uid,
641
+ buidUsername: finalUsername,
642
+ guardLevel: zminfoUser.guard_level || 0,
643
+ guardLevelText: zminfoUser.guard_level_text || '',
644
+ maxGuardLevel: Math.max(bind.maxGuardLevel || 0, zminfoUser.guard_level || 0),
645
+ maxGuardLevelText: zminfoUser.guard_level > (bind.maxGuardLevel || 0)
646
+ ? zminfoUser.guard_level_text
644
647
  : bind.maxGuardLevelText,
645
- medalName: buidUser.medal?.name || '',
646
- medalLevel: buidUser.medal?.level || 0,
647
- wealthMedalLevel: buidUser.wealth_medal_level || 0,
648
+ medalName: zminfoUser.medal?.name || '',
649
+ medalLevel: zminfoUser.medal?.level || 0,
650
+ wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
648
651
  lastActiveTime: new Date(),
649
652
  lastModified: new Date()
650
653
  });
651
654
  this.logger.info('入群审批', `已更新绑定 - QQ: ${qq}, UID: ${uid}`, true);
652
655
  }
653
- // 4. 更新群昵称
656
+ // 4. 更新群昵称(使用标准格式)
654
657
  try {
655
658
  const groupId = this.reviewConfig.targetGroupId;
656
- const nickname = `${buidUser.username}_${bind.mcUsername || 'MCID'}`;
659
+ const mcInfo = bind.mcUsername && !bind.mcUsername.startsWith('_temp_')
660
+ ? bind.mcUsername
661
+ : '未绑定';
662
+ const nickname = `${finalUsername}(ID:${mcInfo})`;
657
663
  await bot.internal.setGroupCard(groupId, qq, nickname);
658
664
  this.logger.info('入群审批', `已更新群昵称 - QQ: ${qq}, 昵称: ${nickname}`);
659
665
  }
@@ -661,7 +667,7 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
661
667
  this.logger.warn('入群审批', `更新群昵称失败: ${error.message}`);
662
668
  // 昵称更新失败不影响绑定
663
669
  }
664
- this.logger.info('入群审批', `自动绑定完成 - QQ: ${qq}, UID: ${uid}, 用户名: ${buidUser.username}`, true);
670
+ this.logger.info('入群审批', `自动绑定完成 - QQ: ${qq}, UID: ${uid}, 用户名: ${finalUsername}`, true);
665
671
  }
666
672
  catch (error) {
667
673
  this.logger.error('入群审批', `自动绑定失败: ${error.message}`, error);
@@ -7,9 +7,15 @@ import type { MojangProfile, ZminfoUser } from '../types';
7
7
  export declare class ApiService {
8
8
  private logger;
9
9
  private config;
10
+ private cookieString;
10
11
  constructor(logger: LoggerService, config: {
11
12
  zminfoApiUrl: string;
13
+ SESSDATA?: string;
12
14
  });
15
+ /**
16
+ * 处理cookie字符串,支持完整cookie或单独SESSDATA
17
+ */
18
+ private processCookie;
13
19
  /**
14
20
  * 验证 Minecraft 用户名是否存在
15
21
  * @param username MC 用户名
@@ -12,9 +12,30 @@ const axios_1 = __importDefault(require("axios"));
12
12
  class ApiService {
13
13
  logger;
14
14
  config;
15
+ cookieString;
15
16
  constructor(logger, config) {
16
17
  this.logger = logger;
17
18
  this.config = config;
19
+ this.cookieString = this.processCookie(config.SESSDATA || '');
20
+ }
21
+ /**
22
+ * 处理cookie字符串,支持完整cookie或单独SESSDATA
23
+ */
24
+ processCookie(input) {
25
+ if (!input || input.trim() === '') {
26
+ return '';
27
+ }
28
+ const trimmedInput = input.trim();
29
+ // 如果输入包含多个cookie字段(包含分号),则认为是完整cookie
30
+ if (trimmedInput.includes(';')) {
31
+ return trimmedInput;
32
+ }
33
+ // 如果输入只是SESSDATA值(不包含"SESSDATA="前缀)
34
+ if (!trimmedInput.startsWith('SESSDATA=')) {
35
+ return `SESSDATA=${trimmedInput}`;
36
+ }
37
+ // 如果输入已经是"SESSDATA=xxx"格式
38
+ return trimmedInput;
18
39
  }
19
40
  // =========== Mojang API ===========
20
41
  /**
@@ -197,14 +218,20 @@ class ApiService {
197
218
  return null;
198
219
  }
199
220
  this.logger.debug('B站官方API', `开始查询UID ${uid} 的官方信息`);
221
+ const headers = {
222
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
223
+ Referer: 'https://space.bilibili.com/',
224
+ Origin: 'https://space.bilibili.com'
225
+ };
226
+ // 如果配置了Cookie,则添加到请求头(避免被风控)
227
+ if (this.cookieString) {
228
+ headers.Cookie = this.cookieString;
229
+ this.logger.debug('B站官方API', '使用Cookie进行请求');
230
+ }
200
231
  const response = await axios_1.default.get('https://api.bilibili.com/x/space/acc/info', {
201
232
  params: { mid: uid },
202
233
  timeout: 10000,
203
- headers: {
204
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
205
- Referer: 'https://space.bilibili.com/',
206
- Origin: 'https://space.bilibili.com'
207
- }
234
+ headers
208
235
  });
209
236
  if (response.data.code === 0 && response.data.data) {
210
237
  const userData = response.data.data;
@@ -14,7 +14,10 @@ class ServiceContainer {
14
14
  nickname;
15
15
  constructor(ctx, config, logger, mcidbindRepo, normalizeQQId) {
16
16
  // 1. 实例化 API 服务(无依赖)
17
- this.api = new api_service_1.ApiService(logger.createChild('API服务'), { zminfoApiUrl: config.zminfoApiUrl });
17
+ this.api = new api_service_1.ApiService(logger.createChild('API服务'), {
18
+ zminfoApiUrl: config.zminfoApiUrl,
19
+ SESSDATA: config.forceBindSessdata
20
+ });
18
21
  // 2. 实例化数据库服务(依赖 API 服务)
19
22
  this.database = new database_service_1.DatabaseService(ctx, logger.createChild('数据库服务'), mcidbindRepo, normalizeQQId, (uuid) => this.api.getUsernameByUuid(uuid));
20
23
  // 3. 实例化群昵称服务(依赖 API 和数据库服务)
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.1.6",
4
+ "version": "2.1.7",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [