koishi-plugin-bind-bot 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/group-request-review.handler.d.ts +97 -0
- package/lib/handlers/group-request-review.handler.js +582 -0
- package/lib/handlers/index.d.ts +1 -0
- package/lib/handlers/index.js +1 -0
- 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 +282 -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 +12 -16
- package/lib/types/api.d.ts +90 -0
- package/lib/types/common.d.ts +45 -0
- package/lib/types/config.d.ts +88 -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
|
@@ -197,7 +197,7 @@ class MCIDBINDRepository {
|
|
|
197
197
|
*/
|
|
198
198
|
async deleteAll() {
|
|
199
199
|
try {
|
|
200
|
-
this.logger.debug('数据库',
|
|
200
|
+
this.logger.debug('数据库', '删除所有绑定记录');
|
|
201
201
|
const result = await this.ctx.database.remove('mcidbind', {});
|
|
202
202
|
this.logger.info('数据库', `成功删除所有绑定记录(删除${result.removed}条)`, true);
|
|
203
203
|
return result.removed;
|
|
@@ -324,7 +324,7 @@ class MCIDBINDRepository {
|
|
|
324
324
|
*/
|
|
325
325
|
async findAllAdmins() {
|
|
326
326
|
try {
|
|
327
|
-
this.logger.debug('数据库',
|
|
327
|
+
this.logger.debug('数据库', '获取所有管理员');
|
|
328
328
|
const allRecords = await this.ctx.database.get('mcidbind', {});
|
|
329
329
|
return allRecords.filter(record => record.isAdmin);
|
|
330
330
|
}
|
|
@@ -50,7 +50,7 @@ class ScheduleMuteRepository {
|
|
|
50
50
|
*/
|
|
51
51
|
async findAll() {
|
|
52
52
|
try {
|
|
53
|
-
this.logger.debug('数据库',
|
|
53
|
+
this.logger.debug('数据库', '获取所有定时禁言任务');
|
|
54
54
|
const tasks = await this.ctx.database.get('schedule_mute_tasks', {});
|
|
55
55
|
return tasks;
|
|
56
56
|
}
|
|
@@ -65,7 +65,7 @@ class ScheduleMuteRepository {
|
|
|
65
65
|
*/
|
|
66
66
|
async findAllEnabled() {
|
|
67
67
|
try {
|
|
68
|
-
this.logger.debug('数据库',
|
|
68
|
+
this.logger.debug('数据库', '获取所有已启用的定时禁言任务');
|
|
69
69
|
const tasks = await this.ctx.database.get('schedule_mute_tasks', { enabled: true });
|
|
70
70
|
return tasks;
|
|
71
71
|
}
|
|
@@ -28,7 +28,7 @@ class ApiService {
|
|
|
28
28
|
const response = await axios_1.default.get(`https://api.mojang.com/users/profiles/minecraft/${username}`, {
|
|
29
29
|
timeout: 10000, // 添加10秒超时
|
|
30
30
|
headers: {
|
|
31
|
-
'User-Agent': 'KoishiMCVerifier/1.0'
|
|
31
|
+
'User-Agent': 'KoishiMCVerifier/1.0' // 添加User-Agent头
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
if (response.status === 200 && response.data) {
|
|
@@ -54,13 +54,15 @@ class ApiService {
|
|
|
54
54
|
: error.message || '未知错误';
|
|
55
55
|
this.logger.error('Mojang API', `验证用户名"${username}"时发生错误: ${errorMessage}`);
|
|
56
56
|
// 如果是网络相关错误,尝试使用备用API检查
|
|
57
|
-
if (axios_1.default.isAxiosError(error) &&
|
|
58
|
-
error.code === '
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
if (axios_1.default.isAxiosError(error) &&
|
|
58
|
+
(error.code === 'ENOTFOUND' ||
|
|
59
|
+
error.code === 'ETIMEDOUT' ||
|
|
60
|
+
error.code === 'ECONNRESET' ||
|
|
61
|
+
error.code === 'ECONNREFUSED' ||
|
|
62
|
+
error.code === 'ECONNABORTED' ||
|
|
63
|
+
error.response?.status === 429 || // 添加429 (Too Many Requests)
|
|
64
|
+
error.response?.status === 403)) {
|
|
65
|
+
// 添加403 (Forbidden)
|
|
64
66
|
// 尝试使用playerdb.co作为备用API
|
|
65
67
|
this.logger.info('Mojang API', `遇到错误(${error.code || error.response?.status}),将尝试使用备用API`);
|
|
66
68
|
return this.tryBackupAPI(username);
|
|
@@ -84,7 +86,7 @@ class ApiService {
|
|
|
84
86
|
'User-Agent': 'KoishiMCVerifier/1.0'
|
|
85
87
|
}
|
|
86
88
|
});
|
|
87
|
-
if (backupResponse.status === 200 && backupResponse.data?.code ===
|
|
89
|
+
if (backupResponse.status === 200 && backupResponse.data?.code === 'player.found') {
|
|
88
90
|
const playerData = backupResponse.data.data.player;
|
|
89
91
|
const rawId = playerData.raw_id || playerData.id.replace(/-/g, ''); // 确保使用不带连字符的UUID
|
|
90
92
|
this.logger.info('备用API', `用户名"${username}"验证成功,UUID: ${rawId},标准名称: ${playerData.username}`);
|
|
@@ -117,7 +119,7 @@ class ApiService {
|
|
|
117
119
|
const response = await axios_1.default.get(`https://api.mojang.com/user/profile/${cleanUuid}`, {
|
|
118
120
|
timeout: 10000,
|
|
119
121
|
headers: {
|
|
120
|
-
'User-Agent': 'KoishiMCVerifier/1.0'
|
|
122
|
+
'User-Agent': 'KoishiMCVerifier/1.0'
|
|
121
123
|
}
|
|
122
124
|
});
|
|
123
125
|
if (response.status === 200 && response.data) {
|
|
@@ -131,13 +133,15 @@ class ApiService {
|
|
|
131
133
|
}
|
|
132
134
|
catch (error) {
|
|
133
135
|
// 如果是网络相关错误,尝试使用备用API
|
|
134
|
-
if (axios_1.default.isAxiosError(error) &&
|
|
135
|
-
error.code === '
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
if (axios_1.default.isAxiosError(error) &&
|
|
137
|
+
(error.code === 'ENOTFOUND' ||
|
|
138
|
+
error.code === 'ETIMEDOUT' ||
|
|
139
|
+
error.code === 'ECONNRESET' ||
|
|
140
|
+
error.code === 'ECONNREFUSED' ||
|
|
141
|
+
error.code === 'ECONNABORTED' ||
|
|
142
|
+
error.response?.status === 429 || // 添加429 (Too Many Requests)
|
|
143
|
+
error.response?.status === 403)) {
|
|
144
|
+
// 添加403 (Forbidden)
|
|
141
145
|
this.logger.info('Mojang API', `通过UUID查询用户名时遇到错误(${error.code || error.response?.status}),将尝试使用备用API`);
|
|
142
146
|
return this.getUsernameByUuidBackupAPI(uuid);
|
|
143
147
|
}
|
|
@@ -161,10 +165,10 @@ class ApiService {
|
|
|
161
165
|
const response = await axios_1.default.get(`https://playerdb.co/api/player/minecraft/${formattedUuid}`, {
|
|
162
166
|
timeout: 10000,
|
|
163
167
|
headers: {
|
|
164
|
-
'User-Agent': 'KoishiMCVerifier/1.0'
|
|
168
|
+
'User-Agent': 'KoishiMCVerifier/1.0'
|
|
165
169
|
}
|
|
166
170
|
});
|
|
167
|
-
if (response.status === 200 && response.data?.code ===
|
|
171
|
+
if (response.status === 200 && response.data?.code === 'player.found') {
|
|
168
172
|
const playerData = response.data.data.player;
|
|
169
173
|
this.logger.debug('备用API', `UUID "${formattedUuid}" 当前用户名: ${playerData.username}`);
|
|
170
174
|
return playerData.username;
|
|
@@ -193,13 +197,13 @@ class ApiService {
|
|
|
193
197
|
return null;
|
|
194
198
|
}
|
|
195
199
|
this.logger.debug('B站官方API', `开始查询UID ${uid} 的官方信息`);
|
|
196
|
-
const response = await axios_1.default.get(
|
|
200
|
+
const response = await axios_1.default.get('https://api.bilibili.com/x/space/acc/info', {
|
|
197
201
|
params: { mid: uid },
|
|
198
202
|
timeout: 10000,
|
|
199
203
|
headers: {
|
|
200
204
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
Referer: 'https://space.bilibili.com/',
|
|
206
|
+
Origin: 'https://space.bilibili.com'
|
|
203
207
|
}
|
|
204
208
|
});
|
|
205
209
|
if (response.data.code === 0 && response.data.data) {
|
|
@@ -314,7 +318,7 @@ class ApiService {
|
|
|
314
318
|
'dungeons', // 地下城风格
|
|
315
319
|
'facepalm', // 捂脸
|
|
316
320
|
'mojavatar', // Mojave姿态
|
|
317
|
-
'head'
|
|
321
|
+
'head' // 头部特写
|
|
318
322
|
];
|
|
319
323
|
// 随机选择一个动作
|
|
320
324
|
const randomPose = poses[Math.floor(Math.random() * poses.length)];
|
|
@@ -27,7 +27,7 @@ class DatabaseService {
|
|
|
27
27
|
try {
|
|
28
28
|
// 处理空值
|
|
29
29
|
if (!qqId) {
|
|
30
|
-
this.logger.warn('MCIDBIND',
|
|
30
|
+
this.logger.warn('MCIDBIND', '尝试查询空QQ号');
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
33
|
const normalizedQQId = this.normalizeQQId(qqId);
|
|
@@ -45,7 +45,7 @@ class DatabaseService {
|
|
|
45
45
|
async getMcBindByUsername(mcUsername) {
|
|
46
46
|
// 处理空值
|
|
47
47
|
if (!mcUsername) {
|
|
48
|
-
this.logger.warn('MCIDBIND',
|
|
48
|
+
this.logger.warn('MCIDBIND', '尝试查询空MC用户名');
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
// 使用 Repository 查询
|
|
@@ -58,16 +58,16 @@ class DatabaseService {
|
|
|
58
58
|
try {
|
|
59
59
|
// 验证输入参数
|
|
60
60
|
if (!userId) {
|
|
61
|
-
this.logger.error('MCIDBIND',
|
|
61
|
+
this.logger.error('MCIDBIND', '创建/更新绑定失败: 无效的用户ID');
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
64
|
if (!mcUsername) {
|
|
65
|
-
this.logger.error('MCIDBIND',
|
|
65
|
+
this.logger.error('MCIDBIND', '创建/更新绑定失败: 无效的MC用户名');
|
|
66
66
|
return false;
|
|
67
67
|
}
|
|
68
68
|
const normalizedQQId = this.normalizeQQId(userId);
|
|
69
69
|
if (!normalizedQQId) {
|
|
70
|
-
this.logger.error('MCIDBIND',
|
|
70
|
+
this.logger.error('MCIDBIND', '创建/更新绑定失败: 无法提取有效的QQ号');
|
|
71
71
|
return false;
|
|
72
72
|
}
|
|
73
73
|
// 查询是否已存在绑定记录
|
|
@@ -118,12 +118,12 @@ class DatabaseService {
|
|
|
118
118
|
try {
|
|
119
119
|
// 验证输入参数
|
|
120
120
|
if (!userId) {
|
|
121
|
-
this.logger.error('MCIDBIND',
|
|
121
|
+
this.logger.error('MCIDBIND', '删除绑定失败: 无效的用户ID');
|
|
122
122
|
return false;
|
|
123
123
|
}
|
|
124
124
|
const normalizedQQId = this.normalizeQQId(userId);
|
|
125
125
|
if (!normalizedQQId) {
|
|
126
|
-
this.logger.error('MCIDBIND',
|
|
126
|
+
this.logger.error('MCIDBIND', '删除绑定失败: 无法提取有效的QQ号');
|
|
127
127
|
return false;
|
|
128
128
|
}
|
|
129
129
|
// 查询是否存在绑定记录
|
|
@@ -161,7 +161,7 @@ class DatabaseService {
|
|
|
161
161
|
try {
|
|
162
162
|
// 验证输入参数
|
|
163
163
|
if (!username) {
|
|
164
|
-
this.logger.warn('绑定检查',
|
|
164
|
+
this.logger.warn('绑定检查', '尝试检查空MC用户名');
|
|
165
165
|
return false;
|
|
166
166
|
}
|
|
167
167
|
// 跳过临时用户名的检查
|
|
@@ -207,7 +207,7 @@ class DatabaseService {
|
|
|
207
207
|
async getBuidBindByBuid(buid) {
|
|
208
208
|
try {
|
|
209
209
|
if (!buid) {
|
|
210
|
-
this.logger.warn('B站账号绑定',
|
|
210
|
+
this.logger.warn('B站账号绑定', '尝试查询空B站UID');
|
|
211
211
|
return null;
|
|
212
212
|
}
|
|
213
213
|
const bind = await this.mcidbindRepo.findByBuidUid(buid);
|
|
@@ -245,7 +245,7 @@ class DatabaseService {
|
|
|
245
245
|
try {
|
|
246
246
|
const normalizedQQId = this.normalizeQQId(userId);
|
|
247
247
|
if (!normalizedQQId) {
|
|
248
|
-
this.logger.error('B站账号绑定',
|
|
248
|
+
this.logger.error('B站账号绑定', '创建/更新绑定失败: 无法提取有效的QQ号');
|
|
249
249
|
return false;
|
|
250
250
|
}
|
|
251
251
|
// 检查该UID是否已被其他用户绑定(安全检查)
|
|
@@ -266,7 +266,9 @@ class DatabaseService {
|
|
|
266
266
|
medalName: buidUser.medal?.name || '',
|
|
267
267
|
medalLevel: buidUser.medal?.level || 0,
|
|
268
268
|
wealthMedalLevel: buidUser.wealthMedalLevel || 0,
|
|
269
|
-
lastActiveTime: buidUser.last_active_time
|
|
269
|
+
lastActiveTime: buidUser.last_active_time
|
|
270
|
+
? new Date(buidUser.last_active_time)
|
|
271
|
+
: new Date(),
|
|
270
272
|
lastModified: new Date()
|
|
271
273
|
};
|
|
272
274
|
if (bind) {
|
|
@@ -302,7 +304,7 @@ class DatabaseService {
|
|
|
302
304
|
try {
|
|
303
305
|
const normalizedQQId = this.normalizeQQId(userId);
|
|
304
306
|
if (!normalizedQQId) {
|
|
305
|
-
this.logger.error('B站账号信息更新',
|
|
307
|
+
this.logger.error('B站账号信息更新', '更新失败: 无法提取有效的QQ号');
|
|
306
308
|
return false;
|
|
307
309
|
}
|
|
308
310
|
// 查询是否已存在绑定记录
|
|
@@ -339,7 +341,7 @@ class DatabaseService {
|
|
|
339
341
|
async checkAndUpdateUsername(bind) {
|
|
340
342
|
try {
|
|
341
343
|
if (!bind || !bind.mcUuid) {
|
|
342
|
-
this.logger.warn('用户名更新',
|
|
344
|
+
this.logger.warn('用户名更新', '无法检查用户名更新: 空绑定或空UUID');
|
|
343
345
|
return bind;
|
|
344
346
|
}
|
|
345
347
|
// 通过UUID查询最新用户名
|
|
@@ -377,7 +379,7 @@ class DatabaseService {
|
|
|
377
379
|
async checkAndUpdateUsernameWithCache(bind) {
|
|
378
380
|
try {
|
|
379
381
|
if (!bind || !bind.mcUuid) {
|
|
380
|
-
this.logger.warn('改名检测缓存',
|
|
382
|
+
this.logger.warn('改名检测缓存', '无法检查用户名更新: 空绑定或空UUID');
|
|
381
383
|
return bind;
|
|
382
384
|
}
|
|
383
385
|
const now = new Date();
|
|
@@ -28,7 +28,7 @@ class NicknameService {
|
|
|
28
28
|
if (!nickname || !buidUsername)
|
|
29
29
|
return false;
|
|
30
30
|
// 期望格式:B站名称(ID:MC用户名)或 B站名称(ID:未绑定)
|
|
31
|
-
const mcInfo = mcUsername && !mcUsername.startsWith('_temp_') ? mcUsername :
|
|
31
|
+
const mcInfo = mcUsername && !mcUsername.startsWith('_temp_') ? mcUsername : '未绑定';
|
|
32
32
|
const expectedFormat = `${buidUsername}(ID:${mcInfo})`;
|
|
33
33
|
return nickname === expectedFormat;
|
|
34
34
|
}
|
|
@@ -40,14 +40,14 @@ class NicknameService {
|
|
|
40
40
|
// 1. 尝试获取B站官方API的用户信息(最权威)
|
|
41
41
|
let officialUsername = null;
|
|
42
42
|
try {
|
|
43
|
-
this.logger.debug('群昵称设置',
|
|
43
|
+
this.logger.debug('群昵称设置', '正在查询B站官方API...');
|
|
44
44
|
const officialInfo = await this.getBilibiliOfficialUserInfo(buidUid);
|
|
45
45
|
if (officialInfo && officialInfo.name) {
|
|
46
46
|
officialUsername = officialInfo.name;
|
|
47
47
|
this.logger.info('群昵称设置', `[层1-官方API] ✅ "${officialUsername}"`, true);
|
|
48
48
|
}
|
|
49
49
|
else {
|
|
50
|
-
this.logger.warn('群昵称设置',
|
|
50
|
+
this.logger.warn('群昵称设置', '[层1-官方API] ❌ 查询失败');
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
catch (officialError) {
|
|
@@ -56,13 +56,13 @@ class NicknameService {
|
|
|
56
56
|
// 2. 尝试获取ZMINFO API的用户信息(可能有缓存)
|
|
57
57
|
let zminfoUserData = null;
|
|
58
58
|
try {
|
|
59
|
-
this.logger.debug('群昵称设置',
|
|
59
|
+
this.logger.debug('群昵称设置', '正在查询ZMINFO API...');
|
|
60
60
|
zminfoUserData = await this.validateBUID(buidUid);
|
|
61
61
|
if (zminfoUserData && zminfoUserData.username) {
|
|
62
62
|
this.logger.debug('群昵称设置', `[层2-ZMINFO] "${zminfoUserData.username}"`);
|
|
63
63
|
}
|
|
64
64
|
else {
|
|
65
|
-
this.logger.warn('群昵称设置',
|
|
65
|
+
this.logger.warn('群昵称设置', '[层2-ZMINFO] 查询失败');
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
catch (zminfoError) {
|
|
@@ -101,7 +101,7 @@ class NicknameService {
|
|
|
101
101
|
return; // 无需更新
|
|
102
102
|
}
|
|
103
103
|
if (!zminfoData) {
|
|
104
|
-
this.logger.debug('群昵称设置',
|
|
104
|
+
this.logger.debug('群昵称设置', '无ZMINFO数据,跳过数据库同步');
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
try {
|
|
@@ -136,7 +136,7 @@ class NicknameService {
|
|
|
136
136
|
else {
|
|
137
137
|
this.logger.warn('群昵称设置', `⚠️ 验证失败,期望"${nickname}",实际"${verifyNickname}",可能是权限不足或API延迟`);
|
|
138
138
|
if (!currentNickname) {
|
|
139
|
-
this.logger.warn('群昵称设置',
|
|
139
|
+
this.logger.warn('群昵称设置', '建议检查: 1.机器人是否为群管理员 2.群设置是否允许管理员修改昵称 3.OneBot实现是否支持该功能');
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -159,7 +159,7 @@ class NicknameService {
|
|
|
159
159
|
const actualUserId = targetUserId || session.userId;
|
|
160
160
|
const normalizedUserId = this.normalizeQQId(actualUserId);
|
|
161
161
|
const targetGroupId = specifiedGroupId || this.config.autoNicknameGroupId;
|
|
162
|
-
const mcInfo =
|
|
162
|
+
const mcInfo = mcUsername && !mcUsername.startsWith('_temp_') ? mcUsername : '未绑定';
|
|
163
163
|
this.logger.debug('群昵称设置', `开始处理QQ(${normalizedUserId})的群昵称设置,目标群: ${targetGroupId}`);
|
|
164
164
|
// 检查前置条件
|
|
165
165
|
if (!session.bot.internal) {
|
|
@@ -173,7 +173,7 @@ class NicknameService {
|
|
|
173
173
|
// 获取最新的B站用户名
|
|
174
174
|
let latestBuidUsername = buidUsername;
|
|
175
175
|
if (buidUid) {
|
|
176
|
-
this.logger.debug('群昵称设置',
|
|
176
|
+
this.logger.debug('群昵称设置', '开始四层判断获取最新B站用户名...');
|
|
177
177
|
this.logger.debug('群昵称设置', `[层3-数据库] "${buidUsername}"`);
|
|
178
178
|
const result = await this.getLatestBuidUsername(buidUid, buidUsername);
|
|
179
179
|
latestBuidUsername = result.username;
|
|
@@ -189,16 +189,12 @@ class NicknameService {
|
|
|
189
189
|
const currentGroupInfo = await session.bot.internal.getGroupMemberInfo(targetGroupId, normalizedUserId);
|
|
190
190
|
const currentNickname = currentGroupInfo.card || currentGroupInfo.nickname || '';
|
|
191
191
|
this.logger.debug('群昵称设置', `当前昵称: "${currentNickname}"`);
|
|
192
|
-
//
|
|
192
|
+
// 【调试信息】提取当前昵称中的BUID用户名(仅用于日志)
|
|
193
193
|
if (buidUid && currentNickname) {
|
|
194
194
|
const currentNicknameUsername = (0, helpers_1.extractBuidUsernameFromNickname)(currentNickname);
|
|
195
195
|
this.logger.debug('群昵称设置', `[层4-群昵称] "${currentNicknameUsername || '(无法提取)'}"`);
|
|
196
|
-
if (currentNicknameUsername && currentNicknameUsername === latestBuidUsername) {
|
|
197
|
-
this.logger.info('群昵称设置', `✅ 群昵称已包含最新名称"${latestBuidUsername}",跳过修改`, true);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
196
|
}
|
|
201
|
-
//
|
|
197
|
+
// 如果昵称完全一致,跳过修改
|
|
202
198
|
if (currentNickname === targetNickname) {
|
|
203
199
|
this.logger.info('群昵称设置', `QQ(${normalizedUserId})群昵称已经是"${targetNickname}",跳过修改`, true);
|
|
204
200
|
return;
|
|
@@ -210,7 +206,7 @@ class NicknameService {
|
|
|
210
206
|
catch (getInfoError) {
|
|
211
207
|
// 无法获取当前昵称,直接设置新昵称
|
|
212
208
|
this.logger.warn('群昵称设置', `获取QQ(${normalizedUserId})当前群昵称失败: ${getInfoError.message}`);
|
|
213
|
-
this.logger.debug('群昵称设置',
|
|
209
|
+
this.logger.debug('群昵称设置', '将直接尝试设置新昵称...');
|
|
214
210
|
await this.setAndVerifyNickname(session, targetGroupId, normalizedUserId, targetNickname);
|
|
215
211
|
}
|
|
216
212
|
}
|
package/lib/types/api.d.ts
CHANGED
|
@@ -5,59 +5,149 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* Mojang API 响应接口
|
|
7
7
|
* 用于获取MC用户UUID和用户名
|
|
8
|
+
*
|
|
9
|
+
* @see https://api.mojang.com/users/profiles/minecraft/{username}
|
|
10
|
+
* @see https://api.mojang.com/user/profile/{uuid}
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```json
|
|
14
|
+
* {
|
|
15
|
+
* "id": "069a79f444e94726a5befca90e38aaf5",
|
|
16
|
+
* "name": "Notch"
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
8
19
|
*/
|
|
9
20
|
export interface MojangProfile {
|
|
21
|
+
/** UUID (不带连字符,32位十六进制字符串) */
|
|
10
22
|
id: string;
|
|
23
|
+
/** 玩家名称 (Mojang返回的标准大小写) */
|
|
11
24
|
name: string;
|
|
12
25
|
}
|
|
13
26
|
/**
|
|
14
27
|
* ZMINFO API - 用户信息接口
|
|
28
|
+
*
|
|
29
|
+
* @see {zminfoApiUrl}/api/user/{uid}
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```json
|
|
33
|
+
* {
|
|
34
|
+
* "uid": "12345678",
|
|
35
|
+
* "username": "用户名",
|
|
36
|
+
* "avatar_url": "https://...",
|
|
37
|
+
* "guard_level": 3,
|
|
38
|
+
* "guard_level_text": "总督",
|
|
39
|
+
* "max_guard_level": 3,
|
|
40
|
+
* "max_guard_level_text": "总督",
|
|
41
|
+
* "medal": {
|
|
42
|
+
* "name": "粉丝牌",
|
|
43
|
+
* "level": 20,
|
|
44
|
+
* "uid": "87654321",
|
|
45
|
+
* "room": 123456
|
|
46
|
+
* },
|
|
47
|
+
* "wealthMedalLevel": 15,
|
|
48
|
+
* "last_active_time": "2025-10-23T12:00:00Z"
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
15
51
|
*/
|
|
16
52
|
export interface ZminfoUser {
|
|
53
|
+
/** B站用户UID */
|
|
17
54
|
uid: string;
|
|
55
|
+
/** B站用户名 */
|
|
18
56
|
username: string;
|
|
57
|
+
/** 用户头像URL */
|
|
19
58
|
avatar_url: string;
|
|
59
|
+
/** 当前舰长等级 (0=无, 1=总督, 2=提督, 3=舰长) */
|
|
20
60
|
guard_level: number;
|
|
61
|
+
/** 当前舰长等级文本描述 */
|
|
21
62
|
guard_level_text: string;
|
|
63
|
+
/** 历史最高舰长等级 */
|
|
22
64
|
max_guard_level: number;
|
|
65
|
+
/** 历史最高舰长等级文本描述 */
|
|
23
66
|
max_guard_level_text: string;
|
|
67
|
+
/** 粉丝牌信息 (如果用户拥有) */
|
|
24
68
|
medal: {
|
|
69
|
+
/** 粉丝牌名称 */
|
|
25
70
|
name: string;
|
|
71
|
+
/** 粉丝牌等级 */
|
|
26
72
|
level: number;
|
|
73
|
+
/** UP主UID */
|
|
27
74
|
uid: string;
|
|
75
|
+
/** 直播间房间号 */
|
|
28
76
|
room: number;
|
|
29
77
|
} | null;
|
|
78
|
+
/** 荣耀等级 (财富勋章等级) */
|
|
30
79
|
wealthMedalLevel: number;
|
|
80
|
+
/** 最后活跃时间 (ISO 8601格式) */
|
|
31
81
|
last_active_time: string;
|
|
32
82
|
}
|
|
33
83
|
/**
|
|
34
84
|
* ZMINFO API 响应接口
|
|
85
|
+
*
|
|
86
|
+
* @example 成功响应
|
|
87
|
+
* ```json
|
|
88
|
+
* {
|
|
89
|
+
* "success": true,
|
|
90
|
+
* "message": "success",
|
|
91
|
+
* "data": {
|
|
92
|
+
* "user": { ... }
|
|
93
|
+
* }
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @example 失败响应
|
|
98
|
+
* ```json
|
|
99
|
+
* {
|
|
100
|
+
* "success": false,
|
|
101
|
+
* "message": "User not found"
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
35
104
|
*/
|
|
36
105
|
export interface ZminfoApiResponse {
|
|
106
|
+
/** 请求是否成功 */
|
|
37
107
|
success: boolean;
|
|
108
|
+
/** 响应消息 */
|
|
38
109
|
message: string;
|
|
110
|
+
/** 响应数据 (仅成功时存在) */
|
|
39
111
|
data?: {
|
|
112
|
+
/** 用户信息 */
|
|
40
113
|
user?: ZminfoUser;
|
|
41
114
|
};
|
|
42
115
|
}
|
|
43
116
|
/**
|
|
44
117
|
* Bilibili Live API - 粉丝勋章信息接口
|
|
118
|
+
*
|
|
119
|
+
* @see https://api.live.bilibili.com/xlive/app-ucenter/v1/user/GetMyMedals
|
|
45
120
|
*/
|
|
46
121
|
export interface MedalInfo {
|
|
122
|
+
/** UP主UID */
|
|
47
123
|
target_id: number;
|
|
124
|
+
/** 粉丝牌等级 */
|
|
48
125
|
level: number;
|
|
126
|
+
/** 粉丝牌名称 */
|
|
49
127
|
medal_name: string;
|
|
128
|
+
/** 粉丝牌渐变色起始颜色 */
|
|
50
129
|
medal_color_start: number;
|
|
130
|
+
/** 粉丝牌渐变色结束颜色 */
|
|
51
131
|
medal_color_end: number;
|
|
132
|
+
/** 粉丝牌边框颜色 */
|
|
52
133
|
medal_color_border: number;
|
|
134
|
+
/** 舰长等级 (0=无, 1=总督, 2=提督, 3=舰长) */
|
|
53
135
|
guard_level: number;
|
|
136
|
+
/** 佩戴状态 (0=未佩戴, 1=已佩戴) */
|
|
54
137
|
wearing_status: number;
|
|
138
|
+
/** 粉丝牌ID */
|
|
55
139
|
medal_id: number;
|
|
140
|
+
/** 当前亲密度 */
|
|
56
141
|
intimacy: number;
|
|
142
|
+
/** 下一级所需亲密度 */
|
|
57
143
|
next_intimacy: number;
|
|
144
|
+
/** 今日亲密度 */
|
|
58
145
|
today_feed: number;
|
|
146
|
+
/** 每日亲密度上限 */
|
|
59
147
|
day_limit: number;
|
|
148
|
+
/** 舰长图标URL */
|
|
60
149
|
guard_icon: string;
|
|
150
|
+
/** 荣耀图标URL */
|
|
61
151
|
honor_icon: string;
|
|
62
152
|
}
|
|
63
153
|
/**
|
package/lib/types/common.d.ts
CHANGED
|
@@ -38,3 +38,48 @@ export interface LotteryResult {
|
|
|
38
38
|
host_uid: number;
|
|
39
39
|
host_username: string;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* 待审批的入群申请信息
|
|
43
|
+
*/
|
|
44
|
+
export interface PendingRequest {
|
|
45
|
+
/** 播报消息的ID(用于监听表情回应) */
|
|
46
|
+
broadcastMessageId: string;
|
|
47
|
+
/** OneBot的请求标识(用于批准/拒绝) */
|
|
48
|
+
requestFlag: string;
|
|
49
|
+
/** 申请人QQ号 */
|
|
50
|
+
applicantQQ: string;
|
|
51
|
+
/** 申请人昵称 */
|
|
52
|
+
applicantNickname: string;
|
|
53
|
+
/** 申请人头像URL */
|
|
54
|
+
applicantAvatar: string;
|
|
55
|
+
/** 目标群号 */
|
|
56
|
+
targetGroupId: string;
|
|
57
|
+
/** 申请人回答的内容(UID) */
|
|
58
|
+
answer: string;
|
|
59
|
+
/** 申请时间戳 */
|
|
60
|
+
timestamp: number;
|
|
61
|
+
/** 审批状态 */
|
|
62
|
+
status: 'pending' | 'approved' | 'rejected' | 'processing';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 拒绝流程状态
|
|
66
|
+
*/
|
|
67
|
+
export interface RejectFlow {
|
|
68
|
+
/** 对应的待审批申请 */
|
|
69
|
+
pendingRequest: PendingRequest;
|
|
70
|
+
/** 发起拒绝的管理员QQ号 */
|
|
71
|
+
operatorId: string;
|
|
72
|
+
/** 询问消息ID */
|
|
73
|
+
askMessageId: string;
|
|
74
|
+
/** 超时时间戳 */
|
|
75
|
+
timeout: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 管理员权限缓存
|
|
79
|
+
*/
|
|
80
|
+
export interface AdminCache {
|
|
81
|
+
/** 管理员QQ号列表 */
|
|
82
|
+
admins: string[];
|
|
83
|
+
/** 最后更新时间戳 */
|
|
84
|
+
lastUpdate: number;
|
|
85
|
+
}
|