koishi-plugin-group-verification 1.0.28 → 1.0.29
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/index.d.ts +11 -1
- package/lib/index.js +117 -69
- package/package.json +9 -2
- package/readme.md +8 -1
- package/src/index.ts +146 -83
package/lib/index.d.ts
CHANGED
|
@@ -42,6 +42,10 @@ export interface Config {
|
|
|
42
42
|
defaultReminderMessage?: string;
|
|
43
43
|
enableStrictGroupCheck?: boolean;
|
|
44
44
|
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
45
|
+
permissionDeniedMessage?: string;
|
|
46
|
+
invalidGroupMessage?: string;
|
|
47
|
+
parameterConflictMessage?: string;
|
|
48
|
+
noKeywordsMessage?: string;
|
|
45
49
|
}
|
|
46
50
|
export declare const Config: Schema<Config>;
|
|
47
51
|
export declare const inject: string[];
|
|
@@ -87,13 +91,19 @@ export declare function mergeReminder(existingConfig: any | null, cleanedOptions
|
|
|
87
91
|
message?: string;
|
|
88
92
|
enableMessage?: boolean;
|
|
89
93
|
disableMessage?: boolean;
|
|
90
|
-
}, hasRealMessageParam: boolean, hasRealEnableMessageParam: boolean, hasRealDisableMessageParam: boolean, logger: any): {
|
|
94
|
+
}, hasRealMessageParam: boolean, hasRealEnableMessageParam: boolean, hasRealDisableMessageParam: boolean, logger: any, defaultMessage?: string): {
|
|
91
95
|
reminderEnabled: boolean;
|
|
92
96
|
reminderMessage: string;
|
|
93
97
|
};
|
|
94
98
|
export declare function syncTotalStats(ctx: Context): Promise<void>;
|
|
95
99
|
export declare function updateStats(ctx: Context, groupId: string, action: 'autoApproved' | 'manuallyApproved' | 'rejected'): Promise<void>;
|
|
96
100
|
export declare function incrementTotal(ctx: Context, groupId: string): Promise<void>;
|
|
101
|
+
export interface ThresholdResult {
|
|
102
|
+
reviewParameters: number;
|
|
103
|
+
error?: string;
|
|
104
|
+
autoInfo?: 'methodChange' | 'kwChange';
|
|
105
|
+
}
|
|
106
|
+
export declare function resolveThreshold(existingConfig: any | null, keywordList: string[], reviewMethod: 0 | 1 | 2 | 3, thresholdStr?: string, methodChanged?: boolean): ThresholdResult;
|
|
97
107
|
export declare function checkPermission(session: any, targetGroupId?: string): Promise<[boolean, string?]>;
|
|
98
108
|
export declare function handleGuildMemberRequestEvent(ctx: Context, session: any): Promise<void>;
|
|
99
109
|
export declare function __getAutoQueue(): Map<string, Set<string>>;
|
package/lib/index.js
CHANGED
|
@@ -31,6 +31,7 @@ __export(src_exports, {
|
|
|
31
31
|
mergeReminder: () => mergeReminder,
|
|
32
32
|
name: () => name,
|
|
33
33
|
parseConfigArgs: () => parseConfigArgs,
|
|
34
|
+
resolveThreshold: () => resolveThreshold,
|
|
34
35
|
syncTotalStats: () => syncTotalStats,
|
|
35
36
|
tokenize: () => tokenize,
|
|
36
37
|
updateStats: () => updateStats,
|
|
@@ -42,9 +43,13 @@ var import_koishi = require("koishi");
|
|
|
42
43
|
var name = "group-verification";
|
|
43
44
|
var logger = console;
|
|
44
45
|
var Config = import_koishi.Schema.object({
|
|
45
|
-
defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用
|
|
46
|
+
defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)").default("{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}\n使用 gva 同意或 gvr 拒绝申请"),
|
|
46
47
|
enableStrictGroupCheck: import_koishi.Schema.boolean().description("是否启用严格的群号检查(检查群号长度)").default(false),
|
|
47
|
-
logLevel: import_koishi.Schema.union(["debug", "info", "warn", "error"]).description("日志级别").default("info")
|
|
48
|
+
logLevel: import_koishi.Schema.union(["debug", "info", "warn", "error"]).description("日志级别").default("info"),
|
|
49
|
+
permissionDeniedMessage: import_koishi.Schema.string().description("权限不足时返回给调用者的提示").default("权限不足:需要群主/管理员权限或koishi三级以上权限"),
|
|
50
|
+
invalidGroupMessage: import_koishi.Schema.string().description("无效群号或机器人未在该群时的提示").default("群号 {group} 格式不合法或机器人不在该群中"),
|
|
51
|
+
parameterConflictMessage: import_koishi.Schema.string().description("参数冲突时提示").default("参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)"),
|
|
52
|
+
noKeywordsMessage: import_koishi.Schema.string().description("未提供关键词且无法从现有配置继承时的提示").default("请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置")
|
|
48
53
|
}).description("群组验证插件配置");
|
|
49
54
|
var inject = ["database"];
|
|
50
55
|
var ESC_QUOTE = "\0";
|
|
@@ -175,24 +180,24 @@ function usageString() {
|
|
|
175
180
|
使用 \\n 换行`;
|
|
176
181
|
}
|
|
177
182
|
__name(usageString, "usageString");
|
|
178
|
-
function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasRealEnableMessageParam, hasRealDisableMessageParam, logger2) {
|
|
183
|
+
function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasRealEnableMessageParam, hasRealDisableMessageParam, logger2, defaultMessage) {
|
|
179
184
|
let reminderEnabled = true;
|
|
180
|
-
let reminderMessage = "{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}";
|
|
185
|
+
let reminderMessage = defaultMessage || "{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}";
|
|
181
186
|
if (existingConfig) {
|
|
182
187
|
reminderEnabled = existingConfig.reminderEnabled;
|
|
183
188
|
reminderMessage = existingConfig.reminderMessage || reminderMessage;
|
|
184
189
|
}
|
|
185
190
|
if (hasRealDisableMessageParam) {
|
|
186
191
|
reminderEnabled = false;
|
|
187
|
-
logger2.
|
|
192
|
+
logger2.debug("禁用提醒消息功能 (保留现有内容)");
|
|
188
193
|
} else if (hasRealEnableMessageParam) {
|
|
189
194
|
reminderEnabled = true;
|
|
190
|
-
logger2.
|
|
195
|
+
logger2.debug(`启用提醒消息(保留原消息): ${reminderMessage.substring(0, 50)}...`);
|
|
191
196
|
} else if (hasRealMessageParam) {
|
|
192
197
|
reminderEnabled = true;
|
|
193
198
|
if (cleanedOptions.message !== void 0) {
|
|
194
199
|
reminderMessage = cleanedOptions.message.replace(/\\n/g, "\n");
|
|
195
|
-
logger2.
|
|
200
|
+
logger2.debug(`设置自定义提醒消息: ${reminderMessage.substring(0, 50)}...`);
|
|
196
201
|
}
|
|
197
202
|
}
|
|
198
203
|
return { reminderEnabled, reminderMessage };
|
|
@@ -216,7 +221,7 @@ async function syncTotalStats(ctx) {
|
|
|
216
221
|
totalJoined,
|
|
217
222
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
218
223
|
});
|
|
219
|
-
logger.
|
|
224
|
+
logger.debug(`总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}, 入群${totalJoined}`);
|
|
220
225
|
}
|
|
221
226
|
} catch (error) {
|
|
222
227
|
logger.error("同步总计统计时出错:", error);
|
|
@@ -265,30 +270,72 @@ async function incrementTotal(ctx, groupId) {
|
|
|
265
270
|
await syncTotalStats(ctx);
|
|
266
271
|
}
|
|
267
272
|
__name(incrementTotal, "incrementTotal");
|
|
273
|
+
function resolveThreshold(existingConfig, keywordList, reviewMethod, thresholdStr, methodChanged = false) {
|
|
274
|
+
let reviewParameters = 0;
|
|
275
|
+
if (existingConfig) {
|
|
276
|
+
reviewParameters = existingConfig.reviewParameters || 0;
|
|
277
|
+
if (isNaN(reviewParameters)) reviewParameters = 0;
|
|
278
|
+
}
|
|
279
|
+
if (thresholdStr !== void 0) {
|
|
280
|
+
const thresholdNum = parseInt(thresholdStr);
|
|
281
|
+
if (isNaN(thresholdNum)) {
|
|
282
|
+
return { reviewParameters, error: "阈值参数必须为数字" };
|
|
283
|
+
}
|
|
284
|
+
if (reviewMethod === 1) {
|
|
285
|
+
if (thresholdNum < 0 || thresholdNum > keywordList.length) {
|
|
286
|
+
return { reviewParameters, error: `数量阈值必须在0-${keywordList.length}之间(0表示全部同意)` };
|
|
287
|
+
}
|
|
288
|
+
} else if (reviewMethod === 2) {
|
|
289
|
+
if (thresholdNum < 0 || thresholdNum > 100) {
|
|
290
|
+
return { reviewParameters, error: "比例阈值必须在0-100之间(0表示全部同意)" };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return { reviewParameters: thresholdNum };
|
|
294
|
+
}
|
|
295
|
+
if (methodChanged) {
|
|
296
|
+
if (reviewMethod === 1) {
|
|
297
|
+
reviewParameters = keywordList.length;
|
|
298
|
+
return { reviewParameters, autoInfo: "methodChange" };
|
|
299
|
+
}
|
|
300
|
+
if (reviewMethod === 2) {
|
|
301
|
+
reviewParameters = 100;
|
|
302
|
+
return { reviewParameters, autoInfo: "methodChange" };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (existingConfig && reviewMethod === 1 && reviewParameters !== 0) {
|
|
306
|
+
const oldKeywordCount = existingConfig.keywords.length;
|
|
307
|
+
const newKeywordCount = keywordList.length;
|
|
308
|
+
if (oldKeywordCount !== newKeywordCount) {
|
|
309
|
+
reviewParameters = newKeywordCount;
|
|
310
|
+
return { reviewParameters, autoInfo: "kwChange" };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return { reviewParameters };
|
|
314
|
+
}
|
|
315
|
+
__name(resolveThreshold, "resolveThreshold");
|
|
268
316
|
async function checkPermission(session, targetGroupId) {
|
|
269
317
|
const groupId = targetGroupId || session.guildId;
|
|
270
318
|
if (!groupId) {
|
|
271
319
|
return [false, "请在群聊中使用此命令或使用 -i 参数指定群号"];
|
|
272
320
|
}
|
|
273
|
-
logger.
|
|
321
|
+
logger.debug(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`);
|
|
274
322
|
const koishiAuthority = session.author?.authority || session.user?.authority;
|
|
275
|
-
logger.
|
|
323
|
+
logger.debug(`权限检查 - Koishi权限等级: ${koishiAuthority || "未获取到"}`);
|
|
276
324
|
if (!session.author) {
|
|
277
|
-
logger.
|
|
325
|
+
logger.debug(`权限检查 - session中可能的权限字段:`, {
|
|
278
326
|
authority: session.authority,
|
|
279
327
|
permission: session.permission,
|
|
280
328
|
role: session.role
|
|
281
329
|
});
|
|
282
330
|
} else {
|
|
283
|
-
logger.
|
|
284
|
-
authority: session.author.authority,
|
|
331
|
+
logger.debug(`权限检查 - author对象中的字段:`, {
|
|
285
332
|
permission: session.author.permission,
|
|
286
333
|
role: session.author.role,
|
|
287
334
|
permissions: session.author.permissions
|
|
288
335
|
});
|
|
289
336
|
}
|
|
290
337
|
if (session.user) {
|
|
291
|
-
logger.
|
|
338
|
+
logger.debug(`权限检查 - user对象中的权限信息:`, {
|
|
292
339
|
authority: session.user.authority,
|
|
293
340
|
permission: session.user.permission,
|
|
294
341
|
role: session.user.role
|
|
@@ -346,12 +393,12 @@ async function handleGuildMemberRequestEvent(ctx, session) {
|
|
|
346
393
|
return;
|
|
347
394
|
}
|
|
348
395
|
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session);
|
|
349
|
-
logger.
|
|
396
|
+
logger.debug(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
|
|
350
397
|
if (isValid) {
|
|
351
398
|
if (requestId) {
|
|
352
399
|
try {
|
|
353
400
|
await session.bot.handleGuildMemberRequest(requestId, true);
|
|
354
|
-
logger.
|
|
401
|
+
logger.debug(`自动同意 requestId=${requestId}`);
|
|
355
402
|
if (!autoQueue.has(guildId)) autoQueue.set(guildId, /* @__PURE__ */ new Set());
|
|
356
403
|
autoQueue.get(guildId).add(userId);
|
|
357
404
|
} catch (e) {
|
|
@@ -491,7 +538,7 @@ async function verifyApplication(config, message, session) {
|
|
|
491
538
|
isValid = false;
|
|
492
539
|
requiredThreshold = "null";
|
|
493
540
|
}
|
|
494
|
-
logger.
|
|
541
|
+
logger.debug(`verifyApplication msg="${message}" keywords=${JSON.stringify(config.keywords)} matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
|
|
495
542
|
return { isValid, matchedCount, requiredThreshold };
|
|
496
543
|
}
|
|
497
544
|
__name(verifyApplication, "verifyApplication");
|
|
@@ -504,7 +551,7 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
|
|
|
504
551
|
}
|
|
505
552
|
const username = session.username || "未知用户";
|
|
506
553
|
const message = session.content || "";
|
|
507
|
-
logger.
|
|
554
|
+
logger.debug(`处理失败验证 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold}`);
|
|
508
555
|
if (matchedCount === void 0 || requiredThreshold === void 0) {
|
|
509
556
|
const result = await verifyApplication(config, message, session);
|
|
510
557
|
matchedCount = result.matchedCount;
|
|
@@ -527,7 +574,7 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
|
|
|
527
574
|
applyTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
528
575
|
});
|
|
529
576
|
if (!config.reminderEnabled || !config.reminderMessage || config.reminderMessage === "") {
|
|
530
|
-
logger.
|
|
577
|
+
logger.debug(`群 ${guildId} 的提醒消息已被禁用,跳过发送`);
|
|
531
578
|
return;
|
|
532
579
|
}
|
|
533
580
|
let reminderMsg = config.reminderMessage;
|
|
@@ -575,6 +622,7 @@ function apply(ctx, config) {
|
|
|
575
622
|
autoInc: true
|
|
576
623
|
});
|
|
577
624
|
logger = ctx.logger("group-verification");
|
|
625
|
+
if (config.logLevel) logger.level = config.logLevel;
|
|
578
626
|
logger.info("群组验证插件已启动");
|
|
579
627
|
logger.info(`默认提醒消息: ${config.defaultReminderMessage}`);
|
|
580
628
|
logger.info(`严格群号检查: ${config.enableStrictGroupCheck ? "启用" : "禁用"}`);
|
|
@@ -640,19 +688,19 @@ function apply(ctx, config) {
|
|
|
640
688
|
async function checkPermission2(session, targetGroupId) {
|
|
641
689
|
const groupId = targetGroupId || session.guildId;
|
|
642
690
|
if (!groupId) {
|
|
643
|
-
return [false, "请在群聊中使用此命令或使用 -i 参数指定群号"];
|
|
691
|
+
return [false, config.invalidGroupMessage || "请在群聊中使用此命令或使用 -i 参数指定群号"];
|
|
644
692
|
}
|
|
645
|
-
logger.
|
|
693
|
+
logger.debug(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`);
|
|
646
694
|
const koishiAuthority = session.author?.authority || session.user?.authority;
|
|
647
|
-
logger.
|
|
695
|
+
logger.debug(`权限检查 - Koishi权限等级: ${koishiAuthority || "未获取到"}`);
|
|
648
696
|
if (!session.author) {
|
|
649
|
-
logger.
|
|
697
|
+
logger.debug(`权限检查 - session中可能的权限字段:`, {
|
|
650
698
|
authority: session.authority,
|
|
651
699
|
permission: session.permission,
|
|
652
700
|
role: session.role
|
|
653
701
|
});
|
|
654
702
|
} else {
|
|
655
|
-
logger.
|
|
703
|
+
logger.debug(`权限检查 - author对象中的字段:`, {
|
|
656
704
|
authority: session.author.authority,
|
|
657
705
|
permission: session.author.permission,
|
|
658
706
|
role: session.author.role,
|
|
@@ -660,19 +708,19 @@ function apply(ctx, config) {
|
|
|
660
708
|
});
|
|
661
709
|
}
|
|
662
710
|
if (session.user) {
|
|
663
|
-
logger.
|
|
711
|
+
logger.debug(`权限检查 - user对象中的权限信息:`, {
|
|
664
712
|
authority: session.user.authority,
|
|
665
713
|
permission: session.user.permission,
|
|
666
714
|
role: session.user.role
|
|
667
715
|
});
|
|
668
716
|
}
|
|
669
717
|
if (koishiAuthority && koishiAuthority >= 3) {
|
|
670
|
-
logger.
|
|
718
|
+
logger.debug(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`);
|
|
671
719
|
return [true];
|
|
672
720
|
}
|
|
673
721
|
try {
|
|
674
722
|
const member = await session.bot.getGuildMember(groupId, session.userId);
|
|
675
|
-
logger.
|
|
723
|
+
logger.debug(`权限检查 - 获取到成员信息:`, {
|
|
676
724
|
roles: member?.roles,
|
|
677
725
|
permissions: member?.permissions
|
|
678
726
|
});
|
|
@@ -682,7 +730,7 @@ function apply(ctx, config) {
|
|
|
682
730
|
return [true];
|
|
683
731
|
}
|
|
684
732
|
if (member.roles?.includes("admin") || member.permissions?.includes("ADMINISTRATOR")) {
|
|
685
|
-
logger.
|
|
733
|
+
logger.debug(`权限检查 - 用户是管理员`);
|
|
686
734
|
return [true];
|
|
687
735
|
}
|
|
688
736
|
}
|
|
@@ -690,9 +738,9 @@ function apply(ctx, config) {
|
|
|
690
738
|
logger.warn(`权限检查 - 获取群成员信息失败:`, error);
|
|
691
739
|
return [false, `无法获取群 ${groupId} 的成员信息,请确认机器人已在该群中`];
|
|
692
740
|
}
|
|
693
|
-
logger.
|
|
741
|
+
logger.debug(`权限检查 - 权限不足`);
|
|
694
742
|
const debugInfo = `调试信息 - 用户ID:${session.userId}, 群号:${groupId}, 权限等级:${koishiAuthority || "未知"}`;
|
|
695
|
-
return [false,
|
|
743
|
+
return [false, (config.permissionDeniedMessage || "权限不足:需要群主/管理员权限或koishi三级以上权限") + `
|
|
696
744
|
${debugInfo}`];
|
|
697
745
|
}
|
|
698
746
|
__name(checkPermission2, "checkPermission");
|
|
@@ -729,7 +777,7 @@ ${debugInfo}`];
|
|
|
729
777
|
};
|
|
730
778
|
logger.info(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}`);
|
|
731
779
|
if ((cleanedOptions.query || cleanedOptions.remove) && (parsedKeywords.length > 0 || cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || cleanedOptions.message !== void 0 || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
|
|
732
|
-
return "参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)";
|
|
780
|
+
return config.parameterConflictMessage || "参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)";
|
|
733
781
|
}
|
|
734
782
|
const hasRealMessageParam = cleanedOptions.message !== void 0;
|
|
735
783
|
const hasRealEnableMessageParam = cleanedOptions.enableMessage === true;
|
|
@@ -841,7 +889,7 @@ ${debugInfo}`];
|
|
|
841
889
|
}
|
|
842
890
|
if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
|
|
843
891
|
if (!existingConfig) {
|
|
844
|
-
return "请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置";
|
|
892
|
+
return config.noKeywordsMessage || "请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置";
|
|
845
893
|
}
|
|
846
894
|
keywordList = existingConfig.keywords;
|
|
847
895
|
}
|
|
@@ -851,7 +899,8 @@ ${debugInfo}`];
|
|
|
851
899
|
hasRealMessageParam,
|
|
852
900
|
hasRealEnableMessageParam,
|
|
853
901
|
hasRealDisableMessageParam,
|
|
854
|
-
logger
|
|
902
|
+
logger,
|
|
903
|
+
config.defaultReminderMessage
|
|
855
904
|
);
|
|
856
905
|
let reviewMethod = 0;
|
|
857
906
|
let reviewParameters = 0;
|
|
@@ -869,44 +918,45 @@ ${debugInfo}`];
|
|
|
869
918
|
if (isNaN(methodNum) || methodNum < 0 || methodNum > 3) {
|
|
870
919
|
return "审核方式参数错误:0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝";
|
|
871
920
|
}
|
|
921
|
+
const oldMethod = reviewMethod;
|
|
872
922
|
reviewMethod = methodNum;
|
|
873
|
-
logger.
|
|
923
|
+
logger.debug(`审核方式明确指定为: ${reviewMethod}`);
|
|
924
|
+
var methodChanged = oldMethod !== reviewMethod;
|
|
874
925
|
} else {
|
|
875
|
-
logger.
|
|
926
|
+
logger.debug(`未指定审核方式,保持原有值: ${reviewMethod}`);
|
|
927
|
+
var methodChanged = false;
|
|
876
928
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
929
|
+
const thresholdResult = resolveThreshold(
|
|
930
|
+
existingConfig,
|
|
931
|
+
keywordList,
|
|
932
|
+
reviewMethod,
|
|
933
|
+
cleanedOptions.threshold,
|
|
934
|
+
methodChanged
|
|
935
|
+
);
|
|
936
|
+
if (thresholdResult.error) {
|
|
937
|
+
return thresholdResult.error;
|
|
938
|
+
}
|
|
939
|
+
reviewParameters = thresholdResult.reviewParameters;
|
|
940
|
+
if (existingConfig && reviewParameters !== existingConfig.reviewParameters) {
|
|
941
|
+
await ctx.database.set("group_verification_config", { id: existingConfig.id }, {
|
|
942
|
+
reviewParameters,
|
|
943
|
+
updatedBy: session.username || session.userId,
|
|
944
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
945
|
+
});
|
|
946
|
+
logger.debug(`自动调整并更新数据库阈值为: ${reviewParameters}`);
|
|
947
|
+
}
|
|
948
|
+
let autoAdjustNote = "";
|
|
949
|
+
if (thresholdResult.autoInfo === "methodChange") {
|
|
882
950
|
if (reviewMethod === 1) {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
}
|
|
886
|
-
reviewParameters = thresholdNum;
|
|
887
|
-
logger.info(`明确指定阈值: ${thresholdNum}`);
|
|
951
|
+
autoAdjustNote = `⚠️ 审核方式由 ${existingConfig?.reviewMethod} 改为 ${reviewMethod},阈值自动设为 ${reviewParameters}
|
|
952
|
+
`;
|
|
888
953
|
} else if (reviewMethod === 2) {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
}
|
|
892
|
-
reviewParameters = thresholdNum;
|
|
893
|
-
logger.info(`明确指定阈值: ${thresholdNum}%`);
|
|
894
|
-
}
|
|
895
|
-
} else if (existingConfig && reviewMethod === 1 && reviewParameters !== 0) {
|
|
896
|
-
const oldKeywordCount = existingConfig.keywords.length;
|
|
897
|
-
const newKeywordCount = keywordList.length;
|
|
898
|
-
if (oldKeywordCount !== newKeywordCount) {
|
|
899
|
-
reviewParameters = newKeywordCount;
|
|
900
|
-
logger.info(`关键词数量从${oldKeywordCount}变为${newKeywordCount},自动调整阈值`);
|
|
901
|
-
await ctx.database.set("group_verification_config", { id: existingConfig.id }, {
|
|
902
|
-
reviewParameters,
|
|
903
|
-
updatedBy: session.username || session.userId,
|
|
904
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
905
|
-
});
|
|
906
|
-
logger.info(`已更新数据库阈值为: ${reviewParameters}`);
|
|
907
|
-
} else {
|
|
908
|
-
logger.info(`关键词数量未变化(${newKeywordCount}),保持原阈值: ${reviewParameters}`);
|
|
954
|
+
autoAdjustNote = `⚠️ 审核方式由 ${existingConfig?.reviewMethod} 改为 ${reviewMethod},阈值自动设为 ${reviewParameters}%
|
|
955
|
+
`;
|
|
909
956
|
}
|
|
957
|
+
} else if (thresholdResult.autoInfo === "kwChange") {
|
|
958
|
+
autoAdjustNote = `⚠️ 关键词数量从${existingConfig?.keywords.length}变为${keywordList.length},阈值已自动调整为${reviewParameters}
|
|
959
|
+
`;
|
|
910
960
|
}
|
|
911
961
|
const encodedKeywords = keywordList.map((keyword) => {
|
|
912
962
|
return keyword.replace(/,/g, "[[COMMA]]");
|
|
@@ -940,6 +990,7 @@ ${debugInfo}`];
|
|
|
940
990
|
const decodedKeywords = keywordList;
|
|
941
991
|
let feedbackMessage = `群 ${targetGroupId} 配置已更新:
|
|
942
992
|
`;
|
|
993
|
+
if (autoAdjustNote) feedbackMessage += autoAdjustNote;
|
|
943
994
|
const displayKeywords = keywordList.map((k) => k.replace(/\[\[COMMA\]\]/g, ","));
|
|
944
995
|
feedbackMessage += `关键词: ${displayKeywords.map((k) => `"${k}"`).join(", ")}
|
|
945
996
|
`;
|
|
@@ -955,10 +1006,6 @@ ${debugInfo}`];
|
|
|
955
1006
|
`;
|
|
956
1007
|
if (reminderMessage && reminderEnabled) {
|
|
957
1008
|
feedbackMessage += `提醒消息: ${reminderMessage.substring(0, 30)}${reminderMessage.length > 30 ? "..." : ""}
|
|
958
|
-
`;
|
|
959
|
-
}
|
|
960
|
-
if (existingConfig && reviewMethod === 1 && keywordList.length !== existingConfig.keywords.length) {
|
|
961
|
-
feedbackMessage += `⚠️ 关键词数量从${existingConfig.keywords.length}变为${keywordList.length},阈值已自动调整为${keywordList.length}
|
|
962
1009
|
`;
|
|
963
1010
|
}
|
|
964
1011
|
logger.info(`准备存储到数据库的关键词: ${JSON.stringify(encodedKeywords)}`);
|
|
@@ -1363,6 +1410,7 @@ __name(apply, "apply");
|
|
|
1363
1410
|
mergeReminder,
|
|
1364
1411
|
name,
|
|
1365
1412
|
parseConfigArgs,
|
|
1413
|
+
resolveThreshold,
|
|
1366
1414
|
syncTotalStats,
|
|
1367
1415
|
tokenize,
|
|
1368
1416
|
updateStats,
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-group-verification",
|
|
3
|
-
"description": "
|
|
4
|
-
"
|
|
3
|
+
"description": "Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式和详细统计功能。",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/LHDyx/koishi-plugin-group-verification.git"
|
|
7
|
+
},
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/LHDyx/koishi-plugin-group-verification/issues"
|
|
10
|
+
},
|
|
11
|
+
"version": "1.0.29",
|
|
5
12
|
"main": "lib/index.js",
|
|
6
13
|
"typings": "lib/index.d.ts",
|
|
7
14
|
"files": [
|
package/readme.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
# Koishi 群组验证插件
|
|
1
|
+
# Koishi 群组验证插件
|
|
2
2
|
|
|
3
3
|
一个功能完整的 Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式和详细统计功能。
|
|
4
4
|
|
|
5
|
+
**仓库**: https://github.com/LHDyx/koishi-plugin-group-verification
|
|
6
|
+
**问题反馈**: https://github.com/LHDyx/koishi-plugin-group-verification/issues
|
|
7
|
+
|
|
5
8
|
## 🌟 主要特性
|
|
6
9
|
|
|
7
10
|
- **多关键词匹配**:支持多个关键词的灵活配置
|
|
@@ -13,6 +16,8 @@
|
|
|
13
16
|
|
|
14
17
|
## 🚀 安装使用
|
|
15
18
|
|
|
19
|
+
> 本插件已脱离开发阶段,可在 Koishi 插件管理页直接安装并配置。部分提示词可在管理界面自定义。
|
|
20
|
+
|
|
16
21
|
```
|
|
17
22
|
# 在 Koishi 控制台中搜索并安装
|
|
18
23
|
koishi-plugin-group-verification
|
|
@@ -99,6 +104,8 @@ group-verify.stats total
|
|
|
99
104
|
## ⚙️ 参数说明
|
|
100
105
|
|
|
101
106
|
### 审核方式 (-m)
|
|
107
|
+
|
|
108
|
+
*如果改变审核方式而未提供 `-t`,阈值会自动设置为最大值(方式1为关键词数量,方式2为100)。*
|
|
102
109
|
- `0` - 全部同意(默认)
|
|
103
110
|
- `1` - 按数量同意(需配合 -t 使用)
|
|
104
111
|
- `2` - 按比例同意(需配合 -t 使用)
|
package/src/index.ts
CHANGED
|
@@ -57,15 +57,25 @@ export interface Config {
|
|
|
57
57
|
defaultReminderMessage?: string
|
|
58
58
|
enableStrictGroupCheck?: boolean // 群号合法性检查配置
|
|
59
59
|
logLevel?: 'debug' | 'info' | 'warn' | 'error' // 日志等级配置
|
|
60
|
+
// 以下为可自定义的命令反馈提示词,可在插件管理界面调整
|
|
61
|
+
permissionDeniedMessage?: string
|
|
62
|
+
invalidGroupMessage?: string
|
|
63
|
+
parameterConflictMessage?: string
|
|
64
|
+
noKeywordsMessage?: string
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
export const Config: Schema<Config> = Schema.object({
|
|
63
68
|
defaultReminderMessage: Schema.string()
|
|
64
|
-
.description('默认提醒消息模板(使用
|
|
69
|
+
.description('默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)')
|
|
65
70
|
.default('{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}\n使用 gva 同意或 gvr 拒绝申请'),
|
|
66
71
|
enableStrictGroupCheck: Schema.boolean().description('是否启用严格的群号检查(检查群号长度)').default(false),
|
|
67
|
-
logLevel: Schema.union(['debug', 'info', 'warn', 'error']).description('日志级别').default('info')
|
|
68
|
-
|
|
72
|
+
logLevel: Schema.union(['debug', 'info', 'warn', 'error']).description('日志级别').default('info'),
|
|
73
|
+
permissionDeniedMessage: Schema.string().description('权限不足时返回给调用者的提示').default('权限不足:需要群主/管理员权限或koishi三级以上权限'),
|
|
74
|
+
invalidGroupMessage: Schema.string().description('无效群号或机器人未在该群时的提示').default('群号 {group} 格式不合法或机器人不在该群中'),
|
|
75
|
+
parameterConflictMessage: Schema.string().description('参数冲突时提示').default('参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'),
|
|
76
|
+
noKeywordsMessage: Schema.string().description('未提供关键词且无法从现有配置继承时的提示').default('请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'),
|
|
77
|
+
})
|
|
78
|
+
.description('群组验证插件配置')
|
|
69
79
|
|
|
70
80
|
export const inject = ['database']
|
|
71
81
|
|
|
@@ -257,10 +267,12 @@ export function mergeReminder(
|
|
|
257
267
|
hasRealMessageParam: boolean,
|
|
258
268
|
hasRealEnableMessageParam: boolean,
|
|
259
269
|
hasRealDisableMessageParam: boolean,
|
|
260
|
-
logger: any
|
|
270
|
+
logger: any,
|
|
271
|
+
defaultMessage?: string
|
|
261
272
|
) {
|
|
262
273
|
let reminderEnabled = true;
|
|
263
|
-
|
|
274
|
+
// 优先使用传入的默认模板,其次使用已有配置,再 fallback 到老写死的样式
|
|
275
|
+
let reminderMessage = defaultMessage || '{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}';
|
|
264
276
|
|
|
265
277
|
if (existingConfig) {
|
|
266
278
|
reminderEnabled = existingConfig.reminderEnabled;
|
|
@@ -271,15 +283,15 @@ export function mergeReminder(
|
|
|
271
283
|
// 优先级:disable > bare enable > new message content
|
|
272
284
|
if (hasRealDisableMessageParam) {
|
|
273
285
|
reminderEnabled = false;
|
|
274
|
-
logger.
|
|
286
|
+
logger.debug('禁用提醒消息功能 (保留现有内容)');
|
|
275
287
|
} else if (hasRealEnableMessageParam) {
|
|
276
288
|
reminderEnabled = true;
|
|
277
|
-
logger.
|
|
289
|
+
logger.debug(`启用提醒消息(保留原消息): ${reminderMessage.substring(0, 50)}...`);
|
|
278
290
|
} else if (hasRealMessageParam) {
|
|
279
291
|
reminderEnabled = true;
|
|
280
292
|
if (cleanedOptions.message !== undefined) {
|
|
281
293
|
reminderMessage = cleanedOptions.message.replace(/\\n/g, '\n');
|
|
282
|
-
logger.
|
|
294
|
+
logger.debug(`设置自定义提醒消息: ${reminderMessage.substring(0, 50)}...`);
|
|
283
295
|
}
|
|
284
296
|
}
|
|
285
297
|
return { reminderEnabled, reminderMessage };
|
|
@@ -319,7 +331,7 @@ export async function syncTotalStats(ctx: Context) {
|
|
|
319
331
|
lastUpdated: new Date().toISOString()
|
|
320
332
|
})
|
|
321
333
|
|
|
322
|
-
logger.
|
|
334
|
+
logger.debug(`总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}, 入群${totalJoined}`)
|
|
323
335
|
}
|
|
324
336
|
} catch (error) {
|
|
325
337
|
logger.error('同步总计统计时出错:', error)
|
|
@@ -373,6 +385,65 @@ export async function incrementTotal(ctx: Context, groupId: string) {
|
|
|
373
385
|
await syncTotalStats(ctx)
|
|
374
386
|
}
|
|
375
387
|
|
|
388
|
+
// helper to decide reviewParameters based on existing configuration, keyword list, user inputs,
|
|
389
|
+
// and whether the audit method has been changed by the command.
|
|
390
|
+
export interface ThresholdResult {
|
|
391
|
+
reviewParameters: number
|
|
392
|
+
error?: string
|
|
393
|
+
autoInfo?: 'methodChange' | 'kwChange'
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export function resolveThreshold(
|
|
397
|
+
existingConfig: any | null,
|
|
398
|
+
keywordList: string[],
|
|
399
|
+
reviewMethod: 0 | 1 | 2 | 3,
|
|
400
|
+
thresholdStr?: string,
|
|
401
|
+
methodChanged: boolean = false
|
|
402
|
+
): ThresholdResult {
|
|
403
|
+
let reviewParameters = 0
|
|
404
|
+
if (existingConfig) {
|
|
405
|
+
reviewParameters = existingConfig.reviewParameters || 0
|
|
406
|
+
if (isNaN(reviewParameters)) reviewParameters = 0
|
|
407
|
+
}
|
|
408
|
+
// explicit threshold provided by user
|
|
409
|
+
if (thresholdStr !== undefined) {
|
|
410
|
+
const thresholdNum = parseInt(thresholdStr)
|
|
411
|
+
if (isNaN(thresholdNum)) {
|
|
412
|
+
return { reviewParameters, error: '阈值参数必须为数字' }
|
|
413
|
+
}
|
|
414
|
+
if (reviewMethod === 1) {
|
|
415
|
+
if (thresholdNum < 0 || thresholdNum > keywordList.length) {
|
|
416
|
+
return { reviewParameters, error: `数量阈值必须在0-${keywordList.length}之间(0表示全部同意)` }
|
|
417
|
+
}
|
|
418
|
+
} else if (reviewMethod === 2) {
|
|
419
|
+
if (thresholdNum < 0 || thresholdNum > 100) {
|
|
420
|
+
return { reviewParameters, error: '比例阈值必须在0-100之间(0表示全部同意)' }
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return { reviewParameters: thresholdNum }
|
|
424
|
+
}
|
|
425
|
+
// no threshold specified by user
|
|
426
|
+
if (methodChanged) {
|
|
427
|
+
if (reviewMethod === 1) {
|
|
428
|
+
reviewParameters = keywordList.length
|
|
429
|
+
return { reviewParameters, autoInfo: 'methodChange' }
|
|
430
|
+
}
|
|
431
|
+
if (reviewMethod === 2) {
|
|
432
|
+
reviewParameters = 100
|
|
433
|
+
return { reviewParameters, autoInfo: 'methodChange' }
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (existingConfig && reviewMethod === 1 && reviewParameters !== 0) {
|
|
437
|
+
const oldKeywordCount = existingConfig.keywords.length
|
|
438
|
+
const newKeywordCount = keywordList.length
|
|
439
|
+
if (oldKeywordCount !== newKeywordCount) {
|
|
440
|
+
reviewParameters = newKeywordCount
|
|
441
|
+
return { reviewParameters, autoInfo: 'kwChange' }
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return { reviewParameters }
|
|
445
|
+
}
|
|
446
|
+
|
|
376
447
|
// 权限检查函数(也可用于命令)
|
|
377
448
|
export async function checkPermission(session: any, targetGroupId?: string): Promise<[boolean, string?]> {
|
|
378
449
|
const groupId = targetGroupId || session.guildId
|
|
@@ -382,19 +453,18 @@ export async function checkPermission(session: any, targetGroupId?: string): Pro
|
|
|
382
453
|
return [false, '请在群聊中使用此命令或使用 -i 参数指定群号']
|
|
383
454
|
}
|
|
384
455
|
|
|
385
|
-
logger.
|
|
456
|
+
logger.debug(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`)
|
|
386
457
|
const koishiAuthority = session.author?.authority || session.user?.authority
|
|
387
|
-
logger.
|
|
458
|
+
logger.debug(`权限检查 - Koishi权限等级: ${koishiAuthority || '未获取到'}`)
|
|
388
459
|
|
|
389
460
|
if (!session.author) {
|
|
390
|
-
logger.
|
|
461
|
+
logger.debug(`权限检查 - session中可能的权限字段:`, {
|
|
391
462
|
authority: session.authority,
|
|
392
463
|
permission: session.permission,
|
|
393
464
|
role: session.role
|
|
394
465
|
})
|
|
395
466
|
} else {
|
|
396
|
-
logger.
|
|
397
|
-
authority: session.author.authority,
|
|
467
|
+
logger.debug(`权限检查 - author对象中的字段:`, {
|
|
398
468
|
permission: session.author.permission,
|
|
399
469
|
role: session.author.role,
|
|
400
470
|
permissions: session.author.permissions
|
|
@@ -402,7 +472,7 @@ export async function checkPermission(session: any, targetGroupId?: string): Pro
|
|
|
402
472
|
}
|
|
403
473
|
|
|
404
474
|
if (session.user) {
|
|
405
|
-
logger.
|
|
475
|
+
logger.debug(`权限检查 - user对象中的权限信息:`, {
|
|
406
476
|
authority: session.user.authority,
|
|
407
477
|
permission: session.user.permission,
|
|
408
478
|
role: session.user.role
|
|
@@ -464,13 +534,13 @@ export async function handleGuildMemberRequestEvent(ctx: Context, session: any)
|
|
|
464
534
|
}
|
|
465
535
|
|
|
466
536
|
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session);
|
|
467
|
-
logger.
|
|
537
|
+
logger.debug(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
|
|
468
538
|
|
|
469
539
|
if (isValid) {
|
|
470
540
|
if (requestId) {
|
|
471
541
|
try {
|
|
472
542
|
await session.bot.handleGuildMemberRequest(requestId, true);
|
|
473
|
-
logger.
|
|
543
|
+
logger.debug(`自动同意 requestId=${requestId}`);
|
|
474
544
|
if (!autoQueue.has(guildId)) autoQueue.set(guildId, new Set());
|
|
475
545
|
autoQueue.get(guildId)!.add(userId);
|
|
476
546
|
} catch (e) {
|
|
@@ -640,7 +710,7 @@ export async function verifyApplication(config: GroupVerificationConfig, message
|
|
|
640
710
|
requiredThreshold = 'null'
|
|
641
711
|
}
|
|
642
712
|
|
|
643
|
-
logger.
|
|
713
|
+
logger.debug(`verifyApplication msg="${message}" keywords=${JSON.stringify(config.keywords)} matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`)
|
|
644
714
|
return { isValid, matchedCount, requiredThreshold }
|
|
645
715
|
}
|
|
646
716
|
|
|
@@ -661,7 +731,7 @@ export async function handleFailedVerification(
|
|
|
661
731
|
}
|
|
662
732
|
const username = session.username || '未知用户'
|
|
663
733
|
const message = session.content || ''
|
|
664
|
-
logger.
|
|
734
|
+
logger.debug(`处理失败验证 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold}`)
|
|
665
735
|
// 如果未传入匹配信息,则重新计算一次(老调用)
|
|
666
736
|
if (matchedCount === undefined || requiredThreshold === undefined) {
|
|
667
737
|
const result = await verifyApplication(config, message, session)
|
|
@@ -695,7 +765,7 @@ export async function handleFailedVerification(
|
|
|
695
765
|
})
|
|
696
766
|
// 如果提醒消息被禁用,直接返回
|
|
697
767
|
if (!config.reminderEnabled || !config.reminderMessage || config.reminderMessage === '') {
|
|
698
|
-
logger.
|
|
768
|
+
logger.debug(`群 ${guildId} 的提醒消息已被禁用,跳过发送`)
|
|
699
769
|
return
|
|
700
770
|
}
|
|
701
771
|
|
|
@@ -760,6 +830,8 @@ export function apply(ctx: Context, config: Config) {
|
|
|
760
830
|
|
|
761
831
|
// 获取logger实例并保存到模块级变量
|
|
762
832
|
logger = ctx.logger('group-verification')
|
|
833
|
+
// 根据配置调整日志等级
|
|
834
|
+
if (config.logLevel) logger.level = config.logLevel
|
|
763
835
|
|
|
764
836
|
// 设置日志级别
|
|
765
837
|
// 注意:Koishi logger的level设置可能需要不同的方式
|
|
@@ -857,26 +929,26 @@ export function apply(ctx: Context, config: Config) {
|
|
|
857
929
|
|
|
858
930
|
// 私聊情况下必须指定群号
|
|
859
931
|
if (!groupId) {
|
|
860
|
-
return [false, '请在群聊中使用此命令或使用 -i 参数指定群号']
|
|
932
|
+
return [false, config.invalidGroupMessage || '请在群聊中使用此命令或使用 -i 参数指定群号']
|
|
861
933
|
}
|
|
862
934
|
|
|
863
935
|
// 使用Koishi logger输出调试信息
|
|
864
|
-
logger.
|
|
936
|
+
logger.debug(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`)
|
|
865
937
|
|
|
866
938
|
// 检查koishi权限等级(最高优先级)
|
|
867
939
|
const koishiAuthority = session.author?.authority || session.user?.authority
|
|
868
|
-
logger.
|
|
940
|
+
logger.debug(`权限检查 - Koishi权限等级: ${koishiAuthority || '未获取到'}`)
|
|
869
941
|
|
|
870
942
|
// 尝试其他可能的权限字段
|
|
871
943
|
if (!session.author) {
|
|
872
|
-
logger.
|
|
944
|
+
logger.debug(`权限检查 - session中可能的权限字段:`, {
|
|
873
945
|
authority: session.authority,
|
|
874
946
|
permission: session.permission,
|
|
875
947
|
role: session.role
|
|
876
948
|
})
|
|
877
949
|
} else {
|
|
878
950
|
// 检查author对象中的其他权限字段
|
|
879
|
-
logger.
|
|
951
|
+
logger.debug(`权限检查 - author对象中的字段:`, {
|
|
880
952
|
authority: session.author.authority,
|
|
881
953
|
permission: session.author.permission,
|
|
882
954
|
role: session.author.role,
|
|
@@ -886,7 +958,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
886
958
|
|
|
887
959
|
// 尝试从user对象获取权限信息
|
|
888
960
|
if (session.user) {
|
|
889
|
-
logger.
|
|
961
|
+
logger.debug(`权限检查 - user对象中的权限信息:`, {
|
|
890
962
|
authority: session.user.authority,
|
|
891
963
|
permission: session.user.permission,
|
|
892
964
|
role: session.user.role
|
|
@@ -895,14 +967,14 @@ export function apply(ctx: Context, config: Config) {
|
|
|
895
967
|
|
|
896
968
|
// 先检查koishi权限等级(最高优先级)
|
|
897
969
|
if (koishiAuthority && koishiAuthority >= 3) {
|
|
898
|
-
logger.
|
|
970
|
+
logger.debug(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`)
|
|
899
971
|
return [true]
|
|
900
972
|
}
|
|
901
973
|
|
|
902
974
|
// 再检查是否为群主或管理员(次优先级)
|
|
903
975
|
try {
|
|
904
976
|
const member = await session.bot.getGuildMember(groupId, session.userId)
|
|
905
|
-
logger.
|
|
977
|
+
logger.debug(`权限检查 - 获取到成员信息:`, {
|
|
906
978
|
roles: member?.roles,
|
|
907
979
|
permissions: member?.permissions
|
|
908
980
|
})
|
|
@@ -915,7 +987,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
915
987
|
}
|
|
916
988
|
// 检查管理员权限
|
|
917
989
|
if (member.roles?.includes('admin') || member.permissions?.includes('ADMINISTRATOR')) {
|
|
918
|
-
logger.
|
|
990
|
+
logger.debug(`权限检查 - 用户是管理员`)
|
|
919
991
|
return [true]
|
|
920
992
|
}
|
|
921
993
|
}
|
|
@@ -924,9 +996,9 @@ export function apply(ctx: Context, config: Config) {
|
|
|
924
996
|
return [false, `无法获取群 ${groupId} 的成员信息,请确认机器人已在该群中`]
|
|
925
997
|
}
|
|
926
998
|
|
|
927
|
-
logger.
|
|
999
|
+
logger.debug(`权限检查 - 权限不足`)
|
|
928
1000
|
const debugInfo = `调试信息 - 用户ID:${session.userId}, 群号:${groupId}, 权限等级:${koishiAuthority || '未知'}`
|
|
929
|
-
return [false,
|
|
1001
|
+
return [false, (config.permissionDeniedMessage || '权限不足:需要群主/管理员权限或koishi三级以上权限') + `\n${debugInfo}`]
|
|
930
1002
|
}
|
|
931
1003
|
|
|
932
1004
|
// Create main command with aliases
|
|
@@ -985,7 +1057,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
985
1057
|
if ((cleanedOptions.query || cleanedOptions.remove) &&
|
|
986
1058
|
(parsedKeywords.length > 0 || cleanedOptions.method !== undefined || cleanedOptions.threshold !== undefined ||
|
|
987
1059
|
cleanedOptions.message !== undefined || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
|
|
988
|
-
return '参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'
|
|
1060
|
+
return config.parameterConflictMessage || '参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'
|
|
989
1061
|
}
|
|
990
1062
|
|
|
991
1063
|
// 检查消息参数冲突
|
|
@@ -1138,7 +1210,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1138
1210
|
// 处理关键词:如果没有关键词但有其他参数,从现有配置中获取
|
|
1139
1211
|
if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
|
|
1140
1212
|
if (!existingConfig) {
|
|
1141
|
-
return '请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'
|
|
1213
|
+
return config.noKeywordsMessage || '请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'
|
|
1142
1214
|
}
|
|
1143
1215
|
keywordList = existingConfig.keywords
|
|
1144
1216
|
}
|
|
@@ -1150,7 +1222,8 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1150
1222
|
hasRealMessageParam,
|
|
1151
1223
|
hasRealEnableMessageParam,
|
|
1152
1224
|
hasRealDisableMessageParam,
|
|
1153
|
-
logger
|
|
1225
|
+
logger,
|
|
1226
|
+
config.defaultReminderMessage
|
|
1154
1227
|
)
|
|
1155
1228
|
|
|
1156
1229
|
// 解析审核方式和阈值(基于新的数据库模型设计)
|
|
@@ -1178,54 +1251,48 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1178
1251
|
if (isNaN(methodNum) || methodNum < 0 || methodNum > 3) {
|
|
1179
1252
|
return '审核方式参数错误:0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝'
|
|
1180
1253
|
}
|
|
1254
|
+
const oldMethod = reviewMethod
|
|
1181
1255
|
reviewMethod = methodNum as 0 | 1 | 2 | 3
|
|
1182
|
-
logger.
|
|
1256
|
+
logger.debug(`审核方式明确指定为: ${reviewMethod}`)
|
|
1257
|
+
var methodChanged = oldMethod !== reviewMethod
|
|
1183
1258
|
} else {
|
|
1184
|
-
logger.
|
|
1259
|
+
logger.debug(`未指定审核方式,保持原有值: ${reviewMethod}`)
|
|
1260
|
+
var methodChanged = false
|
|
1185
1261
|
}
|
|
1186
|
-
|
|
1187
|
-
//
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
if (
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
// 立即更新数据库中的阈值
|
|
1219
|
-
await ctx.database.set('group_verification_config', { id: existingConfig.id }, {
|
|
1220
|
-
reviewParameters: reviewParameters,
|
|
1221
|
-
updatedBy: session.username || session.userId,
|
|
1222
|
-
updatedAt: new Date().toISOString()
|
|
1223
|
-
})
|
|
1224
|
-
logger.info(`已更新数据库阈值为: ${reviewParameters}`)
|
|
1225
|
-
} else {
|
|
1226
|
-
// 关键词数量相同,保持原阈值
|
|
1227
|
-
logger.info(`关键词数量未变化(${newKeywordCount}),保持原阈值: ${reviewParameters}`)
|
|
1262
|
+
|
|
1263
|
+
// 决定最终的阈值,可能会触发自动调整
|
|
1264
|
+
const thresholdResult = resolveThreshold(
|
|
1265
|
+
existingConfig,
|
|
1266
|
+
keywordList,
|
|
1267
|
+
reviewMethod,
|
|
1268
|
+
cleanedOptions.threshold,
|
|
1269
|
+
methodChanged
|
|
1270
|
+
)
|
|
1271
|
+
if (thresholdResult.error) {
|
|
1272
|
+
return thresholdResult.error
|
|
1273
|
+
}
|
|
1274
|
+
reviewParameters = thresholdResult.reviewParameters
|
|
1275
|
+
|
|
1276
|
+
// 如果参数通过自动逻辑发生变化,回写数据库并记录提示
|
|
1277
|
+
if (existingConfig && reviewParameters !== existingConfig.reviewParameters) {
|
|
1278
|
+
await ctx.database.set('group_verification_config', { id: existingConfig.id }, {
|
|
1279
|
+
reviewParameters,
|
|
1280
|
+
updatedBy: session.username || session.userId,
|
|
1281
|
+
updatedAt: new Date().toISOString()
|
|
1282
|
+
})
|
|
1283
|
+
logger.debug(`自动调整并更新数据库阈值为: ${reviewParameters}`)
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
// 自动调整说明,用于反馈消息
|
|
1287
|
+
let autoAdjustNote = ''
|
|
1288
|
+
if (thresholdResult.autoInfo === 'methodChange') {
|
|
1289
|
+
if (reviewMethod === 1) {
|
|
1290
|
+
autoAdjustNote = `⚠️ 审核方式由 ${existingConfig?.reviewMethod} 改为 ${reviewMethod},阈值自动设为 ${reviewParameters}\n`
|
|
1291
|
+
} else if (reviewMethod === 2) {
|
|
1292
|
+
autoAdjustNote = `⚠️ 审核方式由 ${existingConfig?.reviewMethod} 改为 ${reviewMethod},阈值自动设为 ${reviewParameters}%\n`
|
|
1228
1293
|
}
|
|
1294
|
+
} else if (thresholdResult.autoInfo === 'kwChange') {
|
|
1295
|
+
autoAdjustNote = `⚠️ 关键词数量从${existingConfig?.keywords.length}变为${keywordList.length},阈值已自动调整为${reviewParameters}\n`
|
|
1229
1296
|
}
|
|
1230
1297
|
|
|
1231
1298
|
// 数据库存储前的关键词编码处理
|
|
@@ -1270,6 +1337,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1270
1337
|
|
|
1271
1338
|
// 构建完整的配置反馈信息
|
|
1272
1339
|
let feedbackMessage = `群 ${targetGroupId} 配置已更新:\n`
|
|
1340
|
+
if (autoAdjustNote) feedbackMessage += autoAdjustNote
|
|
1273
1341
|
// 使用原始输入的关键词显示,而不是处理后的keywordList
|
|
1274
1342
|
const displayKeywords = keywordList.map(k => k.replace(/\[\[COMMA\]\]/g, ','))
|
|
1275
1343
|
feedbackMessage += `关键词: ${displayKeywords.map(k => `"${k}"`).join(', ')}\n`
|
|
@@ -1288,11 +1356,6 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1288
1356
|
feedbackMessage += `提醒消息: ${reminderMessage.substring(0, 30)}${reminderMessage.length > 30 ? '...' : ''}\n`
|
|
1289
1357
|
}
|
|
1290
1358
|
|
|
1291
|
-
// 添加阈值自动调整提醒(如果有)
|
|
1292
|
-
if (existingConfig && reviewMethod === 1 &&
|
|
1293
|
-
keywordList.length !== existingConfig.keywords.length) {
|
|
1294
|
-
feedbackMessage += `⚠️ 关键词数量从${existingConfig.keywords.length}变为${keywordList.length},阈值已自动调整为${keywordList.length}\n`
|
|
1295
|
-
}
|
|
1296
1359
|
|
|
1297
1360
|
// 同时更新数据库存储时也要确保正确格式
|
|
1298
1361
|
logger.info(`准备存储到数据库的关键词: ${JSON.stringify(encodedKeywords)}`)
|