@dsh-plugin/dsh-synapse 0.1.0-dev.41044228975 → 0.1.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/LICENSE +661 -661
- package/README.md +0 -14
- package/package.json +1 -1
- package/vendor/gugle-chat-bot/LICENSE +661 -661
- package/vendor/gugle-chat-bot/package.json +57 -57
- package/vendor/gugle-chat-bot/src/allow-rule.yml +9345 -9345
- package/vendor/gugle-chat-bot/src/api.json +4284 -4284
- package/vendor/gugle-chat-bot/src/command/index.ts +141 -141
- package/vendor/gugle-chat-bot/src/config/features.ts +547 -547
- package/vendor/gugle-chat-bot/src/config/index.ts +184 -184
- package/vendor/gugle-chat-bot/src/config/manager.ts +356 -356
- package/vendor/gugle-chat-bot/src/constants/index.ts +13 -13
- package/vendor/gugle-chat-bot/src/custom.ts +548 -548
- package/vendor/gugle-chat-bot/src/event/index.ts +118 -118
- package/vendor/gugle-chat-bot/src/features/admin/index.ts +414 -414
- package/vendor/gugle-chat-bot/src/features/bili/image/impl/video.ts +39 -39
- package/vendor/gugle-chat-bot/src/features/bili/image/index.ts +37 -37
- package/vendor/gugle-chat-bot/src/features/bili/index.ts +65 -65
- package/vendor/gugle-chat-bot/src/features/bili/template/style/video.css +97 -97
- package/vendor/gugle-chat-bot/src/features/bili/template/video.html +56 -56
- package/vendor/gugle-chat-bot/src/features/discord-bridge/index.ts +1128 -1128
- package/vendor/gugle-chat-bot/src/features/discord-bridge/markdown.ts +105 -105
- package/vendor/gugle-chat-bot/src/features/discord-bridge/slash.ts +355 -355
- package/vendor/gugle-chat-bot/src/features/discord-bridge/source.ts +96 -96
- package/vendor/gugle-chat-bot/src/features/discord-bridge/template/message.html +14 -14
- package/vendor/gugle-chat-bot/src/features/discord-bridge/template/style/message.css +144 -144
- package/vendor/gugle-chat-bot/src/features/github/api.ts +93 -93
- package/vendor/gugle-chat-bot/src/features/github/binding.ts +161 -161
- package/vendor/gugle-chat-bot/src/features/github/image/impl/issue.ts +132 -132
- package/vendor/gugle-chat-bot/src/features/github/image/impl/pr.ts +144 -144
- package/vendor/gugle-chat-bot/src/features/github/image/impl/release.ts +160 -160
- package/vendor/gugle-chat-bot/src/features/github/image/index.ts +310 -310
- package/vendor/gugle-chat-bot/src/features/github/index.ts +302 -302
- package/vendor/gugle-chat-bot/src/features/github/template/issue.html +118 -118
- package/vendor/gugle-chat-bot/src/features/github/template/pull_request.html +125 -125
- package/vendor/gugle-chat-bot/src/features/github/template/release.html +53 -53
- package/vendor/gugle-chat-bot/src/features/github/template/style/issue.css +468 -468
- package/vendor/gugle-chat-bot/src/features/github/template/style/pull_request.css +468 -468
- package/vendor/gugle-chat-bot/src/features/github/template/style/release.css +412 -412
- package/vendor/gugle-chat-bot/src/features/hermes/client.ts +184 -184
- package/vendor/gugle-chat-bot/src/features/hermes/config.ts +210 -210
- package/vendor/gugle-chat-bot/src/features/hermes/context.ts +67 -67
- package/vendor/gugle-chat-bot/src/features/hermes/history.ts +86 -86
- package/vendor/gugle-chat-bot/src/features/hermes/index.ts +1533 -1533
- package/vendor/gugle-chat-bot/src/features/hermes/message.ts +319 -319
- package/vendor/gugle-chat-bot/src/features/hermes/reaction.ts +181 -181
- package/vendor/gugle-chat-bot/src/features/hermes/render.ts +118 -118
- package/vendor/gugle-chat-bot/src/features/hermes/reply-units.ts +331 -331
- package/vendor/gugle-chat-bot/src/features/hermes/skills.ts +406 -406
- package/vendor/gugle-chat-bot/src/features/hermes/template/approval.html +30 -30
- package/vendor/gugle-chat-bot/src/features/hermes/template/progress.html +21 -21
- package/vendor/gugle-chat-bot/src/features/hermes/template/style/approval.css +81 -81
- package/vendor/gugle-chat-bot/src/features/hermes/template/style/progress.css +120 -120
- package/vendor/gugle-chat-bot/src/features/hermes/trigger.ts +86 -86
- package/vendor/gugle-chat-bot/src/features/hermes/types.ts +327 -327
- package/vendor/gugle-chat-bot/src/features/management/index.ts +156 -156
- package/vendor/gugle-chat-bot/src/features/minecraft/index.ts +127 -127
- package/vendor/gugle-chat-bot/src/features/modrinth/index.ts +71 -71
- package/vendor/gugle-chat-bot/src/features/parentheses/index.ts +117 -117
- package/vendor/gugle-chat-bot/src/features/peak-valley-timer/index.ts +451 -451
- package/vendor/gugle-chat-bot/src/features/poke/index.ts +24 -24
- package/vendor/gugle-chat-bot/src/features/version-tracker/index.ts +77 -77
- package/vendor/gugle-chat-bot/src/features/welcome/index.ts +57 -57
- package/vendor/gugle-chat-bot/src/github-ip.json +5814 -5814
- package/vendor/gugle-chat-bot/src/image/index.ts +278 -278
- package/vendor/gugle-chat-bot/src/index.ts +695 -695
- package/vendor/gugle-chat-bot/src/logger/index.ts +54 -54
- package/vendor/gugle-chat-bot/src/result.json +359 -359
- package/vendor/gugle-chat-bot/src/type/bili.d.ts +825 -825
- package/vendor/gugle-chat-bot/src/type/event.d.ts +146 -146
- package/vendor/gugle-chat-bot/src/type/github.d.ts +446 -446
- package/vendor/gugle-chat-bot/src/type/index.d.ts +254 -254
- package/vendor/gugle-chat-bot/tsconfig.json +46 -46
- package/scripts/dump-npm-latest.mjs +0 -30
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
import { QQBot } from '@/index';
|
|
2
|
-
import { GroupDecreaseNoticeWSMSG, GroupIncreaseNoticeWSMSG, GroupRequestWSMSG } from '@/type';
|
|
3
|
-
import { EventDataManager } from '@/event';
|
|
4
|
-
import { getFeatureConfig } from '@/config/features';
|
|
5
|
-
|
|
6
|
-
function handleGroupDecreaseNotice(bot: QQBot, msg: GroupDecreaseNoticeWSMSG) {
|
|
7
|
-
if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;
|
|
8
|
-
EventDataManager.getStorage('management', 'ban_list').then(raw => {
|
|
9
|
-
// 归一化后再判重:兼容历史字符串条目,避免同一用户被写成两份(数字 + 字符串)
|
|
10
|
-
const banList = Management.normalizeBanList(raw);
|
|
11
|
-
const userId = Number(msg.user_id);
|
|
12
|
-
if (!Number.isSafeInteger(userId) || userId <= 0) return;
|
|
13
|
-
// 已归一化集合里存在即视为已拉黑;顺带把归一化结果写回,清理历史脏数据
|
|
14
|
-
const alreadyBanned = banList.includes(userId);
|
|
15
|
-
if (alreadyBanned && JSON.stringify(banList) === JSON.stringify(raw)) return;
|
|
16
|
-
if (!alreadyBanned) banList.push(userId);
|
|
17
|
-
EventDataManager.setStorage('management', 'ban_list', banList).then(() => {
|
|
18
|
-
if (alreadyBanned) return; // 仅做数据清理,不再重复播报
|
|
19
|
-
const reason = msg.sub_type === 'kick' ? '被管理员移出群聊' : '退出群聊';
|
|
20
|
-
bot.getUserInfo(msg.user_id).then(userInfo => {
|
|
21
|
-
bot.sendGroupMsg(msg.group_id, [
|
|
22
|
-
{
|
|
23
|
-
type: 'text',
|
|
24
|
-
data: {
|
|
25
|
-
text: `用户 ${userInfo.nickname}(${userInfo.user_id}) 已${reason},现已加入黑名单,将自动拒绝后续加群申请!`
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
]);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function handleGroupIncreaseNotice(bot: QQBot, msg: GroupIncreaseNoticeWSMSG) {
|
|
35
|
-
if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;
|
|
36
|
-
EventDataManager.getStorage('management', 'ban_list').then(raw => {
|
|
37
|
-
const banList = Management.normalizeBanList(raw);
|
|
38
|
-
const userId = Number(msg.user_id);
|
|
39
|
-
if (!banList.includes(userId)) return;
|
|
40
|
-
if (msg.sub_type === 'invite' && getFeatureConfig('management').operators?.includes(msg.operator_id)) {
|
|
41
|
-
// 管理员主动邀请 = 视为赦免:移出黑名单
|
|
42
|
-
EventDataManager.setStorage(
|
|
43
|
-
'management',
|
|
44
|
-
'ban_list',
|
|
45
|
-
banList.filter(id => id !== userId)
|
|
46
|
-
).then();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
bot.kick(msg.group_id, msg.user_id);
|
|
50
|
-
bot.getUserInfo(msg.user_id).then(userInfo => {
|
|
51
|
-
bot.sendGroupMsg(msg.group_id, [
|
|
52
|
-
{
|
|
53
|
-
type: 'text',
|
|
54
|
-
data: {
|
|
55
|
-
text: `黑名单用户 ${userInfo.nickname}(${userInfo.user_id}) 加群,已自动移出!`
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function handleGroupRequest(bot: QQBot, msg: GroupRequestWSMSG) {
|
|
64
|
-
if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;
|
|
65
|
-
if (msg.sub_type !== 'add') return;
|
|
66
|
-
EventDataManager.getStorage('management', 'ban_list').then(raw => {
|
|
67
|
-
const banList = Management.normalizeBanList(raw);
|
|
68
|
-
if (!banList.includes(Number(msg.user_id))) return;
|
|
69
|
-
bot.denyGroupAddRequest(msg.flag, '你已被拉入黑名单,系统自动拒绝!');
|
|
70
|
-
bot.getUserInfo(msg.user_id).then(userInfo => {
|
|
71
|
-
bot.sendGroupMsg(msg.group_id, [
|
|
72
|
-
{
|
|
73
|
-
type: 'text',
|
|
74
|
-
data: {
|
|
75
|
-
text: `黑名单用户 ${userInfo.nickname}(${userInfo.user_id}) 申请加群,已自动拒绝!`
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
]);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export class Management {
|
|
84
|
-
public static handleGroupDecreaseNotice(bot: QQBot, msg: GroupDecreaseNoticeWSMSG) {
|
|
85
|
-
handleGroupDecreaseNotice(bot, msg);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
public static handleGroupIncreaseNotice(bot: QQBot, msg: GroupIncreaseNoticeWSMSG) {
|
|
89
|
-
handleGroupIncreaseNotice(bot, msg);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public static handleGroupRequest(bot: QQBot, msg: GroupRequestWSMSG) {
|
|
93
|
-
handleGroupRequest(bot, msg);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* /pardon <QQ号>:把用户从黑名单移除(赦免)。
|
|
98
|
-
* 仅限 management 功能启用的群 + 管理员使用。
|
|
99
|
-
*
|
|
100
|
-
* 权限校验(**全部在此处完成**,不依赖调用方):
|
|
101
|
-
* - QQ 侧 operatorId 为 number → 必须在 management.json `operators` 白名单内;
|
|
102
|
-
* - Discord 侧 operatorId 为 `dc:<用户ID>` → 必须是 management.json `discordOperators`
|
|
103
|
-
* 白名单成员。注意:此前仅校验 `dc:` 前缀即放行,导致被桥接频道里任意成员都能赦免黑名单用户。
|
|
104
|
-
*
|
|
105
|
-
* @returns true=已移除;false=不在黑名单/无权限
|
|
106
|
-
*/
|
|
107
|
-
public static async pardon(groupId: number, operatorId: number | string, userId: number): Promise<boolean> {
|
|
108
|
-
const config = getFeatureConfig('management');
|
|
109
|
-
if (!config.groups.includes(groupId)) return false;
|
|
110
|
-
if (!this.isOperatorAllowed(operatorId)) return false;
|
|
111
|
-
|
|
112
|
-
// 黑名单历史数据/不同 OneBot 实现可能写入字符串形态的 QQ 号,
|
|
113
|
-
// 统一按数值比较,避免「明明在黑名单里却删不掉」。
|
|
114
|
-
const banList = this.normalizeBanList(await EventDataManager.getStorage('management', 'ban_list'));
|
|
115
|
-
if (!banList.includes(userId)) return false;
|
|
116
|
-
|
|
117
|
-
const next = banList.filter(id => id !== userId);
|
|
118
|
-
await EventDataManager.setStorage('management', 'ban_list', next);
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* 操作者是否有权执行管理操作。
|
|
124
|
-
*
|
|
125
|
-
* - number(QQ 号)→ operators 白名单;
|
|
126
|
-
* - `dc:` 前缀 → discordOperators 白名单(必须是显式配置的用户);
|
|
127
|
-
* - 其他形态 → 拒绝。
|
|
128
|
-
*/
|
|
129
|
-
public static isOperatorAllowed(operatorId: number | string): boolean {
|
|
130
|
-
const config = getFeatureConfig('management');
|
|
131
|
-
if (typeof operatorId === 'number') {
|
|
132
|
-
return config.operators?.includes(operatorId) ?? false;
|
|
133
|
-
}
|
|
134
|
-
if (typeof operatorId === 'string' && /^dc:/i.test(operatorId)) {
|
|
135
|
-
const target = operatorId.slice(3).trim();
|
|
136
|
-
if (!target) return false;
|
|
137
|
-
return (config.discordOperators ?? []).some(item => item.replace(/^dc:/i, '').trim() === target);
|
|
138
|
-
}
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* 归一化黑名单:字符串数字转 number、剔除非法项、去重。
|
|
144
|
-
* 兼容历史脏数据(如被写成 `"2017065007"` 的条目),避免「明明在黑名单里却删不掉」。
|
|
145
|
-
*/
|
|
146
|
-
public static normalizeBanList(raw: unknown): number[] {
|
|
147
|
-
if (!Array.isArray(raw)) return [];
|
|
148
|
-
const result: number[] = [];
|
|
149
|
-
for (const item of raw) {
|
|
150
|
-
const value = typeof item === 'string' ? Number(item.trim()) : item;
|
|
151
|
-
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) continue;
|
|
152
|
-
if (!result.includes(value)) result.push(value);
|
|
153
|
-
}
|
|
154
|
-
return result;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
1
|
+
import { QQBot } from '@/index';
|
|
2
|
+
import { GroupDecreaseNoticeWSMSG, GroupIncreaseNoticeWSMSG, GroupRequestWSMSG } from '@/type';
|
|
3
|
+
import { EventDataManager } from '@/event';
|
|
4
|
+
import { getFeatureConfig } from '@/config/features';
|
|
5
|
+
|
|
6
|
+
function handleGroupDecreaseNotice(bot: QQBot, msg: GroupDecreaseNoticeWSMSG) {
|
|
7
|
+
if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;
|
|
8
|
+
EventDataManager.getStorage('management', 'ban_list').then(raw => {
|
|
9
|
+
// 归一化后再判重:兼容历史字符串条目,避免同一用户被写成两份(数字 + 字符串)
|
|
10
|
+
const banList = Management.normalizeBanList(raw);
|
|
11
|
+
const userId = Number(msg.user_id);
|
|
12
|
+
if (!Number.isSafeInteger(userId) || userId <= 0) return;
|
|
13
|
+
// 已归一化集合里存在即视为已拉黑;顺带把归一化结果写回,清理历史脏数据
|
|
14
|
+
const alreadyBanned = banList.includes(userId);
|
|
15
|
+
if (alreadyBanned && JSON.stringify(banList) === JSON.stringify(raw)) return;
|
|
16
|
+
if (!alreadyBanned) banList.push(userId);
|
|
17
|
+
EventDataManager.setStorage('management', 'ban_list', banList).then(() => {
|
|
18
|
+
if (alreadyBanned) return; // 仅做数据清理,不再重复播报
|
|
19
|
+
const reason = msg.sub_type === 'kick' ? '被管理员移出群聊' : '退出群聊';
|
|
20
|
+
bot.getUserInfo(msg.user_id).then(userInfo => {
|
|
21
|
+
bot.sendGroupMsg(msg.group_id, [
|
|
22
|
+
{
|
|
23
|
+
type: 'text',
|
|
24
|
+
data: {
|
|
25
|
+
text: `用户 ${userInfo.nickname}(${userInfo.user_id}) 已${reason},现已加入黑名单,将自动拒绝后续加群申请!`
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function handleGroupIncreaseNotice(bot: QQBot, msg: GroupIncreaseNoticeWSMSG) {
|
|
35
|
+
if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;
|
|
36
|
+
EventDataManager.getStorage('management', 'ban_list').then(raw => {
|
|
37
|
+
const banList = Management.normalizeBanList(raw);
|
|
38
|
+
const userId = Number(msg.user_id);
|
|
39
|
+
if (!banList.includes(userId)) return;
|
|
40
|
+
if (msg.sub_type === 'invite' && getFeatureConfig('management').operators?.includes(msg.operator_id)) {
|
|
41
|
+
// 管理员主动邀请 = 视为赦免:移出黑名单
|
|
42
|
+
EventDataManager.setStorage(
|
|
43
|
+
'management',
|
|
44
|
+
'ban_list',
|
|
45
|
+
banList.filter(id => id !== userId)
|
|
46
|
+
).then();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
bot.kick(msg.group_id, msg.user_id);
|
|
50
|
+
bot.getUserInfo(msg.user_id).then(userInfo => {
|
|
51
|
+
bot.sendGroupMsg(msg.group_id, [
|
|
52
|
+
{
|
|
53
|
+
type: 'text',
|
|
54
|
+
data: {
|
|
55
|
+
text: `黑名单用户 ${userInfo.nickname}(${userInfo.user_id}) 加群,已自动移出!`
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
]);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function handleGroupRequest(bot: QQBot, msg: GroupRequestWSMSG) {
|
|
64
|
+
if (!getFeatureConfig('management').groups.includes(msg.group_id)) return;
|
|
65
|
+
if (msg.sub_type !== 'add') return;
|
|
66
|
+
EventDataManager.getStorage('management', 'ban_list').then(raw => {
|
|
67
|
+
const banList = Management.normalizeBanList(raw);
|
|
68
|
+
if (!banList.includes(Number(msg.user_id))) return;
|
|
69
|
+
bot.denyGroupAddRequest(msg.flag, '你已被拉入黑名单,系统自动拒绝!');
|
|
70
|
+
bot.getUserInfo(msg.user_id).then(userInfo => {
|
|
71
|
+
bot.sendGroupMsg(msg.group_id, [
|
|
72
|
+
{
|
|
73
|
+
type: 'text',
|
|
74
|
+
data: {
|
|
75
|
+
text: `黑名单用户 ${userInfo.nickname}(${userInfo.user_id}) 申请加群,已自动拒绝!`
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class Management {
|
|
84
|
+
public static handleGroupDecreaseNotice(bot: QQBot, msg: GroupDecreaseNoticeWSMSG) {
|
|
85
|
+
handleGroupDecreaseNotice(bot, msg);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public static handleGroupIncreaseNotice(bot: QQBot, msg: GroupIncreaseNoticeWSMSG) {
|
|
89
|
+
handleGroupIncreaseNotice(bot, msg);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public static handleGroupRequest(bot: QQBot, msg: GroupRequestWSMSG) {
|
|
93
|
+
handleGroupRequest(bot, msg);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* /pardon <QQ号>:把用户从黑名单移除(赦免)。
|
|
98
|
+
* 仅限 management 功能启用的群 + 管理员使用。
|
|
99
|
+
*
|
|
100
|
+
* 权限校验(**全部在此处完成**,不依赖调用方):
|
|
101
|
+
* - QQ 侧 operatorId 为 number → 必须在 management.json `operators` 白名单内;
|
|
102
|
+
* - Discord 侧 operatorId 为 `dc:<用户ID>` → 必须是 management.json `discordOperators`
|
|
103
|
+
* 白名单成员。注意:此前仅校验 `dc:` 前缀即放行,导致被桥接频道里任意成员都能赦免黑名单用户。
|
|
104
|
+
*
|
|
105
|
+
* @returns true=已移除;false=不在黑名单/无权限
|
|
106
|
+
*/
|
|
107
|
+
public static async pardon(groupId: number, operatorId: number | string, userId: number): Promise<boolean> {
|
|
108
|
+
const config = getFeatureConfig('management');
|
|
109
|
+
if (!config.groups.includes(groupId)) return false;
|
|
110
|
+
if (!this.isOperatorAllowed(operatorId)) return false;
|
|
111
|
+
|
|
112
|
+
// 黑名单历史数据/不同 OneBot 实现可能写入字符串形态的 QQ 号,
|
|
113
|
+
// 统一按数值比较,避免「明明在黑名单里却删不掉」。
|
|
114
|
+
const banList = this.normalizeBanList(await EventDataManager.getStorage('management', 'ban_list'));
|
|
115
|
+
if (!banList.includes(userId)) return false;
|
|
116
|
+
|
|
117
|
+
const next = banList.filter(id => id !== userId);
|
|
118
|
+
await EventDataManager.setStorage('management', 'ban_list', next);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 操作者是否有权执行管理操作。
|
|
124
|
+
*
|
|
125
|
+
* - number(QQ 号)→ operators 白名单;
|
|
126
|
+
* - `dc:` 前缀 → discordOperators 白名单(必须是显式配置的用户);
|
|
127
|
+
* - 其他形态 → 拒绝。
|
|
128
|
+
*/
|
|
129
|
+
public static isOperatorAllowed(operatorId: number | string): boolean {
|
|
130
|
+
const config = getFeatureConfig('management');
|
|
131
|
+
if (typeof operatorId === 'number') {
|
|
132
|
+
return config.operators?.includes(operatorId) ?? false;
|
|
133
|
+
}
|
|
134
|
+
if (typeof operatorId === 'string' && /^dc:/i.test(operatorId)) {
|
|
135
|
+
const target = operatorId.slice(3).trim();
|
|
136
|
+
if (!target) return false;
|
|
137
|
+
return (config.discordOperators ?? []).some(item => item.replace(/^dc:/i, '').trim() === target);
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 归一化黑名单:字符串数字转 number、剔除非法项、去重。
|
|
144
|
+
* 兼容历史脏数据(如被写成 `"2017065007"` 的条目),避免「明明在黑名单里却删不掉」。
|
|
145
|
+
*/
|
|
146
|
+
public static normalizeBanList(raw: unknown): number[] {
|
|
147
|
+
if (!Array.isArray(raw)) return [];
|
|
148
|
+
const result: number[] = [];
|
|
149
|
+
for (const item of raw) {
|
|
150
|
+
const value = typeof item === 'string' ? Number(item.trim()) : item;
|
|
151
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) continue;
|
|
152
|
+
if (!result.includes(value)) result.push(value);
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -1,127 +1,127 @@
|
|
|
1
|
-
import axios, { AxiosResponse } from 'axios';
|
|
2
|
-
import { JSDOM } from 'jsdom';
|
|
3
|
-
|
|
4
|
-
export declare type Version = {
|
|
5
|
-
success: boolean;
|
|
6
|
-
latest: {
|
|
7
|
-
release: string;
|
|
8
|
-
snapshot: string;
|
|
9
|
-
};
|
|
10
|
-
versions: {
|
|
11
|
-
id: string;
|
|
12
|
-
type: string;
|
|
13
|
-
url: string;
|
|
14
|
-
time: string;
|
|
15
|
-
releaseTime: string;
|
|
16
|
-
sha1: string;
|
|
17
|
-
complianceLevel: number;
|
|
18
|
-
}[];
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export declare type MinecraftServerStatus = {
|
|
22
|
-
online: boolean;
|
|
23
|
-
version: string;
|
|
24
|
-
motd: string;
|
|
25
|
-
players: { online: number; max: number; list: string[] };
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export declare type WikiResult = {
|
|
29
|
-
success: boolean;
|
|
30
|
-
url: string;
|
|
31
|
-
title: string;
|
|
32
|
-
desc: string;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export class MinecraftAPI {
|
|
36
|
-
private static readonly VERSION_MANIFEST_URL: string =
|
|
37
|
-
'https://piston-meta.mojang.com/mc/game/version_manifest_v2.json';
|
|
38
|
-
|
|
39
|
-
public static async getVersion(): Promise<Version> {
|
|
40
|
-
try {
|
|
41
|
-
const res: AxiosResponse<{
|
|
42
|
-
latest: {
|
|
43
|
-
release: '';
|
|
44
|
-
snapshot: '';
|
|
45
|
-
};
|
|
46
|
-
versions: [];
|
|
47
|
-
}> = await axios.get(MinecraftAPI.VERSION_MANIFEST_URL);
|
|
48
|
-
return {
|
|
49
|
-
success: true,
|
|
50
|
-
...res.data
|
|
51
|
-
};
|
|
52
|
-
} catch (_) {
|
|
53
|
-
return {
|
|
54
|
-
success: false,
|
|
55
|
-
latest: {
|
|
56
|
-
release: '',
|
|
57
|
-
snapshot: ''
|
|
58
|
-
},
|
|
59
|
-
versions: []
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public static async getMinecraftServerStatus(ip: string, port?: number): Promise<MinecraftServerStatus> {
|
|
65
|
-
try {
|
|
66
|
-
if (ip.includes(':')) port = undefined;
|
|
67
|
-
const res: AxiosResponse = await axios.get(
|
|
68
|
-
`https://api.mcstatus.io/v2/status/java/${ip}${port ? ':' + port : ''}`
|
|
69
|
-
);
|
|
70
|
-
const result: MinecraftServerStatus = {
|
|
71
|
-
online: res.data.online,
|
|
72
|
-
version: res.data.version.name_clean,
|
|
73
|
-
motd: res.data.motd.clean,
|
|
74
|
-
players: {
|
|
75
|
-
online: res.data.players.online,
|
|
76
|
-
max: res.data.players.max,
|
|
77
|
-
list: []
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
for (const player of res.data.players.list as { name_clean: string }[]) {
|
|
81
|
-
result.players.list.push(player.name_clean);
|
|
82
|
-
}
|
|
83
|
-
return result;
|
|
84
|
-
} catch (e) {
|
|
85
|
-
return {
|
|
86
|
-
online: false,
|
|
87
|
-
version: '',
|
|
88
|
-
motd: '',
|
|
89
|
-
players: {
|
|
90
|
-
online: 0,
|
|
91
|
-
max: 0,
|
|
92
|
-
list: []
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
public static async getMinecraftWiki(keyword: string): Promise<WikiResult> {
|
|
99
|
-
try {
|
|
100
|
-
const res: AxiosResponse = await axios.get(`https://zh.minecraft.wiki/w/?search=${keyword}`);
|
|
101
|
-
const dom = new JSDOM(res.data);
|
|
102
|
-
const document = dom.window.document;
|
|
103
|
-
const content = document.getElementById('mw-content-text');
|
|
104
|
-
const result: WikiResult = {
|
|
105
|
-
success: !content?.getElementsByClassName('mw-search-nonefound').length || false,
|
|
106
|
-
url: res.request.res.responseUrl || '',
|
|
107
|
-
title: document.getElementById('firstHeading')?.textContent || '',
|
|
108
|
-
desc: ''
|
|
109
|
-
};
|
|
110
|
-
for (let child of content?.getElementsByClassName('mw-parser-output').item(0)?.children || []) {
|
|
111
|
-
if (child.id == 'toc') break;
|
|
112
|
-
if (child.tagName != 'P') continue;
|
|
113
|
-
if (!child.textContent) continue;
|
|
114
|
-
if (child.textContent == '\n' || child.textContent == '\\n') continue;
|
|
115
|
-
result.desc += '\r\n' + child.textContent.replace('\n', '').replace('\\n', '');
|
|
116
|
-
}
|
|
117
|
-
return result;
|
|
118
|
-
} catch (_) {
|
|
119
|
-
return {
|
|
120
|
-
success: false,
|
|
121
|
-
url: '',
|
|
122
|
-
title: '',
|
|
123
|
-
desc: ''
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
1
|
+
import axios, { AxiosResponse } from 'axios';
|
|
2
|
+
import { JSDOM } from 'jsdom';
|
|
3
|
+
|
|
4
|
+
export declare type Version = {
|
|
5
|
+
success: boolean;
|
|
6
|
+
latest: {
|
|
7
|
+
release: string;
|
|
8
|
+
snapshot: string;
|
|
9
|
+
};
|
|
10
|
+
versions: {
|
|
11
|
+
id: string;
|
|
12
|
+
type: string;
|
|
13
|
+
url: string;
|
|
14
|
+
time: string;
|
|
15
|
+
releaseTime: string;
|
|
16
|
+
sha1: string;
|
|
17
|
+
complianceLevel: number;
|
|
18
|
+
}[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export declare type MinecraftServerStatus = {
|
|
22
|
+
online: boolean;
|
|
23
|
+
version: string;
|
|
24
|
+
motd: string;
|
|
25
|
+
players: { online: number; max: number; list: string[] };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export declare type WikiResult = {
|
|
29
|
+
success: boolean;
|
|
30
|
+
url: string;
|
|
31
|
+
title: string;
|
|
32
|
+
desc: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export class MinecraftAPI {
|
|
36
|
+
private static readonly VERSION_MANIFEST_URL: string =
|
|
37
|
+
'https://piston-meta.mojang.com/mc/game/version_manifest_v2.json';
|
|
38
|
+
|
|
39
|
+
public static async getVersion(): Promise<Version> {
|
|
40
|
+
try {
|
|
41
|
+
const res: AxiosResponse<{
|
|
42
|
+
latest: {
|
|
43
|
+
release: '';
|
|
44
|
+
snapshot: '';
|
|
45
|
+
};
|
|
46
|
+
versions: [];
|
|
47
|
+
}> = await axios.get(MinecraftAPI.VERSION_MANIFEST_URL);
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
...res.data
|
|
51
|
+
};
|
|
52
|
+
} catch (_) {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
latest: {
|
|
56
|
+
release: '',
|
|
57
|
+
snapshot: ''
|
|
58
|
+
},
|
|
59
|
+
versions: []
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public static async getMinecraftServerStatus(ip: string, port?: number): Promise<MinecraftServerStatus> {
|
|
65
|
+
try {
|
|
66
|
+
if (ip.includes(':')) port = undefined;
|
|
67
|
+
const res: AxiosResponse = await axios.get(
|
|
68
|
+
`https://api.mcstatus.io/v2/status/java/${ip}${port ? ':' + port : ''}`
|
|
69
|
+
);
|
|
70
|
+
const result: MinecraftServerStatus = {
|
|
71
|
+
online: res.data.online,
|
|
72
|
+
version: res.data.version.name_clean,
|
|
73
|
+
motd: res.data.motd.clean,
|
|
74
|
+
players: {
|
|
75
|
+
online: res.data.players.online,
|
|
76
|
+
max: res.data.players.max,
|
|
77
|
+
list: []
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
for (const player of res.data.players.list as { name_clean: string }[]) {
|
|
81
|
+
result.players.list.push(player.name_clean);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
} catch (e) {
|
|
85
|
+
return {
|
|
86
|
+
online: false,
|
|
87
|
+
version: '',
|
|
88
|
+
motd: '',
|
|
89
|
+
players: {
|
|
90
|
+
online: 0,
|
|
91
|
+
max: 0,
|
|
92
|
+
list: []
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public static async getMinecraftWiki(keyword: string): Promise<WikiResult> {
|
|
99
|
+
try {
|
|
100
|
+
const res: AxiosResponse = await axios.get(`https://zh.minecraft.wiki/w/?search=${keyword}`);
|
|
101
|
+
const dom = new JSDOM(res.data);
|
|
102
|
+
const document = dom.window.document;
|
|
103
|
+
const content = document.getElementById('mw-content-text');
|
|
104
|
+
const result: WikiResult = {
|
|
105
|
+
success: !content?.getElementsByClassName('mw-search-nonefound').length || false,
|
|
106
|
+
url: res.request.res.responseUrl || '',
|
|
107
|
+
title: document.getElementById('firstHeading')?.textContent || '',
|
|
108
|
+
desc: ''
|
|
109
|
+
};
|
|
110
|
+
for (let child of content?.getElementsByClassName('mw-parser-output').item(0)?.children || []) {
|
|
111
|
+
if (child.id == 'toc') break;
|
|
112
|
+
if (child.tagName != 'P') continue;
|
|
113
|
+
if (!child.textContent) continue;
|
|
114
|
+
if (child.textContent == '\n' || child.textContent == '\\n') continue;
|
|
115
|
+
result.desc += '\r\n' + child.textContent.replace('\n', '').replace('\\n', '');
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
} catch (_) {
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
url: '',
|
|
122
|
+
title: '',
|
|
123
|
+
desc: ''
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|