koishi-plugin-bind-bot 2.1.0 → 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 +28 -22
- 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/types/database.d.ts
CHANGED
|
@@ -4,27 +4,77 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
6
|
* MCIDBIND表结构 - MC和B站账号绑定主表
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* 该表存储用户的 Minecraft 账号和 B站账号绑定信息
|
|
10
|
+
* - 使用 qqId 作为主键
|
|
11
|
+
* - mcUsername 具有唯一性约束
|
|
12
|
+
* - 支持单独绑定 MC 或 B站账号
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* {
|
|
17
|
+
* qqId: '12345678',
|
|
18
|
+
* mcUsername: 'Notch',
|
|
19
|
+
* mcUuid: '069a79f4-44e9-4726-a5be-fca90e38aaf5',
|
|
20
|
+
* lastModified: new Date(),
|
|
21
|
+
* isAdmin: false,
|
|
22
|
+
* whitelist: ['survival', 'creative'],
|
|
23
|
+
* tags: ['VIP', '建筑师'],
|
|
24
|
+
* buidUid: '87654321',
|
|
25
|
+
* buidUsername: 'B站用户名',
|
|
26
|
+
* guardLevel: 3,
|
|
27
|
+
* guardLevelText: '舰长',
|
|
28
|
+
* maxGuardLevel: 3,
|
|
29
|
+
* maxGuardLevelText: '舰长',
|
|
30
|
+
* medalName: '粉丝牌',
|
|
31
|
+
* medalLevel: 20,
|
|
32
|
+
* wealthMedalLevel: 15,
|
|
33
|
+
* lastActiveTime: new Date(),
|
|
34
|
+
* reminderCount: 0
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
7
37
|
*/
|
|
8
38
|
export interface MCIDBIND {
|
|
39
|
+
/** 纯QQ号 (作为主键, 不包含 'qq:' 前缀) */
|
|
9
40
|
qqId: string;
|
|
41
|
+
/** MC用户名 (具有唯一性约束) */
|
|
10
42
|
mcUsername: string;
|
|
43
|
+
/** MC UUID (格式: 带连字符的标准UUID) */
|
|
11
44
|
mcUuid: string;
|
|
45
|
+
/** MC用户名上次检查时间 (用于改名检测缓存) */
|
|
12
46
|
usernameLastChecked?: Date;
|
|
47
|
+
/** 用户名检查失败次数 (连续失败计数, 失败≥3次延长冷却期至72小时) */
|
|
13
48
|
usernameCheckFailCount?: number;
|
|
49
|
+
/** 上次修改时间 (绑定/解绑操作的时间戳) */
|
|
14
50
|
lastModified: Date;
|
|
51
|
+
/** 是否为MC绑定管理员 (拥有管理员权限) */
|
|
15
52
|
isAdmin: boolean;
|
|
53
|
+
/** 已添加白名单的服务器ID列表 */
|
|
16
54
|
whitelist: string[];
|
|
55
|
+
/** 用户标签列表 (用于分类管理) */
|
|
17
56
|
tags: string[];
|
|
57
|
+
/** B站UID */
|
|
18
58
|
buidUid: string;
|
|
59
|
+
/** B站用户名 */
|
|
19
60
|
buidUsername: string;
|
|
61
|
+
/** 当前舰长等级 (0=无, 1=总督, 2=提督, 3=舰长) */
|
|
20
62
|
guardLevel: number;
|
|
63
|
+
/** 当前舰长等级文本 (例: '舰长', '提督', '总督') */
|
|
21
64
|
guardLevelText: string;
|
|
65
|
+
/** 历史最高舰长等级 */
|
|
22
66
|
maxGuardLevel: number;
|
|
67
|
+
/** 历史最高舰长等级文本 */
|
|
23
68
|
maxGuardLevelText: string;
|
|
69
|
+
/** 粉丝牌名称 */
|
|
24
70
|
medalName: string;
|
|
71
|
+
/** 粉丝牌等级 */
|
|
25
72
|
medalLevel: number;
|
|
73
|
+
/** 荣耀等级 (财富勋章等级) */
|
|
26
74
|
wealthMedalLevel: number;
|
|
75
|
+
/** 最后活跃时间 (B站活跃时间) */
|
|
27
76
|
lastActiveTime: Date;
|
|
77
|
+
/** 随机提醒次数 (用于天选时刻等功能) */
|
|
28
78
|
reminderCount: number;
|
|
29
79
|
}
|
|
30
80
|
/**
|
|
@@ -3,43 +3,126 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
5
|
* MC绑定更新数据接口
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* 用于更新 MCIDBIND 表中的 MC 相关字段
|
|
9
|
+
* 所有字段都是可选的,只更新提供的字段
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* // 仅更新用户名
|
|
14
|
+
* {
|
|
15
|
+
* mcUsername: 'NewUsername'
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* // 更新用户名和UUID
|
|
19
|
+
* {
|
|
20
|
+
* mcUsername: 'NewUsername',
|
|
21
|
+
* mcUuid: '069a79f4-44e9-4726-a5be-fca90e38aaf5',
|
|
22
|
+
* lastModified: new Date()
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
6
25
|
*/
|
|
7
26
|
export interface UpdateMcBindData {
|
|
27
|
+
/** MC用户名 */
|
|
8
28
|
mcUsername?: string;
|
|
29
|
+
/** MC UUID */
|
|
9
30
|
mcUuid?: string;
|
|
31
|
+
/** 上次修改时间 */
|
|
10
32
|
lastModified?: Date;
|
|
33
|
+
/** 是否为管理员 */
|
|
11
34
|
isAdmin?: boolean;
|
|
35
|
+
/** 用户名上次检查时间 */
|
|
12
36
|
usernameLastChecked?: Date;
|
|
37
|
+
/** 用户名检查失败次数 */
|
|
13
38
|
usernameCheckFailCount?: number;
|
|
14
39
|
}
|
|
15
40
|
/**
|
|
16
41
|
* BUID绑定更新数据接口(完整更新)
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* 用于更新 MCIDBIND 表中的 B站相关字段
|
|
45
|
+
* 该接口会同时更新 lastModified 字段,表示绑定操作时间
|
|
46
|
+
*
|
|
47
|
+
* @see {@link UpdateBuidInfoData} 仅刷新信息不更新 lastModified
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* {
|
|
52
|
+
* buidUid: '87654321',
|
|
53
|
+
* buidUsername: 'B站用户名',
|
|
54
|
+
* guardLevel: 3,
|
|
55
|
+
* guardLevelText: '舰长',
|
|
56
|
+
* maxGuardLevel: 3,
|
|
57
|
+
* maxGuardLevelText: '舰长',
|
|
58
|
+
* medalName: '粉丝牌',
|
|
59
|
+
* medalLevel: 20,
|
|
60
|
+
* wealthMedalLevel: 15,
|
|
61
|
+
* lastActiveTime: new Date(),
|
|
62
|
+
* lastModified: new Date()
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
17
65
|
*/
|
|
18
66
|
export interface UpdateBuidBindData {
|
|
67
|
+
/** B站UID (数据库中存储为字符串) */
|
|
19
68
|
buidUid?: string;
|
|
69
|
+
/** B站用户名 */
|
|
20
70
|
buidUsername?: string;
|
|
71
|
+
/** 当前舰长等级 */
|
|
21
72
|
guardLevel?: number;
|
|
73
|
+
/** 当前舰长等级文本 */
|
|
22
74
|
guardLevelText?: string;
|
|
75
|
+
/** 历史最高舰长等级 */
|
|
23
76
|
maxGuardLevel?: number;
|
|
77
|
+
/** 历史最高舰长等级文本 */
|
|
24
78
|
maxGuardLevelText?: string;
|
|
79
|
+
/** 粉丝牌名称 */
|
|
25
80
|
medalName?: string;
|
|
81
|
+
/** 粉丝牌等级 */
|
|
26
82
|
medalLevel?: number;
|
|
83
|
+
/** 荣耀等级 */
|
|
27
84
|
wealthMedalLevel?: number;
|
|
85
|
+
/** 最后活跃时间 */
|
|
28
86
|
lastActiveTime?: Date;
|
|
87
|
+
/** 上次修改时间 (绑定操作时间) */
|
|
29
88
|
lastModified?: Date;
|
|
30
89
|
}
|
|
31
90
|
/**
|
|
32
91
|
* BUID信息更新数据接口(仅更新信息,不更新lastModified)
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* 用于查询时刷新 B站信息,不影响绑定时间
|
|
95
|
+
* 与 {@link UpdateBuidBindData} 的区别是不包含 lastModified 字段
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* // 查询时刷新B站信息
|
|
100
|
+
* {
|
|
101
|
+
* buidUsername: '新用户名',
|
|
102
|
+
* guardLevel: 3,
|
|
103
|
+
* guardLevelText: '舰长',
|
|
104
|
+
* medalLevel: 21
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
33
107
|
*/
|
|
34
108
|
export interface UpdateBuidInfoData {
|
|
109
|
+
/** B站用户名 */
|
|
35
110
|
buidUsername?: string;
|
|
111
|
+
/** 当前舰长等级 */
|
|
36
112
|
guardLevel?: number;
|
|
113
|
+
/** 当前舰长等级文本 */
|
|
37
114
|
guardLevelText?: string;
|
|
115
|
+
/** 历史最高舰长等级 */
|
|
38
116
|
maxGuardLevel?: number;
|
|
117
|
+
/** 历史最高舰长等级文本 */
|
|
39
118
|
maxGuardLevelText?: string;
|
|
119
|
+
/** 粉丝牌名称 */
|
|
40
120
|
medalName?: string;
|
|
121
|
+
/** 粉丝牌等级 */
|
|
41
122
|
medalLevel?: number;
|
|
123
|
+
/** 荣耀等级 */
|
|
42
124
|
wealthMedalLevel?: number;
|
|
125
|
+
/** 最后活跃时间 */
|
|
43
126
|
lastActiveTime?: Date;
|
|
44
127
|
}
|
|
45
128
|
/**
|
package/lib/utils/error-utils.js
CHANGED
|
@@ -51,10 +51,14 @@ function getUserFacingErrorMessage(errorMsg) {
|
|
|
51
51
|
}
|
|
52
52
|
// RCON相关错误
|
|
53
53
|
if (errorMsg.includes('RCON') || errorMsg.includes('服务器')) {
|
|
54
|
-
if (errorMsg.includes('authentication') ||
|
|
54
|
+
if (errorMsg.includes('authentication') ||
|
|
55
|
+
errorMsg.includes('auth') ||
|
|
56
|
+
errorMsg.includes('认证')) {
|
|
55
57
|
return 'RCON认证失败,服务器拒绝访问,请联系管理员检查密码';
|
|
56
58
|
}
|
|
57
|
-
if (errorMsg.includes('ECONNREFUSED') ||
|
|
59
|
+
if (errorMsg.includes('ECONNREFUSED') ||
|
|
60
|
+
errorMsg.includes('ETIMEDOUT') ||
|
|
61
|
+
errorMsg.includes('无法连接')) {
|
|
58
62
|
return '无法连接到游戏服务器,请确认服务器是否在线或联系管理员';
|
|
59
63
|
}
|
|
60
64
|
if (errorMsg.includes('command') || errorMsg.includes('执行命令')) {
|
|
@@ -101,11 +105,6 @@ function isWarningError(errorMsg) {
|
|
|
101
105
|
* @returns 是否为严重错误
|
|
102
106
|
*/
|
|
103
107
|
function isCriticalError(errorMsg) {
|
|
104
|
-
const criticalPatterns = [
|
|
105
|
-
'无法连接',
|
|
106
|
-
'RCON认证失败',
|
|
107
|
-
'服务器通信失败',
|
|
108
|
-
'数据库操作出错'
|
|
109
|
-
];
|
|
108
|
+
const criticalPatterns = ['无法连接', 'RCON认证失败', '服务器通信失败', '数据库操作出错'];
|
|
110
109
|
return criticalPatterns.some(pattern => errorMsg.includes(pattern));
|
|
111
110
|
}
|
package/lib/utils/helpers.js
CHANGED
|
@@ -32,7 +32,7 @@ exports.extractBuidUsernameFromNickname = extractBuidUsernameFromNickname;
|
|
|
32
32
|
function normalizeQQId(userId, logger) {
|
|
33
33
|
// 处理空值情况
|
|
34
34
|
if (!userId) {
|
|
35
|
-
logger?.warn(
|
|
35
|
+
logger?.warn('[用户ID] 收到空用户ID');
|
|
36
36
|
return '';
|
|
37
37
|
}
|
|
38
38
|
let extractedId = '';
|
|
@@ -165,7 +165,7 @@ function getStarlightSkinUrl(username, logger) {
|
|
|
165
165
|
'dungeons', // 地下城风格
|
|
166
166
|
'facepalm', // 捂脸
|
|
167
167
|
'mojavatar', // Mojave姿态
|
|
168
|
-
'head'
|
|
168
|
+
'head' // 头部特写
|
|
169
169
|
];
|
|
170
170
|
// 随机选择一个动作
|
|
171
171
|
const randomPose = poses[Math.floor(Math.random() * poses.length)];
|
|
@@ -186,7 +186,39 @@ function checkIrrelevantInput(state, content) {
|
|
|
186
186
|
if (!content)
|
|
187
187
|
return false;
|
|
188
188
|
// 常见的聊天用语或明显无关的内容
|
|
189
|
-
const chatKeywords = [
|
|
189
|
+
const chatKeywords = [
|
|
190
|
+
'你好',
|
|
191
|
+
'hello',
|
|
192
|
+
'hi',
|
|
193
|
+
'在吗',
|
|
194
|
+
'在不在',
|
|
195
|
+
'怎么样',
|
|
196
|
+
'什么',
|
|
197
|
+
'为什么',
|
|
198
|
+
'好的',
|
|
199
|
+
'谢谢',
|
|
200
|
+
'哈哈',
|
|
201
|
+
'呵呵',
|
|
202
|
+
'早上好',
|
|
203
|
+
'晚上好',
|
|
204
|
+
'晚安',
|
|
205
|
+
'再见',
|
|
206
|
+
'拜拜',
|
|
207
|
+
'666',
|
|
208
|
+
'牛',
|
|
209
|
+
'厉害',
|
|
210
|
+
'真的吗',
|
|
211
|
+
'不是吧',
|
|
212
|
+
'哇',
|
|
213
|
+
'哦',
|
|
214
|
+
'嗯',
|
|
215
|
+
'好吧',
|
|
216
|
+
'行',
|
|
217
|
+
'可以',
|
|
218
|
+
'没事',
|
|
219
|
+
'没问题',
|
|
220
|
+
'没关系'
|
|
221
|
+
];
|
|
190
222
|
const lowercaseContent = content.toLowerCase();
|
|
191
223
|
// 检查是否包含明显的聊天用语
|
|
192
224
|
if (chatKeywords.some(keyword => lowercaseContent.includes(keyword))) {
|
|
@@ -212,7 +244,10 @@ function checkIrrelevantInput(state, content) {
|
|
|
212
244
|
return true;
|
|
213
245
|
}
|
|
214
246
|
// 如果是明显的指令格式
|
|
215
|
-
if (content.startsWith('.') ||
|
|
247
|
+
if (content.startsWith('.') ||
|
|
248
|
+
content.startsWith('/') ||
|
|
249
|
+
content.startsWith('mcid') ||
|
|
250
|
+
content.startsWith('buid')) {
|
|
216
251
|
return true;
|
|
217
252
|
}
|
|
218
253
|
}
|
|
@@ -230,7 +265,10 @@ function checkIrrelevantInput(state, content) {
|
|
|
230
265
|
return true;
|
|
231
266
|
}
|
|
232
267
|
// 如果是明显的指令格式
|
|
233
|
-
if (content.startsWith('.') ||
|
|
268
|
+
if (content.startsWith('.') ||
|
|
269
|
+
content.startsWith('/') ||
|
|
270
|
+
content.startsWith('mcid') ||
|
|
271
|
+
content.startsWith('buid')) {
|
|
234
272
|
return true;
|
|
235
273
|
}
|
|
236
274
|
}
|
|
@@ -265,7 +303,7 @@ function cleanUserInput(content, session, botNickname, logger) {
|
|
|
265
303
|
// @Bot昵称 格式(如果配置了botNickname)
|
|
266
304
|
botNickname ? new RegExp(`^@${escapeRegExp(botNickname)}\\s+`, 'i') : null,
|
|
267
305
|
// @botUserId 格式
|
|
268
|
-
new RegExp(`^@${escapeRegExp(botUserId)}\\s+`, 'i')
|
|
306
|
+
new RegExp(`^@${escapeRegExp(botUserId)}\\s+`, 'i')
|
|
269
307
|
].filter(Boolean);
|
|
270
308
|
let cleanedContent = content.trim();
|
|
271
309
|
// 尝试匹配并移除@Bot前缀
|
|
@@ -333,7 +371,7 @@ function calculateSimilarity(str1, str2) {
|
|
|
333
371
|
*/
|
|
334
372
|
function normalizeUsername(username, logger) {
|
|
335
373
|
if (!username) {
|
|
336
|
-
logger?.warn(
|
|
374
|
+
logger?.warn('[用户名规范化] 收到空用户名');
|
|
337
375
|
return '';
|
|
338
376
|
}
|
|
339
377
|
// 移除首尾空格
|
|
@@ -47,8 +47,12 @@ class MessageUtils {
|
|
|
47
47
|
// 处理私聊和群聊的消息格式
|
|
48
48
|
// 主动消息不引用原消息
|
|
49
49
|
const promptMessage = session.channelId?.startsWith('private:')
|
|
50
|
-
?
|
|
51
|
-
|
|
50
|
+
? isProactiveMessage
|
|
51
|
+
? content
|
|
52
|
+
: [koishi_1.h.quote(session.messageId), ...content]
|
|
53
|
+
: isProactiveMessage
|
|
54
|
+
? [koishi_1.h.at(normalizedQQId), '\n', ...content]
|
|
55
|
+
: [koishi_1.h.quote(session.messageId), koishi_1.h.at(normalizedQQId), '\n', ...content];
|
|
52
56
|
// 发送消息并获取返回的消息ID
|
|
53
57
|
const messageResult = await session.send(promptMessage);
|
|
54
58
|
this.logger.debug('消息', `成功向QQ(${normalizedQQId})发送消息,频道: ${session.channelId}`);
|
|
@@ -58,11 +62,18 @@ class MessageUtils {
|
|
|
58
62
|
// 但如果用户在绑定会话中发送聊天消息(不包括指令),不撤回
|
|
59
63
|
// 主动消息不撤回用户消息
|
|
60
64
|
const bindingSession = this.getBindingSessionFn(session.userId, session.channelId);
|
|
61
|
-
const isBindingCommand = session.content &&
|
|
62
|
-
session.content.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const isBindingCommand = session.content &&
|
|
66
|
+
(session.content.trim() === '绑定' ||
|
|
67
|
+
(session.content.includes('@') && session.content.includes('绑定')));
|
|
68
|
+
const shouldNotRecallUserMessage = bindingSession &&
|
|
69
|
+
session.content &&
|
|
70
|
+
!isBindingCommand &&
|
|
71
|
+
(0, helpers_1.checkIrrelevantInput)(bindingSession.state, session.content.trim());
|
|
72
|
+
if (this.config.recallUserMessage &&
|
|
73
|
+
isGroupMessage &&
|
|
74
|
+
session.messageId &&
|
|
75
|
+
!shouldNotRecallUserMessage &&
|
|
76
|
+
!isProactiveMessage) {
|
|
66
77
|
setTimeout(async () => {
|
|
67
78
|
try {
|
|
68
79
|
await session.bot.deleteMessage(session.channelId, session.messageId);
|
|
@@ -78,7 +89,7 @@ class MessageUtils {
|
|
|
78
89
|
this.logger.debug('消息', `QQ(${normalizedQQId})在绑定会话中发送聊天消息,跳过撤回用户消息`);
|
|
79
90
|
}
|
|
80
91
|
else if (isProactiveMessage) {
|
|
81
|
-
this.logger.debug('消息',
|
|
92
|
+
this.logger.debug('消息', '主动发送的消息,跳过撤回用户消息');
|
|
82
93
|
}
|
|
83
94
|
// 处理撤回机器人消息 - 只在群聊中撤回机器人自己的消息
|
|
84
95
|
// 检查是否为不应撤回的重要提示消息(只有绑定会话超时提醒)
|
|
@@ -105,9 +116,10 @@ class MessageUtils {
|
|
|
105
116
|
}
|
|
106
117
|
else if (messageResult && typeof messageResult === 'object') {
|
|
107
118
|
// 尝试提取各种可能的消息ID格式
|
|
108
|
-
messageId =
|
|
109
|
-
messageResult.
|
|
110
|
-
|
|
119
|
+
messageId =
|
|
120
|
+
messageResult.messageId ||
|
|
121
|
+
messageResult.id ||
|
|
122
|
+
messageResult.message_id;
|
|
111
123
|
}
|
|
112
124
|
if (messageId) {
|
|
113
125
|
// 设置定时器延迟撤回
|
|
@@ -123,11 +135,11 @@ class MessageUtils {
|
|
|
123
135
|
this.logger.debug('消息', `已设置 ${this.config.autoRecallTime} 秒后自动撤回机器人消息 ${messageId}`);
|
|
124
136
|
}
|
|
125
137
|
else {
|
|
126
|
-
this.logger.warn('消息',
|
|
138
|
+
this.logger.warn('消息', '无法获取消息ID,自动撤回功能无法生效');
|
|
127
139
|
}
|
|
128
140
|
}
|
|
129
141
|
else {
|
|
130
|
-
this.logger.debug('消息',
|
|
142
|
+
this.logger.debug('消息', '检测到私聊消息,不撤回机器人回复');
|
|
131
143
|
}
|
|
132
144
|
}
|
|
133
145
|
}
|
|
@@ -151,7 +163,7 @@ class MessageUtils {
|
|
|
151
163
|
const actualUserId = targetUserId || session.userId;
|
|
152
164
|
const normalizedUserId = (0, helpers_1.normalizeQQId)(actualUserId);
|
|
153
165
|
// 根据MC绑定状态设置不同的格式(临时用户名视为未绑定)
|
|
154
|
-
const mcInfo =
|
|
166
|
+
const mcInfo = mcUsername && !mcUsername.startsWith('_temp_') ? mcUsername : '未绑定';
|
|
155
167
|
let currentBuidUsername = buidUsername;
|
|
156
168
|
const newNickname = `${currentBuidUsername}(ID:${mcInfo})`;
|
|
157
169
|
// 使用指定的群ID,如果没有指定则使用配置的默认群ID
|
|
@@ -184,30 +196,31 @@ class MessageUtils {
|
|
|
184
196
|
this.logger.debug('群昵称设置', `API返回B站用户名: "${latestBuidUser.username}"`);
|
|
185
197
|
// 2. 智能三层判断
|
|
186
198
|
// 情况A: API返回 == 当前群昵称中的名称
|
|
187
|
-
if (currentNicknameUsername &&
|
|
199
|
+
if (currentNicknameUsername &&
|
|
200
|
+
latestBuidUser.username === currentNicknameUsername) {
|
|
188
201
|
this.logger.info('群昵称设置', `✅ 当前群昵称"${currentNickname}"已包含正确的B站名称"${currentNicknameUsername}"`);
|
|
189
202
|
// 群昵称已经正确,仅需更新数据库(如果数据库是旧的)
|
|
190
203
|
if (latestBuidUser.username !== buidUsername) {
|
|
191
204
|
this.logger.info('群昵称设置', `数据库中的B站名称需要更新: "${buidUsername}" → "${latestBuidUser.username}"`);
|
|
192
205
|
try {
|
|
193
206
|
await this.updateBuidInfoOnly(normalizedUserId, latestBuidUser);
|
|
194
|
-
this.logger.info('群昵称设置',
|
|
207
|
+
this.logger.info('群昵称设置', '已更新数据库中的B站用户名');
|
|
195
208
|
}
|
|
196
209
|
catch (updateError) {
|
|
197
210
|
this.logger.warn('群昵称设置', `更新数据库失败: ${updateError.message}`);
|
|
198
211
|
}
|
|
199
212
|
}
|
|
200
213
|
else {
|
|
201
|
-
this.logger.debug('群昵称设置',
|
|
214
|
+
this.logger.debug('群昵称设置', '数据库中的B站名称已是最新,无需更新');
|
|
202
215
|
}
|
|
203
216
|
// 跳过群昵称修改
|
|
204
|
-
this.logger.info('群昵称设置',
|
|
217
|
+
this.logger.info('群昵称设置', '群昵称格式正确且名称最新,跳过修改');
|
|
205
218
|
return;
|
|
206
219
|
}
|
|
207
220
|
// 情况B: API返回 == 数据库(都是旧的)
|
|
208
221
|
if (latestBuidUser.username === buidUsername) {
|
|
209
222
|
this.logger.warn('群昵称设置', `⚠️ API返回的B站名称"${latestBuidUser.username}"与数据库一致,但与群昵称不符`);
|
|
210
|
-
this.logger.warn('群昵称设置',
|
|
223
|
+
this.logger.warn('群昵称设置', '可能是API缓存未刷新,采用保守策略:不修改群昵称');
|
|
211
224
|
return;
|
|
212
225
|
}
|
|
213
226
|
// 情况C: API返回新数据(!= 群昵称 且 != 数据库)
|
|
@@ -258,7 +271,7 @@ class MessageUtils {
|
|
|
258
271
|
// 如果获取当前昵称失败,直接尝试设置新昵称
|
|
259
272
|
this.logger.warn('群昵称设置', `获取QQ(${normalizedUserId})当前群昵称失败: ${getInfoError.message}`);
|
|
260
273
|
this.logger.warn('群昵称设置', `错误详情: ${JSON.stringify(getInfoError)}`);
|
|
261
|
-
this.logger.debug('群昵称设置',
|
|
274
|
+
this.logger.debug('群昵称设置', '将直接尝试设置新昵称...');
|
|
262
275
|
// 如果传入了 buidUid,尝试获取最新的B站用户信息
|
|
263
276
|
if (buidUid && this.validateBUID && this.updateBuidInfoOnly) {
|
|
264
277
|
this.logger.debug('群昵称设置', `尝试获取B站UID ${buidUid} 的最新信息...`);
|
|
@@ -269,7 +282,7 @@ class MessageUtils {
|
|
|
269
282
|
// 智能判断:API返回 == 数据库(都是旧的)
|
|
270
283
|
if (latestBuidUser.username === buidUsername) {
|
|
271
284
|
this.logger.warn('群昵称设置', `⚠️ API返回的B站名称"${latestBuidUser.username}"与数据库一致`);
|
|
272
|
-
this.logger.warn('群昵称设置',
|
|
285
|
+
this.logger.warn('群昵称设置', '可能是API缓存未刷新,且无法获取当前群昵称,采用保守策略:跳过修改');
|
|
273
286
|
return;
|
|
274
287
|
}
|
|
275
288
|
// API返回新数据(!= 数据库)
|
|
@@ -285,7 +298,7 @@ class MessageUtils {
|
|
|
285
298
|
}
|
|
286
299
|
}
|
|
287
300
|
else {
|
|
288
|
-
this.logger.warn('群昵称设置',
|
|
301
|
+
this.logger.warn('群昵称设置', '获取最新B站用户信息失败');
|
|
289
302
|
}
|
|
290
303
|
}
|
|
291
304
|
catch (validateError) {
|
|
@@ -307,7 +320,7 @@ class MessageUtils {
|
|
|
307
320
|
}
|
|
308
321
|
else {
|
|
309
322
|
this.logger.warn('群昵称设置', `⚠️ 验证失败,期望"${nicknameToSet}",实际"${verifyNickname}",可能是权限不足`);
|
|
310
|
-
this.logger.warn('群昵称设置',
|
|
323
|
+
this.logger.warn('群昵称设置', '建议检查: 1.机器人是否为群管理员 2.群设置是否允许管理员修改昵称 3.OneBot实现是否支持该功能');
|
|
311
324
|
}
|
|
312
325
|
}
|
|
313
326
|
catch (verifyError) {
|
|
@@ -48,7 +48,12 @@ class SessionManager {
|
|
|
48
48
|
// 发送超时消息,@用户
|
|
49
49
|
const normalizedUser = (0, helpers_1.normalizeQQId)(userId);
|
|
50
50
|
this.ctx.bots.forEach(bot => {
|
|
51
|
-
bot
|
|
51
|
+
bot
|
|
52
|
+
.sendMessage(channelId, [
|
|
53
|
+
koishi_1.h.at(normalizedUser),
|
|
54
|
+
koishi_1.h.text(' 绑定会话已超时,请重新开始绑定流程\n\n⚠️ 温馨提醒:若在管理员多次提醒后仍不配合绑定账号信息,将按群规进行相应处理。')
|
|
55
|
+
])
|
|
56
|
+
.catch(() => { });
|
|
52
57
|
});
|
|
53
58
|
this.logger.info('交互绑定', `QQ(${normalizedUser})的绑定会话因超时被清理`, true);
|
|
54
59
|
}, this.sessionTimeout);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-bind-bot",
|
|
3
3
|
"description": "[WittF自用] BIND-BOT - 账号绑定管理机器人,支持Minecraft账号和B站账号绑定与管理。",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.2",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
],
|
|
10
10
|
"license": "CC-BY-NC-SA-4.0",
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build": "tsc"
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"lint:fix": "eslint src --ext .ts --fix && prettier --write \"src/**/*.ts\""
|
|
13
14
|
},
|
|
14
15
|
"keywords": [
|
|
15
16
|
"chatbot",
|
|
@@ -25,7 +26,16 @@
|
|
|
25
26
|
}
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
29
|
+
"@types/jest": "^30.0.0",
|
|
28
30
|
"@types/node": "^20.10.0",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
|
32
|
+
"@typescript-eslint/parser": "^8.46.2",
|
|
33
|
+
"eslint": "^9.38.0",
|
|
34
|
+
"eslint-config-prettier": "^10.1.8",
|
|
35
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
36
|
+
"jest": "^30.2.0",
|
|
37
|
+
"prettier": "^3.6.2",
|
|
38
|
+
"ts-jest": "^29.4.5",
|
|
29
39
|
"typescript": "^5.5.4"
|
|
30
40
|
},
|
|
31
41
|
"peerDependencies": {
|