koishi-plugin-bind-bot 2.2.8 → 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() : '',
@@ -8,6 +8,34 @@ import { LoggerService } from '../utils/logger';
8
8
  export declare class BindingHandler extends BaseHandler {
9
9
  private readonly BINDING_SESSION_TIMEOUT;
10
10
  constructor(ctx: Context, config: any, logger: LoggerService, repos: Repositories, deps: HandlerDependencies);
11
+ /**
12
+ * 格式化绑定信息
13
+ * @param bind 绑定记录
14
+ * @param prefix 前缀文本
15
+ * @returns 格式化后的绑定信息
16
+ */
17
+ private formatBindInfo;
18
+ /**
19
+ * 处理已完成全部绑定的场景
20
+ */
21
+ private handleCompletedBinds;
22
+ /**
23
+ * 处理只绑定MC未绑定B站的场景
24
+ */
25
+ private handleMcOnlyBind;
26
+ /**
27
+ * 处理只绑定B站未绑定MC的场景
28
+ */
29
+ private handleBuidOnlyBind;
30
+ /**
31
+ * 处理都未绑定的场景
32
+ */
33
+ private handleNoBind;
34
+ /**
35
+ * 处理绑定流程
36
+ * @param ctx 绑定流程上下文
37
+ */
38
+ private handleBindingFlow;
11
39
  /**
12
40
  * 注册交互式绑定命令
13
41
  */
@@ -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站双重绑定
@@ -15,6 +16,156 @@ class BindingHandler extends base_handler_1.BaseHandler {
15
16
  // 从配置中获取会话超时时间,默认3分钟
16
17
  this.BINDING_SESSION_TIMEOUT = 3 * 60 * 1000;
17
18
  }
19
+ /**
20
+ * 格式化绑定信息
21
+ * @param bind 绑定记录
22
+ * @param prefix 前缀文本
23
+ * @returns 格式化后的绑定信息
24
+ */
25
+ formatBindInfo(bind, prefix = '') {
26
+ const displayUsername = bind_status_1.BindStatus.getDisplayMcUsername(bind, '未绑定');
27
+ let bindInfo = `${prefix}\n✅ MC账号: ${displayUsername}\n✅ B站账号: ${bind.buidUsername} (UID: ${bind.buidUid})`;
28
+ if (bind.guardLevel > 0) {
29
+ bindInfo += `\n舰长等级: ${(0, helpers_1.getGuardLevelText)(bind.guardLevel)}`;
30
+ }
31
+ if (bind.medalName) {
32
+ bindInfo += `\n粉丝牌: ${bind.medalName} Lv.${bind.medalLevel}`;
33
+ }
34
+ return bindInfo;
35
+ }
36
+ /**
37
+ * 处理已完成全部绑定的场景
38
+ */
39
+ async handleCompletedBinds(ctx, bind) {
40
+ const { session, userId, isAdminAction } = ctx;
41
+ this.logger.info('交互绑定', `QQ(${userId})已完成全部绑定`, true);
42
+ const prefix = isAdminAction
43
+ ? `用户 ${userId} 已完成全部账号绑定:`
44
+ : '您已完成全部账号绑定:';
45
+ let bindInfo = this.formatBindInfo(bind, prefix);
46
+ // 用户自己绑定时显示修改提示
47
+ if (!isAdminAction) {
48
+ bindInfo += `\n\n如需修改绑定信息,请使用:\n- ${this.deps.formatCommand('mcid change <新用户名>')} 修改MC账号\n- ${this.deps.formatCommand('buid bind <新UID>')} 修改B站账号`;
49
+ }
50
+ await this.deps.sendMessage(session, [koishi_1.h.text(bindInfo)]);
51
+ }
52
+ /**
53
+ * 处理只绑定MC未绑定B站的场景
54
+ */
55
+ async handleMcOnlyBind(ctx, bind) {
56
+ const { session, userId, rawUserId, channelId, isAdminAction } = ctx;
57
+ this.logger.info('交互绑定', `QQ(${userId})已绑定MC,进入B站绑定流程`, true);
58
+ const displayUsername = bind_status_1.BindStatus.getDisplayMcUsername(bind, '未绑定');
59
+ if (isAdminAction) {
60
+ // 管理员操作:更新会话状态并@目标用户
61
+ this.deps.updateBindingSession(rawUserId, channelId, {
62
+ state: 'waiting_buid',
63
+ mcUsername: bind.mcUsername,
64
+ mcUuid: bind.mcUuid
65
+ });
66
+ await this.deps.sendMessage(session, [
67
+ koishi_1.h.at(userId),
68
+ koishi_1.h.text(` 管理员为您启动了B站绑定流程\n🎮 已绑定MC: ${displayUsername}\n🔗 请发送您的B站UID`)
69
+ ]);
70
+ }
71
+ else {
72
+ // 用户自己操作:创建带超时的会话
73
+ const timeout = setTimeout(() => {
74
+ this.deps.bindingSessions.delete(`${userId}_${channelId}`);
75
+ this.ctx.bots.forEach(bot => {
76
+ bot
77
+ .sendMessage(channelId, [
78
+ koishi_1.h.at(userId),
79
+ koishi_1.h.text(' 绑定会话已超时,请重新开始绑定流程\n\n⚠️ 温馨提醒:若在管理员多次提醒后仍不配合绑定账号信息,将按群规进行相应处理。')
80
+ ])
81
+ .catch(() => { });
82
+ });
83
+ this.logger.info('交互绑定', `QQ(${userId})的绑定会话因超时被清理`, true);
84
+ }, this.BINDING_SESSION_TIMEOUT);
85
+ const sessionData = {
86
+ userId: rawUserId,
87
+ channelId: channelId,
88
+ state: 'waiting_buid',
89
+ startTime: Date.now(),
90
+ timeout: timeout,
91
+ mcUsername: bind.mcUsername,
92
+ mcUuid: bind.mcUuid
93
+ };
94
+ this.deps.bindingSessions.set(`${userId}_${channelId}`, sessionData);
95
+ await this.deps.sendMessage(session, [
96
+ koishi_1.h.text(`🎮 已绑定MC: ${displayUsername}\n🔗 请发送您的B站UID`)
97
+ ]);
98
+ }
99
+ }
100
+ /**
101
+ * 处理只绑定B站未绑定MC的场景
102
+ */
103
+ async handleBuidOnlyBind(ctx, bind) {
104
+ const { session, userId, rawUserId, channelId, isAdminAction } = ctx;
105
+ this.logger.info('交互绑定', `QQ(${userId})${isAdminAction ? '已' : '只'}绑定${isAdminAction ? 'B站' : '了B站'},进入MC绑定流程`, true);
106
+ if (isAdminAction) {
107
+ // 管理员操作:更新会话状态并@目标用户
108
+ this.deps.updateBindingSession(rawUserId, channelId, {
109
+ state: 'waiting_mc_username',
110
+ buidUid: bind.buidUid,
111
+ buidUsername: bind.buidUsername
112
+ });
113
+ await this.deps.sendMessage(session, [
114
+ koishi_1.h.at(userId),
115
+ koishi_1.h.text(` 管理员为您启动了MC绑定流程\n✅ 已绑定B站: ${bind.buidUsername}\n🎮 请发送您的MC用户名,或发送"跳过"保持当前状态`)
116
+ ]);
117
+ }
118
+ else {
119
+ // 用户自己操作:创建会话并更新状态
120
+ this.deps.createBindingSession(rawUserId, channelId, 'waiting_mc_username');
121
+ const bindingSession = this.deps.getBindingSession(rawUserId, channelId);
122
+ bindingSession.state = 'waiting_mc_username';
123
+ await this.deps.sendMessage(session, [
124
+ koishi_1.h.text(`✅ 已绑定B站: ${bind.buidUsername}\n🎮 请发送您的MC用户名,或发送"跳过"保持当前状态`)
125
+ ]);
126
+ }
127
+ }
128
+ /**
129
+ * 处理都未绑定的场景
130
+ */
131
+ async handleNoBind(ctx) {
132
+ const { session, userId, isAdminAction } = ctx;
133
+ const messageElements = isAdminAction
134
+ ? [
135
+ koishi_1.h.at(userId),
136
+ koishi_1.h.text(' 管理员为您启动了账号绑定流程\n📋 请选择绑定方式:\n1. 发送您的B站UID进行B站绑定\n2. 发送"跳过"仅绑定MC账号')
137
+ ]
138
+ : [koishi_1.h.text('📋 请选择绑定方式:\n1. 发送您的B站UID进行B站绑定\n2. 发送"跳过"仅绑定MC账号')];
139
+ await this.deps.sendMessage(session, messageElements);
140
+ }
141
+ /**
142
+ * 处理绑定流程
143
+ * @param ctx 绑定流程上下文
144
+ */
145
+ async handleBindingFlow(ctx) {
146
+ const { userId, rawUserId, channelId, bind, isAdminAction } = ctx;
147
+ // 场景1: 已完成全部绑定
148
+ if (bind_status_1.BindStatus.hasCompletedAllBinds(bind)) {
149
+ return this.handleCompletedBinds(ctx, bind);
150
+ }
151
+ // 创建绑定会话(用户自己绑定时在后续场景中创建)
152
+ if (isAdminAction) {
153
+ this.deps.createBindingSession(rawUserId, channelId, 'waiting_buid');
154
+ }
155
+ // 场景2: 只绑定MC → 进入B站绑定流程
156
+ if (bind_status_1.BindStatus.hasOnlyMcBind(bind)) {
157
+ return this.handleMcOnlyBind(ctx, bind);
158
+ }
159
+ // 场景3: 只绑定B站 → 进入MC绑定流程
160
+ if (bind_status_1.BindStatus.hasOnlyBuidBind(bind)) {
161
+ return this.handleBuidOnlyBind(ctx, bind);
162
+ }
163
+ // 场景4: 都未绑定 → 显示选择菜单
164
+ if (!isAdminAction) {
165
+ this.deps.createBindingSession(rawUserId, channelId, 'waiting_buid');
166
+ }
167
+ return this.handleNoBind(ctx);
168
+ }
18
169
  /**
19
170
  * 注册交互式绑定命令
20
171
  */
@@ -58,48 +209,16 @@ class BindingHandler extends base_handler_1.BaseHandler {
58
209
  koishi_1.h.text(`用户 ${normalizedTargetId} 已有进行中的绑定会话`)
59
210
  ]);
60
211
  }
61
- // 检查目标用户当前绑定状态
212
+ // 检查目标用户当前绑定状态并处理绑定流程
62
213
  const targetBind = await this.deps.databaseService.getMcBindByQQId(normalizedTargetId);
63
- // 如果两个账号都已绑定,不需要进入绑定流程
64
- if (bind_status_1.BindStatus.hasCompletedAllBinds(targetBind)) {
65
- this.logger.info('交互绑定', `QQ(${normalizedTargetId})已完成全部绑定`, true);
66
- // 显示当前绑定信息
67
- const displayUsername = bind_status_1.BindStatus.getDisplayMcUsername(targetBind, '未绑定');
68
- let bindInfo = `用户 ${normalizedTargetId} 已完成全部账号绑定:\n✅ MC账号: ${displayUsername}\n✅ B站账号: ${targetBind.buidUsername} (UID: ${targetBind.buidUid})`;
69
- if (targetBind.guardLevel > 0) {
70
- bindInfo += `\n舰长等级: ${targetBind.guardLevelText}`;
71
- }
72
- if (targetBind.medalName) {
73
- bindInfo += `\n粉丝牌: ${targetBind.medalName} Lv.${targetBind.medalLevel}`;
74
- }
75
- return this.deps.sendMessage(session, [koishi_1.h.text(bindInfo)]);
76
- }
77
- // 为目标用户创建绑定会话
78
- this.deps.createBindingSession(target, channelId, 'waiting_buid');
79
- // 如果已绑定MC但未绑定B站,直接进入B站绑定流程
80
- if (bind_status_1.BindStatus.hasValidMcBind(targetBind) && !bind_status_1.BindStatus.hasValidBuidBind(targetBind)) {
81
- this.logger.info('交互绑定', `QQ(${normalizedTargetId})已绑定MC,进入B站绑定流程`, true);
82
- // 更新会话状态
83
- this.deps.updateBindingSession(target, channelId, {
84
- state: 'waiting_buid',
85
- mcUsername: bind_status_1.BindStatus.hasValidMcBind(targetBind)
86
- ? targetBind.mcUsername
87
- : null,
88
- mcUuid: targetBind.mcUuid
89
- });
90
- // 向目标用户发送提示(@他们)
91
- const displayUsername = bind_status_1.BindStatus.getDisplayMcUsername(targetBind, '未绑定');
92
- await this.deps.sendMessage(session, [
93
- koishi_1.h.at(normalizedTargetId),
94
- koishi_1.h.text(` 管理员为您启动了B站绑定流程\n🎮 已绑定MC: ${displayUsername}\n🔗 请发送您的B站UID`)
95
- ]);
96
- return;
97
- }
98
- // 向目标用户发送提示(@他们)
99
- await this.deps.sendMessage(session, [
100
- koishi_1.h.at(normalizedTargetId),
101
- koishi_1.h.text(' 管理员为您启动了账号绑定流程\n📋 请选择绑定方式:\n1. 发送您的B站UID进行B站绑定\n2. 发送"跳过"仅绑定MC账号')
102
- ]);
214
+ await this.handleBindingFlow({
215
+ session,
216
+ userId: normalizedTargetId,
217
+ rawUserId: target,
218
+ channelId,
219
+ bind: targetBind,
220
+ isAdminAction: true
221
+ });
103
222
  return;
104
223
  }
105
224
  // 为自己启动绑定流程
@@ -112,73 +231,16 @@ class BindingHandler extends base_handler_1.BaseHandler {
112
231
  koishi_1.h.text('您已有进行中的绑定会话,请先完成当前绑定或等待会话超时')
113
232
  ]);
114
233
  }
115
- // 检查用户当前绑定状态
234
+ // 检查用户当前绑定状态并处理绑定流程
116
235
  const existingBind = await this.deps.databaseService.getMcBindByQQId(normalizedUserId);
117
- // 如果两个账号都已绑定(且MC不是temp用户名),不需要进入绑定流程
118
- if (existingBind && bind_status_1.BindStatus.hasValidMcBind(existingBind) && existingBind.buidUid) {
119
- this.logger.info('交互绑定', `QQ(${normalizedUserId})已完成全部绑定`, true);
120
- // 显示当前绑定信息
121
- const displayUsername = existingBind.mcUsername;
122
- let bindInfo = `您已完成全部账号绑定:\n✅ MC账号: ${displayUsername}\n✅ B站账号: ${existingBind.buidUsername} (UID: ${existingBind.buidUid})`;
123
- if (existingBind.guardLevel > 0) {
124
- bindInfo += `\n舰长等级: ${existingBind.guardLevelText}`;
125
- }
126
- if (existingBind.medalName) {
127
- bindInfo += `\n粉丝牌: ${existingBind.medalName} Lv.${existingBind.medalLevel}`;
128
- }
129
- bindInfo += `\n\n如需修改绑定信息,请使用:\n- ${this.deps.formatCommand('mcid change <新用户名>')} 修改MC账号\n- ${this.deps.formatCommand('buid bind <新UID>')} 修改B站账号`;
130
- return this.deps.sendMessage(session, [koishi_1.h.text(bindInfo)]);
131
- }
132
- // 如果已绑定MC(且不是temp用户名)但未绑定B站,直接进入B站绑定流程
133
- if (existingBind && bind_status_1.BindStatus.hasValidMcBind(existingBind) && !existingBind.buidUid) {
134
- this.logger.info('交互绑定', `QQ(${normalizedUserId})已绑定MC,进入B站绑定流程`, true);
135
- // 创建绑定会话,状态直接设为等待B站UID
136
- const timeout = setTimeout(() => {
137
- this.deps.bindingSessions.delete(`${normalizedUserId}_${channelId}`);
138
- this.ctx.bots.forEach(bot => {
139
- bot
140
- .sendMessage(channelId, [
141
- koishi_1.h.at(normalizedUserId),
142
- koishi_1.h.text(' 绑定会话已超时,请重新开始绑定流程\n\n⚠️ 温馨提醒:若在管理员多次提醒后仍不配合绑定账号信息,将按群规进行相应处理。')
143
- ])
144
- .catch(() => { });
145
- });
146
- this.logger.info('交互绑定', `QQ(${normalizedUserId})的绑定会话因超时被清理`, true);
147
- }, this.BINDING_SESSION_TIMEOUT);
148
- const sessionData = {
149
- userId: session.userId,
150
- channelId: channelId,
151
- state: 'waiting_buid',
152
- startTime: Date.now(),
153
- timeout: timeout,
154
- mcUsername: existingBind.mcUsername,
155
- mcUuid: existingBind.mcUuid
156
- };
157
- this.deps.bindingSessions.set(`${normalizedUserId}_${channelId}`, sessionData);
158
- return this.deps.sendMessage(session, [
159
- koishi_1.h.text(`🎮 已绑定MC: ${existingBind.mcUsername}\n🔗 请发送您的B站UID`)
160
- ]);
161
- }
162
- // 如果只绑定了B站(MC是temp用户名),提醒绑定MC账号
163
- if (existingBind &&
164
- existingBind.buidUid &&
165
- existingBind.buidUsername &&
166
- !bind_status_1.BindStatus.hasValidMcBind(existingBind)) {
167
- this.logger.info('交互绑定', `QQ(${normalizedUserId})只绑定了B站,进入MC绑定流程`, true);
168
- // 创建绑定会话,状态设为等待MC用户名
169
- this.deps.createBindingSession(session.userId, channelId, 'waiting_mc_username');
170
- const bindingSession = this.deps.getBindingSession(session.userId, channelId);
171
- bindingSession.state = 'waiting_mc_username';
172
- return this.deps.sendMessage(session, [
173
- koishi_1.h.text(`✅ 已绑定B站: ${existingBind.buidUsername}\n🎮 请发送您的MC用户名,或发送"跳过"保持当前状态`)
174
- ]);
175
- }
176
- // 如果未绑定账号,让用户选择绑定方式,优先B站绑定
177
- this.deps.createBindingSession(session.userId, channelId, 'waiting_buid');
178
- // 发送绑定选项提示
179
- return this.deps.sendMessage(session, [
180
- koishi_1.h.text('📋 请选择绑定方式:\n1. 发送您的B站UID进行B站绑定\n2. 发送"跳过"仅绑定MC账号')
181
- ]);
236
+ await this.handleBindingFlow({
237
+ session,
238
+ userId: normalizedUserId,
239
+ rawUserId: session.userId,
240
+ channelId,
241
+ bind: existingBind,
242
+ isAdminAction: false
243
+ });
182
244
  }
183
245
  catch (error) {
184
246
  const normalizedUserId = this.deps.normalizeQQId(session.userId);
@@ -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,
@@ -87,9 +87,14 @@ export declare class GroupRequestReviewHandler extends BaseHandler {
87
87
  */
88
88
  private notifyAdmin;
89
89
  /**
90
- * 执行自动绑定
90
+ * 执行自动绑定(仅数据库操作,不设置昵称)
91
+ * @returns 绑定信息,包含B站用户名和MC用户名
91
92
  */
92
93
  private performAutoBind;
94
+ /**
95
+ * 用户进群后设置群昵称
96
+ */
97
+ private setGroupNicknameAfterJoin;
93
98
  /**
94
99
  * 定时清理过期记录
95
100
  */
@@ -292,7 +292,7 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
292
292
  else {
293
293
  elements.push(koishi_1.h.text(`⚠️ 未提供有效的 B 站 UID\n\n`));
294
294
  }
295
- elements.push(koishi_1.h.text('请点击表情回应:\n'), koishi_1.h.text('👍 - 通过并自动绑定\n'), koishi_1.h.text('😊 - 通过并交互式绑定\n'), koishi_1.h.text(' - 拒绝申请'));
295
+ elements.push(koishi_1.h.text('请点击表情回应:\n'), koishi_1.h.text('/赞 - 通过并自动绑定\n'), koishi_1.h.text('/OK - 通过并交互式绑定\n'), koishi_1.h.text('/NO - 拒绝申请'));
296
296
  try {
297
297
  const result = await session.bot.sendMessage(this.reviewConfig.reviewGroupId, elements);
298
298
  // result 通常是数组,第一个元素是消息ID
@@ -366,21 +366,23 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
366
366
  return;
367
367
  }
368
368
  this.logger.info('入群审批', `开始自动绑定 - QQ: ${pendingReq.applicantQQ}, UID: ${uid}`);
369
- // 2. 先执行绑定(在批准入群前)
370
- await this.performAutoBind(pendingReq.applicantQQ, uid, session.bot);
369
+ // 2. 先执行绑定(在批准入群前,仅数据库操作,不设置昵称)
370
+ const bindResult = await this.performAutoBind(pendingReq.applicantQQ, uid, session.bot);
371
371
  this.logger.info('入群审批', `预绑定完成 - QQ: ${pendingReq.applicantQQ}, UID: ${uid}`);
372
372
  // 3. 批准入群
373
373
  await session.bot.handleGuildMemberRequest(pendingReq.requestFlag, true, '欢迎加入!');
374
374
  this.logger.info('入群审批', `已批准入群 - QQ: ${pendingReq.applicantQQ}`, true);
375
375
  // 4. 等待用户进群
376
376
  const joined = await this.waitForUserJoin(pendingReq.applicantQQ, pendingReq.targetGroupId, 10000);
377
- if (!joined) {
378
- await this.notifyAdmin(operatorId, session, `⚠️ 已完成绑定并批准 ${pendingReq.applicantQQ} 入群,但用户未在10秒内进群`);
379
- pendingReq.status = 'approved';
380
- return;
377
+ if (joined) {
378
+ // 5. 用户进群后再设置群昵称
379
+ await this.setGroupNicknameAfterJoin(pendingReq.applicantQQ, bindResult.buidUsername, bindResult.mcUsername, session.bot);
380
+ // 6. 通知管理员
381
+ await this.notifyAdmin(operatorId, session, `✅ 已批准 ${pendingReq.applicantQQ} 入群并完成自动绑定\nUID: ${uid}`);
382
+ }
383
+ else {
384
+ await this.notifyAdmin(operatorId, session, `⚠️ 已完成绑定并批准 ${pendingReq.applicantQQ} 入群,但用户未在10秒内进群\n昵称将在用户进群后由系统自动设置`);
381
385
  }
382
- // 5. 通知管理员
383
- await this.notifyAdmin(operatorId, session, `✅ 已批准 ${pendingReq.applicantQQ} 入群并完成自动绑定\nUID: ${uid}`);
384
386
  pendingReq.status = 'approved';
385
387
  }
386
388
  catch (error) {
@@ -605,86 +607,86 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
605
607
  }
606
608
  }
607
609
  /**
608
- * 执行自动绑定
610
+ * 执行自动绑定(仅数据库操作,不设置昵称)
611
+ * @returns 绑定信息,包含B站用户名和MC用户名
609
612
  */
610
613
  async performAutoBind(qq, uid, bot) {
614
+ // 1. 使用强制绑定模式获取最新用户信息(避免频率限制)
615
+ this.logger.debug('入群审批', `开始获取 B站 UID ${uid} 的信息`);
616
+ // 使用强制绑定模式获取完整信息(与 bind -f 保持一致)
617
+ this.logger.debug('入群审批', '正在使用强制绑定模式获取B站用户信息...');
618
+ const enhancedUser = await this.deps.forceBinder.forceBindUser(uid);
619
+ const zminfoUser = this.deps.forceBinder.convertToZminfoUser(enhancedUser);
620
+ if (!zminfoUser) {
621
+ throw new Error(`无法验证B站UID: ${uid}`);
622
+ }
623
+ this.logger.info('入群审批', `✅ 获取到用户名: "${zminfoUser.username}"`, true);
624
+ // 2. 检查是否已被其他人绑定
625
+ const existingBind = await this.repos.mcidbind.findByBuidUid(uid);
626
+ if (existingBind && existingBind.qqId !== qq) {
627
+ throw new Error(`UID ${uid} 已被其他用户绑定`);
628
+ }
629
+ // 3. 获取或创建绑定记录
630
+ let bind = await this.repos.mcidbind.findByQQId(qq);
631
+ let mcUsername = null;
632
+ if (!bind) {
633
+ // 创建新绑定(不使用临时MC用户名)
634
+ bind = await this.repos.mcidbind.create({
635
+ qqId: qq,
636
+ mcUsername: null,
637
+ mcUuid: null,
638
+ buidUid: zminfoUser.uid,
639
+ buidUsername: zminfoUser.username,
640
+ guardLevel: zminfoUser.guard_level || 0,
641
+ maxGuardLevel: zminfoUser.guard_level || 0,
642
+ medalName: zminfoUser.medal?.name || '',
643
+ medalLevel: zminfoUser.medal?.level || 0,
644
+ wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
645
+ lastActiveTime: new Date(),
646
+ lastModified: new Date(),
647
+ hasMcBind: false,
648
+ hasBuidBind: true
649
+ });
650
+ this.logger.info('入群审批', `已创建新绑定 - QQ: ${qq}, UID: ${uid}`, true);
651
+ }
652
+ else {
653
+ // 保存现有的MC用户名
654
+ mcUsername = bind_status_1.BindStatus.getDisplayMcUsername(bind, null);
655
+ // 更新现有绑定
656
+ await this.repos.mcidbind.update(qq, {
657
+ buidUid: zminfoUser.uid,
658
+ buidUsername: zminfoUser.username,
659
+ guardLevel: zminfoUser.guard_level || 0,
660
+ maxGuardLevel: Math.max(bind.maxGuardLevel || 0, zminfoUser.guard_level || 0),
661
+ medalName: zminfoUser.medal?.name || '',
662
+ medalLevel: zminfoUser.medal?.level || 0,
663
+ wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
664
+ lastActiveTime: new Date(),
665
+ lastModified: new Date(),
666
+ hasBuidBind: true
667
+ });
668
+ this.logger.info('入群审批', `已更新绑定 - QQ: ${qq}, UID: ${uid}`, true);
669
+ }
670
+ this.logger.info('入群审批', `自动绑定完成 - QQ: ${qq}, UID: ${uid}, 用户名: ${zminfoUser.username}`, true);
671
+ // 返回绑定信息供后续使用(昵称设置在用户进群后执行)
672
+ return {
673
+ buidUsername: zminfoUser.username,
674
+ mcUsername: mcUsername
675
+ };
676
+ }
677
+ /**
678
+ * 用户进群后设置群昵称
679
+ */
680
+ async setGroupNicknameAfterJoin(qq, buidUsername, mcUsername, bot) {
611
681
  try {
612
- // 1. 使用强制绑定模式获取最新用户信息(避免频率限制)
613
- this.logger.debug('入群审批', `开始获取 B站 UID ${uid} 的信息`);
614
- // 使用强制绑定模式获取完整信息(与 bind -f 保持一致)
615
- this.logger.debug('入群审批', '正在使用强制绑定模式获取B站用户信息...');
616
- const enhancedUser = await this.deps.forceBinder.forceBindUser(uid);
617
- const zminfoUser = this.deps.forceBinder.convertToZminfoUser(enhancedUser);
618
- if (!zminfoUser) {
619
- throw new Error(`无法验证B站UID: ${uid}`);
620
- }
621
- this.logger.info('入群审批', `✅ 获取到用户名: "${zminfoUser.username}"`, true);
622
- // 2. 检查是否已被其他人绑定
623
- const existingBind = await this.repos.mcidbind.findByBuidUid(uid);
624
- if (existingBind && existingBind.qqId !== qq) {
625
- throw new Error(`UID ${uid} 已被其他用户绑定`);
626
- }
627
- // 3. 获取或创建绑定记录
628
- let bind = await this.repos.mcidbind.findByQQId(qq);
629
- if (!bind) {
630
- // 创建新绑定(不使用临时MC用户名)
631
- bind = await this.repos.mcidbind.create({
632
- qqId: qq,
633
- mcUsername: null,
634
- mcUuid: null,
635
- buidUid: zminfoUser.uid,
636
- buidUsername: zminfoUser.username,
637
- guardLevel: zminfoUser.guard_level || 0,
638
- guardLevelText: zminfoUser.guard_level_text || '',
639
- maxGuardLevel: zminfoUser.guard_level || 0,
640
- maxGuardLevelText: zminfoUser.guard_level_text || '',
641
- medalName: zminfoUser.medal?.name || '',
642
- medalLevel: zminfoUser.medal?.level || 0,
643
- wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
644
- lastActiveTime: new Date(),
645
- lastModified: new Date(),
646
- hasMcBind: false,
647
- hasBuidBind: true
648
- });
649
- this.logger.info('入群审批', `已创建新绑定 - QQ: ${qq}, UID: ${uid}`, true);
650
- }
651
- else {
652
- // 更新现有绑定
653
- await this.repos.mcidbind.update(qq, {
654
- buidUid: zminfoUser.uid,
655
- buidUsername: zminfoUser.username,
656
- guardLevel: zminfoUser.guard_level || 0,
657
- guardLevelText: zminfoUser.guard_level_text || '',
658
- maxGuardLevel: Math.max(bind.maxGuardLevel || 0, zminfoUser.guard_level || 0),
659
- maxGuardLevelText: zminfoUser.guard_level > (bind.maxGuardLevel || 0)
660
- ? zminfoUser.guard_level_text
661
- : bind.maxGuardLevelText,
662
- medalName: zminfoUser.medal?.name || '',
663
- medalLevel: zminfoUser.medal?.level || 0,
664
- wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
665
- lastActiveTime: new Date(),
666
- lastModified: new Date(),
667
- hasBuidBind: true
668
- });
669
- this.logger.info('入群审批', `已更新绑定 - QQ: ${qq}, UID: ${uid}`, true);
670
- }
671
- // 4. 更新群昵称(使用标准格式)
672
- try {
673
- const groupId = this.reviewConfig.targetGroupId;
674
- const mcInfo = bind_status_1.BindStatus.getDisplayMcUsername(bind, '未绑定');
675
- const nickname = `${zminfoUser.username}(ID:${mcInfo})`;
676
- await bot.internal.setGroupCard(groupId, qq, nickname);
677
- this.logger.info('入群审批', `已更新群昵称 - QQ: ${qq}, 昵称: ${nickname}`);
678
- }
679
- catch (error) {
680
- this.logger.warn('入群审批', `更新群昵称失败: ${error.message}`);
681
- // 昵称更新失败不影响绑定
682
- }
683
- this.logger.info('入群审批', `自动绑定完成 - QQ: ${qq}, UID: ${uid}, 用户名: ${zminfoUser.username}`, true);
682
+ const groupId = this.reviewConfig.targetGroupId;
683
+ const mcInfo = mcUsername || '未绑定';
684
+ const nickname = `${buidUsername}(ID:${mcInfo})`;
685
+ await bot.internal.setGroupCard(groupId, qq, nickname);
686
+ this.logger.info('入群审批', `已更新群昵称 - QQ: ${qq}, 昵称: ${nickname}`);
684
687
  }
685
688
  catch (error) {
686
- this.logger.error('入群审批', `自动绑定失败: ${error.message}`, error);
687
- throw error;
689
+ this.logger.warn('入群审批', `更新群昵称失败: ${error.message}`);
688
690
  }
689
691
  }
690
692
  /**