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.
- package/lib/export-utils.d.ts +49 -0
- package/lib/export-utils.js +305 -0
- package/lib/force-bind-utils.d.ts +40 -0
- package/lib/force-bind-utils.js +242 -0
- package/lib/handlers/base.handler.d.ts +61 -0
- package/lib/handlers/base.handler.js +22 -0
- package/lib/handlers/binding.handler.d.ts +45 -0
- package/lib/handlers/binding.handler.js +285 -0
- package/lib/handlers/buid.handler.d.ts +71 -0
- package/lib/handlers/buid.handler.js +694 -0
- package/lib/handlers/index.d.ts +6 -0
- package/lib/handlers/index.js +22 -0
- package/lib/handlers/mcid.handler.d.ts +101 -0
- package/lib/handlers/mcid.handler.js +1045 -0
- package/lib/handlers/tag.handler.d.ts +14 -0
- package/lib/handlers/tag.handler.js +382 -0
- package/lib/handlers/whitelist.handler.d.ts +84 -0
- package/lib/handlers/whitelist.handler.js +1011 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +2693 -0
- package/lib/managers/rcon-manager.d.ts +24 -0
- package/lib/managers/rcon-manager.js +308 -0
- package/lib/repositories/mcidbind.repository.d.ts +105 -0
- package/lib/repositories/mcidbind.repository.js +288 -0
- package/lib/repositories/schedule-mute.repository.d.ts +68 -0
- package/lib/repositories/schedule-mute.repository.js +175 -0
- package/lib/types/api.d.ts +135 -0
- package/lib/types/api.js +6 -0
- package/lib/types/common.d.ts +40 -0
- package/lib/types/common.js +6 -0
- package/lib/types/config.d.ts +55 -0
- package/lib/types/config.js +6 -0
- package/lib/types/database.d.ts +47 -0
- package/lib/types/database.js +6 -0
- package/lib/types/index.d.ts +8 -0
- package/lib/types/index.js +28 -0
- package/lib/utils/helpers.d.ts +76 -0
- package/lib/utils/helpers.js +275 -0
- package/lib/utils/logger.d.ts +75 -0
- package/lib/utils/logger.js +134 -0
- package/lib/utils/message-utils.d.ts +46 -0
- package/lib/utils/message-utils.js +234 -0
- package/lib/utils/rate-limiter.d.ts +26 -0
- package/lib/utils/rate-limiter.js +47 -0
- package/lib/utils/session-manager.d.ts +70 -0
- package/lib/utils/session-manager.js +120 -0
- package/package.json +39 -0
- 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,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,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,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
|
+
}
|