koishi-plugin-bind-bot 2.0.4 → 2.0.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.
@@ -38,7 +38,7 @@ export interface HandlerDependencies {
38
38
  isAdmin: (userId: string) => Promise<boolean>;
39
39
  isMaster: (userId: string) => boolean;
40
40
  sendMessage: (session: Session, content: any[], options?: any) => Promise<void>;
41
- autoSetGroupNickname: (session: Session, mcUsername: string | null, buidUsername: string, targetUserId?: string, specifiedGroupId?: string) => Promise<void>;
41
+ autoSetGroupNickname: (session: Session, mcUsername: string | null, buidUsername: string, buidUid?: string, targetUserId?: string, specifiedGroupId?: string) => Promise<void>;
42
42
  checkNicknameFormat: (nickname: string, buidUsername: string, mcUsername: string | null) => boolean;
43
43
  getBindInfo: (qqId: string) => Promise<MCIDBIND | null>;
44
44
  getServerConfigById: (serverId: string) => any;
@@ -499,7 +499,7 @@ class BuidHandler extends base_handler_1.BaseHandler {
499
499
  const latestTargetBind = await this.repos.mcidbind.findByQQId(normalizedTargetId);
500
500
  if (latestTargetBind) {
501
501
  const mcName = latestTargetBind.mcUsername && !latestTargetBind.mcUsername.startsWith('_temp_') ? latestTargetBind.mcUsername : null;
502
- await this.deps.autoSetGroupNickname(session, mcName, enhancedUser.username, normalizedTargetId);
502
+ await this.deps.autoSetGroupNickname(session, mcName, enhancedUser.username, String(enhancedUser.uid), normalizedTargetId);
503
503
  }
504
504
  }
505
505
  catch (renameError) {
@@ -530,7 +530,7 @@ class BuidHandler extends base_handler_1.BaseHandler {
530
530
  const latestBind = await this.repos.mcidbind.findByQQId(operatorQQId);
531
531
  if (latestBind) {
532
532
  const mcName = latestBind.mcUsername && !latestBind.mcUsername.startsWith('_temp_') ? latestBind.mcUsername : null;
533
- await this.deps.autoSetGroupNickname(session, mcName, enhancedUser.username);
533
+ await this.deps.autoSetGroupNickname(session, mcName, enhancedUser.username, String(enhancedUser.uid));
534
534
  }
535
535
  }
536
536
  catch (renameError) {
@@ -599,7 +599,7 @@ class BuidHandler extends base_handler_1.BaseHandler {
599
599
  const latestTargetBind = await this.repos.mcidbind.findByQQId(normalizedTargetId);
600
600
  if (latestTargetBind) {
601
601
  const mcName = latestTargetBind.mcUsername && !latestTargetBind.mcUsername.startsWith('_temp_') ? latestTargetBind.mcUsername : null;
602
- await this.deps.autoSetGroupNickname(session, mcName, buidUser.username, normalizedTargetId);
602
+ await this.deps.autoSetGroupNickname(session, mcName, buidUser.username, actualUid, normalizedTargetId);
603
603
  this.logger.info('绑定', `管理员QQ(${operatorQQId})为QQ(${normalizedTargetId})B站绑定完成,已尝试设置群昵称`);
604
604
  }
605
605
  }
@@ -653,7 +653,7 @@ class BuidHandler extends base_handler_1.BaseHandler {
653
653
  const latestBind = await this.repos.mcidbind.findByQQId(operatorQQId);
654
654
  if (latestBind) {
655
655
  const mcName = latestBind.mcUsername && !latestBind.mcUsername.startsWith('_temp_') ? latestBind.mcUsername : null;
656
- await this.deps.autoSetGroupNickname(session, mcName, buidUser.username);
656
+ await this.deps.autoSetGroupNickname(session, mcName, buidUser.username, actualUid);
657
657
  this.logger.info('绑定', `QQ(${operatorQQId})B站绑定完成,已尝试设置群昵称`);
658
658
  }
659
659
  }
@@ -224,7 +224,7 @@ class McidCommandHandler extends base_handler_1.BaseHandler {
224
224
  // 异步设置群昵称
225
225
  if (bind.buidUid && bind.buidUsername) {
226
226
  const mcName = bind.mcUsername && !bind.mcUsername.startsWith('_temp_') ? bind.mcUsername : null;
227
- this.deps.autoSetGroupNickname(session, mcName, bind.buidUsername, targetId || undefined)
227
+ this.deps.autoSetGroupNickname(session, mcName, bind.buidUsername, bind.buidUid, targetId || undefined)
228
228
  .catch(err => this.logger.warn('查询', `群昵称设置失败: ${err.message}`));
229
229
  }
230
230
  else {
@@ -360,7 +360,7 @@ class McidCommandHandler extends base_handler_1.BaseHandler {
360
360
  try {
361
361
  const latestTargetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
362
362
  if (latestTargetBind && latestTargetBind.buidUid && latestTargetBind.buidUsername) {
363
- await this.deps.autoSetGroupNickname(session, username, latestTargetBind.buidUsername, normalizedTargetId);
363
+ await this.deps.autoSetGroupNickname(session, username, latestTargetBind.buidUsername, latestTargetBind.buidUid, normalizedTargetId);
364
364
  this.logger.info('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})MC绑定完成,已尝试设置群昵称`);
365
365
  targetBuidStatus = '\n✅ 该用户已绑定B站账号,群昵称已更新';
366
366
  }
@@ -431,7 +431,7 @@ class McidCommandHandler extends base_handler_1.BaseHandler {
431
431
  try {
432
432
  const latestBind = await this.deps.getMcBindByQQId(operatorId);
433
433
  if (latestBind && latestBind.buidUid && latestBind.buidUsername) {
434
- await this.deps.autoSetGroupNickname(session, username, latestBind.buidUsername);
434
+ await this.deps.autoSetGroupNickname(session, username, latestBind.buidUsername, latestBind.buidUid);
435
435
  this.logger.info('绑定', `QQ(${operatorId})MC绑定完成,已尝试设置群昵称`);
436
436
  }
437
437
  else {
@@ -876,7 +876,7 @@ class McidCommandHandler extends base_handler_1.BaseHandler {
876
876
  const isCorrect = this.deps.checkNicknameFormat(currentNickname, bind.buidUsername, mcInfo);
877
877
  if (!isCorrect) {
878
878
  // 修复群昵称
879
- await this.deps.autoSetGroupNickname(session, mcInfo, bind.buidUsername, bind.qqId, targetGroupId);
879
+ await this.deps.autoSetGroupNickname(session, mcInfo, bind.buidUsername, bind.buidUid, bind.qqId, targetGroupId);
880
880
  fixedCount++;
881
881
  const expectedFormat = `${bind.buidUsername}(ID:${mcInfo || '未绑定'})`;
882
882
  results.push(`✅ ${bind.qqId}: "${currentNickname}" → "${expectedFormat}"`);
package/lib/index.js CHANGED
@@ -260,14 +260,15 @@ function apply(ctx, config) {
260
260
  }
261
261
  };
262
262
  // 自动群昵称设置功能
263
- const autoSetGroupNickname = async (session, mcUsername, buidUsername, targetUserId, specifiedGroupId) => {
263
+ const autoSetGroupNickname = async (session, mcUsername, buidUsername, buidUid, targetUserId, specifiedGroupId) => {
264
264
  try {
265
265
  // 如果指定了目标用户ID,使用目标用户ID,否则使用session的用户ID
266
266
  const actualUserId = targetUserId || session.userId;
267
267
  const normalizedUserId = normalizeQQId(actualUserId);
268
268
  // 根据MC绑定状态设置不同的格式(临时用户名视为未绑定)
269
269
  const mcInfo = (mcUsername && !mcUsername.startsWith('_temp_')) ? mcUsername : "未绑定";
270
- const newNickname = `${buidUsername}(ID:${mcInfo})`;
270
+ let currentBuidUsername = buidUsername;
271
+ const newNickname = `${currentBuidUsername}(ID:${mcInfo})`;
271
272
  // 使用指定的群ID,如果没有指定则使用配置的默认群ID
272
273
  const targetGroupId = specifiedGroupId || config.autoNicknameGroupId;
273
274
  logger.debug(`[群昵称设置] 开始处理QQ(${normalizedUserId})的群昵称设置,目标群: ${targetGroupId}`);
@@ -286,20 +287,82 @@ function apply(ctx, config) {
286
287
  logger.info(`[群昵称设置] QQ(${normalizedUserId})群昵称已经是"${newNickname}",跳过修改`);
287
288
  return;
288
289
  }
290
+ // 昵称不一致,先尝试获取最新的B站用户信息
291
+ if (buidUid) {
292
+ logger.debug(`[群昵称设置] 检测到昵称不一致,尝试获取B站UID ${buidUid} 的最新信息...`);
293
+ // 1. 提取当前群昵称中的B站用户名
294
+ const currentNicknameUsername = (0, helpers_1.extractBuidUsernameFromNickname)(currentNickname);
295
+ logger.debug(`[群昵称设置] 从当前群昵称"${currentNickname}"中提取到B站名称: ${currentNicknameUsername || '(无法提取)'}`);
296
+ try {
297
+ const latestBuidUser = await validateBUID(buidUid);
298
+ if (latestBuidUser && latestBuidUser.username) {
299
+ logger.debug(`[群昵称设置] API返回B站用户名: "${latestBuidUser.username}"`);
300
+ // 2. 智能三层判断
301
+ // 情况A: API返回 == 当前群昵称中的名称
302
+ if (currentNicknameUsername && latestBuidUser.username === currentNicknameUsername) {
303
+ logger.info(`[群昵称设置] ✅ 当前群昵称"${currentNickname}"已包含正确的B站名称"${currentNicknameUsername}"`);
304
+ // 群昵称已经正确,仅需更新数据库(如果数据库是旧的)
305
+ if (latestBuidUser.username !== buidUsername) {
306
+ logger.info(`[群昵称设置] 数据库中的B站名称需要更新: "${buidUsername}" → "${latestBuidUser.username}"`);
307
+ try {
308
+ await updateBuidInfoOnly(normalizedUserId, latestBuidUser);
309
+ logger.info(`[群昵称设置] 已更新数据库中的B站用户名`);
310
+ }
311
+ catch (updateError) {
312
+ logger.warn(`[群昵称设置] 更新数据库失败: ${updateError.message}`);
313
+ }
314
+ }
315
+ else {
316
+ logger.debug(`[群昵称设置] 数据库中的B站名称已是最新,无需更新`);
317
+ }
318
+ // 跳过群昵称修改
319
+ logger.info(`[群昵称设置] 群昵称格式正确且名称最新,跳过修改`);
320
+ return;
321
+ }
322
+ // 情况B: API返回 == 数据库(都是旧的)
323
+ if (latestBuidUser.username === buidUsername) {
324
+ logger.warn(`[群昵称设置] ⚠️ API返回的B站名称"${latestBuidUser.username}"与数据库一致,但与群昵称不符`);
325
+ logger.warn(`[群昵称设置] 可能是API缓存未刷新,采用保守策略:不修改群昵称`);
326
+ return;
327
+ }
328
+ // 情况C: API返回新数据(!= 群昵称 且 != 数据库)
329
+ logger.info(`[群昵称设置] 检测到B站用户名已更新: "${buidUsername}" → "${latestBuidUser.username}"`);
330
+ currentBuidUsername = latestBuidUser.username;
331
+ // 更新数据库中的B站信息
332
+ try {
333
+ await updateBuidInfoOnly(normalizedUserId, latestBuidUser);
334
+ logger.info(`[群昵称设置] 已更新数据库中的B站用户名为: ${currentBuidUsername}`);
335
+ }
336
+ catch (updateError) {
337
+ logger.warn(`[群昵称设置] 更新数据库中的B站用户名失败: ${updateError.message}`);
338
+ // 即使更新数据库失败,也继续使用最新的用户名设置昵称
339
+ }
340
+ }
341
+ else {
342
+ logger.warn(`[群昵称设置] 获取最新B站用户信息失败,使用数据库中的用户名: ${currentBuidUsername}`);
343
+ }
344
+ }
345
+ catch (validateError) {
346
+ logger.warn(`[群昵称设置] 获取最新B站用户信息时出错: ${validateError.message},使用数据库中的用户名: ${currentBuidUsername}`);
347
+ // API调用失败时降级处理,使用数据库中的旧名称
348
+ }
349
+ }
350
+ // 使用(可能已更新的)用户名重新生成昵称
351
+ const finalNickname = `${currentBuidUsername}(ID:${mcInfo})`;
289
352
  // 昵称不一致,执行修改
290
- logger.debug(`[群昵称设置] 昵称不一致,正在修改群昵称...`);
291
- await session.bot.internal.setGroupCard(targetGroupId, normalizedUserId, newNickname);
292
- logger.info(`[群昵称设置] 成功在群${targetGroupId}中将QQ(${normalizedUserId})群昵称从"${currentNickname}"修改为"${newNickname}"`);
353
+ logger.debug(`[群昵称设置] 昵称不一致,正在修改群昵称为: "${finalNickname}"`);
354
+ await session.bot.internal.setGroupCard(targetGroupId, normalizedUserId, finalNickname);
355
+ logger.info(`[群昵称设置] 成功在群${targetGroupId}中将QQ(${normalizedUserId})群昵称从"${currentNickname}"修改为"${finalNickname}"`);
293
356
  // 验证设置是否生效 - 再次获取群昵称确认
294
357
  try {
295
358
  await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
296
359
  const verifyGroupInfo = await session.bot.internal.getGroupMemberInfo(targetGroupId, normalizedUserId);
297
360
  const verifyNickname = verifyGroupInfo.card || verifyGroupInfo.nickname || '';
298
- if (verifyNickname === newNickname) {
361
+ if (verifyNickname === finalNickname) {
299
362
  logger.info(`[群昵称设置] ✅ 验证成功,群昵称已生效: "${verifyNickname}"`);
300
363
  }
301
364
  else {
302
- logger.warn(`[群昵称设置] ⚠️ 验证失败,期望"${newNickname}",实际"${verifyNickname}",可能是权限不足或API延迟`);
365
+ logger.warn(`[群昵称设置] ⚠️ 验证失败,期望"${finalNickname}",实际"${verifyNickname}",可能是权限不足或API延迟`);
303
366
  }
304
367
  }
305
368
  catch (verifyError) {
@@ -311,19 +374,54 @@ function apply(ctx, config) {
311
374
  logger.warn(`[群昵称设置] 获取QQ(${normalizedUserId})当前群昵称失败: ${getInfoError.message}`);
312
375
  logger.warn(`[群昵称设置] 错误详情: ${JSON.stringify(getInfoError)}`);
313
376
  logger.debug(`[群昵称设置] 将直接尝试设置新昵称...`);
377
+ // 如果传入了 buidUid,尝试获取最新的B站用户信息
378
+ if (buidUid) {
379
+ logger.debug(`[群昵称设置] 尝试获取B站UID ${buidUid} 的最新信息...`);
380
+ try {
381
+ const latestBuidUser = await validateBUID(buidUid);
382
+ if (latestBuidUser && latestBuidUser.username) {
383
+ logger.debug(`[群昵称设置] API返回B站用户名: "${latestBuidUser.username}"`);
384
+ // 智能判断:API返回 == 数据库(都是旧的)
385
+ if (latestBuidUser.username === buidUsername) {
386
+ logger.warn(`[群昵称设置] ⚠️ API返回的B站名称"${latestBuidUser.username}"与数据库一致`);
387
+ logger.warn(`[群昵称设置] 可能是API缓存未刷新,且无法获取当前群昵称,采用保守策略:跳过修改`);
388
+ return;
389
+ }
390
+ // API返回新数据(!= 数据库)
391
+ logger.info(`[群昵称设置] 检测到B站用户名已更新: "${buidUsername}" → "${latestBuidUser.username}"`);
392
+ currentBuidUsername = latestBuidUser.username;
393
+ // 更新数据库
394
+ try {
395
+ await updateBuidInfoOnly(normalizedUserId, latestBuidUser);
396
+ logger.info(`[群昵称设置] 已更新数据库中的B站用户名为: ${currentBuidUsername}`);
397
+ }
398
+ catch (updateError) {
399
+ logger.warn(`[群昵称设置] 更新数据库失败: ${updateError.message}`);
400
+ }
401
+ }
402
+ else {
403
+ logger.warn(`[群昵称设置] 获取最新B站用户信息失败`);
404
+ }
405
+ }
406
+ catch (validateError) {
407
+ logger.warn(`[群昵称设置] 获取最新B站用户信息失败: ${validateError.message}`);
408
+ }
409
+ }
314
410
  try {
315
- await session.bot.internal.setGroupCard(targetGroupId, normalizedUserId, newNickname);
316
- logger.info(`[群昵称设置] 成功在群${targetGroupId}中将QQ(${normalizedUserId})群昵称设置为: ${newNickname}`);
411
+ // 使用(可能已更新的)用户名生成昵称
412
+ const fallbackNickname = `${currentBuidUsername}(ID:${mcInfo})`;
413
+ await session.bot.internal.setGroupCard(targetGroupId, normalizedUserId, fallbackNickname);
414
+ logger.info(`[群昵称设置] 成功在群${targetGroupId}中将QQ(${normalizedUserId})群昵称设置为: ${fallbackNickname}`);
317
415
  // 验证设置是否生效
318
416
  try {
319
417
  await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
320
418
  const verifyGroupInfo = await session.bot.internal.getGroupMemberInfo(targetGroupId, normalizedUserId);
321
419
  const verifyNickname = verifyGroupInfo.card || verifyGroupInfo.nickname || '';
322
- if (verifyNickname === newNickname) {
420
+ if (verifyNickname === fallbackNickname) {
323
421
  logger.info(`[群昵称设置] ✅ 验证成功,群昵称已生效: "${verifyNickname}"`);
324
422
  }
325
423
  else {
326
- logger.warn(`[群昵称设置] ⚠️ 验证失败,期望"${newNickname}",实际"${verifyNickname}",可能是权限不足`);
424
+ logger.warn(`[群昵称设置] ⚠️ 验证失败,期望"${fallbackNickname}",实际"${verifyNickname}",可能是权限不足`);
327
425
  logger.warn(`[群昵称设置] 建议检查: 1.机器人是否为群管理员 2.群设置是否允许管理员修改昵称 3.OneBot实现是否支持该功能`);
328
426
  }
329
427
  }
@@ -349,7 +447,7 @@ function apply(ctx, config) {
349
447
  const actualUserId = targetUserId || session.userId;
350
448
  const normalizedUserId = normalizeQQId(actualUserId);
351
449
  logger.error(`[群昵称设置] QQ(${normalizedUserId})自动群昵称设置失败: ${error.message}`);
352
- logger.error(`[群昵称设置] 完整错误信息: ${JSON.stringify(error)}`);
450
+ logger.error(`[群昵称称设置] 完整错误信息: ${JSON.stringify(error)}`);
353
451
  }
354
452
  };
355
453
  // 检查是否为无关输入
@@ -2060,7 +2158,7 @@ function apply(ctx, config) {
2060
2158
  const reminderType = reminderCount >= 4 ? '警告' : '提醒';
2061
2159
  const reminderPrefix = `【第${reminderCount}次${reminderType}】`;
2062
2160
  // 自动修改群昵称
2063
- await autoSetGroupNickname(session, mcInfo, bind.buidUsername);
2161
+ await autoSetGroupNickname(session, mcInfo, bind.buidUsername, bind.buidUid);
2064
2162
  setReminderCooldown(normalizedUserId);
2065
2163
  logger.info(`[随机提醒] 为仅绑定B站的用户QQ(${normalizedUserId})修复群昵称并发送第${reminderCount}次${reminderType}`);
2066
2164
  await sendMessage(session, [
@@ -2080,7 +2178,7 @@ function apply(ctx, config) {
2080
2178
  const reminderType = reminderCount >= 4 ? '警告' : '提醒';
2081
2179
  const reminderPrefix = `【第${reminderCount}次${reminderType}】`;
2082
2180
  // 自动修改群昵称
2083
- await autoSetGroupNickname(session, bind.mcUsername, bind.buidUsername);
2181
+ await autoSetGroupNickname(session, bind.mcUsername, bind.buidUsername, bind.buidUid);
2084
2182
  setReminderCooldown(normalizedUserId);
2085
2183
  logger.info(`[随机提醒] 为已完全绑定的用户QQ(${normalizedUserId})修复群昵称并发送第${reminderCount}次${reminderType}`);
2086
2184
  await sendMessage(session, [
@@ -2208,7 +2306,7 @@ function apply(ctx, config) {
2208
2306
  removeBindingSession(session.userId, session.channelId);
2209
2307
  // 设置群昵称
2210
2308
  try {
2211
- await autoSetGroupNickname(session, null, existingBind.buidUsername);
2309
+ await autoSetGroupNickname(session, null, existingBind.buidUsername, existingBind.buidUid);
2212
2310
  logger.info(`[交互绑定] QQ(${normalizedUserId})完成绑定,已设置群昵称`);
2213
2311
  }
2214
2312
  catch (renameError) {
@@ -2298,7 +2396,7 @@ function apply(ctx, config) {
2298
2396
  removeBindingSession(session.userId, session.channelId);
2299
2397
  // 设置群昵称
2300
2398
  try {
2301
- await autoSetGroupNickname(session, username, updatedBind.buidUsername);
2399
+ await autoSetGroupNickname(session, username, updatedBind.buidUsername, updatedBind.buidUid);
2302
2400
  logger.info(`[交互绑定] QQ(${normalizedUserId})绑定完成,已设置群昵称`);
2303
2401
  }
2304
2402
  catch (renameError) {
@@ -2419,7 +2517,7 @@ function apply(ctx, config) {
2419
2517
  try {
2420
2518
  // 检查是否有有效的MC用户名(不是临时用户名)
2421
2519
  const mcName = bindingSession.mcUsername && !bindingSession.mcUsername.startsWith('_temp_') ? bindingSession.mcUsername : null;
2422
- await autoSetGroupNickname(session, mcName, buidUser.username);
2520
+ await autoSetGroupNickname(session, mcName, buidUser.username, String(buidUser.uid));
2423
2521
  logger.info(`[交互绑定] QQ(${normalizedUserId})绑定完成,已设置群昵称`);
2424
2522
  }
2425
2523
  catch (renameError) {
@@ -107,3 +107,16 @@ export declare function normalizeUsername(username: string, logger?: Logger): st
107
107
  * @returns 是否相同
108
108
  */
109
109
  export declare function isSameUsername(username1: string, username2: string): boolean;
110
+ /**
111
+ * 从群昵称中提取B站用户名
112
+ * 用于解析标准格式的群昵称:B站名称(ID:MC名称)
113
+ *
114
+ * @param nickname 群昵称字符串
115
+ * @returns B站用户名,如果格式不匹配返回 null
116
+ *
117
+ * @example
118
+ * extractBuidUsernameFromNickname("张三(ID:Steve)") // => "张三"
119
+ * extractBuidUsernameFromNickname("李四(ID:未绑定)") // => "李四"
120
+ * extractBuidUsernameFromNickname("无效格式") // => null
121
+ */
122
+ export declare function extractBuidUsernameFromNickname(nickname: string): string | null;
@@ -13,6 +13,7 @@ exports.levenshteinDistance = levenshteinDistance;
13
13
  exports.calculateSimilarity = calculateSimilarity;
14
14
  exports.normalizeUsername = normalizeUsername;
15
15
  exports.isSameUsername = isSameUsername;
16
+ exports.extractBuidUsernameFromNickname = extractBuidUsernameFromNickname;
16
17
  /**
17
18
  * 通用工具函数集合
18
19
  */
@@ -360,3 +361,23 @@ function isSameUsername(username1, username2) {
360
361
  return false;
361
362
  return normalizeUsername(username1) === normalizeUsername(username2);
362
363
  }
364
+ /**
365
+ * 从群昵称中提取B站用户名
366
+ * 用于解析标准格式的群昵称:B站名称(ID:MC名称)
367
+ *
368
+ * @param nickname 群昵称字符串
369
+ * @returns B站用户名,如果格式不匹配返回 null
370
+ *
371
+ * @example
372
+ * extractBuidUsernameFromNickname("张三(ID:Steve)") // => "张三"
373
+ * extractBuidUsernameFromNickname("李四(ID:未绑定)") // => "李四"
374
+ * extractBuidUsernameFromNickname("无效格式") // => null
375
+ */
376
+ function extractBuidUsernameFromNickname(nickname) {
377
+ if (!nickname)
378
+ return null;
379
+ // 匹配格式:任意内容(ID:任意内容)
380
+ // 使用非贪婪匹配 .+? 确保正确提取第一个括号前的内容
381
+ const match = nickname.match(/^(.+?)(ID:.+)$/);
382
+ return match ? match[1].trim() : null;
383
+ }
@@ -18,13 +18,17 @@ export declare class MessageUtils {
18
18
  private config;
19
19
  private logger;
20
20
  private getBindingSessionFn;
21
+ private validateBUID?;
22
+ private updateBuidInfoOnly?;
21
23
  /**
22
24
  * 创建消息工具实例
23
25
  * @param config 消息工具配置
24
26
  * @param logger 日志服务
25
27
  * @param getBindingSessionFn 获取绑定会话的函数
28
+ * @param validateBUID 验证B站UID的函数(可选)
29
+ * @param updateBuidInfoOnly 更新B站信息的函数(可选)
26
30
  */
27
- constructor(config: MessageUtilsConfig, logger: LoggerService, getBindingSessionFn: (userId: string, channelId: string) => BindingSession | null);
31
+ constructor(config: MessageUtilsConfig, logger: LoggerService, getBindingSessionFn: (userId: string, channelId: string) => BindingSession | null, validateBUID?: (uid: string) => Promise<any>, updateBuidInfoOnly?: (qqId: string, buidUser: any) => Promise<boolean>);
28
32
  /**
29
33
  * 发送消息并处理自动撤回
30
34
  * @param session Koishi Session对象
@@ -39,8 +43,9 @@ export declare class MessageUtils {
39
43
  * @param session Koishi Session对象
40
44
  * @param mcUsername MC用户名
41
45
  * @param buidUsername B站用户名
46
+ * @param buidUid B站UID(可选,用于实时验证)
42
47
  * @param targetUserId 目标用户ID(可选,用于管理员为他人设置)
43
48
  * @param specifiedGroupId 指定的群ID(可选,默认使用配置的群ID)
44
49
  */
45
- autoSetGroupNickname(session: Session, mcUsername: string | null, buidUsername: string, targetUserId?: string, specifiedGroupId?: string): Promise<void>;
50
+ autoSetGroupNickname(session: Session, mcUsername: string | null, buidUsername: string, buidUid?: string, targetUserId?: string, specifiedGroupId?: string): Promise<void>;
46
51
  }
@@ -11,16 +11,22 @@ class MessageUtils {
11
11
  config;
12
12
  logger;
13
13
  getBindingSessionFn;
14
+ validateBUID;
15
+ updateBuidInfoOnly;
14
16
  /**
15
17
  * 创建消息工具实例
16
18
  * @param config 消息工具配置
17
19
  * @param logger 日志服务
18
20
  * @param getBindingSessionFn 获取绑定会话的函数
21
+ * @param validateBUID 验证B站UID的函数(可选)
22
+ * @param updateBuidInfoOnly 更新B站信息的函数(可选)
19
23
  */
20
- constructor(config, logger, getBindingSessionFn) {
24
+ constructor(config, logger, getBindingSessionFn, validateBUID, updateBuidInfoOnly) {
21
25
  this.config = config;
22
26
  this.logger = logger;
23
27
  this.getBindingSessionFn = getBindingSessionFn;
28
+ this.validateBUID = validateBUID;
29
+ this.updateBuidInfoOnly = updateBuidInfoOnly;
24
30
  }
25
31
  /**
26
32
  * 发送消息并处理自动撤回
@@ -135,17 +141,19 @@ class MessageUtils {
135
141
  * @param session Koishi Session对象
136
142
  * @param mcUsername MC用户名
137
143
  * @param buidUsername B站用户名
144
+ * @param buidUid B站UID(可选,用于实时验证)
138
145
  * @param targetUserId 目标用户ID(可选,用于管理员为他人设置)
139
146
  * @param specifiedGroupId 指定的群ID(可选,默认使用配置的群ID)
140
147
  */
141
- async autoSetGroupNickname(session, mcUsername, buidUsername, targetUserId, specifiedGroupId) {
148
+ async autoSetGroupNickname(session, mcUsername, buidUsername, buidUid, targetUserId, specifiedGroupId) {
142
149
  try {
143
150
  // 如果指定了目标用户ID,使用目标用户ID,否则使用session的用户ID
144
151
  const actualUserId = targetUserId || session.userId;
145
152
  const normalizedUserId = (0, helpers_1.normalizeQQId)(actualUserId);
146
153
  // 根据MC绑定状态设置不同的格式(临时用户名视为未绑定)
147
154
  const mcInfo = (mcUsername && !mcUsername.startsWith('_temp_')) ? mcUsername : "未绑定";
148
- const newNickname = `${buidUsername}(ID:${mcInfo})`;
155
+ let currentBuidUsername = buidUsername;
156
+ const newNickname = `${currentBuidUsername}(ID:${mcInfo})`;
149
157
  // 使用指定的群ID,如果没有指定则使用配置的默认群ID
150
158
  const targetGroupId = specifiedGroupId || this.config.autoNicknameGroupId;
151
159
  this.logger.debug('群昵称设置', `开始处理QQ(${normalizedUserId})的群昵称设置,目标群: ${targetGroupId}`);
@@ -164,20 +172,82 @@ class MessageUtils {
164
172
  this.logger.info('群昵称设置', `QQ(${normalizedUserId})群昵称已经是"${newNickname}",跳过修改`);
165
173
  return;
166
174
  }
175
+ // 昵称不一致,先尝试获取最新的B站用户信息
176
+ if (buidUid && this.validateBUID && this.updateBuidInfoOnly) {
177
+ this.logger.debug('群昵称设置', `检测到昵称不一致,尝试获取B站UID ${buidUid} 的最新信息...`);
178
+ // 1. 提取当前群昵称中的B站用户名
179
+ const currentNicknameUsername = (0, helpers_1.extractBuidUsernameFromNickname)(currentNickname);
180
+ this.logger.debug('群昵称设置', `从当前群昵称"${currentNickname}"中提取到B站名称: ${currentNicknameUsername || '(无法提取)'}`);
181
+ try {
182
+ const latestBuidUser = await this.validateBUID(buidUid);
183
+ if (latestBuidUser && latestBuidUser.username) {
184
+ this.logger.debug('群昵称设置', `API返回B站用户名: "${latestBuidUser.username}"`);
185
+ // 2. 智能三层判断
186
+ // 情况A: API返回 == 当前群昵称中的名称
187
+ if (currentNicknameUsername && latestBuidUser.username === currentNicknameUsername) {
188
+ this.logger.info('群昵称设置', `✅ 当前群昵称"${currentNickname}"已包含正确的B站名称"${currentNicknameUsername}"`);
189
+ // 群昵称已经正确,仅需更新数据库(如果数据库是旧的)
190
+ if (latestBuidUser.username !== buidUsername) {
191
+ this.logger.info('群昵称设置', `数据库中的B站名称需要更新: "${buidUsername}" → "${latestBuidUser.username}"`);
192
+ try {
193
+ await this.updateBuidInfoOnly(normalizedUserId, latestBuidUser);
194
+ this.logger.info('群昵称设置', `已更新数据库中的B站用户名`);
195
+ }
196
+ catch (updateError) {
197
+ this.logger.warn('群昵称设置', `更新数据库失败: ${updateError.message}`);
198
+ }
199
+ }
200
+ else {
201
+ this.logger.debug('群昵称设置', `数据库中的B站名称已是最新,无需更新`);
202
+ }
203
+ // 跳过群昵称修改
204
+ this.logger.info('群昵称设置', `群昵称格式正确且名称最新,跳过修改`);
205
+ return;
206
+ }
207
+ // 情况B: API返回 == 数据库(都是旧的)
208
+ if (latestBuidUser.username === buidUsername) {
209
+ this.logger.warn('群昵称设置', `⚠️ API返回的B站名称"${latestBuidUser.username}"与数据库一致,但与群昵称不符`);
210
+ this.logger.warn('群昵称设置', `可能是API缓存未刷新,采用保守策略:不修改群昵称`);
211
+ return;
212
+ }
213
+ // 情况C: API返回新数据(!= 群昵称 且 != 数据库)
214
+ this.logger.info('群昵称设置', `检测到B站用户名已更新: "${buidUsername}" → "${latestBuidUser.username}"`);
215
+ currentBuidUsername = latestBuidUser.username;
216
+ // 更新数据库中的B站信息
217
+ try {
218
+ await this.updateBuidInfoOnly(normalizedUserId, latestBuidUser);
219
+ this.logger.info('群昵称设置', `已更新数据库中的B站用户名为: ${currentBuidUsername}`);
220
+ }
221
+ catch (updateError) {
222
+ this.logger.warn('群昵称设置', `更新数据库中的B站用户名失败: ${updateError.message}`);
223
+ // 即使更新数据库失败,也继续使用最新的用户名设置昵称
224
+ }
225
+ }
226
+ else {
227
+ this.logger.warn('群昵称设置', `获取最新B站用户信息失败,使用数据库中的用户名: ${currentBuidUsername}`);
228
+ }
229
+ }
230
+ catch (validateError) {
231
+ this.logger.warn('群昵称设置', `获取最新B站用户信息时出错: ${validateError.message},使用数据库中的用户名: ${currentBuidUsername}`);
232
+ // API调用失败时降级处理,使用数据库中的旧名称
233
+ }
234
+ }
235
+ // 使用(可能已更新的)用户名重新生成昵称
236
+ const finalNickname = `${currentBuidUsername}(ID:${mcInfo})`;
167
237
  // 昵称不一致,执行修改
168
- this.logger.debug('群昵称设置', `昵称不一致,正在修改群昵称...`);
169
- await session.bot.internal.setGroupCard(targetGroupId, normalizedUserId, newNickname);
170
- this.logger.info('群昵称设置', `成功在群${targetGroupId}中将QQ(${normalizedUserId})群昵称从"${currentNickname}"修改为"${newNickname}"`, true);
238
+ this.logger.debug('群昵称设置', `昵称不一致,正在修改群昵称为: "${finalNickname}"`);
239
+ await session.bot.internal.setGroupCard(targetGroupId, normalizedUserId, finalNickname);
240
+ this.logger.info('群昵称设置', `成功在群${targetGroupId}中将QQ(${normalizedUserId})群昵称从"${currentNickname}"修改为"${finalNickname}"`, true);
171
241
  // 验证设置是否生效 - 再次获取群昵称确认
172
242
  try {
173
243
  await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
174
244
  const verifyGroupInfo = await session.bot.internal.getGroupMemberInfo(targetGroupId, normalizedUserId);
175
245
  const verifyNickname = verifyGroupInfo.card || verifyGroupInfo.nickname || '';
176
- if (verifyNickname === newNickname) {
246
+ if (verifyNickname === finalNickname) {
177
247
  this.logger.info('群昵称设置', `✅ 验证成功,群昵称已生效: "${verifyNickname}"`, true);
178
248
  }
179
249
  else {
180
- this.logger.warn('群昵称设置', `⚠️ 验证失败,期望"${newNickname}",实际"${verifyNickname}",可能是权限不足或API延迟`);
250
+ this.logger.warn('群昵称设置', `⚠️ 验证失败,期望"${finalNickname}",实际"${verifyNickname}",可能是权限不足或API延迟`);
181
251
  }
182
252
  }
183
253
  catch (verifyError) {
@@ -189,19 +259,54 @@ class MessageUtils {
189
259
  this.logger.warn('群昵称设置', `获取QQ(${normalizedUserId})当前群昵称失败: ${getInfoError.message}`);
190
260
  this.logger.warn('群昵称设置', `错误详情: ${JSON.stringify(getInfoError)}`);
191
261
  this.logger.debug('群昵称设置', `将直接尝试设置新昵称...`);
262
+ // 如果传入了 buidUid,尝试获取最新的B站用户信息
263
+ if (buidUid && this.validateBUID && this.updateBuidInfoOnly) {
264
+ this.logger.debug('群昵称设置', `尝试获取B站UID ${buidUid} 的最新信息...`);
265
+ try {
266
+ const latestBuidUser = await this.validateBUID(buidUid);
267
+ if (latestBuidUser && latestBuidUser.username) {
268
+ this.logger.debug('群昵称设置', `API返回B站用户名: "${latestBuidUser.username}"`);
269
+ // 智能判断:API返回 == 数据库(都是旧的)
270
+ if (latestBuidUser.username === buidUsername) {
271
+ this.logger.warn('群昵称设置', `⚠️ API返回的B站名称"${latestBuidUser.username}"与数据库一致`);
272
+ this.logger.warn('群昵称设置', `可能是API缓存未刷新,且无法获取当前群昵称,采用保守策略:跳过修改`);
273
+ return;
274
+ }
275
+ // API返回新数据(!= 数据库)
276
+ this.logger.info('群昵称设置', `检测到B站用户名已更新: "${buidUsername}" → "${latestBuidUser.username}"`);
277
+ currentBuidUsername = latestBuidUser.username;
278
+ // 更新数据库
279
+ try {
280
+ await this.updateBuidInfoOnly(normalizedUserId, latestBuidUser);
281
+ this.logger.info('群昵称设置', `已更新数据库中的B站用户名为: ${currentBuidUsername}`);
282
+ }
283
+ catch (updateError) {
284
+ this.logger.warn('群昵称设置', `更新数据库失败: ${updateError.message}`);
285
+ }
286
+ }
287
+ else {
288
+ this.logger.warn('群昵称设置', `获取最新B站用户信息失败`);
289
+ }
290
+ }
291
+ catch (validateError) {
292
+ this.logger.warn('群昵称设置', `获取最新B站用户信息失败: ${validateError.message}`);
293
+ }
294
+ }
192
295
  try {
193
- await session.bot.internal.setGroupCard(targetGroupId, normalizedUserId, newNickname);
194
- this.logger.info('群昵称设置', `成功在群${targetGroupId}中将QQ(${normalizedUserId})群昵称设置为: ${newNickname}`, true);
296
+ // 使用(可能已更新的)用户名生成昵称
297
+ const nicknameToSet = `${currentBuidUsername}(ID:${mcInfo})`;
298
+ await session.bot.internal.setGroupCard(targetGroupId, normalizedUserId, nicknameToSet);
299
+ this.logger.info('群昵称设置', `成功在群${targetGroupId}中将QQ(${normalizedUserId})群昵称设置为: ${nicknameToSet}`, true);
195
300
  // 验证设置是否生效
196
301
  try {
197
302
  await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
198
303
  const verifyGroupInfo = await session.bot.internal.getGroupMemberInfo(targetGroupId, normalizedUserId);
199
304
  const verifyNickname = verifyGroupInfo.card || verifyGroupInfo.nickname || '';
200
- if (verifyNickname === newNickname) {
305
+ if (verifyNickname === nicknameToSet) {
201
306
  this.logger.info('群昵称设置', `✅ 验证成功,群昵称已生效: "${verifyNickname}"`, true);
202
307
  }
203
308
  else {
204
- this.logger.warn('群昵称设置', `⚠️ 验证失败,期望"${newNickname}",实际"${verifyNickname}",可能是权限不足`);
309
+ this.logger.warn('群昵称设置', `⚠️ 验证失败,期望"${nicknameToSet}",实际"${verifyNickname}",可能是权限不足`);
205
310
  this.logger.warn('群昵称设置', `建议检查: 1.机器人是否为群管理员 2.群设置是否允许管理员修改昵称 3.OneBot实现是否支持该功能`);
206
311
  }
207
312
  }
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.0.4",
4
+ "version": "2.0.5",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [