koishi-plugin-bind-bot 2.0.0 → 2.0.3
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.
- package/LICENSE +35 -0
- package/lib/handlers/base.handler.d.ts +16 -1
- package/lib/handlers/mcid.handler.d.ts +3 -39
- package/lib/handlers/mcid.handler.js +147 -150
- package/lib/handlers/whitelist.handler.d.ts +0 -4
- package/lib/handlers/whitelist.handler.js +9 -34
- package/lib/index.js +126 -162
- package/lib/repositories/mcidbind.repository.d.ts +13 -1
- package/lib/repositories/mcidbind.repository.js +50 -1
- package/lib/types/database.d.ts +2 -0
- package/lib/utils/error-utils.d.ts +30 -0
- package/lib/utils/error-utils.js +111 -0
- package/lib/utils/helpers.d.ts +33 -0
- package/lib/utils/helpers.js +87 -0
- package/package.json +2 -2
|
@@ -2,17 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.McidCommandHandler = void 0;
|
|
4
4
|
const koishi_1 = require("koishi");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
deps;
|
|
8
|
-
constructor(ctx, deps) {
|
|
9
|
-
this.ctx = ctx;
|
|
10
|
-
this.deps = deps;
|
|
11
|
-
}
|
|
5
|
+
const base_handler_1 = require("./base.handler");
|
|
6
|
+
class McidCommandHandler extends base_handler_1.BaseHandler {
|
|
12
7
|
/**
|
|
13
8
|
* 注册所有MCID命令
|
|
14
9
|
*/
|
|
15
|
-
|
|
10
|
+
register() {
|
|
11
|
+
const cmd = this.ctx.command('mcid', 'MC账号绑定管理');
|
|
16
12
|
// mcid.query - 查询MC账号
|
|
17
13
|
cmd.subcommand('.query [target:string]', '查询用户绑定的MC账号')
|
|
18
14
|
.action(async ({ session }, target) => this.handleQuery(session, target));
|
|
@@ -60,16 +56,16 @@ class McidCommandHandler {
|
|
|
60
56
|
if (target) {
|
|
61
57
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
62
58
|
if (!normalizedTargetId) {
|
|
63
|
-
this.
|
|
59
|
+
this.logger.warn('查询', `QQ(${normalizedUserId})提供的目标用户ID"${target}"无效`);
|
|
64
60
|
if (target.startsWith('@')) {
|
|
65
61
|
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')]);
|
|
66
62
|
}
|
|
67
63
|
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')]);
|
|
68
64
|
}
|
|
69
|
-
this.
|
|
65
|
+
this.logger.info('查询', `QQ(${normalizedUserId})查询QQ(${normalizedTargetId})的MC账号信息`);
|
|
70
66
|
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
71
67
|
if (!targetBind || !targetBind.mcUsername || targetBind.mcUsername.startsWith('_temp_')) {
|
|
72
|
-
this.
|
|
68
|
+
this.logger.info('查询', `QQ(${normalizedTargetId})未绑定MC账号`);
|
|
73
69
|
// 检查是否绑定了B站
|
|
74
70
|
if (targetBind && targetBind.buidUid) {
|
|
75
71
|
const buidUser = await this.deps.validateBUID(targetBind.buidUid);
|
|
@@ -91,7 +87,7 @@ class McidCommandHandler {
|
|
|
91
87
|
buidInfo += `\n粉丝牌: ${refreshedBind.medalName} Lv.${refreshedBind.medalLevel}`;
|
|
92
88
|
}
|
|
93
89
|
const messageElements = [koishi_1.h.text(buidInfo)];
|
|
94
|
-
if (this.
|
|
90
|
+
if (this.config.showAvatar) {
|
|
95
91
|
messageElements.push(koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${refreshedBind.buidUid}?size=160`));
|
|
96
92
|
}
|
|
97
93
|
return this.deps.sendMessage(session, messageElements);
|
|
@@ -100,15 +96,15 @@ class McidCommandHandler {
|
|
|
100
96
|
}
|
|
101
97
|
return this.deps.sendMessage(session, [koishi_1.h.text('该用户尚未绑定MC账号')]);
|
|
102
98
|
}
|
|
103
|
-
// 显示MC
|
|
104
|
-
const updatedBind = await this.deps.
|
|
99
|
+
// 显示MC绑定信息(使用智能缓存检测,避免频繁API调用)
|
|
100
|
+
const updatedBind = await this.deps.checkAndUpdateUsernameWithCache(targetBind);
|
|
105
101
|
return this.buildQueryResponse(session, updatedBind, normalizedTargetId);
|
|
106
102
|
}
|
|
107
103
|
// 查询自己
|
|
108
|
-
this.
|
|
104
|
+
this.logger.info('查询', `QQ(${normalizedUserId})查询自己的MC账号信息`);
|
|
109
105
|
const selfBind = await this.deps.getMcBindByQQId(normalizedUserId);
|
|
110
106
|
if (!selfBind || !selfBind.mcUsername || selfBind.mcUsername.startsWith('_temp_')) {
|
|
111
|
-
this.
|
|
107
|
+
this.logger.info('查询', `QQ(${normalizedUserId})未绑定MC账号`);
|
|
112
108
|
// 检查是否绑定了B站
|
|
113
109
|
if (selfBind && selfBind.buidUid) {
|
|
114
110
|
const buidUser = await this.deps.validateBUID(selfBind.buidUid);
|
|
@@ -131,7 +127,7 @@ class McidCommandHandler {
|
|
|
131
127
|
}
|
|
132
128
|
buidInfo += `\n\n💡 您可以使用 ${this.deps.formatCommand('mcid bind <用户名>')} 绑定MC账号`;
|
|
133
129
|
const messageElements = [koishi_1.h.text(buidInfo)];
|
|
134
|
-
if (this.
|
|
130
|
+
if (this.config.showAvatar) {
|
|
135
131
|
messageElements.push(koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${refreshedBind.buidUid}?size=160`));
|
|
136
132
|
}
|
|
137
133
|
return this.deps.sendMessage(session, messageElements);
|
|
@@ -140,12 +136,13 @@ class McidCommandHandler {
|
|
|
140
136
|
}
|
|
141
137
|
return this.deps.sendMessage(session, [koishi_1.h.text('您尚未绑定MC账号,请使用 ' + this.deps.formatCommand('mcid bind <用户名>') + ' 进行绑定')]);
|
|
142
138
|
}
|
|
143
|
-
|
|
139
|
+
// 使用智能缓存检测,避免频繁API调用
|
|
140
|
+
const updatedBind = await this.deps.checkAndUpdateUsernameWithCache(selfBind);
|
|
144
141
|
return this.buildQueryResponse(session, updatedBind, null);
|
|
145
142
|
}
|
|
146
143
|
catch (error) {
|
|
147
144
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
148
|
-
this.
|
|
145
|
+
this.logger.error('查询', `QQ(${normalizedUserId})查询MC账号失败: ${error.message}`);
|
|
149
146
|
return this.deps.sendMessage(session, [koishi_1.h.text(`查询失败: ${error.message}`)]);
|
|
150
147
|
}
|
|
151
148
|
}
|
|
@@ -156,8 +153,8 @@ class McidCommandHandler {
|
|
|
156
153
|
const formattedUuid = this.deps.formatUuid(bind.mcUuid);
|
|
157
154
|
// MC头像
|
|
158
155
|
let mcAvatarUrl = null;
|
|
159
|
-
if (this.
|
|
160
|
-
if (this.
|
|
156
|
+
if (this.config.showAvatar) {
|
|
157
|
+
if (this.config.showMcSkin) {
|
|
161
158
|
mcAvatarUrl = this.deps.getStarlightSkinUrl(bind.mcUsername);
|
|
162
159
|
}
|
|
163
160
|
else {
|
|
@@ -171,7 +168,7 @@ class McidCommandHandler {
|
|
|
171
168
|
const serverList = bind.whitelist.map((serverId, index) => {
|
|
172
169
|
const server = this.deps.getServerConfigById(serverId);
|
|
173
170
|
if (!server) {
|
|
174
|
-
const disabledServer = this.
|
|
171
|
+
const disabledServer = this.config.servers?.find(s => s.id === serverId);
|
|
175
172
|
if (disabledServer && disabledServer.enabled === false) {
|
|
176
173
|
return `${index < circledNumbers.length ? circledNumbers[index] : (index + 1)} ${disabledServer.name} [已停用]`;
|
|
177
174
|
}
|
|
@@ -206,21 +203,21 @@ class McidCommandHandler {
|
|
|
206
203
|
if (bind.medalName) {
|
|
207
204
|
buidInfo += `\n粉丝牌: ${bind.medalName} Lv.${bind.medalLevel}`;
|
|
208
205
|
}
|
|
209
|
-
if (this.
|
|
206
|
+
if (this.config.showAvatar) {
|
|
210
207
|
buidAvatar = koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${bind.buidUid}?size=160`);
|
|
211
208
|
}
|
|
212
209
|
}
|
|
213
210
|
else {
|
|
214
211
|
buidInfo = targetId ? '该用户尚未绑定B站账号' : `您尚未绑定B站账号,使用 ${this.deps.formatCommand('buid bind <B站UID>')} 进行绑定`;
|
|
215
212
|
}
|
|
216
|
-
this.
|
|
213
|
+
this.logger.info('查询', `QQ(${bind.qqId})的MC账号信息:用户名=${bind.mcUsername}, UUID=${bind.mcUuid}`);
|
|
217
214
|
// 自动设置群昵称
|
|
218
215
|
if (bind.buidUid && bind.buidUsername) {
|
|
219
216
|
const mcName = bind.mcUsername && !bind.mcUsername.startsWith('_temp_') ? bind.mcUsername : null;
|
|
220
217
|
await this.deps.autoSetGroupNickname(session, mcName, bind.buidUsername, targetId || undefined);
|
|
221
218
|
}
|
|
222
219
|
else {
|
|
223
|
-
this.
|
|
220
|
+
this.logger.info('查询', `QQ(${bind.qqId})未绑定B站账号,跳过群昵称设置`);
|
|
224
221
|
}
|
|
225
222
|
const displayUsername = bind.mcUsername && !bind.mcUsername.startsWith('_temp_') ? bind.mcUsername : '未绑定';
|
|
226
223
|
const prefix = targetId ? `用户 ${targetId} 的` : '您的';
|
|
@@ -240,23 +237,23 @@ class McidCommandHandler {
|
|
|
240
237
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
241
238
|
// 检查权限
|
|
242
239
|
if (!await this.deps.isAdmin(session.userId)) {
|
|
243
|
-
this.
|
|
240
|
+
this.logger.warn('反向查询', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
244
241
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能使用此命令')]);
|
|
245
242
|
}
|
|
246
243
|
if (!username) {
|
|
247
|
-
this.
|
|
244
|
+
this.logger.warn('反向查询', `QQ(${normalizedUserId})未提供MC用户名`);
|
|
248
245
|
return this.deps.sendMessage(session, [koishi_1.h.text('请提供要查询的MC用户名')]);
|
|
249
246
|
}
|
|
250
|
-
this.
|
|
247
|
+
this.logger.info('反向查询', `QQ(${normalizedUserId})尝试通过MC用户名"${username}"查询绑定的QQ账号`);
|
|
251
248
|
const bind = await this.deps.getMcBindByUsername(username);
|
|
252
249
|
if (!bind || !bind.qqId) {
|
|
253
|
-
this.
|
|
250
|
+
this.logger.info('反向查询', `MC用户名"${username}"未被任何QQ账号绑定`);
|
|
254
251
|
return this.deps.sendMessage(session, [koishi_1.h.text(`未找到绑定MC用户名"${username}"的QQ账号`)]);
|
|
255
252
|
}
|
|
256
253
|
// MC头像
|
|
257
254
|
let mcAvatarUrl = null;
|
|
258
|
-
if (this.
|
|
259
|
-
if (this.
|
|
255
|
+
if (this.config.showAvatar) {
|
|
256
|
+
if (this.config.showMcSkin) {
|
|
260
257
|
mcAvatarUrl = this.deps.getStarlightSkinUrl(bind.mcUsername);
|
|
261
258
|
}
|
|
262
259
|
else {
|
|
@@ -280,7 +277,7 @@ class McidCommandHandler {
|
|
|
280
277
|
adminInfo += `\n绑定时间: ${bind.lastModified ? new Date(bind.lastModified).toLocaleString() : '未知'}`;
|
|
281
278
|
adminInfo += `\n管理员权限: ${bind.isAdmin ? '是' : '否'}`;
|
|
282
279
|
}
|
|
283
|
-
this.
|
|
280
|
+
this.logger.info('反向查询', `成功: MC用户名"${username}"被QQ(${bind.qqId})绑定`);
|
|
284
281
|
const displayUsername = bind.mcUsername && !bind.mcUsername.startsWith('_temp_') ? bind.mcUsername : '未绑定';
|
|
285
282
|
return this.deps.sendMessage(session, [
|
|
286
283
|
koishi_1.h.text(`MC用户名"${displayUsername}"绑定信息:\nQQ号: ${bind.qqId}\nUUID: ${formattedUuid}${adminInfo}`),
|
|
@@ -289,7 +286,7 @@ class McidCommandHandler {
|
|
|
289
286
|
}
|
|
290
287
|
catch (error) {
|
|
291
288
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
292
|
-
this.
|
|
289
|
+
this.logger.error('反向查询', `QQ(${normalizedUserId})通过MC用户名"${username}"查询失败: ${error.message}`);
|
|
293
290
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
294
291
|
}
|
|
295
292
|
}
|
|
@@ -301,13 +298,13 @@ class McidCommandHandler {
|
|
|
301
298
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
302
299
|
// 检查用户名格式
|
|
303
300
|
if (!username || !/^[a-zA-Z0-9_]{3,16}$/.test(username)) {
|
|
304
|
-
this.
|
|
301
|
+
this.logger.warn('绑定', `QQ(${normalizedUserId})提供的用户名"${username}"格式无效`);
|
|
305
302
|
return this.deps.sendMessage(session, [koishi_1.h.text('请提供有效的Minecraft用户名(3-16位字母、数字、下划线)')]);
|
|
306
303
|
}
|
|
307
304
|
// 验证用户名是否存在
|
|
308
305
|
const profile = await this.deps.validateUsername(username);
|
|
309
306
|
if (!profile) {
|
|
310
|
-
this.
|
|
307
|
+
this.logger.warn('绑定', `QQ(${normalizedUserId})提供的用户名"${username}"不存在`);
|
|
311
308
|
return this.deps.sendMessage(session, [koishi_1.h.text(`无法验证用户名: ${username},该用户可能不存在`)]);
|
|
312
309
|
}
|
|
313
310
|
username = profile.name;
|
|
@@ -321,61 +318,61 @@ class McidCommandHandler {
|
|
|
321
318
|
}
|
|
322
319
|
catch (error) {
|
|
323
320
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
324
|
-
this.
|
|
321
|
+
this.logger.error('绑定', `QQ(${normalizedUserId})绑定MC账号"${username}"失败: ${error.message}`);
|
|
325
322
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
326
323
|
}
|
|
327
324
|
}
|
|
328
325
|
async handleBindForOther(session, username, uuid, target, operatorId) {
|
|
329
326
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
330
327
|
if (!normalizedTargetId) {
|
|
331
|
-
this.
|
|
328
|
+
this.logger.warn('绑定', `QQ(${operatorId})提供的目标用户ID"${target}"无效`);
|
|
332
329
|
if (target.startsWith('@')) {
|
|
333
330
|
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')]);
|
|
334
331
|
}
|
|
335
332
|
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')]);
|
|
336
333
|
}
|
|
337
|
-
this.
|
|
334
|
+
this.logger.debug('绑定', `QQ(${operatorId})尝试为QQ(${normalizedTargetId})绑定MC账号: ${username}(${uuid})`);
|
|
338
335
|
// 检查权限
|
|
339
336
|
if (!await this.deps.isAdmin(session.userId)) {
|
|
340
|
-
this.
|
|
337
|
+
this.logger.warn('绑定', `权限不足: QQ(${operatorId})不是管理员`);
|
|
341
338
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能为其他用户绑定MC账号')]);
|
|
342
339
|
}
|
|
343
|
-
//
|
|
344
|
-
if (await this.deps.checkUsernameExists(username, target)) {
|
|
345
|
-
this.
|
|
340
|
+
// 检查用户名是否已被占用(支持改名检测)
|
|
341
|
+
if (await this.deps.checkUsernameExists(username, target, uuid)) {
|
|
342
|
+
this.logger.warn('绑定', `MC用户名"${username}"已被其他QQ号绑定`);
|
|
346
343
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户名 ${username} 已被其他用户绑定`)]);
|
|
347
344
|
}
|
|
348
345
|
// 绑定
|
|
349
346
|
const bindResult = await this.deps.createOrUpdateMcBind(target, username, uuid);
|
|
350
347
|
if (!bindResult) {
|
|
351
|
-
this.
|
|
348
|
+
this.logger.error('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})绑定MC账号"${username}"失败`);
|
|
352
349
|
return this.deps.sendMessage(session, [koishi_1.h.text(`为用户 ${normalizedTargetId} 绑定MC账号失败: 数据库操作出错,请联系管理员`)]);
|
|
353
350
|
}
|
|
354
|
-
this.
|
|
351
|
+
this.logger.info('绑定', `成功: 管理员QQ(${operatorId})为QQ(${normalizedTargetId})绑定MC账号: ${username}(${uuid})`);
|
|
355
352
|
// 清理绑定会话
|
|
356
353
|
this.deps.removeBindingSession(target, session.channelId);
|
|
357
|
-
this.
|
|
354
|
+
this.logger.info('绑定', `管理员为QQ(${normalizedTargetId})绑定MC账号后,已清理该用户的交互式绑定会话`);
|
|
358
355
|
// 尝试设置群昵称
|
|
359
356
|
let targetBuidStatus = '';
|
|
360
357
|
try {
|
|
361
358
|
const latestTargetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
362
359
|
if (latestTargetBind && latestTargetBind.buidUid && latestTargetBind.buidUsername) {
|
|
363
360
|
await this.deps.autoSetGroupNickname(session, username, latestTargetBind.buidUsername, normalizedTargetId);
|
|
364
|
-
this.
|
|
361
|
+
this.logger.info('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})MC绑定完成,已尝试设置群昵称`);
|
|
365
362
|
targetBuidStatus = '\n✅ 该用户已绑定B站账号,群昵称已更新';
|
|
366
363
|
}
|
|
367
364
|
else {
|
|
368
|
-
this.
|
|
365
|
+
this.logger.info('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})MC绑定完成,但目标用户未绑定B站账号`);
|
|
369
366
|
targetBuidStatus = '\n⚠️ 该用户尚未绑定B站账号,建议提醒其使用 buid bind 命令完成B站绑定';
|
|
370
367
|
}
|
|
371
368
|
}
|
|
372
369
|
catch (renameError) {
|
|
373
|
-
this.
|
|
370
|
+
this.logger.warn('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})MC绑定后群昵称设置失败: ${renameError.message}`);
|
|
374
371
|
}
|
|
375
372
|
// MC头像
|
|
376
373
|
let mcAvatarUrl = null;
|
|
377
|
-
if (this.
|
|
378
|
-
if (this.
|
|
374
|
+
if (this.config.showAvatar) {
|
|
375
|
+
if (this.config.showMcSkin) {
|
|
379
376
|
mcAvatarUrl = this.deps.getStarlightSkinUrl(username);
|
|
380
377
|
}
|
|
381
378
|
else {
|
|
@@ -389,7 +386,7 @@ class McidCommandHandler {
|
|
|
389
386
|
]);
|
|
390
387
|
}
|
|
391
388
|
async handleBindForSelf(session, username, uuid, operatorId) {
|
|
392
|
-
this.
|
|
389
|
+
this.logger.debug('绑定', `QQ(${operatorId})尝试绑定MC账号: ${username}(${uuid})`);
|
|
393
390
|
// 检查是否已绑定
|
|
394
391
|
const selfBind = await this.deps.getMcBindByQQId(operatorId);
|
|
395
392
|
if (selfBind && selfBind.mcUsername) {
|
|
@@ -397,55 +394,55 @@ class McidCommandHandler {
|
|
|
397
394
|
if (!isTempUsername) {
|
|
398
395
|
// 检查冷却时间
|
|
399
396
|
if (!await this.deps.isAdmin(session.userId) && !this.deps.checkCooldown(selfBind.lastModified)) {
|
|
400
|
-
const days = this.
|
|
397
|
+
const days = this.config.cooldownDays;
|
|
401
398
|
const now = new Date();
|
|
402
399
|
const diffTime = now.getTime() - selfBind.lastModified.getTime();
|
|
403
400
|
const passedDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
404
401
|
const remainingDays = days - passedDays;
|
|
405
|
-
this.
|
|
402
|
+
this.logger.warn('绑定', `QQ(${operatorId})已绑定MC账号"${selfBind.mcUsername}",且在冷却期内`);
|
|
406
403
|
const displayUsername = selfBind.mcUsername && !selfBind.mcUsername.startsWith('_temp_') ? selfBind.mcUsername : '未绑定';
|
|
407
404
|
return this.deps.sendMessage(session, [koishi_1.h.text(`您已绑定MC账号: ${displayUsername},如需修改,请在冷却期结束后(还需${remainingDays}天)使用 ` + this.deps.formatCommand('mcid change') + ` 命令或联系管理员。`)]);
|
|
408
405
|
}
|
|
409
|
-
this.
|
|
406
|
+
this.logger.debug('绑定', `QQ(${operatorId})已绑定MC账号"${selfBind.mcUsername}",建议使用change命令`);
|
|
410
407
|
const displayUsername = selfBind.mcUsername && !selfBind.mcUsername.startsWith('_temp_') ? selfBind.mcUsername : '未绑定';
|
|
411
408
|
return this.deps.sendMessage(session, [koishi_1.h.text(`您已绑定MC账号: ${displayUsername},如需修改请使用 ` + this.deps.formatCommand('mcid change') + ` 命令。`)]);
|
|
412
409
|
}
|
|
413
410
|
else {
|
|
414
|
-
this.
|
|
411
|
+
this.logger.debug('绑定', `QQ(${operatorId})之前绑定的是临时用户名"${selfBind.mcUsername}",允许直接使用bind命令`);
|
|
415
412
|
}
|
|
416
413
|
}
|
|
417
|
-
//
|
|
418
|
-
if (await this.deps.checkUsernameExists(username)) {
|
|
419
|
-
this.
|
|
414
|
+
// 检查用户名是否已被占用(支持改名检测)
|
|
415
|
+
if (await this.deps.checkUsernameExists(username, session.userId, uuid)) {
|
|
416
|
+
this.logger.warn('绑定', `MC用户名"${username}"已被其他QQ号绑定`);
|
|
420
417
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户名 ${username} 已被其他用户绑定`)]);
|
|
421
418
|
}
|
|
422
419
|
// 绑定
|
|
423
420
|
const bindResult = await this.deps.createOrUpdateMcBind(session.userId, username, uuid);
|
|
424
421
|
if (!bindResult) {
|
|
425
|
-
this.
|
|
422
|
+
this.logger.error('绑定', `QQ(${operatorId})绑定MC账号"${username}"失败`);
|
|
426
423
|
return this.deps.sendMessage(session, [koishi_1.h.text('绑定失败,数据库操作出错,请联系管理员')]);
|
|
427
424
|
}
|
|
428
|
-
this.
|
|
425
|
+
this.logger.info('绑定', `成功: QQ(${operatorId})绑定MC账号: ${username}(${uuid})`);
|
|
429
426
|
// 尝试设置群昵称
|
|
430
427
|
let buidReminder = '';
|
|
431
428
|
try {
|
|
432
429
|
const latestBind = await this.deps.getMcBindByQQId(operatorId);
|
|
433
430
|
if (latestBind && latestBind.buidUid && latestBind.buidUsername) {
|
|
434
431
|
await this.deps.autoSetGroupNickname(session, username, latestBind.buidUsername);
|
|
435
|
-
this.
|
|
432
|
+
this.logger.info('绑定', `QQ(${operatorId})MC绑定完成,已尝试设置群昵称`);
|
|
436
433
|
}
|
|
437
434
|
else {
|
|
438
435
|
buidReminder = `\n\n💡 提醒:您还未绑定B站账号,建议使用 ${this.deps.formatCommand('buid bind <B站UID>')} 完成B站绑定以享受完整功能`;
|
|
439
|
-
this.
|
|
436
|
+
this.logger.info('绑定', `QQ(${operatorId})MC绑定完成,但未绑定B站账号`);
|
|
440
437
|
}
|
|
441
438
|
}
|
|
442
439
|
catch (renameError) {
|
|
443
|
-
this.
|
|
440
|
+
this.logger.warn('绑定', `QQ(${operatorId})MC绑定后群昵称设置失败: ${renameError.message}`);
|
|
444
441
|
}
|
|
445
442
|
// MC头像
|
|
446
443
|
let mcAvatarUrl = null;
|
|
447
|
-
if (this.
|
|
448
|
-
if (this.
|
|
444
|
+
if (this.config.showAvatar) {
|
|
445
|
+
if (this.config.showMcSkin) {
|
|
449
446
|
mcAvatarUrl = this.deps.getStarlightSkinUrl(username);
|
|
450
447
|
}
|
|
451
448
|
else {
|
|
@@ -466,13 +463,13 @@ class McidCommandHandler {
|
|
|
466
463
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
467
464
|
// 检查用户名格式
|
|
468
465
|
if (!username || !/^[a-zA-Z0-9_]{3,16}$/.test(username)) {
|
|
469
|
-
this.
|
|
466
|
+
this.logger.warn('修改', `QQ(${normalizedUserId})提供的用户名"${username}"格式无效`);
|
|
470
467
|
return this.deps.sendMessage(session, [koishi_1.h.text('请提供有效的Minecraft用户名(3-16位字母、数字、下划线)')]);
|
|
471
468
|
}
|
|
472
469
|
// 验证用户名是否存在
|
|
473
470
|
const profile = await this.deps.validateUsername(username);
|
|
474
471
|
if (!profile) {
|
|
475
|
-
this.
|
|
472
|
+
this.logger.warn('修改', `QQ(${normalizedUserId})提供的用户名"${username}"不存在`);
|
|
476
473
|
return this.deps.sendMessage(session, [koishi_1.h.text(`无法验证用户名: ${username},该用户可能不存在`)]);
|
|
477
474
|
}
|
|
478
475
|
username = profile.name;
|
|
@@ -486,53 +483,53 @@ class McidCommandHandler {
|
|
|
486
483
|
}
|
|
487
484
|
catch (error) {
|
|
488
485
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
489
|
-
this.
|
|
486
|
+
this.logger.error('修改', `QQ(${normalizedUserId})修改MC账号为"${username}"失败: ${error.message}`);
|
|
490
487
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
491
488
|
}
|
|
492
489
|
}
|
|
493
490
|
async handleChangeForOther(session, username, uuid, target, operatorId) {
|
|
494
491
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
495
492
|
if (!normalizedTargetId) {
|
|
496
|
-
this.
|
|
493
|
+
this.logger.warn('修改', `QQ(${operatorId})提供的目标用户ID"${target}"无效`);
|
|
497
494
|
if (target.startsWith('@')) {
|
|
498
495
|
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')]);
|
|
499
496
|
}
|
|
500
497
|
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')]);
|
|
501
498
|
}
|
|
502
|
-
this.
|
|
499
|
+
this.logger.info('修改', `QQ(${operatorId})尝试修改QQ(${normalizedTargetId})的MC账号为: ${username}(${uuid})`);
|
|
503
500
|
// 检查权限
|
|
504
501
|
if (!await this.deps.isAdmin(session.userId)) {
|
|
505
|
-
this.
|
|
502
|
+
this.logger.warn('修改', `权限不足: QQ(${operatorId})不是管理员`);
|
|
506
503
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能修改其他用户的MC账号')]);
|
|
507
504
|
}
|
|
508
505
|
// 获取目标用户信息
|
|
509
506
|
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
510
507
|
if (!targetBind || !targetBind.mcUsername) {
|
|
511
|
-
this.
|
|
508
|
+
this.logger.warn('修改', `QQ(${normalizedTargetId})尚未绑定MC账号`);
|
|
512
509
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 尚未绑定MC账号,请先使用 ` + this.deps.formatCommand('mcid bind') + ` 命令进行绑定`)]);
|
|
513
510
|
}
|
|
514
511
|
// 检查是否与当前用户名相同
|
|
515
512
|
if (targetBind.mcUsername === username) {
|
|
516
|
-
this.
|
|
513
|
+
this.logger.warn('修改', `QQ(${normalizedTargetId})已绑定相同的MC账号"${username}"`);
|
|
517
514
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 当前已绑定此用户名: ${username}`)]);
|
|
518
515
|
}
|
|
519
|
-
//
|
|
520
|
-
if (await this.deps.checkUsernameExists(username, target)) {
|
|
521
|
-
this.
|
|
516
|
+
// 检查用户名是否已被占用(支持改名检测)
|
|
517
|
+
if (await this.deps.checkUsernameExists(username, target, uuid)) {
|
|
518
|
+
this.logger.warn('修改', `MC用户名"${username}"已被其他QQ号绑定`);
|
|
522
519
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户名 ${username} 已被其他用户绑定`)]);
|
|
523
520
|
}
|
|
524
521
|
const oldUsername = targetBind.mcUsername;
|
|
525
522
|
// 更新绑定信息
|
|
526
523
|
const bindResult = await this.deps.createOrUpdateMcBind(target, username, uuid);
|
|
527
524
|
if (!bindResult) {
|
|
528
|
-
this.
|
|
525
|
+
this.logger.error('修改', `管理员QQ(${operatorId})修改QQ(${normalizedTargetId})的MC账号失败`);
|
|
529
526
|
return this.deps.sendMessage(session, [koishi_1.h.text(`修改用户 ${normalizedTargetId} 的MC账号失败: 数据库操作出错,请联系管理员`)]);
|
|
530
527
|
}
|
|
531
|
-
this.
|
|
528
|
+
this.logger.info('修改', `成功: 管理员QQ(${operatorId})修改QQ(${normalizedTargetId})的MC账号: ${oldUsername} -> ${username}(${uuid})`);
|
|
532
529
|
// MC头像
|
|
533
530
|
let mcAvatarUrl = null;
|
|
534
|
-
if (this.
|
|
535
|
-
if (this.
|
|
531
|
+
if (this.config.showAvatar) {
|
|
532
|
+
if (this.config.showMcSkin) {
|
|
536
533
|
mcAvatarUrl = this.deps.getStarlightSkinUrl(username);
|
|
537
534
|
}
|
|
538
535
|
else {
|
|
@@ -549,41 +546,41 @@ class McidCommandHandler {
|
|
|
549
546
|
const selfBind = await this.deps.getMcBindByQQId(operatorId);
|
|
550
547
|
// 检查是否已绑定
|
|
551
548
|
if (!selfBind || !selfBind.mcUsername) {
|
|
552
|
-
this.
|
|
549
|
+
this.logger.warn('修改', `QQ(${operatorId})尚未绑定MC账号`);
|
|
553
550
|
return this.deps.sendMessage(session, [koishi_1.h.text('您尚未绑定MC账号,请使用 ' + this.deps.formatCommand('mcid bind') + ' 命令进行绑定')]);
|
|
554
551
|
}
|
|
555
552
|
// 检查是否与当前用户名相同
|
|
556
553
|
if (selfBind.mcUsername === username) {
|
|
557
|
-
this.
|
|
554
|
+
this.logger.warn('修改', `QQ(${operatorId})已绑定相同的MC账号"${username}"`);
|
|
558
555
|
return this.deps.sendMessage(session, [koishi_1.h.text(`您当前已绑定此用户名: ${username}`)]);
|
|
559
556
|
}
|
|
560
557
|
// 检查冷却时间
|
|
561
558
|
if (!await this.deps.isAdmin(session.userId) && !this.deps.checkCooldown(selfBind.lastModified)) {
|
|
562
|
-
const days = this.
|
|
559
|
+
const days = this.config.cooldownDays;
|
|
563
560
|
const now = new Date();
|
|
564
561
|
const diffTime = now.getTime() - selfBind.lastModified.getTime();
|
|
565
562
|
const passedDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
566
563
|
const remainingDays = days - passedDays;
|
|
567
|
-
this.
|
|
564
|
+
this.logger.warn('修改', `QQ(${operatorId})在冷却期内,无法修改MC账号`);
|
|
568
565
|
return this.deps.sendMessage(session, [koishi_1.h.text(`您的MC账号绑定在冷却期内,还需${remainingDays}天才能修改。如需立即修改,请联系管理员。`)]);
|
|
569
566
|
}
|
|
570
|
-
//
|
|
571
|
-
if (await this.deps.checkUsernameExists(username, session.userId)) {
|
|
572
|
-
this.
|
|
567
|
+
// 检查用户名是否已被占用(支持改名检测)
|
|
568
|
+
if (await this.deps.checkUsernameExists(username, session.userId, uuid)) {
|
|
569
|
+
this.logger.warn('修改', `MC用户名"${username}"已被其他QQ号绑定`);
|
|
573
570
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户名 ${username} 已被其他用户绑定`)]);
|
|
574
571
|
}
|
|
575
572
|
const oldUsername = selfBind.mcUsername;
|
|
576
573
|
// 更新绑定信息
|
|
577
574
|
const bindResult = await this.deps.createOrUpdateMcBind(session.userId, username, uuid);
|
|
578
575
|
if (!bindResult) {
|
|
579
|
-
this.
|
|
576
|
+
this.logger.error('修改', `QQ(${operatorId})修改MC账号失败`);
|
|
580
577
|
return this.deps.sendMessage(session, [koishi_1.h.text('修改失败,数据库操作出错,请联系管理员')]);
|
|
581
578
|
}
|
|
582
|
-
this.
|
|
579
|
+
this.logger.info('修改', `成功: QQ(${operatorId})修改MC账号: ${oldUsername} -> ${username}(${uuid})`);
|
|
583
580
|
// MC头像
|
|
584
581
|
let mcAvatarUrl = null;
|
|
585
|
-
if (this.
|
|
586
|
-
if (this.
|
|
582
|
+
if (this.config.showAvatar) {
|
|
583
|
+
if (this.config.showMcSkin) {
|
|
587
584
|
mcAvatarUrl = this.deps.getStarlightSkinUrl(username);
|
|
588
585
|
}
|
|
589
586
|
else {
|
|
@@ -612,50 +609,50 @@ class McidCommandHandler {
|
|
|
612
609
|
catch (error) {
|
|
613
610
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
614
611
|
const targetInfo = target ? `为QQ(${this.deps.normalizeQQId(target)})` : '';
|
|
615
|
-
this.
|
|
612
|
+
this.logger.error('解绑', `QQ(${normalizedUserId})${targetInfo}解绑MC账号失败: ${error.message}`);
|
|
616
613
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
617
614
|
}
|
|
618
615
|
}
|
|
619
616
|
async handleUnbindForOther(session, target, operatorId) {
|
|
620
617
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
621
618
|
if (!normalizedTargetId) {
|
|
622
|
-
this.
|
|
619
|
+
this.logger.warn('解绑', `QQ(${operatorId})提供的目标用户ID"${target}"无效`);
|
|
623
620
|
if (target.startsWith('@')) {
|
|
624
621
|
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')]);
|
|
625
622
|
}
|
|
626
623
|
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')]);
|
|
627
624
|
}
|
|
628
|
-
this.
|
|
625
|
+
this.logger.info('解绑', `QQ(${operatorId})尝试为QQ(${normalizedTargetId})解绑MC账号`);
|
|
629
626
|
// 检查权限
|
|
630
627
|
if (!await this.deps.isAdmin(session.userId)) {
|
|
631
|
-
this.
|
|
628
|
+
this.logger.warn('解绑', `权限不足: QQ(${operatorId})不是管理员`);
|
|
632
629
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能为其他用户解绑MC账号')]);
|
|
633
630
|
}
|
|
634
631
|
// 获取目标用户信息
|
|
635
632
|
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
636
633
|
if (!targetBind || !targetBind.mcUsername) {
|
|
637
|
-
this.
|
|
634
|
+
this.logger.warn('解绑', `QQ(${normalizedTargetId})尚未绑定MC账号`);
|
|
638
635
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 尚未绑定MC账号`)]);
|
|
639
636
|
}
|
|
640
637
|
const oldUsername = targetBind.mcUsername && !targetBind.mcUsername.startsWith('_temp_') ? targetBind.mcUsername : '未绑定';
|
|
641
638
|
const oldBuidInfo = targetBind.buidUid ? ` 和 B站账号: ${targetBind.buidUsername}(${targetBind.buidUid})` : '';
|
|
642
639
|
// 删除绑定记录
|
|
643
640
|
await this.deps.deleteMcBind(target);
|
|
644
|
-
this.
|
|
641
|
+
this.logger.info('解绑', `成功: 管理员QQ(${operatorId})为QQ(${normalizedTargetId})解绑MC账号: ${oldUsername}${oldBuidInfo}`);
|
|
645
642
|
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功为用户 ${normalizedTargetId} 解绑MC账号: ${oldUsername}${oldBuidInfo}`)]);
|
|
646
643
|
}
|
|
647
644
|
async handleUnbindForSelf(session, operatorId) {
|
|
648
|
-
this.
|
|
645
|
+
this.logger.info('解绑', `QQ(${operatorId})尝试解绑自己的MC账号`);
|
|
649
646
|
const selfBind = await this.deps.getMcBindByQQId(operatorId);
|
|
650
647
|
if (!selfBind || !selfBind.mcUsername) {
|
|
651
|
-
this.
|
|
648
|
+
this.logger.warn('解绑', `QQ(${operatorId})尚未绑定MC账号`);
|
|
652
649
|
return this.deps.sendMessage(session, [koishi_1.h.text('您尚未绑定MC账号')]);
|
|
653
650
|
}
|
|
654
651
|
const oldUsername = selfBind.mcUsername && !selfBind.mcUsername.startsWith('_temp_') ? selfBind.mcUsername : '未绑定';
|
|
655
652
|
const oldBuidInfo = selfBind.buidUid ? ` 和 B站账号: ${selfBind.buidUsername}(${selfBind.buidUid})` : '';
|
|
656
653
|
// 删除绑定记录
|
|
657
654
|
await this.deps.deleteMcBind(operatorId);
|
|
658
|
-
this.
|
|
655
|
+
this.logger.info('解绑', `成功: QQ(${operatorId})解绑MC账号: ${oldUsername}${oldBuidInfo}`);
|
|
659
656
|
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功解绑MC账号: ${oldUsername}${oldBuidInfo}`)]);
|
|
660
657
|
}
|
|
661
658
|
/**
|
|
@@ -665,43 +662,43 @@ class McidCommandHandler {
|
|
|
665
662
|
try {
|
|
666
663
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
667
664
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
668
|
-
this.
|
|
665
|
+
this.logger.info('管理员', `QQ(${normalizedUserId})尝试将QQ(${normalizedTargetId})设为管理员`);
|
|
669
666
|
// 检查是否为主人
|
|
670
667
|
if (!this.deps.isMaster(session.userId)) {
|
|
671
|
-
this.
|
|
668
|
+
this.logger.warn('管理员', `权限不足: QQ(${normalizedUserId})不是主人`);
|
|
672
669
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有主人才能设置管理员')]);
|
|
673
670
|
}
|
|
674
671
|
// 检查目标用户是否已经是管理员
|
|
675
672
|
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
676
673
|
const isAlreadyAdmin = targetBind && targetBind.isAdmin === true;
|
|
677
674
|
if (isAlreadyAdmin) {
|
|
678
|
-
this.
|
|
675
|
+
this.logger.warn('管理员', `QQ(${normalizedTargetId})已经是管理员`);
|
|
679
676
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 已经是管理员`)]);
|
|
680
677
|
}
|
|
681
678
|
// 如果用户存在绑定记录,更新为管理员
|
|
682
679
|
if (targetBind) {
|
|
683
|
-
await this.
|
|
680
|
+
await this.repos.mcidbind.update(normalizedTargetId, {
|
|
684
681
|
isAdmin: true,
|
|
685
682
|
});
|
|
686
|
-
this.
|
|
683
|
+
this.logger.info('管理员', `成功: 主人QQ(${normalizedUserId})将QQ(${normalizedTargetId})设为管理员`);
|
|
687
684
|
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功将用户 ${normalizedTargetId} 设为管理员`)]);
|
|
688
685
|
}
|
|
689
686
|
else {
|
|
690
687
|
// 用户不存在绑定记录,创建一个新记录并设为管理员
|
|
691
688
|
const tempUsername = `_temp_${normalizedTargetId}`;
|
|
692
689
|
try {
|
|
693
|
-
await this.
|
|
690
|
+
await this.repos.mcidbind.create({
|
|
694
691
|
qqId: normalizedTargetId,
|
|
695
692
|
mcUsername: tempUsername,
|
|
696
693
|
mcUuid: '',
|
|
697
694
|
lastModified: new Date(),
|
|
698
695
|
isAdmin: true
|
|
699
696
|
});
|
|
700
|
-
this.
|
|
697
|
+
this.logger.info('管理员', `成功: 主人QQ(${normalizedUserId})将QQ(${normalizedTargetId})设为管理员 (创建新记录)`);
|
|
701
698
|
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功将用户 ${normalizedTargetId} 设为管理员 (未绑定MC账号)`)]);
|
|
702
699
|
}
|
|
703
700
|
catch (createError) {
|
|
704
|
-
this.
|
|
701
|
+
this.logger.error('管理员', `创建管理员记录失败: ${createError.message}`);
|
|
705
702
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(createError))]);
|
|
706
703
|
}
|
|
707
704
|
}
|
|
@@ -709,7 +706,7 @@ class McidCommandHandler {
|
|
|
709
706
|
catch (error) {
|
|
710
707
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
711
708
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
712
|
-
this.
|
|
709
|
+
this.logger.error('管理员', `QQ(${normalizedUserId})将QQ(${normalizedTargetId})设为管理员失败: ${error.message}`);
|
|
713
710
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
714
711
|
}
|
|
715
712
|
}
|
|
@@ -720,30 +717,30 @@ class McidCommandHandler {
|
|
|
720
717
|
try {
|
|
721
718
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
722
719
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
723
|
-
this.
|
|
720
|
+
this.logger.info('管理员', `QQ(${normalizedUserId})尝试撤销QQ(${normalizedTargetId})的管理员权限`);
|
|
724
721
|
// 检查是否为主人
|
|
725
722
|
if (!this.deps.isMaster(session.userId)) {
|
|
726
|
-
this.
|
|
723
|
+
this.logger.warn('管理员', `权限不足: QQ(${normalizedUserId})不是主人`);
|
|
727
724
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有主人才能撤销管理员权限')]);
|
|
728
725
|
}
|
|
729
726
|
// 检查目标用户是否是管理员
|
|
730
727
|
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
731
728
|
const isAdmin = targetBind && targetBind.isAdmin === true;
|
|
732
729
|
if (!isAdmin) {
|
|
733
|
-
this.
|
|
730
|
+
this.logger.warn('管理员', `QQ(${normalizedTargetId})不是管理员`);
|
|
734
731
|
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 不是管理员`)]);
|
|
735
732
|
}
|
|
736
733
|
// 撤销管理员权限
|
|
737
|
-
await this.
|
|
734
|
+
await this.repos.mcidbind.update(normalizedTargetId, {
|
|
738
735
|
isAdmin: false,
|
|
739
736
|
});
|
|
740
|
-
this.
|
|
737
|
+
this.logger.info('管理员', `成功: 主人QQ(${normalizedUserId})撤销了QQ(${normalizedTargetId})的管理员权限`);
|
|
741
738
|
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功撤销用户 ${normalizedTargetId} 的管理员权限`)]);
|
|
742
739
|
}
|
|
743
740
|
catch (error) {
|
|
744
741
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
745
742
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
746
|
-
this.
|
|
743
|
+
this.logger.error('管理员', `QQ(${normalizedUserId})撤销QQ(${normalizedTargetId})的管理员权限失败: ${error.message}`);
|
|
747
744
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
748
745
|
}
|
|
749
746
|
}
|
|
@@ -753,16 +750,16 @@ class McidCommandHandler {
|
|
|
753
750
|
async handleAdminlist(session) {
|
|
754
751
|
try {
|
|
755
752
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
756
|
-
this.
|
|
753
|
+
this.logger.info('管理员', `QQ(${normalizedUserId})尝试查看管理员列表`);
|
|
757
754
|
// 检查是否为主人
|
|
758
755
|
if (!this.deps.isMaster(session.userId)) {
|
|
759
|
-
this.
|
|
756
|
+
this.logger.warn('管理员', `权限不足: QQ(${normalizedUserId})不是主人`);
|
|
760
757
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有主人才能查看管理员列表')]);
|
|
761
758
|
}
|
|
762
759
|
// 查询所有管理员
|
|
763
|
-
const admins = await this.
|
|
760
|
+
const admins = await this.repos.mcidbind.findAllAdmins();
|
|
764
761
|
if (admins.length === 0) {
|
|
765
|
-
this.
|
|
762
|
+
this.logger.info('管理员', `管理员列表为空`);
|
|
766
763
|
return this.deps.sendMessage(session, [koishi_1.h.text('当前没有管理员')]);
|
|
767
764
|
}
|
|
768
765
|
// 格式化管理员列表
|
|
@@ -770,12 +767,12 @@ class McidCommandHandler {
|
|
|
770
767
|
const displayUsername = admin.mcUsername && !admin.mcUsername.startsWith('_temp_') ? admin.mcUsername : null;
|
|
771
768
|
return `- ${admin.qqId}${displayUsername ? ` (MC: ${displayUsername})` : ''}`;
|
|
772
769
|
}).join('\n');
|
|
773
|
-
this.
|
|
770
|
+
this.logger.info('管理员', `成功: 主人QQ(${normalizedUserId})查看了管理员列表`);
|
|
774
771
|
return this.deps.sendMessage(session, [koishi_1.h.text(`管理员列表:\n${adminList}\n\n共 ${admins.length} 名管理员`)]);
|
|
775
772
|
}
|
|
776
773
|
catch (error) {
|
|
777
774
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
778
|
-
this.
|
|
775
|
+
this.logger.error('管理员', `QQ(${normalizedUserId})查看管理员列表失败: ${error.message}`);
|
|
779
776
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
780
777
|
}
|
|
781
778
|
}
|
|
@@ -785,14 +782,14 @@ class McidCommandHandler {
|
|
|
785
782
|
async handleStats(session) {
|
|
786
783
|
try {
|
|
787
784
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
788
|
-
this.
|
|
785
|
+
this.logger.info('统计', `QQ(${normalizedUserId})尝试查看数据库统计`);
|
|
789
786
|
// 检查权限
|
|
790
787
|
if (!await this.deps.isAdmin(session.userId)) {
|
|
791
|
-
this.
|
|
788
|
+
this.logger.warn('统计', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
792
789
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能查看统计信息')]);
|
|
793
790
|
}
|
|
794
791
|
// 查询所有绑定记录
|
|
795
|
-
const allBinds = await this.
|
|
792
|
+
const allBinds = await this.repos.mcidbind.findAll();
|
|
796
793
|
// 统计绑定情况
|
|
797
794
|
let mcidBoundUsers = 0;
|
|
798
795
|
let buidBoundUsers = 0;
|
|
@@ -809,12 +806,12 @@ class McidCommandHandler {
|
|
|
809
806
|
let statsInfo = `📊 绑定统计\n`;
|
|
810
807
|
statsInfo += `\n已绑定MCID: ${mcidBoundUsers}人\n`;
|
|
811
808
|
statsInfo += `已绑定BUID: ${buidBoundUsers}人`;
|
|
812
|
-
this.
|
|
809
|
+
this.logger.info('统计', `成功: 管理员QQ(${normalizedUserId})查看了数据库统计`);
|
|
813
810
|
return this.deps.sendMessage(session, [koishi_1.h.text(statsInfo)]);
|
|
814
811
|
}
|
|
815
812
|
catch (error) {
|
|
816
813
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
817
|
-
this.
|
|
814
|
+
this.logger.error('统计', `QQ(${normalizedUserId})查看统计失败: ${error.message}`);
|
|
818
815
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
819
816
|
}
|
|
820
817
|
}
|
|
@@ -826,7 +823,7 @@ class McidCommandHandler {
|
|
|
826
823
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
827
824
|
// 检查权限
|
|
828
825
|
if (!await this.deps.isAdmin(session.userId)) {
|
|
829
|
-
this.
|
|
826
|
+
this.logger.warn('群昵称修复', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
830
827
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能执行群昵称修复操作')]);
|
|
831
828
|
}
|
|
832
829
|
// 确定目标群ID
|
|
@@ -842,14 +839,14 @@ class McidCommandHandler {
|
|
|
842
839
|
}
|
|
843
840
|
}
|
|
844
841
|
catch (error) {
|
|
845
|
-
this.
|
|
842
|
+
this.logger.warn('群昵称修复', `Bot不在群${targetGroupId}中或无法获取群信息`);
|
|
846
843
|
return this.deps.sendMessage(session, [koishi_1.h.text(`❌ Bot不在群 ${targetGroupId} 中或无权限操作该群`)]);
|
|
847
844
|
}
|
|
848
845
|
const groupDisplayText = groupId ? `群 ${targetGroupId}` : '当前群';
|
|
849
|
-
this.
|
|
846
|
+
this.logger.info('群昵称修复', `管理员QQ(${normalizedUserId})开始批量修复${groupDisplayText}的群昵称`);
|
|
850
847
|
await this.deps.sendMessage(session, [koishi_1.h.text(`🔧 开始检查并修复${groupDisplayText}的所有用户群昵称格式,请稍候...`)]);
|
|
851
848
|
// 获取所有已绑定B站的用户
|
|
852
|
-
const allBinds = await this.
|
|
849
|
+
const allBinds = await this.repos.mcidbind.findAll();
|
|
853
850
|
const usersWithBuid = allBinds.filter(bind => bind.buidUid && bind.buidUsername);
|
|
854
851
|
let checkedCount = 0;
|
|
855
852
|
let fixedCount = 0;
|
|
@@ -894,7 +891,7 @@ class McidCommandHandler {
|
|
|
894
891
|
catch (error) {
|
|
895
892
|
errorCount++;
|
|
896
893
|
results.push(`❌ ${bind.qqId}: 处理出错 - ${error.message}`);
|
|
897
|
-
this.
|
|
894
|
+
this.logger.error('群昵称修复', `处理用户QQ(${bind.qqId})时出错: ${error.message}`);
|
|
898
895
|
}
|
|
899
896
|
}
|
|
900
897
|
// 生成最终报告
|
|
@@ -911,12 +908,12 @@ class McidCommandHandler {
|
|
|
911
908
|
}
|
|
912
909
|
}
|
|
913
910
|
}
|
|
914
|
-
this.
|
|
911
|
+
this.logger.info('群昵称修复', `修复完成: 管理员QQ(${normalizedUserId})在${groupDisplayText}检查${checkedCount}个用户,修复${fixedCount}个,错误${errorCount}个`);
|
|
915
912
|
return this.deps.sendMessage(session, [koishi_1.h.text(resultMessage)]);
|
|
916
913
|
}
|
|
917
914
|
catch (error) {
|
|
918
915
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
919
|
-
this.
|
|
916
|
+
this.logger.error('群昵称修复', `QQ(${normalizedUserId})执行群昵称修复失败: ${error.message}`);
|
|
920
917
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
921
918
|
}
|
|
922
919
|
}
|
|
@@ -928,7 +925,7 @@ class McidCommandHandler {
|
|
|
928
925
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
929
926
|
// 检查权限
|
|
930
927
|
if (!await this.deps.isAdmin(session.userId)) {
|
|
931
|
-
this.
|
|
928
|
+
this.logger.warn('清除冷却', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
932
929
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能清除提醒冷却和次数')]);
|
|
933
930
|
}
|
|
934
931
|
// 注意: reminderCooldown 需要从外部传入或在 deps 中提供
|
|
@@ -937,23 +934,23 @@ class McidCommandHandler {
|
|
|
937
934
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
938
935
|
const bind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
939
936
|
if (bind) {
|
|
940
|
-
await this.
|
|
937
|
+
await this.repos.mcidbind.update(normalizedTargetId, { reminderCount: 0 });
|
|
941
938
|
}
|
|
942
|
-
this.
|
|
939
|
+
this.logger.info('清除冷却', `管理员QQ(${normalizedUserId})清除了QQ(${normalizedTargetId})的提醒次数`);
|
|
943
940
|
return this.deps.sendMessage(session, [koishi_1.h.text(`已清除用户 ${normalizedTargetId} 的随机提醒次数`)]);
|
|
944
941
|
}
|
|
945
942
|
else {
|
|
946
|
-
const allBinds = await this.
|
|
943
|
+
const allBinds = await this.repos.mcidbind.findAll();
|
|
947
944
|
for (const bind of allBinds) {
|
|
948
|
-
await this.
|
|
945
|
+
await this.repos.mcidbind.update(bind.qqId, { reminderCount: 0 });
|
|
949
946
|
}
|
|
950
|
-
this.
|
|
947
|
+
this.logger.info('清除冷却', `管理员QQ(${normalizedUserId})清除了所有用户的提醒次数`);
|
|
951
948
|
return this.deps.sendMessage(session, [koishi_1.h.text(`已清除所有用户的随机提醒次数`)]);
|
|
952
949
|
}
|
|
953
950
|
}
|
|
954
951
|
catch (error) {
|
|
955
952
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
956
|
-
this.
|
|
953
|
+
this.logger.error('清除冷却', `QQ(${normalizedUserId})清除提醒冷却失败: ${error.message}`);
|
|
957
954
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
958
955
|
}
|
|
959
956
|
}
|
|
@@ -965,12 +962,12 @@ class McidCommandHandler {
|
|
|
965
962
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
966
963
|
// 检查权限
|
|
967
964
|
if (!await this.deps.isAdmin(session.userId)) {
|
|
968
|
-
this.
|
|
965
|
+
this.logger.warn('数据导出', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
969
966
|
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能导出群数据')]);
|
|
970
967
|
}
|
|
971
968
|
// 检查是否为私聊
|
|
972
969
|
if (!session.channelId?.startsWith('private:')) {
|
|
973
|
-
this.
|
|
970
|
+
this.logger.warn('数据导出', `QQ(${normalizedUserId})尝试在群聊中使用导出命令`);
|
|
974
971
|
return this.deps.sendMessage(session, [koishi_1.h.text('为了数据安全,导出命令仅支持在私聊中使用')]);
|
|
975
972
|
}
|
|
976
973
|
// 验证群ID格式
|
|
@@ -984,10 +981,10 @@ class McidCommandHandler {
|
|
|
984
981
|
}
|
|
985
982
|
}
|
|
986
983
|
catch (error) {
|
|
987
|
-
this.
|
|
984
|
+
this.logger.warn('数据导出', `Bot不在群${groupId}中或无法获取群信息`);
|
|
988
985
|
return this.deps.sendMessage(session, [koishi_1.h.text(`❌ Bot不在群 ${groupId} 中或无权限操作该群`)]);
|
|
989
986
|
}
|
|
990
|
-
this.
|
|
987
|
+
this.logger.info('数据导出', `管理员QQ(${normalizedUserId})开始导出群${groupId}的数据`);
|
|
991
988
|
await this.deps.sendMessage(session, [koishi_1.h.text(`📊 开始导出群 ${groupId} 的数据,请稍候...`)]);
|
|
992
989
|
try {
|
|
993
990
|
// 导出数据
|
|
@@ -1005,14 +1002,14 @@ class McidCommandHandler {
|
|
|
1005
1002
|
name: fileName
|
|
1006
1003
|
});
|
|
1007
1004
|
await this.deps.sendMessage(session, [koishi_1.h.text(`📁 文件已发送: ${fileName}`)]);
|
|
1008
|
-
this.
|
|
1005
|
+
this.logger.info('数据导出', `成功发送文件到私聊: ${fileName}`);
|
|
1009
1006
|
}
|
|
1010
1007
|
else {
|
|
1011
1008
|
throw new Error('Bot不支持内部API调用');
|
|
1012
1009
|
}
|
|
1013
1010
|
}
|
|
1014
1011
|
catch (fileError) {
|
|
1015
|
-
this.
|
|
1012
|
+
this.logger.error('数据导出', `文件发送失败: ${fileError.message}`);
|
|
1016
1013
|
// 降级方案:保存文件
|
|
1017
1014
|
try {
|
|
1018
1015
|
const filePath = await this.deps.groupExporter.saveExcelFile(excelBuffer, fileName);
|
|
@@ -1020,7 +1017,7 @@ class McidCommandHandler {
|
|
|
1020
1017
|
koishi_1.h.text(`⚠️ 直接发送失败,文件已保存\n文件路径: ${filePath}\n文件名: ${fileName}\n请联系管理员获取文件`)
|
|
1021
1018
|
]);
|
|
1022
1019
|
// 清理过期文件
|
|
1023
|
-
this.deps.groupExporter.cleanupOldFiles().catch(err => this.
|
|
1020
|
+
this.deps.groupExporter.cleanupOldFiles().catch(err => this.logger.warn('数据导出', `清理临时文件时出错: ${err.message}`));
|
|
1024
1021
|
}
|
|
1025
1022
|
catch (saveError) {
|
|
1026
1023
|
await this.deps.sendMessage(session, [
|
|
@@ -1028,16 +1025,16 @@ class McidCommandHandler {
|
|
|
1028
1025
|
]);
|
|
1029
1026
|
}
|
|
1030
1027
|
}
|
|
1031
|
-
this.
|
|
1028
|
+
this.logger.info('数据导出', `管理员QQ(${normalizedUserId})成功导出群${groupId}的数据,文件名: ${fileName}`);
|
|
1032
1029
|
}
|
|
1033
1030
|
catch (exportError) {
|
|
1034
|
-
this.
|
|
1031
|
+
this.logger.error('数据导出', `导出群${groupId}数据失败: ${exportError.message}`);
|
|
1035
1032
|
return this.deps.sendMessage(session, [koishi_1.h.text(`❌ 导出失败: ${exportError.message}`)]);
|
|
1036
1033
|
}
|
|
1037
1034
|
}
|
|
1038
1035
|
catch (error) {
|
|
1039
1036
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
1040
|
-
this.
|
|
1037
|
+
this.logger.error('数据导出', `QQ(${normalizedUserId})导出群数据失败: ${error.message}`);
|
|
1041
1038
|
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
1042
1039
|
}
|
|
1043
1040
|
}
|