koishi-plugin-bind-bot 2.0.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.
- package/lib/export-utils.d.ts +49 -0
- package/lib/export-utils.js +305 -0
- package/lib/force-bind-utils.d.ts +40 -0
- package/lib/force-bind-utils.js +242 -0
- package/lib/handlers/base.handler.d.ts +61 -0
- package/lib/handlers/base.handler.js +22 -0
- package/lib/handlers/binding.handler.d.ts +45 -0
- package/lib/handlers/binding.handler.js +285 -0
- package/lib/handlers/buid.handler.d.ts +71 -0
- package/lib/handlers/buid.handler.js +694 -0
- package/lib/handlers/index.d.ts +6 -0
- package/lib/handlers/index.js +22 -0
- package/lib/handlers/mcid.handler.d.ts +101 -0
- package/lib/handlers/mcid.handler.js +1045 -0
- package/lib/handlers/tag.handler.d.ts +14 -0
- package/lib/handlers/tag.handler.js +382 -0
- package/lib/handlers/whitelist.handler.d.ts +84 -0
- package/lib/handlers/whitelist.handler.js +1011 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +2693 -0
- package/lib/managers/rcon-manager.d.ts +24 -0
- package/lib/managers/rcon-manager.js +308 -0
- package/lib/repositories/mcidbind.repository.d.ts +105 -0
- package/lib/repositories/mcidbind.repository.js +288 -0
- package/lib/repositories/schedule-mute.repository.d.ts +68 -0
- package/lib/repositories/schedule-mute.repository.js +175 -0
- package/lib/types/api.d.ts +135 -0
- package/lib/types/api.js +6 -0
- package/lib/types/common.d.ts +40 -0
- package/lib/types/common.js +6 -0
- package/lib/types/config.d.ts +55 -0
- package/lib/types/config.js +6 -0
- package/lib/types/database.d.ts +47 -0
- package/lib/types/database.js +6 -0
- package/lib/types/index.d.ts +8 -0
- package/lib/types/index.js +28 -0
- package/lib/utils/helpers.d.ts +76 -0
- package/lib/utils/helpers.js +275 -0
- package/lib/utils/logger.d.ts +75 -0
- package/lib/utils/logger.js +134 -0
- package/lib/utils/message-utils.d.ts +46 -0
- package/lib/utils/message-utils.js +234 -0
- package/lib/utils/rate-limiter.d.ts +26 -0
- package/lib/utils/rate-limiter.js +47 -0
- package/lib/utils/session-manager.d.ts +70 -0
- package/lib/utils/session-manager.js +120 -0
- package/package.json +39 -0
- package/readme.md +281 -0
|
@@ -0,0 +1,1045 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.McidCommandHandler = void 0;
|
|
4
|
+
const koishi_1 = require("koishi");
|
|
5
|
+
class McidCommandHandler {
|
|
6
|
+
ctx;
|
|
7
|
+
deps;
|
|
8
|
+
constructor(ctx, deps) {
|
|
9
|
+
this.ctx = ctx;
|
|
10
|
+
this.deps = deps;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 注册所有MCID命令
|
|
14
|
+
*/
|
|
15
|
+
registerCommands(cmd) {
|
|
16
|
+
// mcid.query - 查询MC账号
|
|
17
|
+
cmd.subcommand('.query [target:string]', '查询用户绑定的MC账号')
|
|
18
|
+
.action(async ({ session }, target) => this.handleQuery(session, target));
|
|
19
|
+
// mcid.finduser - 通过MC用户名查询QQ号
|
|
20
|
+
cmd.subcommand('.finduser <username:string>', '[管理员]通过MC用户名查询绑定的QQ账号')
|
|
21
|
+
.action(async ({ session }, username) => this.handleFindUser(session, username));
|
|
22
|
+
// mcid.bind - 绑定MC账号
|
|
23
|
+
cmd.subcommand('.bind <username:string> [target:string]', '绑定MC账号')
|
|
24
|
+
.action(async ({ session }, username, target) => this.handleBind(session, username, target));
|
|
25
|
+
// mcid.change - 修改MC账号
|
|
26
|
+
cmd.subcommand('.change <username:string> [target:string]', '修改绑定的MC账号')
|
|
27
|
+
.action(async ({ session }, username, target) => this.handleChange(session, username, target));
|
|
28
|
+
// mcid.unbind - 解绑MC账号
|
|
29
|
+
cmd.subcommand('.unbind [target:string]', '[管理员]解绑MC账号')
|
|
30
|
+
.action(async ({ session }, target) => this.handleUnbind(session, target));
|
|
31
|
+
// mcid.admin - 设置管理员
|
|
32
|
+
cmd.subcommand('.admin <target:string>', '[主人]将用户设为管理员')
|
|
33
|
+
.action(async ({ session }, target) => this.handleAdmin(session, target));
|
|
34
|
+
// mcid.unadmin - 撤销管理员
|
|
35
|
+
cmd.subcommand('.unadmin <target:string>', '[主人]撤销用户的管理员权限')
|
|
36
|
+
.action(async ({ session }, target) => this.handleUnadmin(session, target));
|
|
37
|
+
// mcid.adminlist - 列出所有管理员
|
|
38
|
+
cmd.subcommand('.adminlist', '[主人]列出所有管理员')
|
|
39
|
+
.action(async ({ session }) => this.handleAdminlist(session));
|
|
40
|
+
// mcid.stats - 查看统计
|
|
41
|
+
cmd.subcommand('.stats', '[管理员]查看数据库统计信息')
|
|
42
|
+
.action(async ({ session }) => this.handleStats(session));
|
|
43
|
+
// mcid.fixnicknames - 修复群昵称
|
|
44
|
+
cmd.subcommand('.fixnicknames [groupId:string]', '[管理员]检查并修复指定群或当前群的用户群昵称格式')
|
|
45
|
+
.action(async ({ session }, groupId) => this.handleFixNicknames(session, groupId));
|
|
46
|
+
// mcid.clearreminder - 清除提醒
|
|
47
|
+
cmd.subcommand('.clearreminder [target:string]', '[管理员]清除用户的随机提醒冷却时间和提醒次数')
|
|
48
|
+
.action(async ({ session }, target) => this.handleClearReminder(session, target));
|
|
49
|
+
// mcid.export - 导出数据
|
|
50
|
+
cmd.subcommand('.export <groupId:string>', '[管理员]导出指定群的成员和绑定信息为Excel文件')
|
|
51
|
+
.action(async ({ session }, groupId) => this.handleExport(session, groupId));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 查询MC账号
|
|
55
|
+
*/
|
|
56
|
+
async handleQuery(session, target) {
|
|
57
|
+
try {
|
|
58
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
59
|
+
// 查询目标用户或自己
|
|
60
|
+
if (target) {
|
|
61
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
62
|
+
if (!normalizedTargetId) {
|
|
63
|
+
this.deps.logger.warn('查询', `QQ(${normalizedUserId})提供的目标用户ID"${target}"无效`);
|
|
64
|
+
if (target.startsWith('@')) {
|
|
65
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')]);
|
|
66
|
+
}
|
|
67
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')]);
|
|
68
|
+
}
|
|
69
|
+
this.deps.logger.info('查询', `QQ(${normalizedUserId})查询QQ(${normalizedTargetId})的MC账号信息`);
|
|
70
|
+
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
71
|
+
if (!targetBind || !targetBind.mcUsername || targetBind.mcUsername.startsWith('_temp_')) {
|
|
72
|
+
this.deps.logger.info('查询', `QQ(${normalizedTargetId})未绑定MC账号`);
|
|
73
|
+
// 检查是否绑定了B站
|
|
74
|
+
if (targetBind && targetBind.buidUid) {
|
|
75
|
+
const buidUser = await this.deps.validateBUID(targetBind.buidUid);
|
|
76
|
+
if (buidUser) {
|
|
77
|
+
await this.deps.updateBuidInfoOnly(targetBind.qqId, buidUser);
|
|
78
|
+
const refreshedBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
79
|
+
if (refreshedBind) {
|
|
80
|
+
let buidInfo = `该用户尚未绑定MC账号\n\nB站账号信息:\nB站UID: ${refreshedBind.buidUid}\n用户名: ${refreshedBind.buidUsername}`;
|
|
81
|
+
if (refreshedBind.guardLevel > 0) {
|
|
82
|
+
buidInfo += `\n舰长等级: ${refreshedBind.guardLevelText} (${refreshedBind.guardLevel})`;
|
|
83
|
+
if (refreshedBind.maxGuardLevel > 0 && refreshedBind.maxGuardLevel < refreshedBind.guardLevel) {
|
|
84
|
+
buidInfo += `\n历史最高: ${refreshedBind.maxGuardLevelText} (${refreshedBind.maxGuardLevel})`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (refreshedBind.maxGuardLevel > 0) {
|
|
88
|
+
buidInfo += `\n历史舰长: ${refreshedBind.maxGuardLevelText} (${refreshedBind.maxGuardLevel})`;
|
|
89
|
+
}
|
|
90
|
+
if (refreshedBind.medalName) {
|
|
91
|
+
buidInfo += `\n粉丝牌: ${refreshedBind.medalName} Lv.${refreshedBind.medalLevel}`;
|
|
92
|
+
}
|
|
93
|
+
const messageElements = [koishi_1.h.text(buidInfo)];
|
|
94
|
+
if (this.deps.config.showAvatar) {
|
|
95
|
+
messageElements.push(koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${refreshedBind.buidUid}?size=160`));
|
|
96
|
+
}
|
|
97
|
+
return this.deps.sendMessage(session, messageElements);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('该用户尚未绑定MC账号')]);
|
|
102
|
+
}
|
|
103
|
+
// 显示MC绑定信息
|
|
104
|
+
const updatedBind = await this.deps.checkAndUpdateUsername(targetBind);
|
|
105
|
+
return this.buildQueryResponse(session, updatedBind, normalizedTargetId);
|
|
106
|
+
}
|
|
107
|
+
// 查询自己
|
|
108
|
+
this.deps.logger.info('查询', `QQ(${normalizedUserId})查询自己的MC账号信息`);
|
|
109
|
+
const selfBind = await this.deps.getMcBindByQQId(normalizedUserId);
|
|
110
|
+
if (!selfBind || !selfBind.mcUsername || selfBind.mcUsername.startsWith('_temp_')) {
|
|
111
|
+
this.deps.logger.info('查询', `QQ(${normalizedUserId})未绑定MC账号`);
|
|
112
|
+
// 检查是否绑定了B站
|
|
113
|
+
if (selfBind && selfBind.buidUid) {
|
|
114
|
+
const buidUser = await this.deps.validateBUID(selfBind.buidUid);
|
|
115
|
+
if (buidUser) {
|
|
116
|
+
await this.deps.updateBuidInfoOnly(selfBind.qqId, buidUser);
|
|
117
|
+
const refreshedBind = await this.deps.getMcBindByQQId(normalizedUserId);
|
|
118
|
+
if (refreshedBind) {
|
|
119
|
+
let buidInfo = `您尚未绑定MC账号\n\nB站账号信息:\nB站UID: ${refreshedBind.buidUid}\n用户名: ${refreshedBind.buidUsername}`;
|
|
120
|
+
if (refreshedBind.guardLevel > 0) {
|
|
121
|
+
buidInfo += `\n舰长等级: ${refreshedBind.guardLevelText} (${refreshedBind.guardLevel})`;
|
|
122
|
+
if (refreshedBind.maxGuardLevel > 0 && refreshedBind.maxGuardLevel < refreshedBind.guardLevel) {
|
|
123
|
+
buidInfo += `\n历史最高: ${refreshedBind.maxGuardLevelText} (${refreshedBind.maxGuardLevel})`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else if (refreshedBind.maxGuardLevel > 0) {
|
|
127
|
+
buidInfo += `\n历史舰长: ${refreshedBind.maxGuardLevelText} (${refreshedBind.maxGuardLevel})`;
|
|
128
|
+
}
|
|
129
|
+
if (refreshedBind.medalName) {
|
|
130
|
+
buidInfo += `\n粉丝牌: ${refreshedBind.medalName} Lv.${refreshedBind.medalLevel}`;
|
|
131
|
+
}
|
|
132
|
+
buidInfo += `\n\n💡 您可以使用 ${this.deps.formatCommand('mcid bind <用户名>')} 绑定MC账号`;
|
|
133
|
+
const messageElements = [koishi_1.h.text(buidInfo)];
|
|
134
|
+
if (this.deps.config.showAvatar) {
|
|
135
|
+
messageElements.push(koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${refreshedBind.buidUid}?size=160`));
|
|
136
|
+
}
|
|
137
|
+
return this.deps.sendMessage(session, messageElements);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('您尚未绑定MC账号,请使用 ' + this.deps.formatCommand('mcid bind <用户名>') + ' 进行绑定')]);
|
|
142
|
+
}
|
|
143
|
+
const updatedBind = await this.deps.checkAndUpdateUsername(selfBind);
|
|
144
|
+
return this.buildQueryResponse(session, updatedBind, null);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
148
|
+
this.deps.logger.error('查询', `QQ(${normalizedUserId})查询MC账号失败: ${error.message}`);
|
|
149
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`查询失败: ${error.message}`)]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 构建查询响应消息
|
|
154
|
+
*/
|
|
155
|
+
async buildQueryResponse(session, bind, targetId) {
|
|
156
|
+
const formattedUuid = this.deps.formatUuid(bind.mcUuid);
|
|
157
|
+
// MC头像
|
|
158
|
+
let mcAvatarUrl = null;
|
|
159
|
+
if (this.deps.config.showAvatar) {
|
|
160
|
+
if (this.deps.config.showMcSkin) {
|
|
161
|
+
mcAvatarUrl = this.deps.getStarlightSkinUrl(bind.mcUsername);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
mcAvatarUrl = this.deps.getCrafatarUrl(bind.mcUuid);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// 白名单信息
|
|
168
|
+
let whitelistInfo = '';
|
|
169
|
+
if (bind.whitelist && bind.whitelist.length > 0) {
|
|
170
|
+
const circledNumbers = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩'];
|
|
171
|
+
const serverList = bind.whitelist.map((serverId, index) => {
|
|
172
|
+
const server = this.deps.getServerConfigById(serverId);
|
|
173
|
+
if (!server) {
|
|
174
|
+
const disabledServer = this.deps.config.servers?.find(s => s.id === serverId);
|
|
175
|
+
if (disabledServer && disabledServer.enabled === false) {
|
|
176
|
+
return `${index < circledNumbers.length ? circledNumbers[index] : (index + 1)} ${disabledServer.name} [已停用]`;
|
|
177
|
+
}
|
|
178
|
+
return `${index < circledNumbers.length ? circledNumbers[index] : (index + 1)} 未知服务器(ID: ${serverId})`;
|
|
179
|
+
}
|
|
180
|
+
const circledNumber = index < circledNumbers.length ? circledNumbers[index] : `${index + 1}`;
|
|
181
|
+
let info = `${circledNumber} ${server.name}`;
|
|
182
|
+
if (server.displayAddress && server.displayAddress.trim()) {
|
|
183
|
+
info += `\n 地址: ${server.displayAddress}`;
|
|
184
|
+
}
|
|
185
|
+
return info;
|
|
186
|
+
}).join('\n');
|
|
187
|
+
whitelistInfo = `\n已加入以下服务器的白名单:\n${serverList}`;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
whitelistInfo = '\n未加入任何服务器的白名单';
|
|
191
|
+
}
|
|
192
|
+
// B站账号信息
|
|
193
|
+
let buidInfo = '';
|
|
194
|
+
let buidAvatar = null;
|
|
195
|
+
if (bind.buidUid) {
|
|
196
|
+
buidInfo = `B站账号信息:\nB站UID: ${bind.buidUid}\n用户名: ${bind.buidUsername}`;
|
|
197
|
+
if (bind.guardLevel > 0) {
|
|
198
|
+
buidInfo += `\n舰长等级: ${bind.guardLevelText} (${bind.guardLevel})`;
|
|
199
|
+
if (bind.maxGuardLevel > 0 && bind.maxGuardLevel < bind.guardLevel) {
|
|
200
|
+
buidInfo += `\n历史最高: ${bind.maxGuardLevelText} (${bind.maxGuardLevel})`;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else if (bind.maxGuardLevel > 0) {
|
|
204
|
+
buidInfo += `\n历史舰长: ${bind.maxGuardLevelText} (${bind.maxGuardLevel})`;
|
|
205
|
+
}
|
|
206
|
+
if (bind.medalName) {
|
|
207
|
+
buidInfo += `\n粉丝牌: ${bind.medalName} Lv.${bind.medalLevel}`;
|
|
208
|
+
}
|
|
209
|
+
if (this.deps.config.showAvatar) {
|
|
210
|
+
buidAvatar = koishi_1.h.image(`https://workers.vrp.moe/bilibili/avatar/${bind.buidUid}?size=160`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
buidInfo = targetId ? '该用户尚未绑定B站账号' : `您尚未绑定B站账号,使用 ${this.deps.formatCommand('buid bind <B站UID>')} 进行绑定`;
|
|
215
|
+
}
|
|
216
|
+
this.deps.logger.info('查询', `QQ(${bind.qqId})的MC账号信息:用户名=${bind.mcUsername}, UUID=${bind.mcUuid}`);
|
|
217
|
+
// 自动设置群昵称
|
|
218
|
+
if (bind.buidUid && bind.buidUsername) {
|
|
219
|
+
const mcName = bind.mcUsername && !bind.mcUsername.startsWith('_temp_') ? bind.mcUsername : null;
|
|
220
|
+
await this.deps.autoSetGroupNickname(session, mcName, bind.buidUsername, targetId || undefined);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
this.deps.logger.info('查询', `QQ(${bind.qqId})未绑定B站账号,跳过群昵称设置`);
|
|
224
|
+
}
|
|
225
|
+
const displayUsername = bind.mcUsername && !bind.mcUsername.startsWith('_temp_') ? bind.mcUsername : '未绑定';
|
|
226
|
+
const prefix = targetId ? `用户 ${targetId} 的` : '您的';
|
|
227
|
+
const messageElements = [
|
|
228
|
+
koishi_1.h.text(`${prefix}MC账号信息:\n用户名: ${displayUsername}\nUUID: ${formattedUuid}${whitelistInfo}`),
|
|
229
|
+
...(mcAvatarUrl ? [koishi_1.h.image(mcAvatarUrl)] : []),
|
|
230
|
+
koishi_1.h.text(`\n${buidInfo}`),
|
|
231
|
+
...(buidAvatar ? [buidAvatar] : [])
|
|
232
|
+
];
|
|
233
|
+
return this.deps.sendMessage(session, messageElements);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 通过MC用户名查询QQ号
|
|
237
|
+
*/
|
|
238
|
+
async handleFindUser(session, username) {
|
|
239
|
+
try {
|
|
240
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
241
|
+
// 检查权限
|
|
242
|
+
if (!await this.deps.isAdmin(session.userId)) {
|
|
243
|
+
this.deps.logger.warn('反向查询', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
244
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能使用此命令')]);
|
|
245
|
+
}
|
|
246
|
+
if (!username) {
|
|
247
|
+
this.deps.logger.warn('反向查询', `QQ(${normalizedUserId})未提供MC用户名`);
|
|
248
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('请提供要查询的MC用户名')]);
|
|
249
|
+
}
|
|
250
|
+
this.deps.logger.info('反向查询', `QQ(${normalizedUserId})尝试通过MC用户名"${username}"查询绑定的QQ账号`);
|
|
251
|
+
const bind = await this.deps.getMcBindByUsername(username);
|
|
252
|
+
if (!bind || !bind.qqId) {
|
|
253
|
+
this.deps.logger.info('反向查询', `MC用户名"${username}"未被任何QQ账号绑定`);
|
|
254
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`未找到绑定MC用户名"${username}"的QQ账号`)]);
|
|
255
|
+
}
|
|
256
|
+
// MC头像
|
|
257
|
+
let mcAvatarUrl = null;
|
|
258
|
+
if (this.deps.config.showAvatar) {
|
|
259
|
+
if (this.deps.config.showMcSkin) {
|
|
260
|
+
mcAvatarUrl = this.deps.getStarlightSkinUrl(bind.mcUsername);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
mcAvatarUrl = this.deps.getCrafatarUrl(bind.mcUuid);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const formattedUuid = this.deps.formatUuid(bind.mcUuid);
|
|
267
|
+
// 管理员信息
|
|
268
|
+
let adminInfo = '';
|
|
269
|
+
if (await this.deps.isAdmin(session.userId)) {
|
|
270
|
+
if (bind.whitelist && bind.whitelist.length > 0) {
|
|
271
|
+
const serverList = bind.whitelist.map(serverId => {
|
|
272
|
+
const server = this.deps.getServerConfigById(serverId);
|
|
273
|
+
return server ? server.name : `未知服务器(${serverId})`;
|
|
274
|
+
}).join('\n- ');
|
|
275
|
+
adminInfo = `\n\n白名单服务器:\n- ${serverList}`;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
adminInfo = '\n\n未加入任何服务器白名单';
|
|
279
|
+
}
|
|
280
|
+
adminInfo += `\n绑定时间: ${bind.lastModified ? new Date(bind.lastModified).toLocaleString() : '未知'}`;
|
|
281
|
+
adminInfo += `\n管理员权限: ${bind.isAdmin ? '是' : '否'}`;
|
|
282
|
+
}
|
|
283
|
+
this.deps.logger.info('反向查询', `成功: MC用户名"${username}"被QQ(${bind.qqId})绑定`);
|
|
284
|
+
const displayUsername = bind.mcUsername && !bind.mcUsername.startsWith('_temp_') ? bind.mcUsername : '未绑定';
|
|
285
|
+
return this.deps.sendMessage(session, [
|
|
286
|
+
koishi_1.h.text(`MC用户名"${displayUsername}"绑定信息:\nQQ号: ${bind.qqId}\nUUID: ${formattedUuid}${adminInfo}`),
|
|
287
|
+
...(mcAvatarUrl ? [koishi_1.h.image(mcAvatarUrl)] : [])
|
|
288
|
+
]);
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
292
|
+
this.deps.logger.error('反向查询', `QQ(${normalizedUserId})通过MC用户名"${username}"查询失败: ${error.message}`);
|
|
293
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* 绑定MC账号
|
|
298
|
+
*/
|
|
299
|
+
async handleBind(session, username, target) {
|
|
300
|
+
try {
|
|
301
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
302
|
+
// 检查用户名格式
|
|
303
|
+
if (!username || !/^[a-zA-Z0-9_]{3,16}$/.test(username)) {
|
|
304
|
+
this.deps.logger.warn('绑定', `QQ(${normalizedUserId})提供的用户名"${username}"格式无效`);
|
|
305
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('请提供有效的Minecraft用户名(3-16位字母、数字、下划线)')]);
|
|
306
|
+
}
|
|
307
|
+
// 验证用户名是否存在
|
|
308
|
+
const profile = await this.deps.validateUsername(username);
|
|
309
|
+
if (!profile) {
|
|
310
|
+
this.deps.logger.warn('绑定', `QQ(${normalizedUserId})提供的用户名"${username}"不存在`);
|
|
311
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`无法验证用户名: ${username},该用户可能不存在`)]);
|
|
312
|
+
}
|
|
313
|
+
username = profile.name;
|
|
314
|
+
const uuid = profile.id;
|
|
315
|
+
// 管理员为他人绑定
|
|
316
|
+
if (target) {
|
|
317
|
+
return this.handleBindForOther(session, username, uuid, target, normalizedUserId);
|
|
318
|
+
}
|
|
319
|
+
// 为自己绑定
|
|
320
|
+
return this.handleBindForSelf(session, username, uuid, normalizedUserId);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
324
|
+
this.deps.logger.error('绑定', `QQ(${normalizedUserId})绑定MC账号"${username}"失败: ${error.message}`);
|
|
325
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async handleBindForOther(session, username, uuid, target, operatorId) {
|
|
329
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
330
|
+
if (!normalizedTargetId) {
|
|
331
|
+
this.deps.logger.warn('绑定', `QQ(${operatorId})提供的目标用户ID"${target}"无效`);
|
|
332
|
+
if (target.startsWith('@')) {
|
|
333
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')]);
|
|
334
|
+
}
|
|
335
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')]);
|
|
336
|
+
}
|
|
337
|
+
this.deps.logger.debug('绑定', `QQ(${operatorId})尝试为QQ(${normalizedTargetId})绑定MC账号: ${username}(${uuid})`);
|
|
338
|
+
// 检查权限
|
|
339
|
+
if (!await this.deps.isAdmin(session.userId)) {
|
|
340
|
+
this.deps.logger.warn('绑定', `权限不足: QQ(${operatorId})不是管理员`);
|
|
341
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能为其他用户绑定MC账号')]);
|
|
342
|
+
}
|
|
343
|
+
// 检查用户名是否已被占用
|
|
344
|
+
if (await this.deps.checkUsernameExists(username, target)) {
|
|
345
|
+
this.deps.logger.warn('绑定', `MC用户名"${username}"已被其他QQ号绑定`);
|
|
346
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户名 ${username} 已被其他用户绑定`)]);
|
|
347
|
+
}
|
|
348
|
+
// 绑定
|
|
349
|
+
const bindResult = await this.deps.createOrUpdateMcBind(target, username, uuid);
|
|
350
|
+
if (!bindResult) {
|
|
351
|
+
this.deps.logger.error('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})绑定MC账号"${username}"失败`);
|
|
352
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`为用户 ${normalizedTargetId} 绑定MC账号失败: 数据库操作出错,请联系管理员`)]);
|
|
353
|
+
}
|
|
354
|
+
this.deps.logger.info('绑定', `成功: 管理员QQ(${operatorId})为QQ(${normalizedTargetId})绑定MC账号: ${username}(${uuid})`);
|
|
355
|
+
// 清理绑定会话
|
|
356
|
+
this.deps.removeBindingSession(target, session.channelId);
|
|
357
|
+
this.deps.logger.info('绑定', `管理员为QQ(${normalizedTargetId})绑定MC账号后,已清理该用户的交互式绑定会话`);
|
|
358
|
+
// 尝试设置群昵称
|
|
359
|
+
let targetBuidStatus = '';
|
|
360
|
+
try {
|
|
361
|
+
const latestTargetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
362
|
+
if (latestTargetBind && latestTargetBind.buidUid && latestTargetBind.buidUsername) {
|
|
363
|
+
await this.deps.autoSetGroupNickname(session, username, latestTargetBind.buidUsername, normalizedTargetId);
|
|
364
|
+
this.deps.logger.info('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})MC绑定完成,已尝试设置群昵称`);
|
|
365
|
+
targetBuidStatus = '\n✅ 该用户已绑定B站账号,群昵称已更新';
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
this.deps.logger.info('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})MC绑定完成,但目标用户未绑定B站账号`);
|
|
369
|
+
targetBuidStatus = '\n⚠️ 该用户尚未绑定B站账号,建议提醒其使用 buid bind 命令完成B站绑定';
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
catch (renameError) {
|
|
373
|
+
this.deps.logger.warn('绑定', `管理员QQ(${operatorId})为QQ(${normalizedTargetId})MC绑定后群昵称设置失败: ${renameError.message}`);
|
|
374
|
+
}
|
|
375
|
+
// MC头像
|
|
376
|
+
let mcAvatarUrl = null;
|
|
377
|
+
if (this.deps.config.showAvatar) {
|
|
378
|
+
if (this.deps.config.showMcSkin) {
|
|
379
|
+
mcAvatarUrl = this.deps.getStarlightSkinUrl(username);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
mcAvatarUrl = this.deps.getCrafatarUrl(uuid);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const formattedUuid = this.deps.formatUuid(uuid);
|
|
386
|
+
return this.deps.sendMessage(session, [
|
|
387
|
+
koishi_1.h.text(`已成功为用户 ${normalizedTargetId} 绑定MC账号\n用户名: ${username}\nUUID: ${formattedUuid}${targetBuidStatus}`),
|
|
388
|
+
...(mcAvatarUrl ? [koishi_1.h.image(mcAvatarUrl)] : [])
|
|
389
|
+
]);
|
|
390
|
+
}
|
|
391
|
+
async handleBindForSelf(session, username, uuid, operatorId) {
|
|
392
|
+
this.deps.logger.debug('绑定', `QQ(${operatorId})尝试绑定MC账号: ${username}(${uuid})`);
|
|
393
|
+
// 检查是否已绑定
|
|
394
|
+
const selfBind = await this.deps.getMcBindByQQId(operatorId);
|
|
395
|
+
if (selfBind && selfBind.mcUsername) {
|
|
396
|
+
const isTempUsername = selfBind.mcUsername.startsWith('_temp_');
|
|
397
|
+
if (!isTempUsername) {
|
|
398
|
+
// 检查冷却时间
|
|
399
|
+
if (!await this.deps.isAdmin(session.userId) && !this.deps.checkCooldown(selfBind.lastModified)) {
|
|
400
|
+
const days = this.deps.config.cooldownDays;
|
|
401
|
+
const now = new Date();
|
|
402
|
+
const diffTime = now.getTime() - selfBind.lastModified.getTime();
|
|
403
|
+
const passedDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
404
|
+
const remainingDays = days - passedDays;
|
|
405
|
+
this.deps.logger.warn('绑定', `QQ(${operatorId})已绑定MC账号"${selfBind.mcUsername}",且在冷却期内`);
|
|
406
|
+
const displayUsername = selfBind.mcUsername && !selfBind.mcUsername.startsWith('_temp_') ? selfBind.mcUsername : '未绑定';
|
|
407
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`您已绑定MC账号: ${displayUsername},如需修改,请在冷却期结束后(还需${remainingDays}天)使用 ` + this.deps.formatCommand('mcid change') + ` 命令或联系管理员。`)]);
|
|
408
|
+
}
|
|
409
|
+
this.deps.logger.debug('绑定', `QQ(${operatorId})已绑定MC账号"${selfBind.mcUsername}",建议使用change命令`);
|
|
410
|
+
const displayUsername = selfBind.mcUsername && !selfBind.mcUsername.startsWith('_temp_') ? selfBind.mcUsername : '未绑定';
|
|
411
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`您已绑定MC账号: ${displayUsername},如需修改请使用 ` + this.deps.formatCommand('mcid change') + ` 命令。`)]);
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
this.deps.logger.debug('绑定', `QQ(${operatorId})之前绑定的是临时用户名"${selfBind.mcUsername}",允许直接使用bind命令`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// 检查用户名是否已被占用
|
|
418
|
+
if (await this.deps.checkUsernameExists(username)) {
|
|
419
|
+
this.deps.logger.warn('绑定', `MC用户名"${username}"已被其他QQ号绑定`);
|
|
420
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户名 ${username} 已被其他用户绑定`)]);
|
|
421
|
+
}
|
|
422
|
+
// 绑定
|
|
423
|
+
const bindResult = await this.deps.createOrUpdateMcBind(session.userId, username, uuid);
|
|
424
|
+
if (!bindResult) {
|
|
425
|
+
this.deps.logger.error('绑定', `QQ(${operatorId})绑定MC账号"${username}"失败`);
|
|
426
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('绑定失败,数据库操作出错,请联系管理员')]);
|
|
427
|
+
}
|
|
428
|
+
this.deps.logger.info('绑定', `成功: QQ(${operatorId})绑定MC账号: ${username}(${uuid})`);
|
|
429
|
+
// 尝试设置群昵称
|
|
430
|
+
let buidReminder = '';
|
|
431
|
+
try {
|
|
432
|
+
const latestBind = await this.deps.getMcBindByQQId(operatorId);
|
|
433
|
+
if (latestBind && latestBind.buidUid && latestBind.buidUsername) {
|
|
434
|
+
await this.deps.autoSetGroupNickname(session, username, latestBind.buidUsername);
|
|
435
|
+
this.deps.logger.info('绑定', `QQ(${operatorId})MC绑定完成,已尝试设置群昵称`);
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
buidReminder = `\n\n💡 提醒:您还未绑定B站账号,建议使用 ${this.deps.formatCommand('buid bind <B站UID>')} 完成B站绑定以享受完整功能`;
|
|
439
|
+
this.deps.logger.info('绑定', `QQ(${operatorId})MC绑定完成,但未绑定B站账号`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
catch (renameError) {
|
|
443
|
+
this.deps.logger.warn('绑定', `QQ(${operatorId})MC绑定后群昵称设置失败: ${renameError.message}`);
|
|
444
|
+
}
|
|
445
|
+
// MC头像
|
|
446
|
+
let mcAvatarUrl = null;
|
|
447
|
+
if (this.deps.config.showAvatar) {
|
|
448
|
+
if (this.deps.config.showMcSkin) {
|
|
449
|
+
mcAvatarUrl = this.deps.getStarlightSkinUrl(username);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
mcAvatarUrl = this.deps.getCrafatarUrl(uuid);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const formattedUuid = this.deps.formatUuid(uuid);
|
|
456
|
+
return this.deps.sendMessage(session, [
|
|
457
|
+
koishi_1.h.text(`已成功绑定MC账号\n用户名: ${username}\nUUID: ${formattedUuid}${buidReminder}`),
|
|
458
|
+
...(mcAvatarUrl ? [koishi_1.h.image(mcAvatarUrl)] : [])
|
|
459
|
+
]);
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* 修改MC账号
|
|
463
|
+
*/
|
|
464
|
+
async handleChange(session, username, target) {
|
|
465
|
+
try {
|
|
466
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
467
|
+
// 检查用户名格式
|
|
468
|
+
if (!username || !/^[a-zA-Z0-9_]{3,16}$/.test(username)) {
|
|
469
|
+
this.deps.logger.warn('修改', `QQ(${normalizedUserId})提供的用户名"${username}"格式无效`);
|
|
470
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('请提供有效的Minecraft用户名(3-16位字母、数字、下划线)')]);
|
|
471
|
+
}
|
|
472
|
+
// 验证用户名是否存在
|
|
473
|
+
const profile = await this.deps.validateUsername(username);
|
|
474
|
+
if (!profile) {
|
|
475
|
+
this.deps.logger.warn('修改', `QQ(${normalizedUserId})提供的用户名"${username}"不存在`);
|
|
476
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`无法验证用户名: ${username},该用户可能不存在`)]);
|
|
477
|
+
}
|
|
478
|
+
username = profile.name;
|
|
479
|
+
const uuid = profile.id;
|
|
480
|
+
// 管理员为他人修改
|
|
481
|
+
if (target) {
|
|
482
|
+
return this.handleChangeForOther(session, username, uuid, target, normalizedUserId);
|
|
483
|
+
}
|
|
484
|
+
// 为自己修改
|
|
485
|
+
return this.handleChangeForSelf(session, username, uuid, normalizedUserId);
|
|
486
|
+
}
|
|
487
|
+
catch (error) {
|
|
488
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
489
|
+
this.deps.logger.error('修改', `QQ(${normalizedUserId})修改MC账号为"${username}"失败: ${error.message}`);
|
|
490
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
async handleChangeForOther(session, username, uuid, target, operatorId) {
|
|
494
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
495
|
+
if (!normalizedTargetId) {
|
|
496
|
+
this.deps.logger.warn('修改', `QQ(${operatorId})提供的目标用户ID"${target}"无效`);
|
|
497
|
+
if (target.startsWith('@')) {
|
|
498
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')]);
|
|
499
|
+
}
|
|
500
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')]);
|
|
501
|
+
}
|
|
502
|
+
this.deps.logger.info('修改', `QQ(${operatorId})尝试修改QQ(${normalizedTargetId})的MC账号为: ${username}(${uuid})`);
|
|
503
|
+
// 检查权限
|
|
504
|
+
if (!await this.deps.isAdmin(session.userId)) {
|
|
505
|
+
this.deps.logger.warn('修改', `权限不足: QQ(${operatorId})不是管理员`);
|
|
506
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能修改其他用户的MC账号')]);
|
|
507
|
+
}
|
|
508
|
+
// 获取目标用户信息
|
|
509
|
+
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
510
|
+
if (!targetBind || !targetBind.mcUsername) {
|
|
511
|
+
this.deps.logger.warn('修改', `QQ(${normalizedTargetId})尚未绑定MC账号`);
|
|
512
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 尚未绑定MC账号,请先使用 ` + this.deps.formatCommand('mcid bind') + ` 命令进行绑定`)]);
|
|
513
|
+
}
|
|
514
|
+
// 检查是否与当前用户名相同
|
|
515
|
+
if (targetBind.mcUsername === username) {
|
|
516
|
+
this.deps.logger.warn('修改', `QQ(${normalizedTargetId})已绑定相同的MC账号"${username}"`);
|
|
517
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 当前已绑定此用户名: ${username}`)]);
|
|
518
|
+
}
|
|
519
|
+
// 检查用户名是否已被占用
|
|
520
|
+
if (await this.deps.checkUsernameExists(username, target)) {
|
|
521
|
+
this.deps.logger.warn('修改', `MC用户名"${username}"已被其他QQ号绑定`);
|
|
522
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户名 ${username} 已被其他用户绑定`)]);
|
|
523
|
+
}
|
|
524
|
+
const oldUsername = targetBind.mcUsername;
|
|
525
|
+
// 更新绑定信息
|
|
526
|
+
const bindResult = await this.deps.createOrUpdateMcBind(target, username, uuid);
|
|
527
|
+
if (!bindResult) {
|
|
528
|
+
this.deps.logger.error('修改', `管理员QQ(${operatorId})修改QQ(${normalizedTargetId})的MC账号失败`);
|
|
529
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`修改用户 ${normalizedTargetId} 的MC账号失败: 数据库操作出错,请联系管理员`)]);
|
|
530
|
+
}
|
|
531
|
+
this.deps.logger.info('修改', `成功: 管理员QQ(${operatorId})修改QQ(${normalizedTargetId})的MC账号: ${oldUsername} -> ${username}(${uuid})`);
|
|
532
|
+
// MC头像
|
|
533
|
+
let mcAvatarUrl = null;
|
|
534
|
+
if (this.deps.config.showAvatar) {
|
|
535
|
+
if (this.deps.config.showMcSkin) {
|
|
536
|
+
mcAvatarUrl = this.deps.getStarlightSkinUrl(username);
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
mcAvatarUrl = this.deps.getCrafatarUrl(uuid);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const formattedUuid = this.deps.formatUuid(uuid);
|
|
543
|
+
return this.deps.sendMessage(session, [
|
|
544
|
+
koishi_1.h.text(`已成功将用户 ${normalizedTargetId} 的MC账号从 ${oldUsername} 修改为 ${username}\nUUID: ${formattedUuid}`),
|
|
545
|
+
...(mcAvatarUrl ? [koishi_1.h.image(mcAvatarUrl)] : [])
|
|
546
|
+
]);
|
|
547
|
+
}
|
|
548
|
+
async handleChangeForSelf(session, username, uuid, operatorId) {
|
|
549
|
+
const selfBind = await this.deps.getMcBindByQQId(operatorId);
|
|
550
|
+
// 检查是否已绑定
|
|
551
|
+
if (!selfBind || !selfBind.mcUsername) {
|
|
552
|
+
this.deps.logger.warn('修改', `QQ(${operatorId})尚未绑定MC账号`);
|
|
553
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('您尚未绑定MC账号,请使用 ' + this.deps.formatCommand('mcid bind') + ' 命令进行绑定')]);
|
|
554
|
+
}
|
|
555
|
+
// 检查是否与当前用户名相同
|
|
556
|
+
if (selfBind.mcUsername === username) {
|
|
557
|
+
this.deps.logger.warn('修改', `QQ(${operatorId})已绑定相同的MC账号"${username}"`);
|
|
558
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`您当前已绑定此用户名: ${username}`)]);
|
|
559
|
+
}
|
|
560
|
+
// 检查冷却时间
|
|
561
|
+
if (!await this.deps.isAdmin(session.userId) && !this.deps.checkCooldown(selfBind.lastModified)) {
|
|
562
|
+
const days = this.deps.config.cooldownDays;
|
|
563
|
+
const now = new Date();
|
|
564
|
+
const diffTime = now.getTime() - selfBind.lastModified.getTime();
|
|
565
|
+
const passedDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
|
566
|
+
const remainingDays = days - passedDays;
|
|
567
|
+
this.deps.logger.warn('修改', `QQ(${operatorId})在冷却期内,无法修改MC账号`);
|
|
568
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`您的MC账号绑定在冷却期内,还需${remainingDays}天才能修改。如需立即修改,请联系管理员。`)]);
|
|
569
|
+
}
|
|
570
|
+
// 检查用户名是否已被占用
|
|
571
|
+
if (await this.deps.checkUsernameExists(username, session.userId)) {
|
|
572
|
+
this.deps.logger.warn('修改', `MC用户名"${username}"已被其他QQ号绑定`);
|
|
573
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户名 ${username} 已被其他用户绑定`)]);
|
|
574
|
+
}
|
|
575
|
+
const oldUsername = selfBind.mcUsername;
|
|
576
|
+
// 更新绑定信息
|
|
577
|
+
const bindResult = await this.deps.createOrUpdateMcBind(session.userId, username, uuid);
|
|
578
|
+
if (!bindResult) {
|
|
579
|
+
this.deps.logger.error('修改', `QQ(${operatorId})修改MC账号失败`);
|
|
580
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('修改失败,数据库操作出错,请联系管理员')]);
|
|
581
|
+
}
|
|
582
|
+
this.deps.logger.info('修改', `成功: QQ(${operatorId})修改MC账号: ${oldUsername} -> ${username}(${uuid})`);
|
|
583
|
+
// MC头像
|
|
584
|
+
let mcAvatarUrl = null;
|
|
585
|
+
if (this.deps.config.showAvatar) {
|
|
586
|
+
if (this.deps.config.showMcSkin) {
|
|
587
|
+
mcAvatarUrl = this.deps.getStarlightSkinUrl(username);
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
mcAvatarUrl = this.deps.getCrafatarUrl(uuid);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const formattedUuid = this.deps.formatUuid(uuid);
|
|
594
|
+
return this.deps.sendMessage(session, [
|
|
595
|
+
koishi_1.h.text(`已成功将MC账号从 ${oldUsername} 修改为 ${username}\nUUID: ${formattedUuid}`),
|
|
596
|
+
...(mcAvatarUrl ? [koishi_1.h.image(mcAvatarUrl)] : [])
|
|
597
|
+
]);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* 解绑MC账号
|
|
601
|
+
*/
|
|
602
|
+
async handleUnbind(session, target) {
|
|
603
|
+
try {
|
|
604
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
605
|
+
// 管理员为他人解绑
|
|
606
|
+
if (target) {
|
|
607
|
+
return this.handleUnbindForOther(session, target, normalizedUserId);
|
|
608
|
+
}
|
|
609
|
+
// 为自己解绑
|
|
610
|
+
return this.handleUnbindForSelf(session, normalizedUserId);
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
614
|
+
const targetInfo = target ? `为QQ(${this.deps.normalizeQQId(target)})` : '';
|
|
615
|
+
this.deps.logger.error('解绑', `QQ(${normalizedUserId})${targetInfo}解绑MC账号失败: ${error.message}`);
|
|
616
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
async handleUnbindForOther(session, target, operatorId) {
|
|
620
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
621
|
+
if (!normalizedTargetId) {
|
|
622
|
+
this.deps.logger.warn('解绑', `QQ(${operatorId})提供的目标用户ID"${target}"无效`);
|
|
623
|
+
if (target.startsWith('@')) {
|
|
624
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')]);
|
|
625
|
+
}
|
|
626
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')]);
|
|
627
|
+
}
|
|
628
|
+
this.deps.logger.info('解绑', `QQ(${operatorId})尝试为QQ(${normalizedTargetId})解绑MC账号`);
|
|
629
|
+
// 检查权限
|
|
630
|
+
if (!await this.deps.isAdmin(session.userId)) {
|
|
631
|
+
this.deps.logger.warn('解绑', `权限不足: QQ(${operatorId})不是管理员`);
|
|
632
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能为其他用户解绑MC账号')]);
|
|
633
|
+
}
|
|
634
|
+
// 获取目标用户信息
|
|
635
|
+
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
636
|
+
if (!targetBind || !targetBind.mcUsername) {
|
|
637
|
+
this.deps.logger.warn('解绑', `QQ(${normalizedTargetId})尚未绑定MC账号`);
|
|
638
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 尚未绑定MC账号`)]);
|
|
639
|
+
}
|
|
640
|
+
const oldUsername = targetBind.mcUsername && !targetBind.mcUsername.startsWith('_temp_') ? targetBind.mcUsername : '未绑定';
|
|
641
|
+
const oldBuidInfo = targetBind.buidUid ? ` 和 B站账号: ${targetBind.buidUsername}(${targetBind.buidUid})` : '';
|
|
642
|
+
// 删除绑定记录
|
|
643
|
+
await this.deps.deleteMcBind(target);
|
|
644
|
+
this.deps.logger.info('解绑', `成功: 管理员QQ(${operatorId})为QQ(${normalizedTargetId})解绑MC账号: ${oldUsername}${oldBuidInfo}`);
|
|
645
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功为用户 ${normalizedTargetId} 解绑MC账号: ${oldUsername}${oldBuidInfo}`)]);
|
|
646
|
+
}
|
|
647
|
+
async handleUnbindForSelf(session, operatorId) {
|
|
648
|
+
this.deps.logger.info('解绑', `QQ(${operatorId})尝试解绑自己的MC账号`);
|
|
649
|
+
const selfBind = await this.deps.getMcBindByQQId(operatorId);
|
|
650
|
+
if (!selfBind || !selfBind.mcUsername) {
|
|
651
|
+
this.deps.logger.warn('解绑', `QQ(${operatorId})尚未绑定MC账号`);
|
|
652
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('您尚未绑定MC账号')]);
|
|
653
|
+
}
|
|
654
|
+
const oldUsername = selfBind.mcUsername && !selfBind.mcUsername.startsWith('_temp_') ? selfBind.mcUsername : '未绑定';
|
|
655
|
+
const oldBuidInfo = selfBind.buidUid ? ` 和 B站账号: ${selfBind.buidUsername}(${selfBind.buidUid})` : '';
|
|
656
|
+
// 删除绑定记录
|
|
657
|
+
await this.deps.deleteMcBind(operatorId);
|
|
658
|
+
this.deps.logger.info('解绑', `成功: QQ(${operatorId})解绑MC账号: ${oldUsername}${oldBuidInfo}`);
|
|
659
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功解绑MC账号: ${oldUsername}${oldBuidInfo}`)]);
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* 设置管理员
|
|
663
|
+
*/
|
|
664
|
+
async handleAdmin(session, target) {
|
|
665
|
+
try {
|
|
666
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
667
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
668
|
+
this.deps.logger.info('管理员', `QQ(${normalizedUserId})尝试将QQ(${normalizedTargetId})设为管理员`);
|
|
669
|
+
// 检查是否为主人
|
|
670
|
+
if (!this.deps.isMaster(session.userId)) {
|
|
671
|
+
this.deps.logger.warn('管理员', `权限不足: QQ(${normalizedUserId})不是主人`);
|
|
672
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有主人才能设置管理员')]);
|
|
673
|
+
}
|
|
674
|
+
// 检查目标用户是否已经是管理员
|
|
675
|
+
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
676
|
+
const isAlreadyAdmin = targetBind && targetBind.isAdmin === true;
|
|
677
|
+
if (isAlreadyAdmin) {
|
|
678
|
+
this.deps.logger.warn('管理员', `QQ(${normalizedTargetId})已经是管理员`);
|
|
679
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 已经是管理员`)]);
|
|
680
|
+
}
|
|
681
|
+
// 如果用户存在绑定记录,更新为管理员
|
|
682
|
+
if (targetBind) {
|
|
683
|
+
await this.deps.mcidbindRepo.update(normalizedTargetId, {
|
|
684
|
+
isAdmin: true,
|
|
685
|
+
});
|
|
686
|
+
this.deps.logger.info('管理员', `成功: 主人QQ(${normalizedUserId})将QQ(${normalizedTargetId})设为管理员`);
|
|
687
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功将用户 ${normalizedTargetId} 设为管理员`)]);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
// 用户不存在绑定记录,创建一个新记录并设为管理员
|
|
691
|
+
const tempUsername = `_temp_${normalizedTargetId}`;
|
|
692
|
+
try {
|
|
693
|
+
await this.deps.mcidbindRepo.create({
|
|
694
|
+
qqId: normalizedTargetId,
|
|
695
|
+
mcUsername: tempUsername,
|
|
696
|
+
mcUuid: '',
|
|
697
|
+
lastModified: new Date(),
|
|
698
|
+
isAdmin: true
|
|
699
|
+
});
|
|
700
|
+
this.deps.logger.info('管理员', `成功: 主人QQ(${normalizedUserId})将QQ(${normalizedTargetId})设为管理员 (创建新记录)`);
|
|
701
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功将用户 ${normalizedTargetId} 设为管理员 (未绑定MC账号)`)]);
|
|
702
|
+
}
|
|
703
|
+
catch (createError) {
|
|
704
|
+
this.deps.logger.error('管理员', `创建管理员记录失败: ${createError.message}`);
|
|
705
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(createError))]);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
catch (error) {
|
|
710
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
711
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
712
|
+
this.deps.logger.error('管理员', `QQ(${normalizedUserId})将QQ(${normalizedTargetId})设为管理员失败: ${error.message}`);
|
|
713
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* 撤销管理员
|
|
718
|
+
*/
|
|
719
|
+
async handleUnadmin(session, target) {
|
|
720
|
+
try {
|
|
721
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
722
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
723
|
+
this.deps.logger.info('管理员', `QQ(${normalizedUserId})尝试撤销QQ(${normalizedTargetId})的管理员权限`);
|
|
724
|
+
// 检查是否为主人
|
|
725
|
+
if (!this.deps.isMaster(session.userId)) {
|
|
726
|
+
this.deps.logger.warn('管理员', `权限不足: QQ(${normalizedUserId})不是主人`);
|
|
727
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有主人才能撤销管理员权限')]);
|
|
728
|
+
}
|
|
729
|
+
// 检查目标用户是否是管理员
|
|
730
|
+
const targetBind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
731
|
+
const isAdmin = targetBind && targetBind.isAdmin === true;
|
|
732
|
+
if (!isAdmin) {
|
|
733
|
+
this.deps.logger.warn('管理员', `QQ(${normalizedTargetId})不是管理员`);
|
|
734
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`用户 ${normalizedTargetId} 不是管理员`)]);
|
|
735
|
+
}
|
|
736
|
+
// 撤销管理员权限
|
|
737
|
+
await this.deps.mcidbindRepo.update(normalizedTargetId, {
|
|
738
|
+
isAdmin: false,
|
|
739
|
+
});
|
|
740
|
+
this.deps.logger.info('管理员', `成功: 主人QQ(${normalizedUserId})撤销了QQ(${normalizedTargetId})的管理员权限`);
|
|
741
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`已成功撤销用户 ${normalizedTargetId} 的管理员权限`)]);
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
745
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
746
|
+
this.deps.logger.error('管理员', `QQ(${normalizedUserId})撤销QQ(${normalizedTargetId})的管理员权限失败: ${error.message}`);
|
|
747
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* 列出所有管理员
|
|
752
|
+
*/
|
|
753
|
+
async handleAdminlist(session) {
|
|
754
|
+
try {
|
|
755
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
756
|
+
this.deps.logger.info('管理员', `QQ(${normalizedUserId})尝试查看管理员列表`);
|
|
757
|
+
// 检查是否为主人
|
|
758
|
+
if (!this.deps.isMaster(session.userId)) {
|
|
759
|
+
this.deps.logger.warn('管理员', `权限不足: QQ(${normalizedUserId})不是主人`);
|
|
760
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有主人才能查看管理员列表')]);
|
|
761
|
+
}
|
|
762
|
+
// 查询所有管理员
|
|
763
|
+
const admins = await this.deps.mcidbindRepo.findAllAdmins();
|
|
764
|
+
if (admins.length === 0) {
|
|
765
|
+
this.deps.logger.info('管理员', `管理员列表为空`);
|
|
766
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('当前没有管理员')]);
|
|
767
|
+
}
|
|
768
|
+
// 格式化管理员列表
|
|
769
|
+
const adminList = admins.map(admin => {
|
|
770
|
+
const displayUsername = admin.mcUsername && !admin.mcUsername.startsWith('_temp_') ? admin.mcUsername : null;
|
|
771
|
+
return `- ${admin.qqId}${displayUsername ? ` (MC: ${displayUsername})` : ''}`;
|
|
772
|
+
}).join('\n');
|
|
773
|
+
this.deps.logger.info('管理员', `成功: 主人QQ(${normalizedUserId})查看了管理员列表`);
|
|
774
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`管理员列表:\n${adminList}\n\n共 ${admins.length} 名管理员`)]);
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
778
|
+
this.deps.logger.error('管理员', `QQ(${normalizedUserId})查看管理员列表失败: ${error.message}`);
|
|
779
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* 查看统计
|
|
784
|
+
*/
|
|
785
|
+
async handleStats(session) {
|
|
786
|
+
try {
|
|
787
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
788
|
+
this.deps.logger.info('统计', `QQ(${normalizedUserId})尝试查看数据库统计`);
|
|
789
|
+
// 检查权限
|
|
790
|
+
if (!await this.deps.isAdmin(session.userId)) {
|
|
791
|
+
this.deps.logger.warn('统计', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
792
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能查看统计信息')]);
|
|
793
|
+
}
|
|
794
|
+
// 查询所有绑定记录
|
|
795
|
+
const allBinds = await this.deps.mcidbindRepo.findAll();
|
|
796
|
+
// 统计绑定情况
|
|
797
|
+
let mcidBoundUsers = 0;
|
|
798
|
+
let buidBoundUsers = 0;
|
|
799
|
+
for (const bind of allBinds) {
|
|
800
|
+
const hasMcid = bind.mcUsername && !bind.mcUsername.startsWith('_temp_');
|
|
801
|
+
if (hasMcid) {
|
|
802
|
+
mcidBoundUsers++;
|
|
803
|
+
}
|
|
804
|
+
const hasBuid = bind.buidUid && bind.buidUid.trim() !== '';
|
|
805
|
+
if (hasBuid) {
|
|
806
|
+
buidBoundUsers++;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
let statsInfo = `📊 绑定统计\n`;
|
|
810
|
+
statsInfo += `\n已绑定MCID: ${mcidBoundUsers}人\n`;
|
|
811
|
+
statsInfo += `已绑定BUID: ${buidBoundUsers}人`;
|
|
812
|
+
this.deps.logger.info('统计', `成功: 管理员QQ(${normalizedUserId})查看了数据库统计`);
|
|
813
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(statsInfo)]);
|
|
814
|
+
}
|
|
815
|
+
catch (error) {
|
|
816
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
817
|
+
this.deps.logger.error('统计', `QQ(${normalizedUserId})查看统计失败: ${error.message}`);
|
|
818
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* 修复群昵称
|
|
823
|
+
*/
|
|
824
|
+
async handleFixNicknames(session, groupId) {
|
|
825
|
+
try {
|
|
826
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
827
|
+
// 检查权限
|
|
828
|
+
if (!await this.deps.isAdmin(session.userId)) {
|
|
829
|
+
this.deps.logger.warn('群昵称修复', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
830
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能执行群昵称修复操作')]);
|
|
831
|
+
}
|
|
832
|
+
// 确定目标群ID
|
|
833
|
+
const targetGroupId = groupId || session.channelId;
|
|
834
|
+
// 验证群ID格式
|
|
835
|
+
if (groupId && !/^\d+$/.test(groupId)) {
|
|
836
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 群号格式无效,请提供正确的群号')]);
|
|
837
|
+
}
|
|
838
|
+
// 检查bot是否在目标群中
|
|
839
|
+
try {
|
|
840
|
+
if (session.bot.internal) {
|
|
841
|
+
await session.bot.internal.getGroupInfo(targetGroupId);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
catch (error) {
|
|
845
|
+
this.deps.logger.warn('群昵称修复', `Bot不在群${targetGroupId}中或无法获取群信息`);
|
|
846
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`❌ Bot不在群 ${targetGroupId} 中或无权限操作该群`)]);
|
|
847
|
+
}
|
|
848
|
+
const groupDisplayText = groupId ? `群 ${targetGroupId}` : '当前群';
|
|
849
|
+
this.deps.logger.info('群昵称修复', `管理员QQ(${normalizedUserId})开始批量修复${groupDisplayText}的群昵称`);
|
|
850
|
+
await this.deps.sendMessage(session, [koishi_1.h.text(`🔧 开始检查并修复${groupDisplayText}的所有用户群昵称格式,请稍候...`)]);
|
|
851
|
+
// 获取所有已绑定B站的用户
|
|
852
|
+
const allBinds = await this.deps.mcidbindRepo.findAll();
|
|
853
|
+
const usersWithBuid = allBinds.filter(bind => bind.buidUid && bind.buidUsername);
|
|
854
|
+
let checkedCount = 0;
|
|
855
|
+
let fixedCount = 0;
|
|
856
|
+
let errorCount = 0;
|
|
857
|
+
const results = [];
|
|
858
|
+
for (const bind of usersWithBuid) {
|
|
859
|
+
try {
|
|
860
|
+
checkedCount++;
|
|
861
|
+
// 获取用户当前群昵称
|
|
862
|
+
let currentNickname = '';
|
|
863
|
+
try {
|
|
864
|
+
if (session.bot.internal) {
|
|
865
|
+
const groupInfo = await session.bot.internal.getGroupMemberInfo(targetGroupId, bind.qqId);
|
|
866
|
+
currentNickname = groupInfo.card || groupInfo.nickname || '';
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
catch (error) {
|
|
870
|
+
errorCount++;
|
|
871
|
+
results.push(`❌ ${bind.qqId}: 获取群信息失败`);
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
// 检查昵称格式
|
|
875
|
+
const mcInfo = bind.mcUsername && !bind.mcUsername.startsWith('_temp_') ? bind.mcUsername : null;
|
|
876
|
+
const isCorrect = this.deps.checkNicknameFormat(currentNickname, bind.buidUsername, mcInfo);
|
|
877
|
+
if (!isCorrect) {
|
|
878
|
+
// 修复群昵称
|
|
879
|
+
await this.deps.autoSetGroupNickname(session, mcInfo, bind.buidUsername, bind.qqId, targetGroupId);
|
|
880
|
+
fixedCount++;
|
|
881
|
+
const expectedFormat = `${bind.buidUsername}(ID:${mcInfo || '未绑定'})`;
|
|
882
|
+
results.push(`✅ ${bind.qqId}: "${currentNickname}" → "${expectedFormat}"`);
|
|
883
|
+
// 添加延迟避免频率限制
|
|
884
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
results.push(`✓ ${bind.qqId}: 格式正确`);
|
|
888
|
+
}
|
|
889
|
+
// 每处理10个用户发送一次进度
|
|
890
|
+
if (checkedCount % 10 === 0) {
|
|
891
|
+
await this.deps.sendMessage(session, [koishi_1.h.text(`进度: ${checkedCount}/${usersWithBuid.length} | 修复: ${fixedCount} | 错误: ${errorCount}`)]);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
catch (error) {
|
|
895
|
+
errorCount++;
|
|
896
|
+
results.push(`❌ ${bind.qqId}: 处理出错 - ${error.message}`);
|
|
897
|
+
this.deps.logger.error('群昵称修复', `处理用户QQ(${bind.qqId})时出错: ${error.message}`);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
// 生成最终报告
|
|
901
|
+
let resultMessage = `🔧 ${groupDisplayText}群昵称修复完成\n共检查${checkedCount}个用户\n✅ 修复: ${fixedCount}个\n❌ 错误: ${errorCount}个`;
|
|
902
|
+
if (usersWithBuid.length <= 20) {
|
|
903
|
+
resultMessage += '\n\n详细结果:\n' + results.join('\n');
|
|
904
|
+
}
|
|
905
|
+
else {
|
|
906
|
+
const fixedResults = results.filter(r => r.includes('→'));
|
|
907
|
+
if (fixedResults.length > 0) {
|
|
908
|
+
resultMessage += '\n\n修复的用户:\n' + fixedResults.slice(0, 10).join('\n');
|
|
909
|
+
if (fixedResults.length > 10) {
|
|
910
|
+
resultMessage += `\n... 还有${fixedResults.length - 10}个用户被修复`;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
this.deps.logger.info('群昵称修复', `修复完成: 管理员QQ(${normalizedUserId})在${groupDisplayText}检查${checkedCount}个用户,修复${fixedCount}个,错误${errorCount}个`);
|
|
915
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(resultMessage)]);
|
|
916
|
+
}
|
|
917
|
+
catch (error) {
|
|
918
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
919
|
+
this.deps.logger.error('群昵称修复', `QQ(${normalizedUserId})执行群昵称修复失败: ${error.message}`);
|
|
920
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* 清除提醒冷却
|
|
925
|
+
*/
|
|
926
|
+
async handleClearReminder(session, target) {
|
|
927
|
+
try {
|
|
928
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
929
|
+
// 检查权限
|
|
930
|
+
if (!await this.deps.isAdmin(session.userId)) {
|
|
931
|
+
this.deps.logger.warn('清除冷却', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
932
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能清除提醒冷却和次数')]);
|
|
933
|
+
}
|
|
934
|
+
// 注意: reminderCooldown 需要从外部传入或在 deps 中提供
|
|
935
|
+
// 这里简化处理,只更新数据库
|
|
936
|
+
if (target) {
|
|
937
|
+
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
938
|
+
const bind = await this.deps.getMcBindByQQId(normalizedTargetId);
|
|
939
|
+
if (bind) {
|
|
940
|
+
await this.deps.mcidbindRepo.update(normalizedTargetId, { reminderCount: 0 });
|
|
941
|
+
}
|
|
942
|
+
this.deps.logger.info('清除冷却', `管理员QQ(${normalizedUserId})清除了QQ(${normalizedTargetId})的提醒次数`);
|
|
943
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`已清除用户 ${normalizedTargetId} 的随机提醒次数`)]);
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
const allBinds = await this.deps.mcidbindRepo.findAll();
|
|
947
|
+
for (const bind of allBinds) {
|
|
948
|
+
await this.deps.mcidbindRepo.update(bind.qqId, { reminderCount: 0 });
|
|
949
|
+
}
|
|
950
|
+
this.deps.logger.info('清除冷却', `管理员QQ(${normalizedUserId})清除了所有用户的提醒次数`);
|
|
951
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`已清除所有用户的随机提醒次数`)]);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
catch (error) {
|
|
955
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
956
|
+
this.deps.logger.error('清除冷却', `QQ(${normalizedUserId})清除提醒冷却失败: ${error.message}`);
|
|
957
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* 导出群数据
|
|
962
|
+
*/
|
|
963
|
+
async handleExport(session, groupId) {
|
|
964
|
+
try {
|
|
965
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
966
|
+
// 检查权限
|
|
967
|
+
if (!await this.deps.isAdmin(session.userId)) {
|
|
968
|
+
this.deps.logger.warn('数据导出', `权限不足: QQ(${normalizedUserId})不是管理员`);
|
|
969
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('只有管理员才能导出群数据')]);
|
|
970
|
+
}
|
|
971
|
+
// 检查是否为私聊
|
|
972
|
+
if (!session.channelId?.startsWith('private:')) {
|
|
973
|
+
this.deps.logger.warn('数据导出', `QQ(${normalizedUserId})尝试在群聊中使用导出命令`);
|
|
974
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('为了数据安全,导出命令仅支持在私聊中使用')]);
|
|
975
|
+
}
|
|
976
|
+
// 验证群ID格式
|
|
977
|
+
if (!groupId || !/^\d+$/.test(groupId)) {
|
|
978
|
+
return this.deps.sendMessage(session, [koishi_1.h.text('❌ 群号格式无效,请提供正确的群号')]);
|
|
979
|
+
}
|
|
980
|
+
// 检查bot是否在目标群中
|
|
981
|
+
try {
|
|
982
|
+
if (session.bot.internal) {
|
|
983
|
+
await session.bot.internal.getGroupInfo(groupId);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
catch (error) {
|
|
987
|
+
this.deps.logger.warn('数据导出', `Bot不在群${groupId}中或无法获取群信息`);
|
|
988
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`❌ Bot不在群 ${groupId} 中或无权限操作该群`)]);
|
|
989
|
+
}
|
|
990
|
+
this.deps.logger.info('数据导出', `管理员QQ(${normalizedUserId})开始导出群${groupId}的数据`);
|
|
991
|
+
await this.deps.sendMessage(session, [koishi_1.h.text(`📊 开始导出群 ${groupId} 的数据,请稍候...`)]);
|
|
992
|
+
try {
|
|
993
|
+
// 导出数据
|
|
994
|
+
const excelBuffer = await this.deps.groupExporter.exportGroupData(session, groupId);
|
|
995
|
+
const fileName = this.deps.groupExporter.getExportFileName(groupId);
|
|
996
|
+
// 先发送成功消息
|
|
997
|
+
await this.deps.sendMessage(session, [koishi_1.h.text(`✅ 群 ${groupId} 数据导出完成!正在发送文件...`)]);
|
|
998
|
+
// 发送文件
|
|
999
|
+
try {
|
|
1000
|
+
const base64Data = excelBuffer.toString('base64');
|
|
1001
|
+
if (session.bot.internal) {
|
|
1002
|
+
await session.bot.internal.uploadPrivateFile({
|
|
1003
|
+
user_id: parseInt(normalizedUserId),
|
|
1004
|
+
file: `base64://${base64Data}`,
|
|
1005
|
+
name: fileName
|
|
1006
|
+
});
|
|
1007
|
+
await this.deps.sendMessage(session, [koishi_1.h.text(`📁 文件已发送: ${fileName}`)]);
|
|
1008
|
+
this.deps.logger.info('数据导出', `成功发送文件到私聊: ${fileName}`);
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
throw new Error('Bot不支持内部API调用');
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
catch (fileError) {
|
|
1015
|
+
this.deps.logger.error('数据导出', `文件发送失败: ${fileError.message}`);
|
|
1016
|
+
// 降级方案:保存文件
|
|
1017
|
+
try {
|
|
1018
|
+
const filePath = await this.deps.groupExporter.saveExcelFile(excelBuffer, fileName);
|
|
1019
|
+
await this.deps.sendMessage(session, [
|
|
1020
|
+
koishi_1.h.text(`⚠️ 直接发送失败,文件已保存\n文件路径: ${filePath}\n文件名: ${fileName}\n请联系管理员获取文件`)
|
|
1021
|
+
]);
|
|
1022
|
+
// 清理过期文件
|
|
1023
|
+
this.deps.groupExporter.cleanupOldFiles().catch(err => this.deps.logger.warn('数据导出', `清理临时文件时出错: ${err.message}`));
|
|
1024
|
+
}
|
|
1025
|
+
catch (saveError) {
|
|
1026
|
+
await this.deps.sendMessage(session, [
|
|
1027
|
+
koishi_1.h.text(`❌ 文件发送和保存都失败了\n导出数据成功但无法发送文件\n请联系管理员检查Bot配置`)
|
|
1028
|
+
]);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
this.deps.logger.info('数据导出', `管理员QQ(${normalizedUserId})成功导出群${groupId}的数据,文件名: ${fileName}`);
|
|
1032
|
+
}
|
|
1033
|
+
catch (exportError) {
|
|
1034
|
+
this.deps.logger.error('数据导出', `导出群${groupId}数据失败: ${exportError.message}`);
|
|
1035
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(`❌ 导出失败: ${exportError.message}`)]);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
catch (error) {
|
|
1039
|
+
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
1040
|
+
this.deps.logger.error('数据导出', `QQ(${normalizedUserId})导出群数据失败: ${error.message}`);
|
|
1041
|
+
return this.deps.sendMessage(session, [koishi_1.h.text(this.deps.getFriendlyErrorMessage(error))]);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
exports.McidCommandHandler = McidCommandHandler;
|