koishi-plugin-bind-bot 2.1.6 → 2.1.8
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.
|
@@ -85,7 +85,7 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
|
|
|
85
85
|
status: 'pending'
|
|
86
86
|
};
|
|
87
87
|
this.pendingRequests.set(broadcastMsgId, pendingReq);
|
|
88
|
-
this.logger.info('入群审批',
|
|
88
|
+
this.logger.info('入群审批', `[DEBUG] 已保存待审批记录 - 申请人: ${normalizedUserId}, 播报消息ID: ${broadcastMsgId}, 目标群: ${this.reviewConfig.reviewGroupId}`, true);
|
|
89
89
|
// 自动添加表情回应选项
|
|
90
90
|
await this.addReactionOptions(broadcastMsgId, session.bot);
|
|
91
91
|
}
|
|
@@ -98,37 +98,38 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
|
|
|
98
98
|
*/
|
|
99
99
|
async handleNotice(session) {
|
|
100
100
|
try {
|
|
101
|
-
//
|
|
101
|
+
// 【调试】最早期日志 - 记录所有收到的notice事件
|
|
102
102
|
this.logger.info('入群审批', `[DEBUG] 收到notice事件 - type: ${session.type}, subtype: ${session.subtype}, guildId: ${session.guildId}`, true);
|
|
103
103
|
// 只处理群表情回应事件
|
|
104
104
|
if (session.subtype !== 'group-msg-emoji-like') {
|
|
105
|
-
this.logger.info('入群审批', `[DEBUG] 跳过: subtype不匹配 (${session.subtype})`, true);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
// 只处理管理群的表情
|
|
109
|
-
if (session.guildId !== this.reviewConfig.reviewGroupId) {
|
|
110
|
-
this.logger.info('入群审批', `[DEBUG] 跳过: guildId不匹配 (收到: ${session.guildId}, 需要: ${this.reviewConfig.reviewGroupId})`, true);
|
|
111
105
|
return;
|
|
112
106
|
}
|
|
113
107
|
// 获取原始事件数据(直接访问 session.onebot,参考luckydraw实现)
|
|
114
108
|
const data = session.onebot;
|
|
115
|
-
|
|
109
|
+
// 【调试】输出完整的原始事件数据
|
|
110
|
+
this.logger.info('入群审批', `[DEBUG] 表情回应原始数据: ${JSON.stringify({
|
|
111
|
+
session_guildId: session.guildId,
|
|
112
|
+
session_channelId: session.channelId,
|
|
113
|
+
onebot_group_id: data?.group_id,
|
|
114
|
+
onebot_message_id: data?.message_id,
|
|
115
|
+
onebot_user_id: data?.user_id,
|
|
116
|
+
onebot_likes: data?.likes
|
|
117
|
+
})}`, true);
|
|
116
118
|
const messageId = data?.message_id;
|
|
117
119
|
const userId = data?.user_id?.toString();
|
|
118
120
|
const likes = data?.likes || [];
|
|
119
121
|
if (!messageId || !userId || likes.length === 0) {
|
|
120
|
-
this.logger.info('入群审批', '[DEBUG] 跳过: 缺少必要数据或likes为空', true);
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
124
|
const msgId = messageId.toString();
|
|
124
125
|
const emojiData = likes;
|
|
125
126
|
const operatorId = this.deps.normalizeQQId(userId);
|
|
126
|
-
this.logger.
|
|
127
|
+
this.logger.debug('入群审批', `收到表情回应 - 消息: ${msgId}, 操作者: ${operatorId}, 表情数: ${emojiData.length}`);
|
|
127
128
|
// 检查是否是待审批的消息
|
|
128
129
|
const pendingReq = this.pendingRequests.get(msgId);
|
|
129
130
|
if (!pendingReq) {
|
|
130
|
-
this.logger.info('入群审批', `[DEBUG]
|
|
131
|
-
this.logger.info('入群审批', `[DEBUG]
|
|
131
|
+
this.logger.info('入群审批', `[DEBUG] 消息${msgId}不在待审批列表中`, true);
|
|
132
|
+
this.logger.info('入群审批', `[DEBUG] 当前待审批消息ID列表: [${Array.from(this.pendingRequests.keys()).join(', ')}]`, true);
|
|
132
133
|
return;
|
|
133
134
|
}
|
|
134
135
|
// 检查是否已处理
|
|
@@ -589,20 +590,35 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
|
|
|
589
590
|
* 执行自动绑定
|
|
590
591
|
*/
|
|
591
592
|
async performAutoBind(qq, uid, bot) {
|
|
592
|
-
const axios = require('axios');
|
|
593
593
|
try {
|
|
594
|
-
// 1.
|
|
595
|
-
this.logger.debug('入群审批',
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
594
|
+
// 1. 使用双API数据源获取最新用户信息(优先B站官方API)
|
|
595
|
+
this.logger.debug('入群审批', `开始获取 B站 UID ${uid} 的信息`);
|
|
596
|
+
// 尝试获取B站官方API的用户信息(最权威)
|
|
597
|
+
let officialUsername = null;
|
|
598
|
+
try {
|
|
599
|
+
this.logger.debug('入群审批', '正在查询B站官方API...');
|
|
600
|
+
const officialInfo = await this.deps.apiService.getBilibiliOfficialUserInfo(uid);
|
|
601
|
+
if (officialInfo && officialInfo.name) {
|
|
602
|
+
officialUsername = officialInfo.name;
|
|
603
|
+
this.logger.info('入群审批', `[官方API] ✅ 获取到用户名: "${officialUsername}"`, true);
|
|
600
604
|
}
|
|
601
|
-
|
|
602
|
-
|
|
605
|
+
else {
|
|
606
|
+
this.logger.warn('入群审批', '[官方API] ❌ 查询失败');
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
catch (officialError) {
|
|
610
|
+
this.logger.warn('入群审批', `[官方API] ❌ 查询出错: ${officialError.message}`);
|
|
611
|
+
}
|
|
612
|
+
// 获取ZMINFO API的完整用户信息(包含粉丝牌、大航海等数据)
|
|
613
|
+
this.logger.debug('入群审批', '正在查询ZMINFO API...');
|
|
614
|
+
const zminfoUser = await this.deps.apiService.validateBUID(uid);
|
|
615
|
+
if (!zminfoUser) {
|
|
603
616
|
throw new Error(`无法验证B站UID: ${uid}`);
|
|
604
617
|
}
|
|
605
|
-
|
|
618
|
+
this.logger.debug('入群审批', `[ZMINFO] 获取到用户名: "${zminfoUser.username}"`);
|
|
619
|
+
// 使用官方API的用户名(如果可用),否则使用ZMINFO的
|
|
620
|
+
const finalUsername = officialUsername || zminfoUser.username;
|
|
621
|
+
this.logger.info('入群审批', `🎯 最终采用用户名: "${finalUsername}"`, true);
|
|
606
622
|
// 2. 检查是否已被其他人绑定
|
|
607
623
|
const existingBind = await this.repos.mcidbind.findByBuidUid(uid);
|
|
608
624
|
if (existingBind && existingBind.qqId !== qq) {
|
|
@@ -617,15 +633,15 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
|
|
|
617
633
|
qqId: qq,
|
|
618
634
|
mcUsername: tempMcUsername,
|
|
619
635
|
mcUuid: '',
|
|
620
|
-
buidUid:
|
|
621
|
-
buidUsername:
|
|
622
|
-
guardLevel:
|
|
623
|
-
guardLevelText:
|
|
624
|
-
maxGuardLevel:
|
|
625
|
-
maxGuardLevelText:
|
|
626
|
-
medalName:
|
|
627
|
-
medalLevel:
|
|
628
|
-
wealthMedalLevel:
|
|
636
|
+
buidUid: zminfoUser.uid,
|
|
637
|
+
buidUsername: finalUsername,
|
|
638
|
+
guardLevel: zminfoUser.guard_level || 0,
|
|
639
|
+
guardLevelText: zminfoUser.guard_level_text || '',
|
|
640
|
+
maxGuardLevel: zminfoUser.guard_level || 0,
|
|
641
|
+
maxGuardLevelText: zminfoUser.guard_level_text || '',
|
|
642
|
+
medalName: zminfoUser.medal?.name || '',
|
|
643
|
+
medalLevel: zminfoUser.medal?.level || 0,
|
|
644
|
+
wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
|
|
629
645
|
lastActiveTime: new Date(),
|
|
630
646
|
lastModified: new Date()
|
|
631
647
|
});
|
|
@@ -634,26 +650,29 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
|
|
|
634
650
|
else {
|
|
635
651
|
// 更新现有绑定
|
|
636
652
|
await this.repos.mcidbind.update(qq, {
|
|
637
|
-
buidUid:
|
|
638
|
-
buidUsername:
|
|
639
|
-
guardLevel:
|
|
640
|
-
guardLevelText:
|
|
641
|
-
maxGuardLevel: Math.max(bind.maxGuardLevel || 0,
|
|
642
|
-
maxGuardLevelText:
|
|
643
|
-
?
|
|
653
|
+
buidUid: zminfoUser.uid,
|
|
654
|
+
buidUsername: finalUsername,
|
|
655
|
+
guardLevel: zminfoUser.guard_level || 0,
|
|
656
|
+
guardLevelText: zminfoUser.guard_level_text || '',
|
|
657
|
+
maxGuardLevel: Math.max(bind.maxGuardLevel || 0, zminfoUser.guard_level || 0),
|
|
658
|
+
maxGuardLevelText: zminfoUser.guard_level > (bind.maxGuardLevel || 0)
|
|
659
|
+
? zminfoUser.guard_level_text
|
|
644
660
|
: bind.maxGuardLevelText,
|
|
645
|
-
medalName:
|
|
646
|
-
medalLevel:
|
|
647
|
-
wealthMedalLevel:
|
|
661
|
+
medalName: zminfoUser.medal?.name || '',
|
|
662
|
+
medalLevel: zminfoUser.medal?.level || 0,
|
|
663
|
+
wealthMedalLevel: zminfoUser.wealthMedalLevel || 0,
|
|
648
664
|
lastActiveTime: new Date(),
|
|
649
665
|
lastModified: new Date()
|
|
650
666
|
});
|
|
651
667
|
this.logger.info('入群审批', `已更新绑定 - QQ: ${qq}, UID: ${uid}`, true);
|
|
652
668
|
}
|
|
653
|
-
// 4.
|
|
669
|
+
// 4. 更新群昵称(使用标准格式)
|
|
654
670
|
try {
|
|
655
671
|
const groupId = this.reviewConfig.targetGroupId;
|
|
656
|
-
const
|
|
672
|
+
const mcInfo = bind.mcUsername && !bind.mcUsername.startsWith('_temp_')
|
|
673
|
+
? bind.mcUsername
|
|
674
|
+
: '未绑定';
|
|
675
|
+
const nickname = `${finalUsername}(ID:${mcInfo})`;
|
|
657
676
|
await bot.internal.setGroupCard(groupId, qq, nickname);
|
|
658
677
|
this.logger.info('入群审批', `已更新群昵称 - QQ: ${qq}, 昵称: ${nickname}`);
|
|
659
678
|
}
|
|
@@ -661,7 +680,7 @@ class GroupRequestReviewHandler extends base_handler_1.BaseHandler {
|
|
|
661
680
|
this.logger.warn('入群审批', `更新群昵称失败: ${error.message}`);
|
|
662
681
|
// 昵称更新失败不影响绑定
|
|
663
682
|
}
|
|
664
|
-
this.logger.info('入群审批', `自动绑定完成 - QQ: ${qq}, UID: ${uid}, 用户名: ${
|
|
683
|
+
this.logger.info('入群审批', `自动绑定完成 - QQ: ${qq}, UID: ${uid}, 用户名: ${finalUsername}`, true);
|
|
665
684
|
}
|
|
666
685
|
catch (error) {
|
|
667
686
|
this.logger.error('入群审批', `自动绑定失败: ${error.message}`, error);
|
|
@@ -7,9 +7,15 @@ import type { MojangProfile, ZminfoUser } from '../types';
|
|
|
7
7
|
export declare class ApiService {
|
|
8
8
|
private logger;
|
|
9
9
|
private config;
|
|
10
|
+
private cookieString;
|
|
10
11
|
constructor(logger: LoggerService, config: {
|
|
11
12
|
zminfoApiUrl: string;
|
|
13
|
+
SESSDATA?: string;
|
|
12
14
|
});
|
|
15
|
+
/**
|
|
16
|
+
* 处理cookie字符串,支持完整cookie或单独SESSDATA
|
|
17
|
+
*/
|
|
18
|
+
private processCookie;
|
|
13
19
|
/**
|
|
14
20
|
* 验证 Minecraft 用户名是否存在
|
|
15
21
|
* @param username MC 用户名
|
|
@@ -12,9 +12,30 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
12
12
|
class ApiService {
|
|
13
13
|
logger;
|
|
14
14
|
config;
|
|
15
|
+
cookieString;
|
|
15
16
|
constructor(logger, config) {
|
|
16
17
|
this.logger = logger;
|
|
17
18
|
this.config = config;
|
|
19
|
+
this.cookieString = this.processCookie(config.SESSDATA || '');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 处理cookie字符串,支持完整cookie或单独SESSDATA
|
|
23
|
+
*/
|
|
24
|
+
processCookie(input) {
|
|
25
|
+
if (!input || input.trim() === '') {
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
const trimmedInput = input.trim();
|
|
29
|
+
// 如果输入包含多个cookie字段(包含分号),则认为是完整cookie
|
|
30
|
+
if (trimmedInput.includes(';')) {
|
|
31
|
+
return trimmedInput;
|
|
32
|
+
}
|
|
33
|
+
// 如果输入只是SESSDATA值(不包含"SESSDATA="前缀)
|
|
34
|
+
if (!trimmedInput.startsWith('SESSDATA=')) {
|
|
35
|
+
return `SESSDATA=${trimmedInput}`;
|
|
36
|
+
}
|
|
37
|
+
// 如果输入已经是"SESSDATA=xxx"格式
|
|
38
|
+
return trimmedInput;
|
|
18
39
|
}
|
|
19
40
|
// =========== Mojang API ===========
|
|
20
41
|
/**
|
|
@@ -197,14 +218,20 @@ class ApiService {
|
|
|
197
218
|
return null;
|
|
198
219
|
}
|
|
199
220
|
this.logger.debug('B站官方API', `开始查询UID ${uid} 的官方信息`);
|
|
221
|
+
const headers = {
|
|
222
|
+
'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',
|
|
223
|
+
Referer: 'https://space.bilibili.com/',
|
|
224
|
+
Origin: 'https://space.bilibili.com'
|
|
225
|
+
};
|
|
226
|
+
// 如果配置了Cookie,则添加到请求头(避免被风控)
|
|
227
|
+
if (this.cookieString) {
|
|
228
|
+
headers.Cookie = this.cookieString;
|
|
229
|
+
this.logger.debug('B站官方API', '使用Cookie进行请求');
|
|
230
|
+
}
|
|
200
231
|
const response = await axios_1.default.get('https://api.bilibili.com/x/space/acc/info', {
|
|
201
232
|
params: { mid: uid },
|
|
202
233
|
timeout: 10000,
|
|
203
|
-
headers
|
|
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',
|
|
205
|
-
Referer: 'https://space.bilibili.com/',
|
|
206
|
-
Origin: 'https://space.bilibili.com'
|
|
207
|
-
}
|
|
234
|
+
headers
|
|
208
235
|
});
|
|
209
236
|
if (response.data.code === 0 && response.data.data) {
|
|
210
237
|
const userData = response.data.data;
|
|
@@ -14,7 +14,10 @@ class ServiceContainer {
|
|
|
14
14
|
nickname;
|
|
15
15
|
constructor(ctx, config, logger, mcidbindRepo, normalizeQQId) {
|
|
16
16
|
// 1. 实例化 API 服务(无依赖)
|
|
17
|
-
this.api = new api_service_1.ApiService(logger.createChild('API服务'), {
|
|
17
|
+
this.api = new api_service_1.ApiService(logger.createChild('API服务'), {
|
|
18
|
+
zminfoApiUrl: config.zminfoApiUrl,
|
|
19
|
+
SESSDATA: config.forceBindSessdata
|
|
20
|
+
});
|
|
18
21
|
// 2. 实例化数据库服务(依赖 API 服务)
|
|
19
22
|
this.database = new database_service_1.DatabaseService(ctx, logger.createChild('数据库服务'), mcidbindRepo, normalizeQQId, (uuid) => this.api.getUsernameByUuid(uuid));
|
|
20
23
|
// 3. 实例化群昵称服务(依赖 API 和数据库服务)
|