koishi-plugin-bind-bot 2.1.4 → 2.1.6

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.
@@ -79,7 +79,7 @@ export declare class GroupRequestReviewHandler extends BaseHandler {
79
79
  */
80
80
  private checkAdminPermission;
81
81
  /**
82
- * 解析UID(支持多种格式)
82
+ * 解析UID(支持多种格式,参考BuidHandler.parseUidInput实现)
83
83
  */
84
84
  private parseUID;
85
85
  /**
@@ -98,33 +98,37 @@ 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);
101
103
  // 只处理群表情回应事件
102
104
  if (session.subtype !== 'group-msg-emoji-like') {
105
+ this.logger.info('入群审批', `[DEBUG] 跳过: subtype不匹配 (${session.subtype})`, true);
103
106
  return;
104
107
  }
105
108
  // 只处理管理群的表情
106
109
  if (session.guildId !== this.reviewConfig.reviewGroupId) {
110
+ this.logger.info('入群审批', `[DEBUG] 跳过: guildId不匹配 (收到: ${session.guildId}, 需要: ${this.reviewConfig.reviewGroupId})`, true);
107
111
  return;
108
112
  }
109
- // 获取原始事件数据(使用类型断言访问 onebot 扩展属性)
110
- const onebotSession = session;
111
- const onebotData = onebotSession.onebot;
112
- if (!onebotData?.likes || onebotData.likes.length === 0) {
113
- return;
114
- }
115
- // 从原始 OneBot 数据中读取(更可靠)
116
- const msgId = onebotData.message_id?.toString() || session.messageId;
117
- const userId = onebotData.user_id?.toString() || session.userId;
118
- const emojiData = onebotData.likes;
119
- if (!msgId || !userId) {
120
- this.logger.warn('入群审批', '表情回应事件缺少必要数据: messageId 或 userId');
113
+ // 获取原始事件数据(直接访问 session.onebot,参考luckydraw实现)
114
+ const data = session.onebot;
115
+ this.logger.info('入群审批', `[DEBUG] onebot数据: ${JSON.stringify(data)}`, true);
116
+ const messageId = data?.message_id;
117
+ const userId = data?.user_id?.toString();
118
+ const likes = data?.likes || [];
119
+ if (!messageId || !userId || likes.length === 0) {
120
+ this.logger.info('入群审批', '[DEBUG] 跳过: 缺少必要数据或likes为空', true);
121
121
  return;
122
122
  }
123
+ const msgId = messageId.toString();
124
+ const emojiData = likes;
123
125
  const operatorId = this.deps.normalizeQQId(userId);
124
- this.logger.debug('入群审批', `收到表情回应 - 消息: ${msgId}, 操作者: ${operatorId}, 表情数: ${emojiData.length}`);
126
+ this.logger.info('入群审批', `收到表情回应 - 消息: ${msgId}, 操作者: ${operatorId}, 表情数: ${emojiData.length}`, true);
125
127
  // 检查是否是待审批的消息
126
128
  const pendingReq = this.pendingRequests.get(msgId);
127
129
  if (!pendingReq) {
130
+ this.logger.info('入群审批', `[DEBUG] 跳过: 消息${msgId}不在待审批列表中`, true);
131
+ this.logger.info('入群审批', `[DEBUG] 当前待审批列表: ${Array.from(this.pendingRequests.keys()).join(', ')}`, true);
128
132
  return;
129
133
  }
130
134
  // 检查是否已处理
@@ -172,25 +176,95 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
172
176
  catch (error) {
173
177
  this.logger.warn('入群审批', `获取用户信息失败,使用默认值: ${error.message}`);
174
178
  }
175
- return { qq, nickname, avatar, answer };
179
+ // 解析并查询 B 站信息
180
+ let buidUid = null;
181
+ let buidUsername = null;
182
+ let medalInfo = null;
183
+ let bindStatus = '❌ UID 未提供';
184
+ this.logger.debug('入群审批', `[DEBUG] 准备解析UID - 原始answer: "${answer}"`);
185
+ const parsedUid = this.parseUID(answer);
186
+ this.logger.debug('入群审批', `[DEBUG] parseUID结果: ${parsedUid ? parsedUid : 'null'}`);
187
+ if (parsedUid) {
188
+ buidUid = parsedUid;
189
+ // 并行查询官方API和ZMINFO API
190
+ const [officialInfo, zminfoData] = await Promise.all([
191
+ this.deps.apiService.getBilibiliOfficialUserInfo(parsedUid).catch(() => null),
192
+ this.deps.apiService.validateBUID(parsedUid).catch(() => null)
193
+ ]);
194
+ // 用户名:优先使用官方API(最准确),降级到ZMINFO
195
+ if (officialInfo?.name) {
196
+ buidUsername = officialInfo.name;
197
+ this.logger.debug('入群审批', `✅ 使用官方API用户名: ${buidUsername}`);
198
+ }
199
+ else if (zminfoData?.username) {
200
+ buidUsername = zminfoData.username;
201
+ this.logger.debug('入群审批', `⚠️ 官方API失败,使用ZMINFO用户名: ${buidUsername}`);
202
+ }
203
+ // 粉丝牌信息:只能从ZMINFO获取(官方API不提供)
204
+ if (zminfoData) {
205
+ const medalLevel = zminfoData.medal?.level || 0;
206
+ const medalName = zminfoData.medal?.name || '';
207
+ if (medalName === this.config.forceBindTargetMedalName) {
208
+ medalInfo = `🎖️ ${medalName} Lv.${medalLevel}`;
209
+ }
210
+ else if (medalLevel > 0) {
211
+ medalInfo = `⚠️ 佩戴其他粉丝牌: ${medalName} Lv.${medalLevel}`;
212
+ }
213
+ else {
214
+ medalInfo = `⚠️ 未获取到 "${this.config.forceBindTargetMedalName}" 粉丝牌`;
215
+ }
216
+ }
217
+ else {
218
+ this.logger.warn('入群审批', 'ZMINFO API查询失败,无法获取粉丝牌信息');
219
+ }
220
+ // 绑定状态:查询数据库
221
+ if (buidUsername) {
222
+ const existingBind = await this.repos.mcidbind.findByBuidUid(parsedUid);
223
+ if (existingBind) {
224
+ if (existingBind.qqId === qq) {
225
+ bindStatus = '✅ 该 UID 已绑定到此 QQ';
226
+ }
227
+ else {
228
+ bindStatus = `⚠️ 该 UID 已被 ${existingBind.qqId} 绑定`;
229
+ }
230
+ }
231
+ else {
232
+ bindStatus = '✅ UID 未被绑定';
233
+ }
234
+ }
235
+ else {
236
+ bindStatus = '❌ UID 查询失败(官方API和ZMINFO均失败)';
237
+ }
238
+ }
239
+ return { qq, nickname, avatar, answer, buidUid, buidUsername, medalInfo, bindStatus };
176
240
  }
177
241
  /**
178
242
  * 发送播报消息到管理群
179
243
  */
180
244
  async sendBroadcastMessage(applicantInfo, session) {
181
- const { qq, nickname, avatar, answer } = applicantInfo;
245
+ const { qq, nickname, avatar, answer, buidUid, buidUsername, medalInfo, bindStatus } = applicantInfo;
182
246
  const elements = [
183
247
  koishi_1.h.text('📢 收到新的入群申请\n\n'),
184
248
  koishi_1.h.image(avatar),
185
- koishi_1.h.text(`\n👤 昵称:${nickname}\n`),
186
- koishi_1.h.text(`🆔 QQ号:${qq}\n`),
187
- koishi_1.h.text(`💬 入群${answer}\n\n`),
188
- koishi_1.h.text('━━━━━━━━━━━━━━━\n'),
189
- koishi_1.h.text('请管理员点击表情回应:\n'),
190
- koishi_1.h.text('👍 /太赞了 - 通过并自动绑定\n'),
191
- koishi_1.h.text('😊 /偷感 - 通过并交互式绑定\n'),
192
- koishi_1.h.text('❌ /NO - 拒绝申请')
249
+ koishi_1.h.text(`\n👤 QQ 昵称:${nickname}\n`),
250
+ koishi_1.h.text(`🆔 QQ 号:${qq}\n`),
251
+ koishi_1.h.text(`💬 入群问题:${answer}\n\n`)
193
252
  ];
253
+ // B 站信息
254
+ if (buidUid) {
255
+ elements.push(koishi_1.h.text(`🎬 B 站 UID:${buidUid}\n`));
256
+ if (buidUsername) {
257
+ elements.push(koishi_1.h.text(`👑 B 站昵称:${buidUsername}\n`));
258
+ }
259
+ if (medalInfo) {
260
+ elements.push(koishi_1.h.text(`${medalInfo}\n`));
261
+ }
262
+ elements.push(koishi_1.h.text(`${bindStatus}\n\n`));
263
+ }
264
+ else {
265
+ elements.push(koishi_1.h.text(`⚠️ 未提供有效的 B 站 UID\n\n`));
266
+ }
267
+ elements.push(koishi_1.h.text('━━━━━━━━━━━━━━━\n'), koishi_1.h.text('请管理员点击表情回应:\n'), koishi_1.h.text('👍 /太赞了 - 通过并自动绑定\n'), koishi_1.h.text('😊 /偷感 - 通过并交互式绑定\n'), koishi_1.h.text('❌ /NO - 拒绝申请'));
194
268
  try {
195
269
  const result = await session.bot.sendMessage(this.reviewConfig.reviewGroupId, elements);
196
270
  // result 通常是数组,第一个元素是消息ID
@@ -444,26 +518,59 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
444
518
  }
445
519
  }
446
520
  /**
447
- * 解析UID(支持多种格式)
521
+ * 解析UID(支持多种格式,参考BuidHandler.parseUidInput实现)
448
522
  */
449
523
  parseUID(input) {
450
524
  if (!input)
451
525
  return null;
452
526
  input = input.trim();
453
- // 格式1: 纯数字
527
+ // 格式1: 纯数字(整行都是数字)
454
528
  if (/^\d+$/.test(input)) {
529
+ this.logger.debug('入群审批', `parseUID: 识别为纯数字 -> ${input}`);
455
530
  return input;
456
531
  }
457
- // 格式2: UID:123456789
458
- const uidMatch = input.match(/^UID:(\d+)$/i);
459
- if (uidMatch) {
460
- return uidMatch[1];
532
+ // 格式2: 包含"答案:"标记的多行文本(如:问题\n答案:123456789
533
+ const answerMatch = input.match(/答案[::]\s*(\d+)/i);
534
+ if (answerMatch) {
535
+ this.logger.debug('入群审批', `parseUID: 从"答案:"提取 -> ${answerMatch[1]}`);
536
+ return answerMatch[1];
537
+ }
538
+ // 格式3: UID:123456789(参考BuidHandler)
539
+ if (input.toLowerCase().startsWith('uid:')) {
540
+ const uid = input.substring(4).trim();
541
+ if (/^\d+$/.test(uid)) {
542
+ this.logger.debug('入群审批', `parseUID: 从"UID:"前缀提取 -> ${uid}`);
543
+ return uid;
544
+ }
545
+ }
546
+ // 格式4: https://space.bilibili.com/123456789(参考BuidHandler的完善处理)
547
+ if (input.includes('space.bilibili.com/')) {
548
+ try {
549
+ let urlPart = input.replace(/^https?:\/\/space\.bilibili\.com\//, '');
550
+ // 移除查询参数
551
+ if (urlPart.includes('?')) {
552
+ urlPart = urlPart.split('?')[0];
553
+ }
554
+ // 移除路径
555
+ if (urlPart.includes('/')) {
556
+ urlPart = urlPart.split('/')[0];
557
+ }
558
+ if (/^\d+$/.test(urlPart)) {
559
+ this.logger.debug('入群审批', `parseUID: 从B站空间URL提取 -> ${urlPart}`);
560
+ return urlPart;
561
+ }
562
+ }
563
+ catch (error) {
564
+ this.logger.warn('入群审批', `parseUID: URL解析失败 - ${error.message}`);
565
+ }
461
566
  }
462
- // 格式3: https://space.bilibili.com/123456789
463
- const urlMatch = input.match(/space\.bilibili\.com\/(\d+)/);
464
- if (urlMatch) {
465
- return urlMatch[1];
567
+ // 格式5: 从文本中提取第一个长数字串(8-12位,B站UID的典型长度)
568
+ const numberMatch = input.match(/\b(\d{8,12})\b/);
569
+ if (numberMatch) {
570
+ this.logger.debug('入群审批', `parseUID: 从文本提取长数字串 -> ${numberMatch[1]}`);
571
+ return numberMatch[1];
466
572
  }
573
+ this.logger.warn('入群审批', `parseUID: 无法解析 - "${input}"`);
467
574
  return null;
468
575
  }
469
576
  /**
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.4",
4
+ "version": "2.1.6",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [