koishi-plugin-maibot 1.7.67 → 1.7.69
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/binding-verify.d.ts +2 -16
- package/lib/binding-verify.d.ts.map +1 -1
- package/lib/binding-verify.js +8 -75
- package/lib/binding-verify.js.map +1 -1
- package/lib/index.d.ts +0 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +67 -62
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/binding-verify.d.ts
CHANGED
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
import type { UserBinding } from './database';
|
|
2
2
|
export declare function normalizePreviewUserId(userId: string | number): string;
|
|
3
|
-
/** 与插件配置 bindingPlayerNameMatch 对应 */
|
|
4
|
-
export interface BindingPlayerNameMatchConfig {
|
|
5
|
-
/** 0–100:规范化后玩家名相似度下限(编辑距离比值×100)。100 须完全一致 */
|
|
6
|
-
minSimilarityPercent?: number;
|
|
7
|
-
}
|
|
8
|
-
export interface BindingNameMatchOptions {
|
|
9
|
-
minSimilarityPercent: number;
|
|
10
|
-
}
|
|
11
|
-
export declare function resolveBindingNameMatchOptions(cfg?: BindingPlayerNameMatchConfig): BindingNameMatchOptions;
|
|
12
|
-
/** 玩家名规范化:去首尾空白 + Unicode NFKC(全角英数、兼容字符等与半角统一) */
|
|
13
|
-
export declare function normalizePlayerNameForMatch(name: string): string;
|
|
14
|
-
/** 0–1,分母为 max(len);用于短玩家名 */
|
|
15
|
-
export declare function playerNameSimilarityRatio(a: string, b: string): number;
|
|
16
3
|
/**
|
|
17
|
-
* 校验 preview
|
|
18
|
-
* @param nameMatch 玩家名:先 NFKC 规范化,再按 minSimilarityPercent 比较编辑距离比值
|
|
4
|
+
* 校验 preview 与绑定是否为同一街机账号:仅比较绑定记录的 maiUid 与二维码 preview 的 UserID。
|
|
19
5
|
*/
|
|
20
6
|
export declare function verifyPreviewMatchesBinding(binding: UserBinding, preview: {
|
|
21
7
|
UserID: string | number;
|
|
22
8
|
UserName?: string;
|
|
23
|
-
}
|
|
9
|
+
}): string | null;
|
|
24
10
|
/** lastStateAt:maibot_user_rebind_state.lastBindChangeAt;bindTime:当前绑定记录的 bindTime(无绑定则 0) */
|
|
25
11
|
export declare function msUntilBindChangeAllowed(lastStateAtMs: number, bindTimeMs: number, minIntervalDays: number): number;
|
|
26
12
|
export declare function formatBindChangeWaitHuman(ms: number): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding-verify.d.ts","sourceRoot":"","sources":["../src/binding-verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEtE;AAED
|
|
1
|
+
{"version":3,"file":"binding-verify.d.ts","sourceRoot":"","sources":["../src/binding-verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEtE;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD,MAAM,GAAG,IAAI,CAcf;AAED,8FAA8F;AAC9F,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB,MAAM,CAMR;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAS5D"}
|
package/lib/binding-verify.js
CHANGED
|
@@ -1,94 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.normalizePreviewUserId = normalizePreviewUserId;
|
|
4
|
-
exports.resolveBindingNameMatchOptions = resolveBindingNameMatchOptions;
|
|
5
|
-
exports.normalizePlayerNameForMatch = normalizePlayerNameForMatch;
|
|
6
|
-
exports.playerNameSimilarityRatio = playerNameSimilarityRatio;
|
|
7
4
|
exports.verifyPreviewMatchesBinding = verifyPreviewMatchesBinding;
|
|
8
5
|
exports.msUntilBindChangeAllowed = msUntilBindChangeAllowed;
|
|
9
6
|
exports.formatBindChangeWaitHuman = formatBindChangeWaitHuman;
|
|
10
7
|
function normalizePreviewUserId(userId) {
|
|
11
8
|
return String(userId);
|
|
12
9
|
}
|
|
13
|
-
const DEFAULT_MIN_SIMILARITY = 100;
|
|
14
|
-
function resolveBindingNameMatchOptions(cfg) {
|
|
15
|
-
let p = cfg?.minSimilarityPercent;
|
|
16
|
-
if (typeof p !== 'number' || Number.isNaN(p))
|
|
17
|
-
p = DEFAULT_MIN_SIMILARITY;
|
|
18
|
-
p = Math.min(100, Math.max(0, p));
|
|
19
|
-
return { minSimilarityPercent: p };
|
|
20
|
-
}
|
|
21
|
-
/** 玩家名规范化:去首尾空白 + Unicode NFKC(全角英数、兼容字符等与半角统一) */
|
|
22
|
-
function normalizePlayerNameForMatch(name) {
|
|
23
|
-
return name.normalize('NFKC').trim();
|
|
24
|
-
}
|
|
25
|
-
function levenshtein(a, b) {
|
|
26
|
-
const m = a.length;
|
|
27
|
-
const n = b.length;
|
|
28
|
-
if (m === 0)
|
|
29
|
-
return n;
|
|
30
|
-
if (n === 0)
|
|
31
|
-
return m;
|
|
32
|
-
const dp = new Uint32Array(n + 1);
|
|
33
|
-
for (let j = 0; j <= n; j++)
|
|
34
|
-
dp[j] = j;
|
|
35
|
-
for (let i = 1; i <= m; i++) {
|
|
36
|
-
let prev = dp[0];
|
|
37
|
-
dp[0] = i;
|
|
38
|
-
for (let j = 1; j <= n; j++) {
|
|
39
|
-
const tmp = dp[j];
|
|
40
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
41
|
-
dp[j] = Math.min(dp[j] + 1, dp[j - 1] + 1, prev + cost);
|
|
42
|
-
prev = tmp;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return dp[n];
|
|
46
|
-
}
|
|
47
|
-
/** 0–1,分母为 max(len);用于短玩家名 */
|
|
48
|
-
function playerNameSimilarityRatio(a, b) {
|
|
49
|
-
if (a === b)
|
|
50
|
-
return 1;
|
|
51
|
-
const maxLen = Math.max(a.length, b.length);
|
|
52
|
-
if (maxLen === 0)
|
|
53
|
-
return 1;
|
|
54
|
-
return Math.max(0, 1 - levenshtein(a, b) / maxLen);
|
|
55
|
-
}
|
|
56
10
|
/**
|
|
57
|
-
* 校验 preview
|
|
58
|
-
* @param nameMatch 玩家名:先 NFKC 规范化,再按 minSimilarityPercent 比较编辑距离比值
|
|
11
|
+
* 校验 preview 与绑定是否为同一街机账号:仅比较绑定记录的 maiUid 与二维码 preview 的 UserID。
|
|
59
12
|
*/
|
|
60
|
-
function verifyPreviewMatchesBinding(binding, preview
|
|
13
|
+
function verifyPreviewMatchesBinding(binding, preview) {
|
|
61
14
|
const pid = normalizePreviewUserId(preview.UserID);
|
|
62
15
|
if (pid === '-1' || preview.UserID === -1) {
|
|
63
|
-
return '❌
|
|
16
|
+
return '❌ 无效或过期的二维码,无法完成验证。请重新获取玩家二维码后重试。';
|
|
64
17
|
}
|
|
65
18
|
if (String(binding.maiUid) !== pid) {
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
const current = String(preview.UserName).trim();
|
|
73
|
-
const na = normalizePlayerNameForMatch(boundName);
|
|
74
|
-
const nb = normalizePlayerNameForMatch(current);
|
|
75
|
-
if (!na) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
if (!nb) {
|
|
79
|
-
return `❌ 玩家名与绑定记录不一致(绑定为「${boundName}」,当前玩家名为空)。如已改名请使用解绑卡流程后重新绑定。`;
|
|
80
|
-
}
|
|
81
|
-
const min = nameMatch.minSimilarityPercent;
|
|
82
|
-
const ratio = playerNameSimilarityRatio(na, nb);
|
|
83
|
-
const pct = ratio * 100;
|
|
84
|
-
if (pct + 1e-9 >= min) {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
const minDisp = Number.isInteger(min) ? String(min) : min.toFixed(1);
|
|
88
|
-
if (min >= 100) {
|
|
89
|
-
return `❌ 玩家名与绑定记录不一致(绑定为「${boundName}」,当前为「${current}」)。如已改名请使用解绑卡流程后重新绑定。`;
|
|
19
|
+
return (`❌ 街机账号 UID 不一致:\n` +
|
|
20
|
+
`• 当前绑定记录的 UID:${binding.maiUid}\n` +
|
|
21
|
+
`• 当前二维码对应的 UID:${pid}\n` +
|
|
22
|
+
`若您已更换游戏账号,请使用 /mai解绑 后重新绑定(换绑冷却期内请使用 /mai解绑卡)。`);
|
|
90
23
|
}
|
|
91
|
-
return
|
|
24
|
+
return null;
|
|
92
25
|
}
|
|
93
26
|
/** lastStateAt:maibot_user_rebind_state.lastBindChangeAt;bindTime:当前绑定记录的 bindTime(无绑定则 0) */
|
|
94
27
|
function msUntilBindChangeAllowed(lastStateAtMs, bindTimeMs, minIntervalDays) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding-verify.js","sourceRoot":"","sources":["../src/binding-verify.ts"],"names":[],"mappings":";;AAEA,wDAEC;
|
|
1
|
+
{"version":3,"file":"binding-verify.js","sourceRoot":"","sources":["../src/binding-verify.ts"],"names":[],"mappings":";;AAEA,wDAEC;AAKD,kEAiBC;AAGD,4DAUC;AAED,8DASC;AAhDD,SAAgB,sBAAsB,CAAC,MAAuB;IAC5D,OAAO,MAAM,CAAC,MAAM,CAAC,CAAA;AACvB,CAAC;AAED;;GAEG;AACH,SAAgB,2BAA2B,CACzC,OAAoB,EACpB,OAAuD;IAEvD,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAClD,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QAC1C,OAAO,mCAAmC,CAAA;IAC5C,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;QACnC,OAAO,CACL,mBAAmB;YACnB,iBAAiB,OAAO,CAAC,MAAM,IAAI;YACnC,kBAAkB,GAAG,IAAI;YACzB,gDAAgD,CACjD,CAAA;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,8FAA8F;AAC9F,SAAgB,wBAAwB,CACtC,aAAqB,EACrB,UAAkB,EAClB,eAAuB;IAEvB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,EAAE,UAAU,IAAI,CAAC,CAAC,CAAA;IAC1D,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAA;IACnB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;IAChE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;IACjC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,CAAA;AACrC,CAAC;AAED,SAAgB,yBAAyB,CAAC,EAAU;IAClD,IAAI,EAAE,IAAI,CAAC;QAAE,OAAO,GAAG,CAAA;IACvB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAA;IAChD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAA;IACrE,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IACpD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAA;IAC3D,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IACrD,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,CAAA;IAC9B,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAA;AAC9B,CAAC"}
|
package/lib/index.d.ts
CHANGED
|
@@ -69,11 +69,6 @@ export interface Config {
|
|
|
69
69
|
minDaysBetweenBindChange: number;
|
|
70
70
|
shopUrl?: string;
|
|
71
71
|
};
|
|
72
|
-
/** SGID / preview 校验时:绑定快照玩家名与当前玩家名的匹配(含 /mai状态、mymai 等) */
|
|
73
|
-
bindingPlayerNameMatch?: {
|
|
74
|
-
/** 0–100:NFKC 规范化后,编辑距离相似度下限;100 为须完全一致。全角如 Milk 与半角 Milk 在 100 下通常视为一致 */
|
|
75
|
-
minSimilarityPercent: number;
|
|
76
|
-
};
|
|
77
72
|
}
|
|
78
73
|
export declare const Config: Schema<Config>;
|
|
79
74
|
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAW,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -88,7 +88,7 @@ exports.Config = koishi_1.Schema.object({
|
|
|
88
88
|
autoRecallProcessingMessages: koishi_1.Schema.boolean().default(true).description('B50任务完成后自动撤回"正在处理"和"已提交"消息'),
|
|
89
89
|
authLevelForCardAdmin: koishi_1.Schema.number().default(4).description('卡密管理指令(生成/删除/导出)需要的 Koishi authority,默认 4'),
|
|
90
90
|
priorityCooldown: koishi_1.Schema.object({
|
|
91
|
-
enabled: koishi_1.Schema.boolean().default(false).description('
|
|
91
|
+
enabled: koishi_1.Schema.boolean().default(false).description('开启后,对参与冷却槽的 mai 指令在指令执行前统一检查间隔(见插件内 commandToCooldownSlot:含发票/B50/状态等分槽,其余多数为 default 槽;帮助、绑定类、maialert、管理员指令等不参与)'),
|
|
92
92
|
adminBypassAuthority: koishi_1.Schema.number()
|
|
93
93
|
.default(4)
|
|
94
94
|
.description('仅当用户 Koishi authority 大于该数值时视为管理员:绕过冷却,并自动写入永久个人优先(带标记,权限回落时自动删除);卡密兑换的优先记录不会被删除。'),
|
|
@@ -123,15 +123,6 @@ exports.Config = koishi_1.Schema.object({
|
|
|
123
123
|
minDaysBetweenBindChange: 30,
|
|
124
124
|
shopUrl: '',
|
|
125
125
|
}),
|
|
126
|
-
bindingPlayerNameMatch: koishi_1.Schema.object({
|
|
127
|
-
minSimilarityPercent: koishi_1.Schema.number()
|
|
128
|
-
.default(100)
|
|
129
|
-
.min(0)
|
|
130
|
-
.max(100)
|
|
131
|
-
.description('玩家名最低相似度(0–100)。先进行 Unicode NFKC 规范化(全角/半角英数字统一)再按编辑距离计算;100 表示须完全一致。略低于 100 可容忍少量字符差异(改名、显示差异等)。'),
|
|
132
|
-
})
|
|
133
|
-
.description('绑定记录中的玩家名与 preview 的校验(maiUid 一致时的第二道校验)')
|
|
134
|
-
.default({ minSimilarityPercent: 100 }),
|
|
135
126
|
});
|
|
136
127
|
// 我认识了很多朋友 以下是我认识的好朋友们!
|
|
137
128
|
// Fracture_Hikaritsu
|
|
@@ -210,6 +201,36 @@ function getSessionCommandUsageHint(session) {
|
|
|
210
201
|
function isMaiPluginCommandName(name) {
|
|
211
202
|
return name.trim().startsWith('mai');
|
|
212
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* <spec:text> 会把「clear -g 群号」整段吃成一个参数;从首尾拆出 -g 群标识,并与 .option('-g') 合并(优先已解析的 -g)。
|
|
206
|
+
*/
|
|
207
|
+
function splitGroupPrioritySpecAndGuild(specRaw, optionGuild) {
|
|
208
|
+
let specStr = (specRaw ?? '').trim();
|
|
209
|
+
let guild = String(optionGuild ?? '').trim();
|
|
210
|
+
const lead = specStr.match(/^\s*-g\s+(\S+)\s+([\s\S]+)$/i);
|
|
211
|
+
if (lead) {
|
|
212
|
+
if (!guild)
|
|
213
|
+
guild = lead[1];
|
|
214
|
+
specStr = lead[2].trim();
|
|
215
|
+
}
|
|
216
|
+
const tail = specStr.match(/^([\s\S]+?)\s+-g\s+(\S+)\s*$/i);
|
|
217
|
+
if (tail) {
|
|
218
|
+
specStr = tail[1].trim();
|
|
219
|
+
if (!guild)
|
|
220
|
+
guild = tail[2];
|
|
221
|
+
}
|
|
222
|
+
return { spec: specStr, guild };
|
|
223
|
+
}
|
|
224
|
+
/** 群优先表里的 guildKey 多为 platform:guildId;仅填数字时按当前会话平台补前缀 */
|
|
225
|
+
function normalizeGuildKeyForPriority(gk, session) {
|
|
226
|
+
const t = gk.trim();
|
|
227
|
+
if (!t)
|
|
228
|
+
return t;
|
|
229
|
+
if (!/^\d+$/.test(t))
|
|
230
|
+
return t;
|
|
231
|
+
const p = String(session.platform || '').trim().toLowerCase();
|
|
232
|
+
return p ? `${p}:${t}` : t;
|
|
233
|
+
}
|
|
213
234
|
async function computeMaiCommandUsageHint(command, session) {
|
|
214
235
|
if (!command || !isMaiPluginCommandName(String(command.name || '')))
|
|
215
236
|
return '';
|
|
@@ -1174,7 +1195,7 @@ async function getQrText(session, ctx, api, binding, config, timeout = 60000, pr
|
|
|
1174
1195
|
if (cacheAge < cacheValidDuration && binding.lastQrCode.startsWith('SGWCMAID')) {
|
|
1175
1196
|
try {
|
|
1176
1197
|
const previewCached = await api.getPreview(config.machineInfo.clientId, binding.lastQrCode);
|
|
1177
|
-
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, previewCached
|
|
1198
|
+
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, previewCached);
|
|
1178
1199
|
if (!vErr) {
|
|
1179
1200
|
if (previewCached.UserName != null && !binding.boundPlayerName?.trim()) {
|
|
1180
1201
|
await ctx.database.set('maibot_bindings', { userId: binding.userId }, {
|
|
@@ -1288,7 +1309,7 @@ async function getQrText(session, ctx, api, binding, config, timeout = 60000, pr
|
|
|
1288
1309
|
return { qrText: '', error: '无效或过期的二维码' };
|
|
1289
1310
|
}
|
|
1290
1311
|
if (binding) {
|
|
1291
|
-
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview
|
|
1312
|
+
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview);
|
|
1292
1313
|
if (vErr) {
|
|
1293
1314
|
await session.send(vErr);
|
|
1294
1315
|
return { qrText: '', error: vErr };
|
|
@@ -1426,20 +1447,29 @@ function apply(ctx, config) {
|
|
|
1426
1447
|
const keys = await getSessionBindingKeys(ctx, session);
|
|
1427
1448
|
return keys[0] || String(session.userId || '');
|
|
1428
1449
|
}
|
|
1429
|
-
async
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1450
|
+
ctx.on('command/before-execute', async (argv) => {
|
|
1451
|
+
const sess = argv.session;
|
|
1452
|
+
const cmd = argv.command;
|
|
1453
|
+
if (!sess || !cmd)
|
|
1454
|
+
return;
|
|
1455
|
+
const cmdName = String(cmd.name || '');
|
|
1456
|
+
if (!priorityCooldownCfg?.enabled)
|
|
1457
|
+
return;
|
|
1458
|
+
if (!isMaiPluginCommandName(cmdName))
|
|
1459
|
+
return;
|
|
1460
|
+
if ((0, priority_cooldown_1.commandToCooldownSlot)(cmdName) === null)
|
|
1437
1461
|
return;
|
|
1438
|
-
const
|
|
1462
|
+
const wl = checkWhitelist(sess, config);
|
|
1463
|
+
if (!wl.allowed)
|
|
1464
|
+
return;
|
|
1465
|
+
const hit = await (0, priority_cooldown_1.checkCommandCooldown)(ctx, sess, priorityCooldownCfg, cmdName, getCooldownPrimaryUserId, async (s) => getSessionBindingKeys(ctx, s));
|
|
1466
|
+
if (hit)
|
|
1467
|
+
return hit;
|
|
1468
|
+
const uid = await getCooldownPrimaryUserId(sess);
|
|
1439
1469
|
if (!uid)
|
|
1440
1470
|
return;
|
|
1441
|
-
await (0, priority_cooldown_1.recordCommandCooldown)(ctx, uid,
|
|
1442
|
-
}
|
|
1471
|
+
await (0, priority_cooldown_1.recordCommandCooldown)(ctx, uid, cmdName, priorityCooldownCfg);
|
|
1472
|
+
});
|
|
1443
1473
|
/**
|
|
1444
1474
|
* 获取上传任务的统计信息(平均处理时长和今日成功率)
|
|
1445
1475
|
* @param commandPrefix 命令前缀,用于筛选日志(如 'mai上传B50' 或 'mai上传落雪b50')
|
|
@@ -2249,7 +2279,7 @@ function apply(ctx, config) {
|
|
|
2249
2279
|
/mai管理员取消群组优先 [群标识] — 取消群组优先;省略时在群内则针对当前群
|
|
2250
2280
|
/mai管理员取消个人优先 <@或ID> — 清除个人优先记录
|
|
2251
2281
|
/mai管理员设置个人优先 <@或ID> <spec> — spec:永久、7d、clear 等
|
|
2252
|
-
/mai管理员设置群组优先 <spec> [-g 群标识] —
|
|
2282
|
+
/mai管理员设置群组优先 <spec> [-g 群标识] — spec 与 -g 可同一段输入(如 clear -g qq:群号);纯数字 -g 会按当前平台补前缀;-g 省略且在群内则当前群
|
|
2253
2283
|
/maibypass <@用户|用户ID> — 清除该用户当前全部指令冷却(别名 /mai管理员清除冷却)`;
|
|
2254
2284
|
}
|
|
2255
2285
|
helpText += `
|
|
@@ -2685,9 +2715,6 @@ function apply(ctx, config) {
|
|
|
2685
2715
|
if (!whitelistCheck.allowed) {
|
|
2686
2716
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
2687
2717
|
}
|
|
2688
|
-
const cdStatus = await gateUserCooldown(session, 'mai状态');
|
|
2689
|
-
if (cdStatus)
|
|
2690
|
-
return cdStatus;
|
|
2691
2718
|
try {
|
|
2692
2719
|
// 获取目标用户绑定
|
|
2693
2720
|
const { binding, isProxy, error } = await getTargetBinding(session, targetUserId);
|
|
@@ -2904,7 +2931,6 @@ function apply(ctx, config) {
|
|
|
2904
2931
|
status: 'success',
|
|
2905
2932
|
result: statusInfo,
|
|
2906
2933
|
});
|
|
2907
|
-
await markUserCooldown(session, 'mai状态');
|
|
2908
2934
|
return appendRefId(statusInfo, refId);
|
|
2909
2935
|
}
|
|
2910
2936
|
catch (error) {
|
|
@@ -3326,9 +3352,6 @@ function apply(ctx, config) {
|
|
|
3326
3352
|
if (!whitelistCheck.allowed) {
|
|
3327
3353
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
3328
3354
|
}
|
|
3329
|
-
const cdInv = await gateUserCooldown(session, 'mai发票');
|
|
3330
|
-
if (cdInv)
|
|
3331
|
-
return cdInv;
|
|
3332
3355
|
const multiple = multipleInput ? Number(multipleInput) : 2;
|
|
3333
3356
|
if (!Number.isInteger(multiple) || multiple < 2 || multiple > 6) {
|
|
3334
3357
|
return '❌ 倍数必须是2-6之间的整数\n例如:/mai发票 3\n例如:/mai发票 6 @userid';
|
|
@@ -3427,7 +3450,6 @@ function apply(ctx, config) {
|
|
|
3427
3450
|
status: 'success',
|
|
3428
3451
|
result: successMessage,
|
|
3429
3452
|
});
|
|
3430
|
-
await markUserCooldown(session, 'mai发票');
|
|
3431
3453
|
return appendRefId(successMessage, refId);
|
|
3432
3454
|
}
|
|
3433
3455
|
catch (error) {
|
|
@@ -3552,9 +3574,6 @@ function apply(ctx, config) {
|
|
|
3552
3574
|
if (!whitelistCheck.allowed) {
|
|
3553
3575
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
3554
3576
|
}
|
|
3555
|
-
const cdB50u = await gateUserCooldown(session, 'mai上传B50');
|
|
3556
|
-
if (cdB50u)
|
|
3557
|
-
return cdB50u;
|
|
3558
3577
|
try {
|
|
3559
3578
|
// 解析参数:可能是SGID或targetUserId
|
|
3560
3579
|
let qrCode;
|
|
@@ -3595,7 +3614,7 @@ function apply(ctx, config) {
|
|
|
3595
3614
|
if (preview.UserID === -1 || (typeof preview.UserID === 'string' && preview.UserID === '-1')) {
|
|
3596
3615
|
return '❌ 无效或过期的二维码,请重新发送';
|
|
3597
3616
|
}
|
|
3598
|
-
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview
|
|
3617
|
+
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview);
|
|
3599
3618
|
if (vErr) {
|
|
3600
3619
|
return vErr;
|
|
3601
3620
|
}
|
|
@@ -3690,7 +3709,6 @@ function apply(ctx, config) {
|
|
|
3690
3709
|
result: successMessage,
|
|
3691
3710
|
apiResponse: result,
|
|
3692
3711
|
});
|
|
3693
|
-
await markUserCooldown(session, 'mai上传B50');
|
|
3694
3712
|
// 发送成功消息并获取消息ID(用于后续撤回)
|
|
3695
3713
|
const successMsgIds = await sendAndGetMessageIds(session, appendRefId(successMessage, refId));
|
|
3696
3714
|
// 合并处理中消息ID和成功消息ID
|
|
@@ -3734,9 +3752,6 @@ function apply(ctx, config) {
|
|
|
3734
3752
|
if (!whitelistCheck.allowed) {
|
|
3735
3753
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
3736
3754
|
}
|
|
3737
|
-
const cdUa = await gateUserCooldown(session, 'maiua');
|
|
3738
|
-
if (cdUa)
|
|
3739
|
-
return cdUa;
|
|
3740
3755
|
try {
|
|
3741
3756
|
// 解析参数:可能是SGID/URL或落雪代码或目标用户
|
|
3742
3757
|
let qrCode;
|
|
@@ -3784,7 +3799,7 @@ function apply(ctx, config) {
|
|
|
3784
3799
|
if (preview.UserID === -1 || (typeof preview.UserID === 'string' && preview.UserID === '-1')) {
|
|
3785
3800
|
return '❌ 无效或过期的二维码,请重新发送';
|
|
3786
3801
|
}
|
|
3787
|
-
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview
|
|
3802
|
+
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview);
|
|
3788
3803
|
if (vErr) {
|
|
3789
3804
|
return vErr;
|
|
3790
3805
|
}
|
|
@@ -4008,9 +4023,6 @@ function apply(ctx, config) {
|
|
|
4008
4023
|
if (results.length === 0) {
|
|
4009
4024
|
return `⚠️ 未能发起上传请求${proxyTip}`;
|
|
4010
4025
|
}
|
|
4011
|
-
if (results.some(r => /B50任务已提交/.test(r) && r.includes('✅'))) {
|
|
4012
|
-
await markUserCooldown(session, 'maiua');
|
|
4013
|
-
}
|
|
4014
4026
|
return `${results.join('\n\n')}${proxyTip ? `\n${proxyTip}` : ''}`;
|
|
4015
4027
|
}
|
|
4016
4028
|
catch (error) {
|
|
@@ -4152,9 +4164,6 @@ function apply(ctx, config) {
|
|
|
4152
4164
|
if (!whitelistCheck.allowed) {
|
|
4153
4165
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
4154
4166
|
}
|
|
4155
|
-
const cdItem = await gateUserCooldown(session, 'mai获取收藏品');
|
|
4156
|
-
if (cdItem)
|
|
4157
|
-
return cdItem;
|
|
4158
4167
|
try {
|
|
4159
4168
|
// 解析首参:可为 SGID/链接 或 目标用户(代操作)
|
|
4160
4169
|
let qrCode;
|
|
@@ -4220,7 +4229,7 @@ function apply(ctx, config) {
|
|
|
4220
4229
|
if (preview.UserID === -1 || (typeof preview.UserID === 'string' && preview.UserID === '-1')) {
|
|
4221
4230
|
return '❌ 无效或过期的二维码,请重新发送';
|
|
4222
4231
|
}
|
|
4223
|
-
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview
|
|
4232
|
+
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview);
|
|
4224
4233
|
if (vErr) {
|
|
4225
4234
|
return vErr;
|
|
4226
4235
|
}
|
|
@@ -4290,7 +4299,6 @@ function apply(ctx, config) {
|
|
|
4290
4299
|
return '❌ 获取收藏品失败:服务器返回未成功,请稍后再试或刷新二维码后再试。';
|
|
4291
4300
|
}
|
|
4292
4301
|
}
|
|
4293
|
-
await markUserCooldown(session, 'mai获取收藏品');
|
|
4294
4302
|
return `✅ 已为 ${maskUserId(binding.maiUid)} 获取收藏品${proxyTip}\n类型: ${selectedType?.label}\nID: ${itemId}\n数量: ${stockFinal}`;
|
|
4295
4303
|
}
|
|
4296
4304
|
catch (error) {
|
|
@@ -4319,9 +4327,6 @@ function apply(ctx, config) {
|
|
|
4319
4327
|
if (!whitelistCheck.allowed) {
|
|
4320
4328
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
4321
4329
|
}
|
|
4322
|
-
const cdVer = await gateUserCooldown(session, 'mai修改版本号');
|
|
4323
|
-
if (cdVer)
|
|
4324
|
-
return cdVer;
|
|
4325
4330
|
try {
|
|
4326
4331
|
let qrCode;
|
|
4327
4332
|
let targetUserId;
|
|
@@ -4347,7 +4352,7 @@ function apply(ctx, config) {
|
|
|
4347
4352
|
if (preview.UserID === -1 || (typeof preview.UserID === 'string' && preview.UserID === '-1')) {
|
|
4348
4353
|
return '❌ 无效或过期的二维码,请重新发送';
|
|
4349
4354
|
}
|
|
4350
|
-
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview
|
|
4355
|
+
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview);
|
|
4351
4356
|
if (vErr) {
|
|
4352
4357
|
return vErr;
|
|
4353
4358
|
}
|
|
@@ -4420,7 +4425,6 @@ function apply(ctx, config) {
|
|
|
4420
4425
|
}
|
|
4421
4426
|
return '❌ 修改版本号失败:服务器返回未成功,请稍后再试或刷新二维码后再试。';
|
|
4422
4427
|
}
|
|
4423
|
-
await markUserCooldown(session, 'mai修改版本号');
|
|
4424
4428
|
return `✅ 已为 ${maskUserId(binding.maiUid)} 修改版本号${proxyTip}\n机台版本: ${romVer}\n数据版本: ${dataVer}`;
|
|
4425
4429
|
}
|
|
4426
4430
|
catch (error) {
|
|
@@ -4695,9 +4699,6 @@ function apply(ctx, config) {
|
|
|
4695
4699
|
if (!whitelistCheck.allowed) {
|
|
4696
4700
|
return whitelistCheck.message || '本群暂时没有被授权使用本Bot的功能,请添加官方群聊1072033605。';
|
|
4697
4701
|
}
|
|
4698
|
-
const cdLx = await gateUserCooldown(session, 'mai上传落雪b50');
|
|
4699
|
-
if (cdLx)
|
|
4700
|
-
return cdLx;
|
|
4701
4702
|
// 解析参数:第一个参数可能是SGID/URL或落雪代码
|
|
4702
4703
|
let qrCode;
|
|
4703
4704
|
let lxnsCode;
|
|
@@ -4752,7 +4753,7 @@ function apply(ctx, config) {
|
|
|
4752
4753
|
if (preview.UserID === -1 || (typeof preview.UserID === 'string' && preview.UserID === '-1')) {
|
|
4753
4754
|
return '❌ 无效或过期的二维码,请重新发送';
|
|
4754
4755
|
}
|
|
4755
|
-
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview
|
|
4756
|
+
const vErr = (0, binding_verify_1.verifyPreviewMatchesBinding)(binding, preview);
|
|
4756
4757
|
if (vErr) {
|
|
4757
4758
|
return vErr;
|
|
4758
4759
|
}
|
|
@@ -4843,7 +4844,6 @@ function apply(ctx, config) {
|
|
|
4843
4844
|
result: successMessage,
|
|
4844
4845
|
apiResponse: result,
|
|
4845
4846
|
});
|
|
4846
|
-
await markUserCooldown(session, 'mai上传落雪b50');
|
|
4847
4847
|
// 发送成功消息并获取消息ID(用于后续撤回)
|
|
4848
4848
|
const successMsgIds = await sendAndGetMessageIds(session, appendRefId(successMessage, refId));
|
|
4849
4849
|
// 合并处理中消息ID和成功消息ID
|
|
@@ -6320,6 +6320,9 @@ function apply(ctx, config) {
|
|
|
6320
6320
|
});
|
|
6321
6321
|
ctx.command('mai管理员设置群组优先 <spec:text>', '直接设置群组优先(-g 指定群,默认当前群)')
|
|
6322
6322
|
.userFields(['authority'])
|
|
6323
|
+
.usage(' 示例:/mai管理员设置群组优先 clear -g qq:5911013814031454\n' +
|
|
6324
|
+
'或:/mai管理员设置群组优先 -g qq:5911013814031454 永久\n' +
|
|
6325
|
+
'(仅数字群号时请写 qq:群号;在群内执行可省略 -g)')
|
|
6323
6326
|
.option('guild', '-g <guildKey:string> 群标识,如 qq:群号')
|
|
6324
6327
|
.action(async ({ session, options }, spec) => {
|
|
6325
6328
|
if (!session)
|
|
@@ -6327,14 +6330,16 @@ function apply(ctx, config) {
|
|
|
6327
6330
|
if ((session.user?.authority ?? 0) < authLevelForCardAdmin) {
|
|
6328
6331
|
return `❌ 权限不足,需要 auth 等级 ${authLevelForCardAdmin} 以上`;
|
|
6329
6332
|
}
|
|
6330
|
-
|
|
6333
|
+
const { spec: specOnly, guild: guildOpt } = splitGroupPrioritySpecAndGuild(spec, options?.guild);
|
|
6334
|
+
if (!specOnly) {
|
|
6331
6335
|
return '❌ 用法:/mai管理员设置群组优先 <永久|7d|clear> [-g 群标识]';
|
|
6332
6336
|
}
|
|
6333
|
-
const sp = (0, priority_cooldown_1.parsePriorityAdminSpec)(
|
|
6337
|
+
const sp = (0, priority_cooldown_1.parsePriorityAdminSpec)(specOnly);
|
|
6334
6338
|
if (sp === null) {
|
|
6335
6339
|
return '❌ 无效的 spec,示例:永久、7d、clear';
|
|
6336
6340
|
}
|
|
6337
|
-
|
|
6341
|
+
let gk = (guildOpt.trim() || (0, priority_cooldown_1.canonicalGuildPriorityKey)(session) || '').trim();
|
|
6342
|
+
gk = normalizeGuildKeyForPriority(gk, session);
|
|
6338
6343
|
if (!gk) {
|
|
6339
6344
|
return '❌ 请使用 -g 指定群标识(platform:guildId),或在群聊内执行。';
|
|
6340
6345
|
}
|