koishi-plugin-bind-bot 2.1.3 → 2.1.5

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.
@@ -43,8 +43,8 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
43
43
  // 监听用户成功进群
44
44
  this.ctx.on('guild-member-added', this.handleUserJoined.bind(this));
45
45
  // 监听表情回应(NapCat扩展事件)
46
- // 使用通用 'message' 事件监听,在 handleNotice 中过滤
47
- this.ctx.on('message', this.handleNotice.bind(this));
46
+ // 使用 'notice' 事件监听群表情回应事件(session.subtype === 'group-msg-emoji-like')
47
+ this.ctx.on('notice', this.handleNotice.bind(this));
48
48
  // 中间件:处理拒绝理由
49
49
  this.ctx.middleware(this.handleRejectReason.bind(this));
50
50
  // 定时清理过期记录
@@ -98,26 +98,41 @@ 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
113
  // 获取原始事件数据(使用类型断言访问 onebot 扩展属性)
110
- const sessionAny = session;
111
- if (!sessionAny.onebot || !sessionAny.onebot.likes) {
114
+ const onebotSession = session;
115
+ const onebotData = onebotSession.onebot;
116
+ this.logger.info('入群审批', `[DEBUG] onebot数据: ${JSON.stringify(onebotData)}`, true);
117
+ if (!onebotData?.likes || onebotData.likes.length === 0) {
118
+ this.logger.info('入群审批', '[DEBUG] 跳过: 没有likes数据', true);
112
119
  return;
113
120
  }
114
- const emojiData = sessionAny.onebot.likes;
115
- const msgId = session.messageId;
116
- const operatorId = this.deps.normalizeQQId(session.userId);
117
- this.logger.debug('入群审批', `收到表情回应 - 消息: ${msgId}, 操作者: ${operatorId}, 表情数: ${emojiData.length}`);
121
+ // 从原始 OneBot 数据中读取(更可靠)
122
+ const msgId = onebotData.message_id?.toString() || session.messageId;
123
+ const userId = onebotData.user_id?.toString() || session.userId;
124
+ const emojiData = onebotData.likes;
125
+ if (!msgId || !userId) {
126
+ this.logger.warn('入群审批', '表情回应事件缺少必要数据: messageId 或 userId');
127
+ return;
128
+ }
129
+ const operatorId = this.deps.normalizeQQId(userId);
130
+ this.logger.info('入群审批', `收到表情回应 - 消息: ${msgId}, 操作者: ${operatorId}, 表情数: ${emojiData.length}`, true);
118
131
  // 检查是否是待审批的消息
119
132
  const pendingReq = this.pendingRequests.get(msgId);
120
133
  if (!pendingReq) {
134
+ this.logger.info('入群审批', `[DEBUG] 跳过: 消息${msgId}不在待审批列表中`, true);
135
+ this.logger.info('入群审批', `[DEBUG] 当前待审批列表: ${Array.from(this.pendingRequests.keys()).join(', ')}`, true);
121
136
  return;
122
137
  }
123
138
  // 检查是否已处理
@@ -165,25 +180,93 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
165
180
  catch (error) {
166
181
  this.logger.warn('入群审批', `获取用户信息失败,使用默认值: ${error.message}`);
167
182
  }
168
- return { qq, nickname, avatar, answer };
183
+ // 解析并查询 B 站信息
184
+ let buidUid = null;
185
+ let buidUsername = null;
186
+ let medalInfo = null;
187
+ let bindStatus = '❌ UID 未提供';
188
+ const parsedUid = this.parseUID(answer);
189
+ if (parsedUid) {
190
+ buidUid = parsedUid;
191
+ // 并行查询官方API和ZMINFO API
192
+ const [officialInfo, zminfoData] = await Promise.all([
193
+ this.deps.apiService.getBilibiliOfficialUserInfo(parsedUid).catch(() => null),
194
+ this.deps.apiService.validateBUID(parsedUid).catch(() => null)
195
+ ]);
196
+ // 用户名:优先使用官方API(最准确),降级到ZMINFO
197
+ if (officialInfo?.name) {
198
+ buidUsername = officialInfo.name;
199
+ this.logger.debug('入群审批', `✅ 使用官方API用户名: ${buidUsername}`);
200
+ }
201
+ else if (zminfoData?.username) {
202
+ buidUsername = zminfoData.username;
203
+ this.logger.debug('入群审批', `⚠️ 官方API失败,使用ZMINFO用户名: ${buidUsername}`);
204
+ }
205
+ // 粉丝牌信息:只能从ZMINFO获取(官方API不提供)
206
+ if (zminfoData) {
207
+ const medalLevel = zminfoData.medal?.level || 0;
208
+ const medalName = zminfoData.medal?.name || '';
209
+ if (medalName === this.config.forceBindTargetMedalName) {
210
+ medalInfo = `🎖️ ${medalName} Lv.${medalLevel}`;
211
+ }
212
+ else if (medalLevel > 0) {
213
+ medalInfo = `⚠️ 佩戴其他粉丝牌: ${medalName} Lv.${medalLevel}`;
214
+ }
215
+ else {
216
+ medalInfo = `⚠️ 未获取到 "${this.config.forceBindTargetMedalName}" 粉丝牌`;
217
+ }
218
+ }
219
+ else {
220
+ this.logger.warn('入群审批', 'ZMINFO API查询失败,无法获取粉丝牌信息');
221
+ }
222
+ // 绑定状态:查询数据库
223
+ if (buidUsername) {
224
+ const existingBind = await this.repos.mcidbind.findByBuidUid(parsedUid);
225
+ if (existingBind) {
226
+ if (existingBind.qqId === qq) {
227
+ bindStatus = '✅ 该 UID 已绑定到此 QQ';
228
+ }
229
+ else {
230
+ bindStatus = `⚠️ 该 UID 已被 ${existingBind.qqId} 绑定`;
231
+ }
232
+ }
233
+ else {
234
+ bindStatus = '✅ UID 未被绑定';
235
+ }
236
+ }
237
+ else {
238
+ bindStatus = '❌ UID 查询失败(官方API和ZMINFO均失败)';
239
+ }
240
+ }
241
+ return { qq, nickname, avatar, answer, buidUid, buidUsername, medalInfo, bindStatus };
169
242
  }
170
243
  /**
171
244
  * 发送播报消息到管理群
172
245
  */
173
246
  async sendBroadcastMessage(applicantInfo, session) {
174
- const { qq, nickname, avatar, answer } = applicantInfo;
247
+ const { qq, nickname, avatar, answer, buidUid, buidUsername, medalInfo, bindStatus } = applicantInfo;
175
248
  const elements = [
176
249
  koishi_1.h.text('📢 收到新的入群申请\n\n'),
177
250
  koishi_1.h.image(avatar),
178
- koishi_1.h.text(`\n👤 昵称:${nickname}\n`),
179
- koishi_1.h.text(`🆔 QQ号:${qq}\n`),
180
- koishi_1.h.text(`💬 回答:${answer}\n\n`),
181
- koishi_1.h.text('━━━━━━━━━━━━━━━\n'),
182
- koishi_1.h.text('请管理员点击表情回应:\n'),
183
- koishi_1.h.text('👍 /太赞了 - 通过并自动绑定\n'),
184
- koishi_1.h.text('😊 /偷感 - 通过并交互式绑定\n'),
185
- koishi_1.h.text('❌ /NO - 拒绝申请')
251
+ koishi_1.h.text(`\n👤 QQ 昵称:${nickname}\n`),
252
+ koishi_1.h.text(`🆔 QQ 号:${qq}\n`),
253
+ koishi_1.h.text(`💬 入群问题:${answer}\n\n`)
186
254
  ];
255
+ // B 站信息
256
+ if (buidUid) {
257
+ elements.push(koishi_1.h.text(`🎬 B 站 UID:${buidUid}\n`));
258
+ if (buidUsername) {
259
+ elements.push(koishi_1.h.text(`👑 B 站昵称:${buidUsername}\n`));
260
+ }
261
+ if (medalInfo) {
262
+ elements.push(koishi_1.h.text(`${medalInfo}\n`));
263
+ }
264
+ elements.push(koishi_1.h.text(`${bindStatus}\n\n`));
265
+ }
266
+ else {
267
+ elements.push(koishi_1.h.text(`⚠️ 未提供有效的 B 站 UID\n\n`));
268
+ }
269
+ 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 - 拒绝申请'));
187
270
  try {
188
271
  const result = await session.bot.sendMessage(this.reviewConfig.reviewGroupId, elements);
189
272
  // result 通常是数组,第一个元素是消息ID
@@ -353,13 +436,15 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
353
436
  // 检查是否是同一个管理员
354
437
  const operatorId = this.deps.normalizeQQId(session.userId);
355
438
  if (operatorId !== rejectFlow.operatorId) {
356
- return '⚠️ 只有发起拒绝的管理员可以提供理由';
439
+ void session.send('⚠️ 只有发起拒绝的管理员可以提供理由');
440
+ return;
357
441
  }
358
442
  // 检查是否超时
359
443
  if (Date.now() > rejectFlow.timeout) {
360
444
  this.rejectFlows.delete(session.quote.id);
361
445
  rejectFlow.pendingRequest.status = 'pending';
362
- return '❌ 拒绝流程已超时,请重新操作';
446
+ void session.send('❌ 拒绝流程已超时,请重新操作');
447
+ return;
363
448
  }
364
449
  // 执行拒绝
365
450
  const reason = session.content;
@@ -369,12 +454,12 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
369
454
  this.logger.info('入群审批', `已拒绝入群 - QQ: ${pendingRequest.applicantQQ}, 理由: ${reason}`, true);
370
455
  pendingRequest.status = 'rejected';
371
456
  this.rejectFlows.delete(session.quote.id);
372
- return `✅ 已拒绝 ${pendingRequest.applicantQQ} 的入群申请\n拒绝理由:${reason}`;
457
+ await session.send(`✅ 已拒绝 ${pendingRequest.applicantQQ} 的入群申请\n拒绝理由:${reason}`);
373
458
  }
374
459
  catch (error) {
375
460
  this.logger.error('入群审批', `拒绝入群失败: ${error.message}`, error);
376
461
  pendingRequest.status = 'pending';
377
- return `❌ 拒绝失败:${error.message}`;
462
+ await session.send(`❌ 拒绝失败:${error.message}`);
378
463
  }
379
464
  }
380
465
  /**
package/lib/index.js CHANGED
@@ -55,11 +55,9 @@ exports.Config = koishi_1.Schema.object({
55
55
  groupRequestReview: koishi_1.Schema.object({
56
56
  enabled: koishi_1.Schema.boolean().description('是否启用入群申请审批功能').default(false),
57
57
  targetGroupId: koishi_1.Schema.string()
58
- .description('需要审批的目标群ID(入群申请来源群)')
59
- .default('931805503'),
58
+ .description('需要审批的目标群ID(入群申请来源群)'),
60
59
  reviewGroupId: koishi_1.Schema.string()
61
- .description('管理员审批操作所在的群ID(播报群)')
62
- .default('290238092'),
60
+ .description('管理员审批操作所在的群ID(播报群)'),
63
61
  approveAutoBindEmoji: koishi_1.Schema.string()
64
62
  .description('批准并自动绑定的表情ID(/太赞了)')
65
63
  .default('389'),
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.3",
4
+ "version": "2.1.5",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [