koishi-plugin-bind-bot 2.1.1 → 2.1.2
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.js +13 -6
- package/lib/force-bind-utils.js +12 -8
- package/lib/handlers/binding.handler.js +63 -27
- package/lib/handlers/buid.handler.js +102 -45
- package/lib/handlers/lottery.handler.js +11 -9
- package/lib/handlers/mcid.handler.js +197 -86
- package/lib/handlers/tag.handler.js +86 -31
- package/lib/handlers/whitelist.handler.js +252 -77
- package/lib/index.js +260 -142
- package/lib/repositories/mcidbind.repository.js +2 -2
- package/lib/repositories/schedule-mute.repository.js +2 -2
- package/lib/services/api.service.js +27 -23
- package/lib/services/database.service.js +16 -14
- package/lib/services/nickname.service.js +10 -10
- package/lib/types/api.d.ts +90 -0
- package/lib/types/config.d.ts +61 -0
- package/lib/types/database.d.ts +50 -0
- package/lib/types/update-data.d.ts +83 -0
- package/lib/utils/error-utils.js +7 -8
- package/lib/utils/helpers.js +45 -7
- package/lib/utils/message-utils.js +36 -23
- package/lib/utils/session-manager.js +6 -1
- package/package.json +12 -2
package/lib/export-utils.js
CHANGED
|
@@ -76,10 +76,14 @@ class GroupExporter {
|
|
|
76
76
|
*/
|
|
77
77
|
translateRole(role) {
|
|
78
78
|
switch (role) {
|
|
79
|
-
case 'owner':
|
|
80
|
-
|
|
81
|
-
case '
|
|
82
|
-
|
|
79
|
+
case 'owner':
|
|
80
|
+
return '群主';
|
|
81
|
+
case 'admin':
|
|
82
|
+
return '管理员';
|
|
83
|
+
case 'member':
|
|
84
|
+
return '成员';
|
|
85
|
+
default:
|
|
86
|
+
return role || '未知';
|
|
83
87
|
}
|
|
84
88
|
}
|
|
85
89
|
/**
|
|
@@ -243,7 +247,7 @@ class GroupExporter {
|
|
|
243
247
|
return excelBuffer;
|
|
244
248
|
}
|
|
245
249
|
catch (error) {
|
|
246
|
-
this.logger.error('群数据导出',
|
|
250
|
+
this.logger.error('群数据导出', '导出群数据失败', error);
|
|
247
251
|
throw error;
|
|
248
252
|
}
|
|
249
253
|
}
|
|
@@ -252,7 +256,10 @@ class GroupExporter {
|
|
|
252
256
|
*/
|
|
253
257
|
getExportFileName(groupId) {
|
|
254
258
|
const now = new Date();
|
|
255
|
-
const dateStr = now
|
|
259
|
+
const dateStr = now
|
|
260
|
+
.toISOString()
|
|
261
|
+
.slice(0, 19)
|
|
262
|
+
.replace(/[:\-T]/g, '');
|
|
256
263
|
return `群${groupId}_绑定数据_${dateStr}.xlsx`;
|
|
257
264
|
}
|
|
258
265
|
/**
|
package/lib/force-bind-utils.js
CHANGED
|
@@ -99,7 +99,7 @@ class ForceBinder {
|
|
|
99
99
|
return response.data;
|
|
100
100
|
}
|
|
101
101
|
catch (error) {
|
|
102
|
-
this.logger.error('强制绑定',
|
|
102
|
+
this.logger.error('强制绑定', '获取B站粉丝勋章失败', error);
|
|
103
103
|
throw error;
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -125,7 +125,7 @@ class ForceBinder {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
catch (error) {
|
|
128
|
-
this.logger.error('强制绑定',
|
|
128
|
+
this.logger.error('强制绑定', '获取ZMINFO用户信息失败', error);
|
|
129
129
|
throw new Error(`无法获取用户信息: ${error.message}`);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -197,13 +197,13 @@ class ForceBinder {
|
|
|
197
197
|
this.logger.debug('强制绑定', `已检查目标粉丝牌,找到: ${targetMedalInfo.found}`);
|
|
198
198
|
}
|
|
199
199
|
else {
|
|
200
|
-
this.logger.warn('强制绑定',
|
|
200
|
+
this.logger.warn('强制绑定', 'B站API未返回粉丝勋章列表数据');
|
|
201
201
|
}
|
|
202
202
|
this.logger.info('强制绑定', `强制绑定完成: 用户=${enhancedUserInfo.username}(${uid}), 目标粉丝牌=${targetMedalInfo.found ? '已找到' : '未找到'}`);
|
|
203
203
|
return enhancedUserInfo;
|
|
204
204
|
}
|
|
205
205
|
catch (error) {
|
|
206
|
-
this.logger.error('强制绑定',
|
|
206
|
+
this.logger.error('强制绑定', '强制绑定过程出错', error);
|
|
207
207
|
throw error; // 直接重抛原始错误,不添加前缀
|
|
208
208
|
}
|
|
209
209
|
}
|
|
@@ -220,7 +220,7 @@ class ForceBinder {
|
|
|
220
220
|
getTargetMedalDetails(enhancedUser) {
|
|
221
221
|
// 检查是否有目标粉丝牌信息(即是否尝试了B站API调用)
|
|
222
222
|
if (!enhancedUser.targetMedal) {
|
|
223
|
-
return
|
|
223
|
+
return 'ℹ️ 未检查粉丝牌信息(B站登录状态异常,请检查SESSDATA配置)';
|
|
224
224
|
}
|
|
225
225
|
if (!enhancedUser.targetMedal.found) {
|
|
226
226
|
return `未找到目标粉丝牌"${this.config.targetMedalName}"(UP主UID: ${this.config.targetUpUid})`;
|
|
@@ -228,9 +228,13 @@ class ForceBinder {
|
|
|
228
228
|
const medal = enhancedUser.targetMedal;
|
|
229
229
|
let details = `🎯 目标粉丝牌: ${medal.name} LV.${medal.level}`;
|
|
230
230
|
if (medal.guard_level && medal.guard_level > 0) {
|
|
231
|
-
const guardText = medal.guard_level === 1
|
|
232
|
-
|
|
233
|
-
|
|
231
|
+
const guardText = medal.guard_level === 1
|
|
232
|
+
? '总督'
|
|
233
|
+
: medal.guard_level === 2
|
|
234
|
+
? '提督'
|
|
235
|
+
: medal.guard_level === 3
|
|
236
|
+
? '舰长'
|
|
237
|
+
: '未知';
|
|
234
238
|
details += ` (${guardText})`;
|
|
235
239
|
}
|
|
236
240
|
if (medal.wearing_status === 1) {
|
|
@@ -18,7 +18,8 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
18
18
|
* 注册交互式绑定命令
|
|
19
19
|
*/
|
|
20
20
|
register() {
|
|
21
|
-
this.ctx
|
|
21
|
+
this.ctx
|
|
22
|
+
.command('绑定 [target:string]', '交互式绑定流程')
|
|
22
23
|
.alias('bind')
|
|
23
24
|
.alias('interact')
|
|
24
25
|
.action(async ({ session }, target) => {
|
|
@@ -28,25 +29,33 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
28
29
|
// 如果指定了目标用户(管理员功能)
|
|
29
30
|
if (target) {
|
|
30
31
|
// 检查权限
|
|
31
|
-
if (!await this.isAdmin(session.userId)) {
|
|
32
|
+
if (!(await this.isAdmin(session.userId))) {
|
|
32
33
|
this.logger.warn('交互绑定', `权限不足: QQ(${normalizedUserId})不是管理员,无法为他人启动绑定`);
|
|
33
|
-
return this.deps.sendMessage(session, [
|
|
34
|
+
return this.deps.sendMessage(session, [
|
|
35
|
+
koishi_1.h.text('只有管理员才能为其他用户启动绑定流程')
|
|
36
|
+
]);
|
|
34
37
|
}
|
|
35
38
|
const normalizedTargetId = this.deps.normalizeQQId(target);
|
|
36
39
|
// 检查目标用户ID是否有效
|
|
37
40
|
if (!normalizedTargetId) {
|
|
38
41
|
this.logger.warn('交互绑定', `QQ(${normalizedUserId})提供的目标用户ID"${target}"无效`);
|
|
39
42
|
if (target.startsWith('@')) {
|
|
40
|
-
return this.deps.sendMessage(session, [
|
|
43
|
+
return this.deps.sendMessage(session, [
|
|
44
|
+
koishi_1.h.text('❌ 请使用真正的@功能,而不是手动输入@符号\n正确做法:点击或长按用户头像选择@功能')
|
|
45
|
+
]);
|
|
41
46
|
}
|
|
42
|
-
return this.deps.sendMessage(session, [
|
|
47
|
+
return this.deps.sendMessage(session, [
|
|
48
|
+
koishi_1.h.text('❌ 目标用户ID无效\n请提供有效的QQ号或使用@功能选择用户')
|
|
49
|
+
]);
|
|
43
50
|
}
|
|
44
51
|
this.logger.info('交互绑定', `管理员QQ(${normalizedUserId})为QQ(${normalizedTargetId})启动交互式绑定流程`, true);
|
|
45
52
|
// 检查目标用户是否已有进行中的会话
|
|
46
53
|
const existingTargetSession = this.deps.getBindingSession(target, channelId);
|
|
47
54
|
if (existingTargetSession) {
|
|
48
55
|
this.logger.warn('交互绑定', `QQ(${normalizedTargetId})已有进行中的绑定会话`);
|
|
49
|
-
return this.deps.sendMessage(session, [
|
|
56
|
+
return this.deps.sendMessage(session, [
|
|
57
|
+
koishi_1.h.text(`用户 ${normalizedTargetId} 已有进行中的绑定会话`)
|
|
58
|
+
]);
|
|
50
59
|
}
|
|
51
60
|
// 检查目标用户当前绑定状态
|
|
52
61
|
const targetBind = await this.deps.databaseService.getMcBindByQQId(normalizedTargetId);
|
|
@@ -54,7 +63,9 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
54
63
|
if (targetBind && targetBind.mcUsername && targetBind.buidUid) {
|
|
55
64
|
this.logger.info('交互绑定', `QQ(${normalizedTargetId})已完成全部绑定`, true);
|
|
56
65
|
// 显示当前绑定信息
|
|
57
|
-
const displayUsername = targetBind.mcUsername && !targetBind.mcUsername.startsWith('_temp_')
|
|
66
|
+
const displayUsername = targetBind.mcUsername && !targetBind.mcUsername.startsWith('_temp_')
|
|
67
|
+
? targetBind.mcUsername
|
|
68
|
+
: '未绑定';
|
|
58
69
|
let bindInfo = `用户 ${normalizedTargetId} 已完成全部账号绑定:\n✅ MC账号: ${displayUsername}\n✅ B站账号: ${targetBind.buidUsername} (UID: ${targetBind.buidUid})`;
|
|
59
70
|
if (targetBind.guardLevel > 0) {
|
|
60
71
|
bindInfo += `\n舰长等级: ${targetBind.guardLevelText}`;
|
|
@@ -72,11 +83,15 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
72
83
|
// 更新会话状态
|
|
73
84
|
this.deps.updateBindingSession(target, channelId, {
|
|
74
85
|
state: 'waiting_buid',
|
|
75
|
-
mcUsername: targetBind.mcUsername && !targetBind.mcUsername.startsWith('_temp_')
|
|
86
|
+
mcUsername: targetBind.mcUsername && !targetBind.mcUsername.startsWith('_temp_')
|
|
87
|
+
? targetBind.mcUsername
|
|
88
|
+
: null,
|
|
76
89
|
mcUuid: targetBind.mcUuid
|
|
77
90
|
});
|
|
78
91
|
// 向目标用户发送提示(@他们)
|
|
79
|
-
const displayUsername = targetBind.mcUsername && !targetBind.mcUsername.startsWith('_temp_')
|
|
92
|
+
const displayUsername = targetBind.mcUsername && !targetBind.mcUsername.startsWith('_temp_')
|
|
93
|
+
? targetBind.mcUsername
|
|
94
|
+
: '未绑定';
|
|
80
95
|
await this.deps.sendMessage(session, [
|
|
81
96
|
koishi_1.h.at(normalizedTargetId),
|
|
82
97
|
koishi_1.h.text(` 管理员为您启动了B站绑定流程\n🎮 已绑定MC: ${displayUsername}\n🔗 请发送您的B站UID`)
|
|
@@ -86,7 +101,7 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
86
101
|
// 向目标用户发送提示(@他们)
|
|
87
102
|
await this.deps.sendMessage(session, [
|
|
88
103
|
koishi_1.h.at(normalizedTargetId),
|
|
89
|
-
koishi_1.h.text(
|
|
104
|
+
koishi_1.h.text(' 管理员为您启动了账号绑定流程\n📋 请选择绑定方式:\n1. 发送您的B站UID进行B站绑定\n2. 发送"跳过"仅绑定MC账号')
|
|
90
105
|
]);
|
|
91
106
|
return;
|
|
92
107
|
}
|
|
@@ -96,12 +111,17 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
96
111
|
const existingSession = this.deps.getBindingSession(session.userId, channelId);
|
|
97
112
|
if (existingSession) {
|
|
98
113
|
this.logger.warn('交互绑定', `QQ(${normalizedUserId})已有进行中的绑定会话`);
|
|
99
|
-
return this.deps.sendMessage(session, [
|
|
114
|
+
return this.deps.sendMessage(session, [
|
|
115
|
+
koishi_1.h.text('您已有进行中的绑定会话,请先完成当前绑定或等待会话超时')
|
|
116
|
+
]);
|
|
100
117
|
}
|
|
101
118
|
// 检查用户当前绑定状态
|
|
102
119
|
const existingBind = await this.deps.databaseService.getMcBindByQQId(normalizedUserId);
|
|
103
120
|
// 如果两个账号都已绑定(且MC不是temp用户名),不需要进入绑定流程
|
|
104
|
-
if (existingBind &&
|
|
121
|
+
if (existingBind &&
|
|
122
|
+
existingBind.mcUsername &&
|
|
123
|
+
!existingBind.mcUsername.startsWith('_temp_') &&
|
|
124
|
+
existingBind.buidUid) {
|
|
105
125
|
this.logger.info('交互绑定', `QQ(${normalizedUserId})已完成全部绑定`, true);
|
|
106
126
|
// 显示当前绑定信息
|
|
107
127
|
const displayUsername = existingBind.mcUsername;
|
|
@@ -116,13 +136,21 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
116
136
|
return this.deps.sendMessage(session, [koishi_1.h.text(bindInfo)]);
|
|
117
137
|
}
|
|
118
138
|
// 如果已绑定MC(且不是temp用户名)但未绑定B站,直接进入B站绑定流程
|
|
119
|
-
if (existingBind &&
|
|
139
|
+
if (existingBind &&
|
|
140
|
+
existingBind.mcUsername &&
|
|
141
|
+
!existingBind.mcUsername.startsWith('_temp_') &&
|
|
142
|
+
!existingBind.buidUid) {
|
|
120
143
|
this.logger.info('交互绑定', `QQ(${normalizedUserId})已绑定MC,进入B站绑定流程`, true);
|
|
121
144
|
// 创建绑定会话,状态直接设为等待B站UID
|
|
122
145
|
const timeout = setTimeout(() => {
|
|
123
146
|
this.deps.bindingSessions.delete(`${normalizedUserId}_${channelId}`);
|
|
124
147
|
this.ctx.bots.forEach(bot => {
|
|
125
|
-
bot
|
|
148
|
+
bot
|
|
149
|
+
.sendMessage(channelId, [
|
|
150
|
+
koishi_1.h.at(normalizedUserId),
|
|
151
|
+
koishi_1.h.text(' 绑定会话已超时,请重新开始绑定流程\n\n⚠️ 温馨提醒:若在管理员多次提醒后仍不配合绑定账号信息,将按群规进行相应处理。')
|
|
152
|
+
])
|
|
153
|
+
.catch(() => { });
|
|
126
154
|
});
|
|
127
155
|
this.logger.info('交互绑定', `QQ(${normalizedUserId})的绑定会话因超时被清理`, true);
|
|
128
156
|
}, this.BINDING_SESSION_TIMEOUT);
|
|
@@ -136,22 +164,31 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
136
164
|
mcUuid: existingBind.mcUuid
|
|
137
165
|
};
|
|
138
166
|
this.deps.bindingSessions.set(`${normalizedUserId}_${channelId}`, sessionData);
|
|
139
|
-
return this.deps.sendMessage(session, [
|
|
167
|
+
return this.deps.sendMessage(session, [
|
|
168
|
+
koishi_1.h.text(`🎮 已绑定MC: ${existingBind.mcUsername}\n🔗 请发送您的B站UID`)
|
|
169
|
+
]);
|
|
140
170
|
}
|
|
141
171
|
// 如果只绑定了B站(MC是temp用户名),提醒绑定MC账号
|
|
142
|
-
if (existingBind &&
|
|
143
|
-
existingBind.
|
|
172
|
+
if (existingBind &&
|
|
173
|
+
existingBind.buidUid &&
|
|
174
|
+
existingBind.buidUsername &&
|
|
175
|
+
existingBind.mcUsername &&
|
|
176
|
+
existingBind.mcUsername.startsWith('_temp_')) {
|
|
144
177
|
this.logger.info('交互绑定', `QQ(${normalizedUserId})只绑定了B站,进入MC绑定流程`, true);
|
|
145
178
|
// 创建绑定会话,状态设为等待MC用户名
|
|
146
179
|
this.deps.createBindingSession(session.userId, channelId, 'waiting_mc_username');
|
|
147
180
|
const bindingSession = this.deps.getBindingSession(session.userId, channelId);
|
|
148
181
|
bindingSession.state = 'waiting_mc_username';
|
|
149
|
-
return this.deps.sendMessage(session, [
|
|
182
|
+
return this.deps.sendMessage(session, [
|
|
183
|
+
koishi_1.h.text(`✅ 已绑定B站: ${existingBind.buidUsername}\n🎮 请发送您的MC用户名,或发送"跳过"保持当前状态`)
|
|
184
|
+
]);
|
|
150
185
|
}
|
|
151
186
|
// 如果未绑定账号,让用户选择绑定方式,优先B站绑定
|
|
152
187
|
this.deps.createBindingSession(session.userId, channelId, 'waiting_buid');
|
|
153
188
|
// 发送绑定选项提示
|
|
154
|
-
return this.deps.sendMessage(session, [
|
|
189
|
+
return this.deps.sendMessage(session, [
|
|
190
|
+
koishi_1.h.text('📋 请选择绑定方式:\n1. 发送您的B站UID进行B站绑定\n2. 发送"跳过"仅绑定MC账号')
|
|
191
|
+
]);
|
|
155
192
|
}
|
|
156
193
|
catch (error) {
|
|
157
194
|
const normalizedUserId = this.deps.normalizeQQId(session.userId);
|
|
@@ -223,10 +260,14 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
223
260
|
}
|
|
224
261
|
// RCON相关错误
|
|
225
262
|
if (errorMsg.includes('RCON') || errorMsg.includes('服务器')) {
|
|
226
|
-
if (errorMsg.includes('authentication') ||
|
|
263
|
+
if (errorMsg.includes('authentication') ||
|
|
264
|
+
errorMsg.includes('auth') ||
|
|
265
|
+
errorMsg.includes('认证')) {
|
|
227
266
|
return 'RCON认证失败,服务器拒绝访问,请联系管理员检查密码';
|
|
228
267
|
}
|
|
229
|
-
if (errorMsg.includes('ECONNREFUSED') ||
|
|
268
|
+
if (errorMsg.includes('ECONNREFUSED') ||
|
|
269
|
+
errorMsg.includes('ETIMEDOUT') ||
|
|
270
|
+
errorMsg.includes('无法连接')) {
|
|
230
271
|
return '无法连接到游戏服务器,请确认服务器是否在线或联系管理员';
|
|
231
272
|
}
|
|
232
273
|
if (errorMsg.includes('command') || errorMsg.includes('执行命令')) {
|
|
@@ -273,12 +314,7 @@ class BindingHandler extends base_handler_1.BaseHandler {
|
|
|
273
314
|
* @returns 是否为严重错误
|
|
274
315
|
*/
|
|
275
316
|
isCriticalError(errorMsg) {
|
|
276
|
-
const criticalPatterns = [
|
|
277
|
-
'无法连接',
|
|
278
|
-
'RCON认证失败',
|
|
279
|
-
'服务器通信失败',
|
|
280
|
-
'数据库操作出错'
|
|
281
|
-
];
|
|
317
|
+
const criticalPatterns = ['无法连接', 'RCON认证失败', '服务器通信失败', '数据库操作出错'];
|
|
282
318
|
return criticalPatterns.some(pattern => errorMsg.includes(pattern));
|
|
283
319
|
}
|
|
284
320
|
}
|