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.
Files changed (48) hide show
  1. package/lib/export-utils.d.ts +49 -0
  2. package/lib/export-utils.js +305 -0
  3. package/lib/force-bind-utils.d.ts +40 -0
  4. package/lib/force-bind-utils.js +242 -0
  5. package/lib/handlers/base.handler.d.ts +61 -0
  6. package/lib/handlers/base.handler.js +22 -0
  7. package/lib/handlers/binding.handler.d.ts +45 -0
  8. package/lib/handlers/binding.handler.js +285 -0
  9. package/lib/handlers/buid.handler.d.ts +71 -0
  10. package/lib/handlers/buid.handler.js +694 -0
  11. package/lib/handlers/index.d.ts +6 -0
  12. package/lib/handlers/index.js +22 -0
  13. package/lib/handlers/mcid.handler.d.ts +101 -0
  14. package/lib/handlers/mcid.handler.js +1045 -0
  15. package/lib/handlers/tag.handler.d.ts +14 -0
  16. package/lib/handlers/tag.handler.js +382 -0
  17. package/lib/handlers/whitelist.handler.d.ts +84 -0
  18. package/lib/handlers/whitelist.handler.js +1011 -0
  19. package/lib/index.d.ts +7 -0
  20. package/lib/index.js +2693 -0
  21. package/lib/managers/rcon-manager.d.ts +24 -0
  22. package/lib/managers/rcon-manager.js +308 -0
  23. package/lib/repositories/mcidbind.repository.d.ts +105 -0
  24. package/lib/repositories/mcidbind.repository.js +288 -0
  25. package/lib/repositories/schedule-mute.repository.d.ts +68 -0
  26. package/lib/repositories/schedule-mute.repository.js +175 -0
  27. package/lib/types/api.d.ts +135 -0
  28. package/lib/types/api.js +6 -0
  29. package/lib/types/common.d.ts +40 -0
  30. package/lib/types/common.js +6 -0
  31. package/lib/types/config.d.ts +55 -0
  32. package/lib/types/config.js +6 -0
  33. package/lib/types/database.d.ts +47 -0
  34. package/lib/types/database.js +6 -0
  35. package/lib/types/index.d.ts +8 -0
  36. package/lib/types/index.js +28 -0
  37. package/lib/utils/helpers.d.ts +76 -0
  38. package/lib/utils/helpers.js +275 -0
  39. package/lib/utils/logger.d.ts +75 -0
  40. package/lib/utils/logger.js +134 -0
  41. package/lib/utils/message-utils.d.ts +46 -0
  42. package/lib/utils/message-utils.js +234 -0
  43. package/lib/utils/rate-limiter.d.ts +26 -0
  44. package/lib/utils/rate-limiter.js +47 -0
  45. package/lib/utils/session-manager.d.ts +70 -0
  46. package/lib/utils/session-manager.js +120 -0
  47. package/package.json +39 -0
  48. package/readme.md +281 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 通用类型定义
3
+ * 包含缓存、会话、天选开奖等公共接口
4
+ */
5
+ /**
6
+ * 头像缓存接口
7
+ */
8
+ export interface AvatarCache {
9
+ url: string;
10
+ timestamp: number;
11
+ }
12
+ /**
13
+ * 交互式绑定会话接口
14
+ * 从 utils/session-manager.ts 重新导出
15
+ */
16
+ export type { BindingSession } from '../utils/session-manager';
17
+ /**
18
+ * 天选开奖 - 中奖用户接口
19
+ */
20
+ export interface LotteryWinner {
21
+ uid: number;
22
+ username: string;
23
+ medal_level: number;
24
+ }
25
+ /**
26
+ * 天选开奖 - 开奖结果接口
27
+ */
28
+ export interface LotteryResult {
29
+ type: string;
30
+ lottery_id: string;
31
+ room_id: number;
32
+ reward_name: string;
33
+ reward_num: number;
34
+ message: string;
35
+ winners_count: number;
36
+ winners: LotteryWinner[];
37
+ timestamp: number;
38
+ host_uid: number;
39
+ host_username: string;
40
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * 通用类型定义
4
+ * 包含缓存、会话、天选开奖等公共接口
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 配置相关类型定义
3
+ * 包含插件配置接口和服务器配置接口
4
+ */
5
+ /**
6
+ * 插件配置接口
7
+ */
8
+ export interface Config {
9
+ cooldownDays: number;
10
+ masterId: string;
11
+ servers: ServerConfig[];
12
+ allowTextPrefix: boolean;
13
+ botNickname: string;
14
+ autoRecallTime: number;
15
+ recallUserMessage: boolean;
16
+ debugMode: boolean;
17
+ showAvatar: boolean;
18
+ showMcSkin: boolean;
19
+ zminfoApiUrl: string;
20
+ enableLotteryBroadcast: boolean;
21
+ autoNicknameGroupId: string;
22
+ forceBindSessdata: string;
23
+ forceBindTargetUpUid: number;
24
+ forceBindTargetRoomId: number;
25
+ forceBindTargetMedalName: string;
26
+ }
27
+ /**
28
+ * 服务器配置接口
29
+ */
30
+ export interface ServerConfig {
31
+ id: string;
32
+ name: string;
33
+ rconAddress: string;
34
+ rconPassword: string;
35
+ addCommand: string;
36
+ removeCommand: string;
37
+ idType: 'username' | 'uuid';
38
+ allowSelfApply: boolean;
39
+ acceptEmptyResponse?: boolean;
40
+ displayAddress?: string;
41
+ description?: string;
42
+ enabled?: boolean;
43
+ }
44
+ /**
45
+ * 强制绑定配置接口
46
+ * 从 force-bind-utils.ts 迁移
47
+ */
48
+ export interface ForceBindConfig {
49
+ SESSDATA: string;
50
+ zminfoApiUrl: string;
51
+ targetUpUid: number;
52
+ targetRoomId: number;
53
+ targetMedalName: string;
54
+ debugMode: boolean;
55
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * 配置相关类型定义
4
+ * 包含插件配置接口和服务器配置接口
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 数据库表结构类型定义
3
+ * 包含 MCIDBIND 表和 SCHEDULE_MUTE_TASKS 表
4
+ */
5
+ /**
6
+ * MCIDBIND表结构 - MC和B站账号绑定主表
7
+ */
8
+ export interface MCIDBIND {
9
+ qqId: string;
10
+ mcUsername: string;
11
+ mcUuid: string;
12
+ lastModified: Date;
13
+ isAdmin: boolean;
14
+ whitelist: string[];
15
+ tags: string[];
16
+ buidUid: string;
17
+ buidUsername: string;
18
+ guardLevel: number;
19
+ guardLevelText: string;
20
+ maxGuardLevel: number;
21
+ maxGuardLevelText: string;
22
+ medalName: string;
23
+ medalLevel: number;
24
+ wealthMedalLevel: number;
25
+ lastActiveTime: Date;
26
+ reminderCount: number;
27
+ }
28
+ /**
29
+ * SCHEDULE_MUTE_TASKS表结构 - 定时禁言任务配置
30
+ */
31
+ export interface SCHEDULE_MUTE_TASKS {
32
+ id: number;
33
+ groupId: string;
34
+ startTime: string;
35
+ endTime: string;
36
+ enabled: boolean;
37
+ setterId: string;
38
+ }
39
+ /**
40
+ * Koishi模块扩展 - 声明数据库表
41
+ */
42
+ declare module 'koishi' {
43
+ interface Tables {
44
+ mcidbind: MCIDBIND;
45
+ schedule_mute_tasks: SCHEDULE_MUTE_TASKS;
46
+ }
47
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * 数据库表结构类型定义
4
+ * 包含 MCIDBIND 表和 SCHEDULE_MUTE_TASKS 表
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 类型定义统一导出
3
+ * 所有其他模块应从此处导入类型
4
+ */
5
+ export * from './config';
6
+ export * from './database';
7
+ export * from './api';
8
+ export * from './common';
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ /**
3
+ * 类型定义统一导出
4
+ * 所有其他模块应从此处导入类型
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ // 配置相关类型
22
+ __exportStar(require("./config"), exports);
23
+ // 数据库表结构类型
24
+ __exportStar(require("./database"), exports);
25
+ // 外部API响应类型
26
+ __exportStar(require("./api"), exports);
27
+ // 通用类型
28
+ __exportStar(require("./common"), exports);
@@ -0,0 +1,76 @@
1
+ import { Logger, Session } from 'koishi';
2
+ /**
3
+ * 通用工具函数集合
4
+ */
5
+ /**
6
+ * 规范化QQ号格式
7
+ * 支持处理:
8
+ * - platform:123456 格式
9
+ * - <at id="123456"/> 格式
10
+ * - @用户 格式
11
+ * - 纯数字格式
12
+ *
13
+ * @param userId 用户ID(可能包含平台前缀或特殊格式)
14
+ * @param logger Koishi Logger实例(用于警告日志)
15
+ * @returns 纯QQ号字符串,失败返回空字符串
16
+ */
17
+ export declare function normalizeQQId(userId: string, logger?: Logger): string;
18
+ /**
19
+ * 格式化命令提示(添加机器人昵称前缀)
20
+ * @param cmd 命令字符串
21
+ * @param allowTextPrefix 是否允许文本前缀
22
+ * @param botNickname 机器人昵称
23
+ * @returns 格式化后的命令字符串
24
+ */
25
+ export declare function formatCommand(cmd: string, allowTextPrefix: boolean, botNickname: string): string;
26
+ /**
27
+ * 格式化UUID(添加连字符,使其符合标准格式)
28
+ * @param uuid 不带连字符的UUID字符串
29
+ * @param logger Koishi Logger实例(用于警告日志)
30
+ * @returns 标准格式的UUID
31
+ */
32
+ export declare function formatUuid(uuid: string, logger?: Logger): string;
33
+ /**
34
+ * 检查冷却时间是否已过
35
+ * @param lastModified 上次修改时间
36
+ * @param cooldownDays 冷却天数
37
+ * @param multiplier 倍数(用于B站UID冷却时间)
38
+ * @returns 是否已过冷却期
39
+ */
40
+ export declare function checkCooldown(lastModified: Date | null, cooldownDays: number, multiplier?: number): boolean;
41
+ /**
42
+ * 获取Crafatar头像URL
43
+ * @param uuid MC用户的UUID
44
+ * @param logger Koishi Logger实例(用于日志)
45
+ * @returns 头像URL或null
46
+ */
47
+ export declare function getCrafatarUrl(uuid: string, logger?: Logger): string | null;
48
+ /**
49
+ * 获取Starlight皮肤渲染URL(随机姿势)
50
+ * @param username MC用户名
51
+ * @param logger Koishi Logger实例(用于日志)
52
+ * @returns 皮肤渲染URL或null
53
+ */
54
+ export declare function getStarlightSkinUrl(username: string, logger?: Logger): string | null;
55
+ /**
56
+ * 检查绑定会话中的输入是否为无关内容
57
+ * @param state 当前会话状态
58
+ * @param content 用户输入内容
59
+ * @returns 是否为无关输入
60
+ */
61
+ export declare function checkIrrelevantInput(state: 'waiting_mc_username' | 'waiting_buid', content: string): boolean;
62
+ /**
63
+ * 转义正则表达式特殊字符
64
+ * @param string 需要转义的字符串
65
+ * @returns 转义后的字符串
66
+ */
67
+ export declare function escapeRegExp(string: string): string;
68
+ /**
69
+ * 清理用户输入中的@Bot前缀
70
+ * @param content 用户输入内容
71
+ * @param session Koishi Session对象
72
+ * @param botNickname 机器人昵称配置
73
+ * @param logger Koishi Logger实例(用于日志)
74
+ * @returns 清理后的输入内容
75
+ */
76
+ export declare function cleanUserInput(content: string, session: Session, botNickname: string, logger?: Logger): string;
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeQQId = normalizeQQId;
4
+ exports.formatCommand = formatCommand;
5
+ exports.formatUuid = formatUuid;
6
+ exports.checkCooldown = checkCooldown;
7
+ exports.getCrafatarUrl = getCrafatarUrl;
8
+ exports.getStarlightSkinUrl = getStarlightSkinUrl;
9
+ exports.checkIrrelevantInput = checkIrrelevantInput;
10
+ exports.escapeRegExp = escapeRegExp;
11
+ exports.cleanUserInput = cleanUserInput;
12
+ /**
13
+ * 通用工具函数集合
14
+ */
15
+ /**
16
+ * 规范化QQ号格式
17
+ * 支持处理:
18
+ * - platform:123456 格式
19
+ * - <at id="123456"/> 格式
20
+ * - @用户 格式
21
+ * - 纯数字格式
22
+ *
23
+ * @param userId 用户ID(可能包含平台前缀或特殊格式)
24
+ * @param logger Koishi Logger实例(用于警告日志)
25
+ * @returns 纯QQ号字符串,失败返回空字符串
26
+ */
27
+ function normalizeQQId(userId, logger) {
28
+ // 处理空值情况
29
+ if (!userId) {
30
+ logger?.warn(`[用户ID] 收到空用户ID`);
31
+ return '';
32
+ }
33
+ let extractedId = '';
34
+ // 检查是否是手动输入的@符号(错误用法)
35
+ if (userId.startsWith('@') && !userId.match(/<at\s+id="[^"]+"\s*\/>/)) {
36
+ logger?.warn(`[用户ID] 检测到手动输入的@符号"${userId}",应使用真正的@功能`);
37
+ return ''; // 返回空字符串表示无效
38
+ }
39
+ // 处理 <at id="..."/> 格式的@用户字符串
40
+ const atMatch = userId.match(/<at id="(\d+)"\s*\/>/);
41
+ if (atMatch) {
42
+ extractedId = atMatch[1];
43
+ }
44
+ else {
45
+ // 如果包含冒号,说明有平台前缀(如 onebot:123456)
46
+ const colonIndex = userId.indexOf(':');
47
+ if (colonIndex !== -1) {
48
+ extractedId = userId.substring(colonIndex + 1);
49
+ }
50
+ else {
51
+ extractedId = userId;
52
+ }
53
+ }
54
+ // 验证提取的ID是否为纯数字QQ号
55
+ if (!/^\d+$/.test(extractedId)) {
56
+ logger?.warn(`[用户ID] 提取的ID"${extractedId}"不是有效的QQ号(必须为纯数字),来源: ${userId}`);
57
+ return ''; // 返回空字符串表示无效
58
+ }
59
+ // 检查QQ号长度是否合理(QQ号通常为5-12位数字)
60
+ if (extractedId.length < 5 || extractedId.length > 12) {
61
+ logger?.warn(`[用户ID] QQ号"${extractedId}"长度异常(${extractedId.length}位),有效范围为5-12位`);
62
+ return '';
63
+ }
64
+ return extractedId;
65
+ }
66
+ /**
67
+ * 格式化命令提示(添加机器人昵称前缀)
68
+ * @param cmd 命令字符串
69
+ * @param allowTextPrefix 是否允许文本前缀
70
+ * @param botNickname 机器人昵称
71
+ * @returns 格式化后的命令字符串
72
+ */
73
+ function formatCommand(cmd, allowTextPrefix, botNickname) {
74
+ if (allowTextPrefix && botNickname) {
75
+ // 检查botNickname是否已经包含@符号,避免重复添加
76
+ const nickname = botNickname.startsWith('@') ? botNickname : `@${botNickname}`;
77
+ return `${nickname} ${cmd}`;
78
+ }
79
+ return cmd;
80
+ }
81
+ /**
82
+ * 格式化UUID(添加连字符,使其符合标准格式)
83
+ * @param uuid 不带连字符的UUID字符串
84
+ * @param logger Koishi Logger实例(用于警告日志)
85
+ * @returns 标准格式的UUID
86
+ */
87
+ function formatUuid(uuid, logger) {
88
+ if (!uuid)
89
+ return '未知';
90
+ if (uuid.includes('-'))
91
+ return uuid; // 已经是带连字符的格式
92
+ // 确保UUID长度正确
93
+ if (uuid.length !== 32) {
94
+ logger?.warn(`[UUID] UUID "${uuid}" 长度异常,无法格式化`);
95
+ return uuid;
96
+ }
97
+ return `${uuid.substring(0, 8)}-${uuid.substring(8, 12)}-${uuid.substring(12, 16)}-${uuid.substring(16, 20)}-${uuid.substring(20)}`;
98
+ }
99
+ /**
100
+ * 检查冷却时间是否已过
101
+ * @param lastModified 上次修改时间
102
+ * @param cooldownDays 冷却天数
103
+ * @param multiplier 倍数(用于B站UID冷却时间)
104
+ * @returns 是否已过冷却期
105
+ */
106
+ function checkCooldown(lastModified, cooldownDays, multiplier = 1) {
107
+ if (!lastModified)
108
+ return true;
109
+ const now = new Date();
110
+ const diffTime = now.getTime() - lastModified.getTime();
111
+ // 使用Math.floor确保冷却时间精确
112
+ const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
113
+ return diffDays >= cooldownDays * multiplier;
114
+ }
115
+ /**
116
+ * 获取Crafatar头像URL
117
+ * @param uuid MC用户的UUID
118
+ * @param logger Koishi Logger实例(用于日志)
119
+ * @returns 头像URL或null
120
+ */
121
+ function getCrafatarUrl(uuid, logger) {
122
+ if (!uuid)
123
+ return null;
124
+ // 检查UUID格式 (不带连字符应为32位,带连字符应为36位)
125
+ const uuidRegex = /^[0-9a-f]{32}$|^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
126
+ if (!uuidRegex.test(uuid)) {
127
+ logger?.warn(`[MC头图] UUID "${uuid}" 格式无效,无法生成头图URL`);
128
+ return null;
129
+ }
130
+ // 移除任何连字符,Crafatar接受不带连字符的UUID
131
+ const cleanUuid = uuid.replace(/-/g, '');
132
+ // 直接生成URL
133
+ const url = `https://crafatar.com/avatars/${cleanUuid}`;
134
+ logger?.debug(`[MC头图] 为UUID "${cleanUuid}" 生成头图URL`);
135
+ return url;
136
+ }
137
+ /**
138
+ * 获取Starlight皮肤渲染URL(随机姿势)
139
+ * @param username MC用户名
140
+ * @param logger Koishi Logger实例(用于日志)
141
+ * @returns 皮肤渲染URL或null
142
+ */
143
+ function getStarlightSkinUrl(username, logger) {
144
+ if (!username)
145
+ return null;
146
+ // 可用的动作列表 (共16种)
147
+ const poses = [
148
+ 'default', // 默认站立
149
+ 'marching', // 行军
150
+ 'walking', // 行走
151
+ 'crouching', // 下蹲
152
+ 'crossed', // 交叉手臂
153
+ 'crisscross', // 交叉腿
154
+ 'cheering', // 欢呼
155
+ 'relaxing', // 放松
156
+ 'trudging', // 艰难行走
157
+ 'cowering', // 退缩
158
+ 'pointing', // 指向
159
+ 'lunging', // 前冲
160
+ 'dungeons', // 地下城风格
161
+ 'facepalm', // 捂脸
162
+ 'mojavatar', // Mojave姿态
163
+ 'head', // 头部特写
164
+ ];
165
+ // 随机选择一个动作
166
+ const randomPose = poses[Math.floor(Math.random() * poses.length)];
167
+ // 视图类型(full为全身图)
168
+ const viewType = 'full';
169
+ // 生成URL
170
+ const url = `https://starlightskins.lunareclipse.studio/render/${randomPose}/${username}/${viewType}`;
171
+ logger?.debug(`[Starlight皮肤] 为用户名"${username}"生成动作"${randomPose}"的渲染URL`);
172
+ return url;
173
+ }
174
+ /**
175
+ * 检查绑定会话中的输入是否为无关内容
176
+ * @param state 当前会话状态
177
+ * @param content 用户输入内容
178
+ * @returns 是否为无关输入
179
+ */
180
+ function checkIrrelevantInput(state, content) {
181
+ if (!content)
182
+ return false;
183
+ // 常见的聊天用语或明显无关的内容
184
+ const chatKeywords = ['你好', 'hello', 'hi', '在吗', '在不在', '怎么样', '什么', '为什么', '好的', '谢谢', '哈哈', '呵呵', '早上好', '晚上好', '晚安', '再见', '拜拜', '666', '牛', '厉害', '真的吗', '不是吧', '哇', '哦', '嗯', '好吧', '行', '可以', '没事', '没问题', '没关系'];
185
+ const lowercaseContent = content.toLowerCase();
186
+ // 检查是否包含明显的聊天用语
187
+ if (chatKeywords.some(keyword => lowercaseContent.includes(keyword))) {
188
+ return true;
189
+ }
190
+ // 检查是否为明显的聊天模式(多个连续的标点符号、表情等)
191
+ if (/[!?。,;:""''()【】〈〉《》「」『』〔〕〖〗〘〙〚〛]{2,}/.test(content) ||
192
+ /[!?.,;:"'()[\]<>{}]{3,}/.test(content)) {
193
+ return true;
194
+ }
195
+ if (state === 'waiting_mc_username') {
196
+ // 先排除跳过命令,这些是有效输入
197
+ if (content === '跳过' || content === 'skip') {
198
+ return false;
199
+ }
200
+ // MC用户名检查
201
+ // 长度明显不符合MC用户名规范(3-16位)
202
+ if (content.length < 2 || content.length > 20) {
203
+ return true;
204
+ }
205
+ // 包含中文或其他明显不是MC用户名的字符
206
+ if (/[\u4e00-\u9fa5]/.test(content) || content.includes(' ') || content.includes('@')) {
207
+ return true;
208
+ }
209
+ // 如果是明显的指令格式
210
+ if (content.startsWith('.') || content.startsWith('/') || content.startsWith('mcid') || content.startsWith('buid')) {
211
+ return true;
212
+ }
213
+ }
214
+ else if (state === 'waiting_buid') {
215
+ // B站UID检查
216
+ // 移除UID:前缀后检查
217
+ let actualContent = content;
218
+ if (content.toLowerCase().startsWith('uid:')) {
219
+ actualContent = content.substring(4);
220
+ }
221
+ // 如果不是纯数字且不是跳过命令
222
+ if (!/^\d+$/.test(actualContent) && content !== '跳过' && content !== 'skip') {
223
+ // 检查是否明显是聊天内容(包含字母、中文、空格等)
224
+ if (/[a-zA-Z\u4e00-\u9fa5\s]/.test(content) && !content.toLowerCase().startsWith('uid:')) {
225
+ return true;
226
+ }
227
+ // 如果是明显的指令格式
228
+ if (content.startsWith('.') || content.startsWith('/') || content.startsWith('mcid') || content.startsWith('buid')) {
229
+ return true;
230
+ }
231
+ }
232
+ }
233
+ return false;
234
+ }
235
+ /**
236
+ * 转义正则表达式特殊字符
237
+ * @param string 需要转义的字符串
238
+ * @returns 转义后的字符串
239
+ */
240
+ function escapeRegExp(string) {
241
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
242
+ }
243
+ /**
244
+ * 清理用户输入中的@Bot前缀
245
+ * @param content 用户输入内容
246
+ * @param session Koishi Session对象
247
+ * @param botNickname 机器人昵称配置
248
+ * @param logger Koishi Logger实例(用于日志)
249
+ * @returns 清理后的输入内容
250
+ */
251
+ function cleanUserInput(content, session, botNickname, logger) {
252
+ if (!content)
253
+ return content;
254
+ // 获取机器人的用户ID
255
+ const botUserId = session.bot.userId;
256
+ // 匹配各种@Bot的格式
257
+ const atPatterns = [
258
+ // <at id="botUserId"/> 格式
259
+ new RegExp(`^<at id="${escapeRegExp(botUserId)}"/>\\s*`, 'i'),
260
+ // @Bot昵称 格式(如果配置了botNickname)
261
+ botNickname ? new RegExp(`^@${escapeRegExp(botNickname)}\\s+`, 'i') : null,
262
+ // @botUserId 格式
263
+ new RegExp(`^@${escapeRegExp(botUserId)}\\s+`, 'i'),
264
+ ].filter(Boolean);
265
+ let cleanedContent = content.trim();
266
+ // 尝试匹配并移除@Bot前缀
267
+ for (const pattern of atPatterns) {
268
+ if (pattern.test(cleanedContent)) {
269
+ cleanedContent = cleanedContent.replace(pattern, '').trim();
270
+ logger?.debug(`[交互绑定] 清理用户输入,原始: "${content}" -> 清理后: "${cleanedContent}"`);
271
+ break;
272
+ }
273
+ }
274
+ return cleanedContent;
275
+ }
@@ -0,0 +1,75 @@
1
+ import { Logger } from 'koishi';
2
+ /**
3
+ * 统一的日志服务类
4
+ * 提供一致的日志接口,支持上下文标签和调试模式
5
+ */
6
+ export declare class LoggerService {
7
+ private logger;
8
+ private debugMode;
9
+ private defaultContext;
10
+ /**
11
+ * 创建日志服务实例
12
+ * @param logger Koishi Logger 实例
13
+ * @param debugMode 是否启用调试模式
14
+ * @param defaultContext 默认上下文标签(可选)
15
+ */
16
+ constructor(logger: Logger, debugMode?: boolean, defaultContext?: string);
17
+ /**
18
+ * 设置调试模式
19
+ */
20
+ setDebugMode(enabled: boolean): void;
21
+ /**
22
+ * 获取当前调试模式状态
23
+ */
24
+ getDebugMode(): boolean;
25
+ /**
26
+ * 输出调试日志(仅在 debugMode 为 true 时输出)
27
+ * @param context 上下文标签
28
+ * @param message 日志消息
29
+ */
30
+ debug(context: string, message: string): void;
31
+ /**
32
+ * 输出信息日志
33
+ * @param context 上下文标签
34
+ * @param message 日志消息
35
+ * @param forceOutput 强制输出(忽略 debugMode 限制)
36
+ */
37
+ info(context: string, message: string, forceOutput?: boolean): void;
38
+ /**
39
+ * 输出警告日志(总是输出)
40
+ * @param context 上下文标签
41
+ * @param message 日志消息
42
+ */
43
+ warn(context: string, message: string): void;
44
+ /**
45
+ * 输出错误日志(总是输出)
46
+ * @param context 上下文标签
47
+ * @param message 错误消息
48
+ * @param error 错误对象或错误字符串(可选)
49
+ */
50
+ error(context: string, message: string, error?: Error | string): void;
51
+ /**
52
+ * 记录用户操作日志(带用户ID和操作结果)
53
+ * @param operation 操作名称(如 "MC账号绑定")
54
+ * @param userId 用户ID(QQ号)
55
+ * @param success 操作是否成功
56
+ * @param details 额外详情(可选)
57
+ */
58
+ logOperation(operation: string, userId: string, success: boolean, details?: string): void;
59
+ /**
60
+ * 规范化QQ号格式(移除platform前缀)
61
+ * @param userId 用户ID(可能包含 platform: 前缀)
62
+ * @returns 纯QQ号字符串
63
+ */
64
+ private normalizeQQId;
65
+ /**
66
+ * 创建子日志服务(带固定上下文)
67
+ * @param context 固定的上下文标签
68
+ * @returns 新的 LoggerService 实例
69
+ */
70
+ createChild(context: string): LoggerService;
71
+ /**
72
+ * 获取原始的 Koishi Logger 实例(用于特殊情况)
73
+ */
74
+ getRawLogger(): Logger;
75
+ }