koishi-plugin-group-verification 1.0.35 → 1.0.36
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 +1 -1
- package/lib/index.js +104 -100
- package/package.json +1 -1
- package/src/index.ts +121 -134
package/lib/index.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface PendingVerification {
|
|
|
47
47
|
export interface Config {
|
|
48
48
|
defaultReminderMessage?: string;
|
|
49
49
|
enableStrictGroupCheck?: boolean;
|
|
50
|
-
logLevel?: 'debug' | 'info' | 'warn'
|
|
50
|
+
logLevel?: 'debug' | 'info' | 'warn';
|
|
51
51
|
permissionDeniedMessage?: string;
|
|
52
52
|
invalidGroupMessage?: string;
|
|
53
53
|
parameterConflictMessage?: string;
|
package/lib/index.js
CHANGED
|
@@ -44,10 +44,27 @@ module.exports = __toCommonJS(src_exports);
|
|
|
44
44
|
var import_koishi = require("koishi");
|
|
45
45
|
var name = "group-verification";
|
|
46
46
|
var logger = console;
|
|
47
|
+
var pluginLogLevel = "中等";
|
|
48
|
+
function clog(lvl, mediumMsg, verboseMsg) {
|
|
49
|
+
if (pluginLogLevel === "简洁") {
|
|
50
|
+
if (lvl === "warn" || lvl === "error") logger[lvl](mediumMsg);
|
|
51
|
+
} else if (pluginLogLevel === "详细") {
|
|
52
|
+
logger[lvl](verboseMsg ?? mediumMsg);
|
|
53
|
+
} else {
|
|
54
|
+
const outputLvl = lvl === "debug" ? "info" : lvl;
|
|
55
|
+
logger[outputLvl](mediumMsg);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
__name(clog, "clog");
|
|
59
|
+
function clogV(msg, ...args) {
|
|
60
|
+
if (pluginLogLevel === "详细") logger.debug(msg, ...args);
|
|
61
|
+
}
|
|
62
|
+
__name(clogV, "clogV");
|
|
47
63
|
var Config = import_koishi.Schema.object({
|
|
48
64
|
defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)").default("{user}({id}) 申请入群\\n申请理由: {question}\\n匹配情况: {answer}/{threshold}\\n使用 gva 同意或 gvr 拒绝申请"),
|
|
49
65
|
enableStrictGroupCheck: import_koishi.Schema.boolean().description("是否启用严格的群号检查(检查群号长度)").default(false),
|
|
50
|
-
logLevel: import_koishi.Schema.union(["debug", "info", "warn"
|
|
66
|
+
logLevel: import_koishi.Schema.union(["debug", "info", "warn"]).description("日志详细程度").default("info"),
|
|
67
|
+
// debug=详细, info=中等, warn=简洁
|
|
51
68
|
permissionDeniedMessage: import_koishi.Schema.string().description("权限不足时返回给调用者的提示").default("权限不足: 需要群主/管理员权限或koishi三级以上权限"),
|
|
52
69
|
invalidGroupMessage: import_koishi.Schema.string().description("无效群号或机器人未在该群时的提示").default("群号 {group} 格式不合法或机器人不在该群中"),
|
|
53
70
|
parameterConflictMessage: import_koishi.Schema.string().description("参数冲突时提示").default("参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)"),
|
|
@@ -201,15 +218,15 @@ function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasR
|
|
|
201
218
|
}
|
|
202
219
|
if (hasRealDisableMessageParam) {
|
|
203
220
|
reminderEnabled = false;
|
|
204
|
-
|
|
221
|
+
clog("debug", "禁用提醒消息功能 (保留现有内容)");
|
|
205
222
|
} else if (hasRealEnableMessageParam) {
|
|
206
223
|
reminderEnabled = true;
|
|
207
|
-
|
|
224
|
+
clog("debug", `启用提醒消息(保留原消息): ${reminderMessage.substring(0, 50)}...`);
|
|
208
225
|
} else if (hasRealMessageParam) {
|
|
209
226
|
reminderEnabled = true;
|
|
210
227
|
if (cleanedOptions.message !== void 0) {
|
|
211
228
|
reminderMessage = cleanedOptions.message;
|
|
212
|
-
|
|
229
|
+
clog("debug", `设置自定义提醒消息: ${reminderMessage.substring(0, 50)}...`);
|
|
213
230
|
}
|
|
214
231
|
}
|
|
215
232
|
return { reminderEnabled, reminderMessage };
|
|
@@ -233,7 +250,7 @@ async function syncTotalStats(ctx) {
|
|
|
233
250
|
totalJoined,
|
|
234
251
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
235
252
|
});
|
|
236
|
-
|
|
253
|
+
clog("debug", `总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}, 入群${totalJoined}`);
|
|
237
254
|
}
|
|
238
255
|
} catch (error) {
|
|
239
256
|
logger.error("同步总计统计时出错:", error);
|
|
@@ -330,46 +347,49 @@ async function checkPermission(session, targetGroupId) {
|
|
|
330
347
|
if (!groupId) {
|
|
331
348
|
return [false, "请在群聊中使用此命令或使用 -i 参数指定群号"];
|
|
332
349
|
}
|
|
333
|
-
logger.debug(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`);
|
|
334
350
|
const koishiAuthority = session.author?.authority || session.user?.authority;
|
|
335
|
-
|
|
351
|
+
clogV(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`);
|
|
352
|
+
clogV(`权限检查 - Koishi权限等级: ${koishiAuthority || "未获取到"}`);
|
|
336
353
|
if (!session.author) {
|
|
337
|
-
|
|
354
|
+
clogV(`权限检查 - session中可能的权限字段:`, {
|
|
338
355
|
authority: session.authority,
|
|
339
356
|
permission: session.permission,
|
|
340
357
|
role: session.role
|
|
341
358
|
});
|
|
342
359
|
} else {
|
|
343
|
-
|
|
360
|
+
clogV(`权限检查 - author对象中的字段:`, {
|
|
344
361
|
permission: session.author.permission,
|
|
345
362
|
role: session.author.role,
|
|
346
363
|
permissions: session.author.permissions
|
|
347
364
|
});
|
|
348
365
|
}
|
|
349
366
|
if (session.user) {
|
|
350
|
-
|
|
367
|
+
clogV(`权限检查 - user对象中的权限信息:`, {
|
|
351
368
|
authority: session.user.authority,
|
|
352
369
|
permission: session.user.permission,
|
|
353
370
|
role: session.user.role
|
|
354
371
|
});
|
|
355
372
|
}
|
|
356
373
|
if (koishiAuthority && koishiAuthority >= 3) {
|
|
357
|
-
|
|
374
|
+
clogV(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`);
|
|
375
|
+
clog("debug", `权限通过(koishi): uid=${session.userId} authority=${koishiAuthority}`);
|
|
358
376
|
return [true];
|
|
359
377
|
}
|
|
360
378
|
try {
|
|
361
379
|
const member = await session.bot.getGuildMember(groupId, session.userId);
|
|
362
|
-
|
|
380
|
+
clogV(`权限检查 - 获取到成员信息:`, {
|
|
363
381
|
roles: member?.roles,
|
|
364
382
|
permissions: member?.permissions
|
|
365
383
|
});
|
|
366
384
|
if (member) {
|
|
367
385
|
if (member.permissions?.includes("OWNER") || member.roles?.includes("owner")) {
|
|
368
|
-
|
|
386
|
+
clogV(`权限检查 - 用户是群主`);
|
|
387
|
+
clog("debug", `权限通过(群主): uid=${session.userId}`);
|
|
369
388
|
return [true];
|
|
370
389
|
}
|
|
371
390
|
if (member.roles?.includes("admin") || member.permissions?.includes("ADMINISTRATOR")) {
|
|
372
|
-
|
|
391
|
+
clogV(`权限检查 - 用户是管理员`);
|
|
392
|
+
clog("debug", `权限通过(管理员): uid=${session.userId}`);
|
|
373
393
|
return [true];
|
|
374
394
|
}
|
|
375
395
|
}
|
|
@@ -380,9 +400,10 @@ async function checkPermission(session, targetGroupId) {
|
|
|
380
400
|
}
|
|
381
401
|
__name(checkPermission, "checkPermission");
|
|
382
402
|
async function handleGuildMemberRequestEvent(ctx, session) {
|
|
383
|
-
|
|
403
|
+
clogV("guild-member-request event", session);
|
|
384
404
|
let guildId = (session.guildId || session.channelId || "").toString().trim();
|
|
385
405
|
const userId = session.userId;
|
|
406
|
+
clog("debug", `guild-member-request: guild=${guildId} user=${userId}`);
|
|
386
407
|
const message = session.content || "";
|
|
387
408
|
if (!guildId) {
|
|
388
409
|
logger.warn("guild-member-request 没有 guildId,跳过处理");
|
|
@@ -393,7 +414,7 @@ async function handleGuildMemberRequestEvent(ctx, session) {
|
|
|
393
414
|
if (!groupConfig || groupConfig.length === 0) return;
|
|
394
415
|
const config = groupConfig[0];
|
|
395
416
|
if (config.reviewMethod === 3) {
|
|
396
|
-
|
|
417
|
+
clog("info", `配置要求全部拒绝,自动拒绝用户 ${userId}`);
|
|
397
418
|
if (requestId) {
|
|
398
419
|
try {
|
|
399
420
|
await session.bot.handleGuildMemberRequest(requestId, false);
|
|
@@ -407,7 +428,7 @@ async function handleGuildMemberRequestEvent(ctx, session) {
|
|
|
407
428
|
try {
|
|
408
429
|
const blacklisted = await isUserBlacklisted(ctx, guildId, userId);
|
|
409
430
|
if (blacklisted) {
|
|
410
|
-
|
|
431
|
+
clog("info", `用户 ${userId} 在群 ${guildId} 或全局黑名单中,自动拒绝申请`);
|
|
411
432
|
if (requestId) {
|
|
412
433
|
try {
|
|
413
434
|
await session.bot.handleGuildMemberRequest(requestId, false);
|
|
@@ -422,12 +443,13 @@ async function handleGuildMemberRequestEvent(ctx, session) {
|
|
|
422
443
|
logger.warn("黑名单检查失败", e);
|
|
423
444
|
}
|
|
424
445
|
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session);
|
|
425
|
-
|
|
446
|
+
clogV(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
|
|
426
447
|
if (isValid) {
|
|
427
448
|
if (requestId) {
|
|
428
449
|
try {
|
|
429
450
|
await session.bot.handleGuildMemberRequest(requestId, true);
|
|
430
|
-
|
|
451
|
+
clogV(`自动同意 requestId=${requestId}`);
|
|
452
|
+
await updateStats(ctx, guildId, "autoApproved");
|
|
431
453
|
if (!autoQueue.has(guildId)) autoQueue.set(guildId, /* @__PURE__ */ new Set());
|
|
432
454
|
autoQueue.get(guildId).add(userId);
|
|
433
455
|
} catch (e) {
|
|
@@ -567,7 +589,11 @@ async function verifyApplication(config, message, session) {
|
|
|
567
589
|
isValid = false;
|
|
568
590
|
requiredThreshold = "null";
|
|
569
591
|
}
|
|
570
|
-
|
|
592
|
+
clog(
|
|
593
|
+
"debug",
|
|
594
|
+
`verify: matched=${matchedCount}/${requiredThreshold} valid=${isValid}`,
|
|
595
|
+
`verifyApplication msg="${message}" keywords=${JSON.stringify(config.keywords)} matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`
|
|
596
|
+
);
|
|
571
597
|
return { isValid, matchedCount, requiredThreshold };
|
|
572
598
|
}
|
|
573
599
|
__name(verifyApplication, "verifyApplication");
|
|
@@ -580,7 +606,11 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
|
|
|
580
606
|
}
|
|
581
607
|
const username = session.username || "未知用户";
|
|
582
608
|
const message = session.content || "";
|
|
583
|
-
|
|
609
|
+
clog(
|
|
610
|
+
"debug",
|
|
611
|
+
`待审核: guild=${guildId} user=${userId} matched=${matchedCount}/${requiredThreshold}`,
|
|
612
|
+
`处理失败验证 guild=${guildId} user=${userId} msg="${session.content || ""}" matched=${matchedCount} threshold=${requiredThreshold}`
|
|
613
|
+
);
|
|
584
614
|
if (matchedCount === void 0 || requiredThreshold === void 0) {
|
|
585
615
|
const result = await verifyApplication(config, message, session);
|
|
586
616
|
matchedCount = result.matchedCount;
|
|
@@ -603,7 +633,7 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
|
|
|
603
633
|
applyTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
604
634
|
});
|
|
605
635
|
if (!config.reminderEnabled || !config.reminderMessage || config.reminderMessage === "") {
|
|
606
|
-
|
|
636
|
+
clog("debug", `群 ${guildId} 的提醒消息已被禁用,跳过发送`);
|
|
607
637
|
return;
|
|
608
638
|
}
|
|
609
639
|
let reminderMsg = config.reminderMessage.replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
|
|
@@ -611,7 +641,7 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
|
|
|
611
641
|
const rawChannel = (session.channelId || "").toString().trim();
|
|
612
642
|
const channel = rawChannel || guildId;
|
|
613
643
|
const target = rawChannel ? [channel, guildId] : guildId;
|
|
614
|
-
|
|
644
|
+
clog("debug", `broadcast target: channel=${channel} guild=${guildId}`);
|
|
615
645
|
if (session.bot && typeof session.bot.broadcast === "function") {
|
|
616
646
|
try {
|
|
617
647
|
await session.bot.broadcast([target], reminderMsg);
|
|
@@ -620,14 +650,14 @@ async function handleFailedVerification(ctx, session, config, matchedCount, requ
|
|
|
620
650
|
if (typeof ctx.broadcast === "function") {
|
|
621
651
|
await ctx.broadcast([target], reminderMsg);
|
|
622
652
|
} else {
|
|
623
|
-
|
|
653
|
+
clog("info", "ctx.broadcast 不可用,跳过发送");
|
|
624
654
|
}
|
|
625
655
|
}
|
|
626
656
|
} else {
|
|
627
657
|
if (typeof ctx.broadcast === "function") {
|
|
628
658
|
await ctx.broadcast([target], reminderMsg);
|
|
629
659
|
} else {
|
|
630
|
-
|
|
660
|
+
clog("info", "ctx.broadcast 不可用,跳过发送");
|
|
631
661
|
}
|
|
632
662
|
}
|
|
633
663
|
}
|
|
@@ -711,7 +741,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
|
|
|
711
741
|
if (session.bot && typeof session.bot.kickGuildMember === "function") {
|
|
712
742
|
try {
|
|
713
743
|
await session.bot.kickGuildMember(group, targetUser);
|
|
714
|
-
|
|
744
|
+
clog("info", `已将黑名单用户 ${targetUser} 从群 ${group} 踢出`);
|
|
715
745
|
} catch (e) {
|
|
716
746
|
logger.warn(`踢出用户 ${targetUser} 失败`, e);
|
|
717
747
|
}
|
|
@@ -863,11 +893,12 @@ function apply(ctx, config) {
|
|
|
863
893
|
autoInc: true
|
|
864
894
|
});
|
|
865
895
|
logger = ctx.logger("group-verification");
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
896
|
+
pluginLogLevel = { debug: "详细", info: "中等", warn: "简洁" }[config?.logLevel ?? "info"] ?? "中等";
|
|
897
|
+
if (pluginLogLevel === "详细") logger.level = 3;
|
|
898
|
+
clog("info", "群组验证插件已启动");
|
|
899
|
+
clog("info", `默认提醒消息: ${config.defaultReminderMessage}`);
|
|
900
|
+
clog("info", `严格群号检查: ${config.enableStrictGroupCheck ? "启用" : "禁用"}`);
|
|
901
|
+
clog("info", `日志详细度: ${config.logLevel ?? "中等"}`);
|
|
871
902
|
ctx.model.extend("group_verification_stats", {
|
|
872
903
|
id: "unsigned",
|
|
873
904
|
groupId: "string",
|
|
@@ -906,54 +937,36 @@ function apply(ctx, config) {
|
|
|
906
937
|
indexes: [["groupId"]]
|
|
907
938
|
});
|
|
908
939
|
ctx.on("guild-member-request", async (session) => {
|
|
909
|
-
|
|
940
|
+
clog("debug", "收到 guild-member-request 事件,转发给处理函数");
|
|
910
941
|
await handleGuildMemberRequestEvent(ctx, session);
|
|
911
942
|
});
|
|
912
943
|
ctx.on("guild-member-added", async (session) => {
|
|
913
944
|
const groupId = session.guildId || "";
|
|
914
945
|
const userId = session.userId || "";
|
|
915
|
-
if (!groupId || !userId)
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
946
|
+
if (!groupId || !userId) return;
|
|
918
947
|
await incrementTotal(ctx, groupId);
|
|
919
|
-
const
|
|
920
|
-
if (
|
|
921
|
-
|
|
922
|
-
set.delete(userId);
|
|
923
|
-
logger.info(`用户 ${userId} 通过机器人审批加入群 ${groupId}(autoQueue),统计已更新`);
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
const pendingRecords = await ctx.database.get("group_verification_pending", {
|
|
927
|
-
groupId,
|
|
928
|
-
userId
|
|
929
|
-
});
|
|
930
|
-
if (pendingRecords.length > 0) {
|
|
931
|
-
await updateStats(ctx, groupId, "autoApproved");
|
|
932
|
-
for (const rec of pendingRecords) {
|
|
933
|
-
await ctx.database.remove("group_verification_pending", { id: rec.id });
|
|
934
|
-
}
|
|
935
|
-
logger.info(`用户 ${userId} 通过验证加入群 ${groupId},已清理 ${pendingRecords.length} 条待审核记录,统计已更新`);
|
|
936
|
-
} else {
|
|
937
|
-
await updateStats(ctx, groupId, "manuallyApproved");
|
|
938
|
-
logger.info(`用户 ${userId} 被手动邀请加入群 ${groupId},手动批准统计已更新`);
|
|
948
|
+
const autoSet = autoQueue.get(groupId);
|
|
949
|
+
if (autoSet && autoSet.has(userId)) {
|
|
950
|
+
autoSet.delete(userId);
|
|
939
951
|
}
|
|
952
|
+
await ctx.database.remove("group_verification_pending", { groupId, userId });
|
|
940
953
|
});
|
|
941
954
|
async function checkPermission2(session, targetGroupId) {
|
|
942
955
|
const groupId = targetGroupId || session.guildId;
|
|
943
956
|
if (!groupId) {
|
|
944
957
|
return [false, config.invalidGroupMessage || "请在群聊中使用此命令或使用 -i 参数指定群号"];
|
|
945
958
|
}
|
|
946
|
-
logger.debug(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`);
|
|
947
959
|
const koishiAuthority = session.author?.authority || session.user?.authority;
|
|
948
|
-
|
|
960
|
+
clogV(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`);
|
|
961
|
+
clogV(`权限检查 - Koishi权限等级: ${koishiAuthority || "未获取到"}`);
|
|
949
962
|
if (!session.author) {
|
|
950
|
-
|
|
963
|
+
clogV(`权限检查 - session中可能的权限字段:`, {
|
|
951
964
|
authority: session.authority,
|
|
952
965
|
permission: session.permission,
|
|
953
966
|
role: session.role
|
|
954
967
|
});
|
|
955
968
|
} else {
|
|
956
|
-
|
|
969
|
+
clogV(`权限检查 - author对象中的字段:`, {
|
|
957
970
|
authority: session.author.authority,
|
|
958
971
|
permission: session.author.permission,
|
|
959
972
|
role: session.author.role,
|
|
@@ -961,29 +974,32 @@ function apply(ctx, config) {
|
|
|
961
974
|
});
|
|
962
975
|
}
|
|
963
976
|
if (session.user) {
|
|
964
|
-
|
|
977
|
+
clogV(`权限检查 - user对象中的权限信息:`, {
|
|
965
978
|
authority: session.user.authority,
|
|
966
979
|
permission: session.user.permission,
|
|
967
980
|
role: session.user.role
|
|
968
981
|
});
|
|
969
982
|
}
|
|
970
983
|
if (koishiAuthority && koishiAuthority >= 3) {
|
|
971
|
-
|
|
984
|
+
clogV(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`);
|
|
985
|
+
clog("debug", `权限通过(koishi): uid=${session.userId} authority=${koishiAuthority}`);
|
|
972
986
|
return [true];
|
|
973
987
|
}
|
|
974
988
|
try {
|
|
975
989
|
const member = await session.bot.getGuildMember(groupId, session.userId);
|
|
976
|
-
|
|
990
|
+
clogV(`权限检查 - 获取到成员信息:`, {
|
|
977
991
|
roles: member?.roles,
|
|
978
992
|
permissions: member?.permissions
|
|
979
993
|
});
|
|
980
994
|
if (member) {
|
|
981
995
|
if (member.permissions?.includes("OWNER") || member.roles?.includes("owner")) {
|
|
982
|
-
|
|
996
|
+
clogV(`权限检查 - 用户是群主`);
|
|
997
|
+
clog("debug", `权限通过(群主): uid=${session.userId}`);
|
|
983
998
|
return [true];
|
|
984
999
|
}
|
|
985
1000
|
if (member.roles?.includes("admin") || member.permissions?.includes("ADMINISTRATOR")) {
|
|
986
|
-
|
|
1001
|
+
clogV(`权限检查 - 用户是管理员`);
|
|
1002
|
+
clog("debug", `权限通过(管理员): uid=${session.userId}`);
|
|
987
1003
|
return [true];
|
|
988
1004
|
}
|
|
989
1005
|
}
|
|
@@ -991,10 +1007,8 @@ function apply(ctx, config) {
|
|
|
991
1007
|
logger.warn(`权限检查 - 获取群成员信息失败:`, error);
|
|
992
1008
|
return [false, `无法获取群 ${groupId} 的成员信息,请确认机器人已在该群中`];
|
|
993
1009
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
return [false, (config.permissionDeniedMessage || "权限不足: 需要群主/管理员权限或koishi三级以上权限") + `
|
|
997
|
-
${debugInfo}`];
|
|
1010
|
+
clogV(`权限检查 - 权限不足`);
|
|
1011
|
+
return [false, config.permissionDeniedMessage || "权限不足: 需要群主/管理员权限或koishi三级以上权限"];
|
|
998
1012
|
}
|
|
999
1013
|
__name(checkPermission2, "checkPermission");
|
|
1000
1014
|
const groupVerify = ctx.command("group-verify", "群组验证管理命令").alias("gv", "gverify");
|
|
@@ -1007,16 +1021,17 @@ ${debugInfo}`];
|
|
|
1007
1021
|
"group-verify.配置",
|
|
1008
1022
|
"gvc"
|
|
1009
1023
|
).option("groupId", "-i <groupId> 指定群号").option("method", "-m <method> 审核方式 (0-3)").option("threshold", "-t <threshold> 阈值参数").option("message", "-msg [message] 自定义提醒消息").option("disableMessage", "-nomsg 禁用提醒消息").option("query", "-? 查询当前配置").option("remove", "-r 删除配置").action(async ({ session, options }, keywords) => {
|
|
1010
|
-
|
|
1011
|
-
|
|
1024
|
+
clogV(`=== 命令解析调试 ===`);
|
|
1025
|
+
clogV(`session内容: guildId=${session.guildId}, userId=${session.userId}`);
|
|
1012
1026
|
const rawInput = session.content.split(/\s+/).slice(1).join(" ");
|
|
1013
|
-
|
|
1027
|
+
clogV(`原始命令参数: "${rawInput}"`);
|
|
1028
|
+
if (pluginLogLevel !== "详细") clog("debug", `gvc: uid=${session.userId} guild=${session.guildId} args="${rawInput}"`);
|
|
1014
1029
|
const parsed = parseConfigArgs(rawInput);
|
|
1015
1030
|
const { keywords: parsedKeywords, flags, error: parseError } = parsed;
|
|
1016
1031
|
if (parseError) {
|
|
1017
1032
|
return parseError;
|
|
1018
1033
|
}
|
|
1019
|
-
|
|
1034
|
+
clog("debug", `解析结果 flags=${JSON.stringify(flags)}, keywords=[${parsedKeywords.join(", ")}]`);
|
|
1020
1035
|
const cleanedOptions = {
|
|
1021
1036
|
groupId: flags.groupId || options.groupId,
|
|
1022
1037
|
method: flags.method || (options.method === "" ? void 0 : options.method),
|
|
@@ -1028,7 +1043,8 @@ ${debugInfo}`];
|
|
|
1028
1043
|
query: flags.query || options.query,
|
|
1029
1044
|
remove: flags.remove || options.remove
|
|
1030
1045
|
};
|
|
1031
|
-
|
|
1046
|
+
clogV(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}
|
|
1047
|
+
`);
|
|
1032
1048
|
if ((cleanedOptions.query || cleanedOptions.remove) && (parsedKeywords.length > 0 || cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || cleanedOptions.message !== void 0 || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
|
|
1033
1049
|
return config.parameterConflictMessage || "参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)";
|
|
1034
1050
|
}
|
|
@@ -1077,7 +1093,7 @@ ${debugInfo}`];
|
|
|
1077
1093
|
const existingConfig2 = await ctx.database.get("group_verification_config", { groupId: targetGroupId });
|
|
1078
1094
|
if (existingConfig2.length > 0) {
|
|
1079
1095
|
await ctx.database.remove("group_verification_config", { id: existingConfig2[0].id });
|
|
1080
|
-
|
|
1096
|
+
clog("info", `配置删除 - 用户ID:${session.userId}, 群号:${targetGroupId}`);
|
|
1081
1097
|
return `已删除群 ${targetGroupId} 的验证配置`;
|
|
1082
1098
|
} else {
|
|
1083
1099
|
return `群 ${targetGroupId} 无验证配置`;
|
|
@@ -1130,7 +1146,7 @@ ${displayMsg}
|
|
|
1130
1146
|
}
|
|
1131
1147
|
}
|
|
1132
1148
|
let keywordList = parsedKeywords.slice();
|
|
1133
|
-
|
|
1149
|
+
clogV(`关键词解析结果: [${keywordList.join(", ")}] - 原始输入: "${rawInput}"`);
|
|
1134
1150
|
if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
|
|
1135
1151
|
const hasConfigParams = cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || hasRealMessageParam || hasRealEnableMessageParam || hasRealDisableMessageParam;
|
|
1136
1152
|
if (!hasConfigParams) {
|
|
@@ -1163,7 +1179,7 @@ ${displayMsg}
|
|
|
1163
1179
|
reviewMethod = existingConfig.reviewMethod;
|
|
1164
1180
|
if (existingConfig.reviewParameters === void 0 || existingConfig.reviewParameters === null || isNaN(existingConfig.reviewParameters)) {
|
|
1165
1181
|
reviewParameters = 0;
|
|
1166
|
-
|
|
1182
|
+
clog("debug", `检测到老版本数据或无效值,使用默认阈值: 0`);
|
|
1167
1183
|
} else {
|
|
1168
1184
|
reviewParameters = existingConfig.reviewParameters;
|
|
1169
1185
|
}
|
|
@@ -1175,10 +1191,10 @@ ${displayMsg}
|
|
|
1175
1191
|
}
|
|
1176
1192
|
const oldMethod = reviewMethod;
|
|
1177
1193
|
reviewMethod = methodNum;
|
|
1178
|
-
|
|
1194
|
+
clog("debug", `审核方式明确指定为: ${reviewMethod}`);
|
|
1179
1195
|
var methodChanged = oldMethod !== reviewMethod;
|
|
1180
1196
|
} else {
|
|
1181
|
-
|
|
1197
|
+
clog("debug", `未指定审核方式,保持原有值: ${reviewMethod}`);
|
|
1182
1198
|
var methodChanged = false;
|
|
1183
1199
|
}
|
|
1184
1200
|
const thresholdResult = resolveThreshold(
|
|
@@ -1198,7 +1214,7 @@ ${displayMsg}
|
|
|
1198
1214
|
updatedBy: session.username || session.userId,
|
|
1199
1215
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1200
1216
|
});
|
|
1201
|
-
|
|
1217
|
+
clog("debug", `自动调整并更新数据库阈值为: ${reviewParameters}`);
|
|
1202
1218
|
}
|
|
1203
1219
|
let autoAdjustNote = "";
|
|
1204
1220
|
if (thresholdResult.autoInfo === "methodChange") {
|
|
@@ -1216,7 +1232,6 @@ ${displayMsg}
|
|
|
1216
1232
|
const encodedKeywords = keywordList.map((keyword) => {
|
|
1217
1233
|
return keyword.replace(/,/g, "[[COMMA]]");
|
|
1218
1234
|
});
|
|
1219
|
-
logger.debug(`编码后准备存储的关键词: ${JSON.stringify(encodedKeywords)}`);
|
|
1220
1235
|
const dbData = {
|
|
1221
1236
|
keywords: encodedKeywords,
|
|
1222
1237
|
reviewMethod,
|
|
@@ -1232,7 +1247,7 @@ ${displayMsg}
|
|
|
1232
1247
|
...dbData,
|
|
1233
1248
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1234
1249
|
});
|
|
1235
|
-
|
|
1250
|
+
clog("info", `更新配置成功 - 审核方式: ${reviewMethod}, 阈值: ${reviewParameters}`);
|
|
1236
1251
|
} else {
|
|
1237
1252
|
await ctx.database.create("group_verification_config", {
|
|
1238
1253
|
groupId: targetGroupId,
|
|
@@ -1240,7 +1255,7 @@ ${displayMsg}
|
|
|
1240
1255
|
createdBy: session.username || session.userId,
|
|
1241
1256
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1242
1257
|
});
|
|
1243
|
-
|
|
1258
|
+
clog("info", `创建配置成功 - 审核方式: ${reviewMethod}, 阈值: ${reviewParameters}`);
|
|
1244
1259
|
}
|
|
1245
1260
|
const decodedKeywords = keywordList;
|
|
1246
1261
|
let feedbackMessage = `群 ${targetGroupId} 配置已更新:
|
|
@@ -1265,21 +1280,7 @@ ${displayMsg}
|
|
|
1265
1280
|
${displayReminder}
|
|
1266
1281
|
`;
|
|
1267
1282
|
}
|
|
1268
|
-
|
|
1269
|
-
logger.debug(`=== 配置处理详情 ===`);
|
|
1270
|
-
logger.debug(`原始输入: ${keywords || "无关键词"}`);
|
|
1271
|
-
logger.debug(`审核方式: ${reviewMethod} (${["全部同意", "按数量", "按比例", "全部拒绝"][reviewMethod]})`);
|
|
1272
|
-
logger.debug(`阈值参数: ${JSON.stringify(reviewParameters)}`);
|
|
1273
|
-
logger.debug(`关键词列表: [${keywordList.map((k) => `"${k}"`).join(", ")}]`);
|
|
1274
|
-
logger.debug(`现有配置: ${existingConfig ? "存在" : "不存在"}`);
|
|
1275
|
-
if (existingConfig) {
|
|
1276
|
-
logger.debug(`原审核方式: ${existingConfig.reviewMethod}`);
|
|
1277
|
-
logger.debug(`原阈值: ${JSON.stringify(existingConfig.reviewParameters)}`);
|
|
1278
|
-
logger.debug(`原关键词数: ${existingConfig.keywords.length}`);
|
|
1279
|
-
logger.debug(`新关键词数: ${keywordList.length}`);
|
|
1280
|
-
}
|
|
1281
|
-
logger.debug(`==================`);
|
|
1282
|
-
logger.info(feedbackMessage.replace(/\n/g, "; "));
|
|
1283
|
+
clog("info", feedbackMessage.replace(/\n/g, "; "));
|
|
1283
1284
|
return feedbackMessage;
|
|
1284
1285
|
});
|
|
1285
1286
|
groupVerify.subcommand(".approve [userId]", "同意加群申请").alias(
|
|
@@ -1315,6 +1316,7 @@ ${displayReminder}
|
|
|
1315
1316
|
if (request2.requestId) {
|
|
1316
1317
|
try {
|
|
1317
1318
|
await session.bot.handleGuildMemberRequest(request2.requestId, true);
|
|
1319
|
+
await updateStats(ctx, groupId, "manuallyApproved");
|
|
1318
1320
|
approvedCount++;
|
|
1319
1321
|
} catch (error) {
|
|
1320
1322
|
logger.warn(`处理申请 ${request2.id} 时出错:`, error);
|
|
@@ -1342,6 +1344,7 @@ ${displayReminder}
|
|
|
1342
1344
|
const reply = `已同意用户 ${displayName} 的加群申请`;
|
|
1343
1345
|
session.bot.handleGuildMemberRequest(request2.requestId, true).catch((e) => logger.warn("自动同意失败", e));
|
|
1344
1346
|
await ctx.database.remove("group_verification_pending", { groupId, userId: request2.userId });
|
|
1347
|
+
await updateStats(ctx, groupId, "manuallyApproved");
|
|
1345
1348
|
return reply;
|
|
1346
1349
|
} catch (error) {
|
|
1347
1350
|
return `处理申请时出错: ${error.message}`;
|
|
@@ -1364,6 +1367,7 @@ ${displayReminder}
|
|
|
1364
1367
|
const reply = `已同意用户 ${displayName} 的加群申请`;
|
|
1365
1368
|
session.bot.handleGuildMemberRequest(request.requestId, true).catch((e) => logger.warn("自动同意失败", e));
|
|
1366
1369
|
await ctx.database.remove("group_verification_pending", { groupId, userId });
|
|
1370
|
+
await updateStats(ctx, groupId, "manuallyApproved");
|
|
1367
1371
|
return reply;
|
|
1368
1372
|
} catch (error) {
|
|
1369
1373
|
return `处理申请时出错: ${error.message}`;
|
|
@@ -1631,7 +1635,7 @@ ${displayReminder}
|
|
|
1631
1635
|
await ctx.database.set("group_verification_pending", { id: p.id }, updates);
|
|
1632
1636
|
}
|
|
1633
1637
|
}
|
|
1634
|
-
|
|
1638
|
+
clog("info", "旧版日期字段迁移完成");
|
|
1635
1639
|
} catch (e) {
|
|
1636
1640
|
logger.warn("迁移旧日期字段时出错", e);
|
|
1637
1641
|
}
|
|
@@ -1644,9 +1648,9 @@ ${displayReminder}
|
|
|
1644
1648
|
rejected: 0,
|
|
1645
1649
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1646
1650
|
});
|
|
1647
|
-
|
|
1651
|
+
clog("info", "已创建总计统计行");
|
|
1648
1652
|
} else {
|
|
1649
|
-
|
|
1653
|
+
clog("info", "总计统计行已存在");
|
|
1650
1654
|
}
|
|
1651
1655
|
await syncTotalStats(ctx);
|
|
1652
1656
|
});
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,10 +1,40 @@
|
|
|
1
|
-
import { Context, Schema, Session } from 'koishi'
|
|
1
|
+
import { Context, Schema, Session } from 'koishi'
|
|
2
2
|
|
|
3
3
|
export const name = 'group-verification'
|
|
4
4
|
|
|
5
5
|
// 模块级日志器,测试时使用 console
|
|
6
6
|
let logger: any = console
|
|
7
7
|
|
|
8
|
+
// 当前日志详细度(由 apply() 在插件激活时写入)
|
|
9
|
+
let pluginLogLevel: '详细' | '中等' | '简洁' = '中等'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 三档日志输出:
|
|
13
|
+
* 简洁 — 只输出 warn/error
|
|
14
|
+
* 中等 — 输出所有级别的简短消息(mediumMsg)
|
|
15
|
+
* 详细 — 输出详细消息(verboseMsg,缺省时使用 mediumMsg)
|
|
16
|
+
*/
|
|
17
|
+
function clog(
|
|
18
|
+
lvl: 'debug' | 'info' | 'warn' | 'error',
|
|
19
|
+
mediumMsg: string,
|
|
20
|
+
verboseMsg?: string
|
|
21
|
+
): void {
|
|
22
|
+
if (pluginLogLevel === '简洁') {
|
|
23
|
+
if (lvl === 'warn' || lvl === 'error') logger[lvl](mediumMsg)
|
|
24
|
+
} else if (pluginLogLevel === '详细') {
|
|
25
|
+
logger[lvl](verboseMsg ?? mediumMsg)
|
|
26
|
+
} else {
|
|
27
|
+
// 中等模式: debug 级别提升为 info 输出,避免被 Koishi 框架过滤
|
|
28
|
+
const outputLvl = lvl === 'debug' ? 'info' : lvl
|
|
29
|
+
logger[outputLvl](mediumMsg)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** 仅在 详细 模式下输出 debug 日志(支持传入对象,与 Koishi logger.debug(msg, obj) 一致) */
|
|
34
|
+
function clogV(msg: string, ...args: any[]): void {
|
|
35
|
+
if (pluginLogLevel === '详细') logger.debug(msg, ...args)
|
|
36
|
+
}
|
|
37
|
+
|
|
8
38
|
// 数据库模型定义
|
|
9
39
|
declare module 'koishi' {
|
|
10
40
|
interface Tables {
|
|
@@ -65,7 +95,7 @@ export interface PendingVerification {
|
|
|
65
95
|
export interface Config {
|
|
66
96
|
defaultReminderMessage?: string
|
|
67
97
|
enableStrictGroupCheck?: boolean // 群号合法性检查配置
|
|
68
|
-
logLevel?: 'debug' | 'info' | 'warn'
|
|
98
|
+
logLevel?: 'debug' | 'info' | 'warn' // 日志详细程度
|
|
69
99
|
// 以下为可自定义的命令反馈提示词,可在插件管理界面调整
|
|
70
100
|
permissionDeniedMessage?: string
|
|
71
101
|
invalidGroupMessage?: string
|
|
@@ -83,7 +113,7 @@ export const Config: Schema<Config> = Schema.object({
|
|
|
83
113
|
.description('默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)')
|
|
84
114
|
.default('{user}({id}) 申请入群\\n申请理由: {question}\\n匹配情况: {answer}/{threshold}\\n使用 gva 同意或 gvr 拒绝申请'),
|
|
85
115
|
enableStrictGroupCheck: Schema.boolean().description('是否启用严格的群号检查(检查群号长度)').default(false),
|
|
86
|
-
logLevel: Schema.union(['debug', 'info', 'warn'
|
|
116
|
+
logLevel: Schema.union(['debug', 'info', 'warn']).description('日志详细程度').default('info'), // debug=详细, info=中等, warn=简洁
|
|
87
117
|
permissionDeniedMessage: Schema.string().description('权限不足时返回给调用者的提示').default('权限不足: 需要群主/管理员权限或koishi三级以上权限'),
|
|
88
118
|
invalidGroupMessage: Schema.string().description('无效群号或机器人未在该群时的提示').default('群号 {group} 格式不合法或机器人不在该群中'),
|
|
89
119
|
parameterConflictMessage: Schema.string().description('参数冲突时提示').default('参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'),
|
|
@@ -307,16 +337,16 @@ export function mergeReminder(
|
|
|
307
337
|
// 优先级: disable > bare enable > new message content
|
|
308
338
|
if (hasRealDisableMessageParam) {
|
|
309
339
|
reminderEnabled = false;
|
|
310
|
-
|
|
340
|
+
clog('debug', '禁用提醒消息功能 (保留现有内容)');
|
|
311
341
|
} else if (hasRealEnableMessageParam) {
|
|
312
342
|
reminderEnabled = true;
|
|
313
|
-
|
|
343
|
+
clog('debug', `启用提醒消息(保留原消息): ${reminderMessage.substring(0, 50)}...`);
|
|
314
344
|
} else if (hasRealMessageParam) {
|
|
315
345
|
reminderEnabled = true;
|
|
316
346
|
if (cleanedOptions.message !== undefined) {
|
|
317
347
|
// 直接存储原始内容(\n 作为换行占位符,渲染时再转换)
|
|
318
348
|
reminderMessage = cleanedOptions.message;
|
|
319
|
-
|
|
349
|
+
clog('debug', `设置自定义提醒消息: ${reminderMessage.substring(0, 50)}...`);
|
|
320
350
|
}
|
|
321
351
|
}
|
|
322
352
|
return { reminderEnabled, reminderMessage };
|
|
@@ -356,7 +386,7 @@ export async function syncTotalStats(ctx: Context) {
|
|
|
356
386
|
lastUpdated: new Date().toISOString()
|
|
357
387
|
})
|
|
358
388
|
|
|
359
|
-
|
|
389
|
+
clog('debug', `总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}, 入群${totalJoined}`)
|
|
360
390
|
}
|
|
361
391
|
} catch (error) {
|
|
362
392
|
logger.error('同步总计统计时出错:', error)
|
|
@@ -478,50 +508,50 @@ export async function checkPermission(session: any, targetGroupId?: string): Pro
|
|
|
478
508
|
return [false, '请在群聊中使用此命令或使用 -i 参数指定群号']
|
|
479
509
|
}
|
|
480
510
|
|
|
481
|
-
logger.debug(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`)
|
|
482
511
|
const koishiAuthority = session.author?.authority || session.user?.authority
|
|
483
|
-
|
|
484
|
-
|
|
512
|
+
clogV(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`)
|
|
513
|
+
clogV(`权限检查 - Koishi权限等级: ${koishiAuthority || '未获取到'}`)
|
|
485
514
|
if (!session.author) {
|
|
486
|
-
|
|
515
|
+
clogV(`权限检查 - session中可能的权限字段:`, {
|
|
487
516
|
authority: session.authority,
|
|
488
517
|
permission: session.permission,
|
|
489
518
|
role: session.role
|
|
490
519
|
})
|
|
491
520
|
} else {
|
|
492
|
-
|
|
521
|
+
clogV(`权限检查 - author对象中的字段:`, {
|
|
493
522
|
permission: session.author.permission,
|
|
494
523
|
role: session.author.role,
|
|
495
524
|
permissions: session.author.permissions
|
|
496
525
|
})
|
|
497
526
|
}
|
|
498
|
-
|
|
499
527
|
if (session.user) {
|
|
500
|
-
|
|
528
|
+
clogV(`权限检查 - user对象中的权限信息:`, {
|
|
501
529
|
authority: session.user.authority,
|
|
502
530
|
permission: session.user.permission,
|
|
503
531
|
role: session.user.role
|
|
504
532
|
})
|
|
505
533
|
}
|
|
506
|
-
|
|
507
534
|
if (koishiAuthority && koishiAuthority >= 3) {
|
|
508
|
-
|
|
535
|
+
clogV(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`)
|
|
536
|
+
clog('debug', `权限通过(koishi): uid=${session.userId} authority=${koishiAuthority}`)
|
|
509
537
|
return [true]
|
|
510
538
|
}
|
|
511
539
|
|
|
512
540
|
try {
|
|
513
541
|
const member = await session.bot.getGuildMember(groupId, session.userId)
|
|
514
|
-
|
|
542
|
+
clogV(`权限检查 - 获取到成员信息:`, {
|
|
515
543
|
roles: member?.roles,
|
|
516
544
|
permissions: member?.permissions
|
|
517
545
|
})
|
|
518
546
|
if (member) {
|
|
519
547
|
if (member.permissions?.includes('OWNER') || member.roles?.includes('owner')) {
|
|
520
|
-
|
|
548
|
+
clogV(`权限检查 - 用户是群主`)
|
|
549
|
+
clog('debug', `权限通过(群主): uid=${session.userId}`)
|
|
521
550
|
return [true]
|
|
522
551
|
}
|
|
523
552
|
if (member.roles?.includes('admin') || member.permissions?.includes('ADMINISTRATOR')) {
|
|
524
|
-
|
|
553
|
+
clogV(`权限检查 - 用户是管理员`)
|
|
554
|
+
clog('debug', `权限通过(管理员): uid=${session.userId}`)
|
|
525
555
|
return [true]
|
|
526
556
|
}
|
|
527
557
|
}
|
|
@@ -534,9 +564,10 @@ export async function checkPermission(session: any, targetGroupId?: string): Pro
|
|
|
534
564
|
|
|
535
565
|
// 提供给测试的辅助函数: 处理 guild-member-request 事件的逻辑
|
|
536
566
|
export async function handleGuildMemberRequestEvent(ctx: Context, session: any) {
|
|
537
|
-
|
|
567
|
+
clogV('guild-member-request event', session)
|
|
538
568
|
let guildId = (session.guildId || session.channelId || '').toString().trim();
|
|
539
569
|
const userId = session.userId;
|
|
570
|
+
clog('debug', `guild-member-request: guild=${guildId} user=${userId}`);
|
|
540
571
|
const message = session.content || '';
|
|
541
572
|
|
|
542
573
|
if (!guildId) {
|
|
@@ -550,7 +581,7 @@ export async function handleGuildMemberRequestEvent(ctx: Context, session: any)
|
|
|
550
581
|
const config = groupConfig[0];
|
|
551
582
|
|
|
552
583
|
if (config.reviewMethod === 3) {
|
|
553
|
-
|
|
584
|
+
clog('info', `配置要求全部拒绝,自动拒绝用户 ${userId}`);
|
|
554
585
|
if (requestId) {
|
|
555
586
|
try { await session.bot.handleGuildMemberRequest(requestId, false); } catch (e) { logger.warn('自动拒绝失败', e); }
|
|
556
587
|
}
|
|
@@ -562,7 +593,7 @@ export async function handleGuildMemberRequestEvent(ctx: Context, session: any)
|
|
|
562
593
|
try {
|
|
563
594
|
const blacklisted = await isUserBlacklisted(ctx, guildId, userId);
|
|
564
595
|
if (blacklisted) {
|
|
565
|
-
|
|
596
|
+
clog('info', `用户 ${userId} 在群 ${guildId} 或全局黑名单中,自动拒绝申请`);
|
|
566
597
|
if (requestId) {
|
|
567
598
|
try { await session.bot.handleGuildMemberRequest(requestId, false); } catch (e) { logger.warn('自动拒绝失败', e); }
|
|
568
599
|
}
|
|
@@ -574,13 +605,15 @@ export async function handleGuildMemberRequestEvent(ctx: Context, session: any)
|
|
|
574
605
|
}
|
|
575
606
|
|
|
576
607
|
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session);
|
|
577
|
-
|
|
608
|
+
clogV(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
|
|
578
609
|
|
|
579
610
|
if (isValid) {
|
|
580
611
|
if (requestId) {
|
|
581
612
|
try {
|
|
582
613
|
await session.bot.handleGuildMemberRequest(requestId, true);
|
|
583
|
-
|
|
614
|
+
clogV(`自动同意 requestId=${requestId}`);
|
|
615
|
+
// 自动批准成功,立即记录统计(不依赖 guild-member-added 事件)
|
|
616
|
+
await updateStats(ctx, guildId, 'autoApproved');
|
|
584
617
|
if (!autoQueue.has(guildId)) autoQueue.set(guildId, new Set());
|
|
585
618
|
autoQueue.get(guildId)!.add(userId);
|
|
586
619
|
} catch (e) {
|
|
@@ -750,7 +783,8 @@ export async function verifyApplication(config: GroupVerificationConfig, message
|
|
|
750
783
|
requiredThreshold = 'null'
|
|
751
784
|
}
|
|
752
785
|
|
|
753
|
-
|
|
786
|
+
clog('debug', `verify: matched=${matchedCount}/${requiredThreshold} valid=${isValid}`,
|
|
787
|
+
`verifyApplication msg="${message}" keywords=${JSON.stringify(config.keywords)} matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`)
|
|
754
788
|
return { isValid, matchedCount, requiredThreshold }
|
|
755
789
|
}
|
|
756
790
|
|
|
@@ -771,7 +805,8 @@ export async function handleFailedVerification(
|
|
|
771
805
|
}
|
|
772
806
|
const username = session.username || '未知用户'
|
|
773
807
|
const message = session.content || ''
|
|
774
|
-
|
|
808
|
+
clog('debug', `待审核: guild=${guildId} user=${userId} matched=${matchedCount}/${requiredThreshold}`,
|
|
809
|
+
`处理失败验证 guild=${guildId} user=${userId} msg="${session.content || ''}" matched=${matchedCount} threshold=${requiredThreshold}`)
|
|
775
810
|
// 如果未传入匹配信息,则重新计算一次(老调用)
|
|
776
811
|
if (matchedCount === undefined || requiredThreshold === undefined) {
|
|
777
812
|
const result = await verifyApplication(config, message, session)
|
|
@@ -805,7 +840,7 @@ export async function handleFailedVerification(
|
|
|
805
840
|
})
|
|
806
841
|
// 如果提醒消息被禁用,直接返回
|
|
807
842
|
if (!config.reminderEnabled || !config.reminderMessage || config.reminderMessage === '') {
|
|
808
|
-
|
|
843
|
+
clog('debug', `群 ${guildId} 的提醒消息已被禁用,跳过发送`)
|
|
809
844
|
return
|
|
810
845
|
}
|
|
811
846
|
|
|
@@ -829,7 +864,7 @@ export async function handleFailedVerification(
|
|
|
829
864
|
const rawChannel = (session.channelId || '').toString().trim()
|
|
830
865
|
const channel = rawChannel || guildId
|
|
831
866
|
const target: string | [string, string] = rawChannel ? [channel, guildId] : guildId
|
|
832
|
-
|
|
867
|
+
clog('debug', `broadcast target: channel=${channel} guild=${guildId}`)
|
|
833
868
|
// 优先使用 bot.broadcast,ctx.broadcast 可能不支持元组格式
|
|
834
869
|
if (session.bot && typeof session.bot.broadcast === 'function') {
|
|
835
870
|
try {
|
|
@@ -840,14 +875,14 @@ export async function handleFailedVerification(
|
|
|
840
875
|
if (typeof (ctx.broadcast) === 'function') {
|
|
841
876
|
await (ctx.broadcast as any)([target], reminderMsg)
|
|
842
877
|
} else {
|
|
843
|
-
|
|
878
|
+
clog('info', 'ctx.broadcast 不可用,跳过发送')
|
|
844
879
|
}
|
|
845
880
|
}
|
|
846
881
|
} else {
|
|
847
882
|
if (typeof (ctx.broadcast) === 'function') {
|
|
848
883
|
await (ctx.broadcast as any)([target], reminderMsg)
|
|
849
884
|
} else {
|
|
850
|
-
|
|
885
|
+
clog('info', 'ctx.broadcast 不可用,跳过发送')
|
|
851
886
|
}
|
|
852
887
|
}
|
|
853
888
|
}
|
|
@@ -948,7 +983,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
|
|
|
948
983
|
if (session.bot && typeof session.bot.kickGuildMember === 'function') {
|
|
949
984
|
try {
|
|
950
985
|
await session.bot.kickGuildMember(group, targetUser)
|
|
951
|
-
|
|
986
|
+
clog('info', `已将黑名单用户 ${targetUser} 从群 ${group} 踢出`)
|
|
952
987
|
} catch (e) {
|
|
953
988
|
logger.warn(`踢出用户 ${targetUser} 失败`, e)
|
|
954
989
|
}
|
|
@@ -1108,17 +1143,16 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1108
1143
|
|
|
1109
1144
|
// 获取logger实例并保存到模块级变量
|
|
1110
1145
|
logger = ctx.logger('group-verification')
|
|
1111
|
-
//
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
// 注意: Koishi logger的level设置可能需要不同的方式
|
|
1146
|
+
// 根据配置设置日志详细度(config 理论上已通过 Schema 校验,此处加防护)
|
|
1147
|
+
pluginLogLevel = ({ debug: '详细', info: '中等', warn: '简洁' }[config?.logLevel ?? 'info'] ?? '中等') as '详细' | '中等' | '简洁'
|
|
1148
|
+
// debug 模式需要把 Koishi logger 的输出阈值提升到 3(debug),否则 [D] 被框架过滤
|
|
1149
|
+
if (pluginLogLevel === '详细') logger.level = 3
|
|
1116
1150
|
|
|
1117
1151
|
// 记录插件启动信息
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1152
|
+
clog('info', '群组验证插件已启动')
|
|
1153
|
+
clog('info', `默认提醒消息: ${config.defaultReminderMessage}`)
|
|
1154
|
+
clog('info', `严格群号检查: ${config.enableStrictGroupCheck ? '启用' : '禁用'}`)
|
|
1155
|
+
clog('info', `日志详细度: ${config.logLevel ?? '中等'}`)
|
|
1122
1156
|
|
|
1123
1157
|
ctx.model.extend('group_verification_stats', {
|
|
1124
1158
|
id: 'unsigned',
|
|
@@ -1166,7 +1200,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1166
1200
|
|
|
1167
1201
|
// 监听 guild-member-request 事件,以便对新申请执行自动审批或拒绝
|
|
1168
1202
|
ctx.on('guild-member-request', async (session) => {
|
|
1169
|
-
|
|
1203
|
+
clog('debug', '收到 guild-member-request 事件,转发给处理函数')
|
|
1170
1204
|
await handleGuildMemberRequestEvent(ctx, session)
|
|
1171
1205
|
})
|
|
1172
1206
|
|
|
@@ -1174,42 +1208,19 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1174
1208
|
ctx.on('guild-member-added', async (session: any) => {
|
|
1175
1209
|
const groupId = session.guildId || ''
|
|
1176
1210
|
const userId = session.userId || ''
|
|
1177
|
-
if (!groupId || !userId)
|
|
1178
|
-
// 无效会话,忽略
|
|
1179
|
-
return
|
|
1180
|
-
}
|
|
1211
|
+
if (!groupId || !userId) return
|
|
1181
1212
|
|
|
1182
|
-
//
|
|
1213
|
+
// 始终增加总入群计数(四列统计相互独立)
|
|
1183
1214
|
await incrementTotal(ctx, groupId)
|
|
1184
1215
|
|
|
1185
|
-
//
|
|
1186
|
-
const
|
|
1187
|
-
if (
|
|
1188
|
-
|
|
1189
|
-
set.delete(userId)
|
|
1190
|
-
logger.info(`用户 ${userId} 通过机器人审批加入群 ${groupId}(autoQueue),统计已更新`)
|
|
1191
|
-
return
|
|
1216
|
+
// 清理 autoQueue 中的缓存(统计已在自动批准时记录)
|
|
1217
|
+
const autoSet = autoQueue.get(groupId)
|
|
1218
|
+
if (autoSet && autoSet.has(userId)) {
|
|
1219
|
+
autoSet.delete(userId)
|
|
1192
1220
|
}
|
|
1193
1221
|
|
|
1194
|
-
//
|
|
1195
|
-
|
|
1196
|
-
groupId: groupId,
|
|
1197
|
-
userId: userId
|
|
1198
|
-
})
|
|
1199
|
-
|
|
1200
|
-
if (pendingRecords.length > 0) {
|
|
1201
|
-
// 通过验证的用户入群,更新统计
|
|
1202
|
-
await updateStats(ctx, groupId, 'autoApproved')
|
|
1203
|
-
// 清除所有该用户的待审核记录
|
|
1204
|
-
for (const rec of pendingRecords) {
|
|
1205
|
-
await ctx.database.remove('group_verification_pending', { id: rec.id })
|
|
1206
|
-
}
|
|
1207
|
-
logger.info(`用户 ${userId} 通过验证加入群 ${groupId},已清理 ${pendingRecords.length} 条待审核记录,统计已更新`)
|
|
1208
|
-
} else {
|
|
1209
|
-
// 手动邀请入群,记录到手动批准统计
|
|
1210
|
-
await updateStats(ctx, groupId, 'manuallyApproved')
|
|
1211
|
-
logger.info(`用户 ${userId} 被手动邀请加入群 ${groupId},手动批准统计已更新`)
|
|
1212
|
-
}
|
|
1222
|
+
// 清理残留的待审核记录(QQ 群管页面直接放行时 pending 记录不会被 gva 删除)
|
|
1223
|
+
await ctx.database.remove('group_verification_pending', { groupId, userId })
|
|
1213
1224
|
})
|
|
1214
1225
|
|
|
1215
1226
|
|
|
@@ -1225,62 +1236,55 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1225
1236
|
return [false, config.invalidGroupMessage || '请在群聊中使用此命令或使用 -i 参数指定群号']
|
|
1226
1237
|
}
|
|
1227
1238
|
|
|
1228
|
-
// 使用Koishi logger输出调试信息
|
|
1229
|
-
logger.debug(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`)
|
|
1230
|
-
|
|
1231
1239
|
// 检查koishi权限等级(最高优先级)
|
|
1232
1240
|
const koishiAuthority = session.author?.authority || session.user?.authority
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
// 尝试其他可能的权限字段
|
|
1241
|
+
clogV(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`)
|
|
1242
|
+
clogV(`权限检查 - Koishi权限等级: ${koishiAuthority || '未获取到'}`)
|
|
1236
1243
|
if (!session.author) {
|
|
1237
|
-
|
|
1244
|
+
clogV(`权限检查 - session中可能的权限字段:`, {
|
|
1238
1245
|
authority: session.authority,
|
|
1239
1246
|
permission: session.permission,
|
|
1240
1247
|
role: session.role
|
|
1241
1248
|
})
|
|
1242
1249
|
} else {
|
|
1243
|
-
|
|
1244
|
-
logger.debug(`权限检查 - author对象中的字段:`, {
|
|
1250
|
+
clogV(`权限检查 - author对象中的字段:`, {
|
|
1245
1251
|
authority: session.author.authority,
|
|
1246
1252
|
permission: session.author.permission,
|
|
1247
1253
|
role: session.author.role,
|
|
1248
1254
|
permissions: session.author.permissions
|
|
1249
1255
|
})
|
|
1250
1256
|
}
|
|
1251
|
-
|
|
1252
|
-
// 尝试从user对象获取权限信息
|
|
1253
1257
|
if (session.user) {
|
|
1254
|
-
|
|
1258
|
+
clogV(`权限检查 - user对象中的权限信息:`, {
|
|
1255
1259
|
authority: session.user.authority,
|
|
1256
1260
|
permission: session.user.permission,
|
|
1257
1261
|
role: session.user.role
|
|
1258
1262
|
})
|
|
1259
1263
|
}
|
|
1260
|
-
|
|
1261
|
-
// 先检查koishi权限等级(最高优先级)
|
|
1262
1264
|
if (koishiAuthority && koishiAuthority >= 3) {
|
|
1263
|
-
|
|
1265
|
+
clogV(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`)
|
|
1266
|
+
clog('debug', `权限通过(koishi): uid=${session.userId} authority=${koishiAuthority}`)
|
|
1264
1267
|
return [true]
|
|
1265
1268
|
}
|
|
1266
1269
|
|
|
1267
1270
|
// 再检查是否为群主或管理员(次优先级)
|
|
1268
1271
|
try {
|
|
1269
1272
|
const member = await session.bot.getGuildMember(groupId, session.userId)
|
|
1270
|
-
|
|
1273
|
+
clogV(`权限检查 - 获取到成员信息:`, {
|
|
1271
1274
|
roles: member?.roles,
|
|
1272
1275
|
permissions: member?.permissions
|
|
1273
1276
|
})
|
|
1274
|
-
|
|
1275
1277
|
if (member) {
|
|
1276
1278
|
// 检查群主权限
|
|
1277
1279
|
if (member.permissions?.includes('OWNER') || member.roles?.includes('owner')) {
|
|
1278
|
-
|
|
1280
|
+
clogV(`权限检查 - 用户是群主`)
|
|
1281
|
+
clog('debug', `权限通过(群主): uid=${session.userId}`)
|
|
1279
1282
|
return [true]
|
|
1280
1283
|
}
|
|
1281
1284
|
// 检查管理员权限
|
|
1282
1285
|
if (member.roles?.includes('admin') || member.permissions?.includes('ADMINISTRATOR')) {
|
|
1283
|
-
|
|
1286
|
+
clogV(`权限检查 - 用户是管理员`)
|
|
1287
|
+
clog('debug', `权限通过(管理员): uid=${session.userId}`)
|
|
1284
1288
|
return [true]
|
|
1285
1289
|
}
|
|
1286
1290
|
}
|
|
@@ -1289,9 +1293,8 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1289
1293
|
return [false, `无法获取群 ${groupId} 的成员信息,请确认机器人已在该群中`]
|
|
1290
1294
|
}
|
|
1291
1295
|
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
return [false, (config.permissionDeniedMessage || '权限不足: 需要群主/管理员权限或koishi三级以上权限') + `\n${debugInfo}`]
|
|
1296
|
+
clogV(`权限检查 - 权限不足`)
|
|
1297
|
+
return [false, config.permissionDeniedMessage || '权限不足: 需要群主/管理员权限或koishi三级以上权限']
|
|
1295
1298
|
}
|
|
1296
1299
|
|
|
1297
1300
|
// 创建主命令及别名
|
|
@@ -1314,13 +1317,14 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1314
1317
|
.option('query', '-? 查询当前配置')
|
|
1315
1318
|
.option('remove', '-r 删除配置')
|
|
1316
1319
|
.action(async ({ session, options }: any, keywords: any) => {
|
|
1317
|
-
//
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1320
|
+
// 详细模式复现 1.0.35 的三行解析调试块
|
|
1321
|
+
clogV(`=== 命令解析调试 ===`)
|
|
1322
|
+
clogV(`session内容: guildId=${session.guildId}, userId=${session.userId}`)
|
|
1321
1323
|
// 重新计算原始参数字符串(去掉命令本身)
|
|
1322
1324
|
const rawInput = session.content.split(/\s+/).slice(1).join(' ')
|
|
1323
|
-
|
|
1325
|
+
clogV(`原始命令参数: "${rawInput}"`)
|
|
1326
|
+
// 中等模式下输出简洁行
|
|
1327
|
+
if (pluginLogLevel !== '详细') clog('debug', `gvc: uid=${session.userId} guild=${session.guildId} args="${rawInput}"`)
|
|
1324
1328
|
|
|
1325
1329
|
// 使用自定义解析函数提取关键词和 flags
|
|
1326
1330
|
const parsed = parseConfigArgs(rawInput)
|
|
@@ -1331,7 +1335,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1331
1335
|
return parseError
|
|
1332
1336
|
}
|
|
1333
1337
|
|
|
1334
|
-
|
|
1338
|
+
clog('debug', `解析结果 flags=${JSON.stringify(flags)}, keywords=[${parsedKeywords.join(', ')}]`)
|
|
1335
1339
|
|
|
1336
1340
|
// 由 flags 和 Koishi options 合并最终选项
|
|
1337
1341
|
const cleanedOptions = {
|
|
@@ -1344,7 +1348,8 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1344
1348
|
query: flags.query || options.query,
|
|
1345
1349
|
remove: flags.remove || options.remove,
|
|
1346
1350
|
}
|
|
1347
|
-
|
|
1351
|
+
clogV(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}
|
|
1352
|
+
`)
|
|
1348
1353
|
|
|
1349
1354
|
// 检查 -? 和 -r 的独占性;允许与 -i 并存
|
|
1350
1355
|
if ((cleanedOptions.query || cleanedOptions.remove) &&
|
|
@@ -1417,7 +1422,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1417
1422
|
const existingConfig = await ctx.database.get('group_verification_config', { groupId: targetGroupId })
|
|
1418
1423
|
if (existingConfig.length > 0) {
|
|
1419
1424
|
await ctx.database.remove('group_verification_config', { id: existingConfig[0].id })
|
|
1420
|
-
|
|
1425
|
+
clog('info', `配置删除 - 用户ID:${session.userId}, 群号:${targetGroupId}`)
|
|
1421
1426
|
return `已删除群 ${targetGroupId} 的验证配置`
|
|
1422
1427
|
} else {
|
|
1423
1428
|
return `群 ${targetGroupId} 无验证配置`
|
|
@@ -1481,8 +1486,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1481
1486
|
|
|
1482
1487
|
// 关键词列表由之前解析得到的 parsedKeywords 构成
|
|
1483
1488
|
let keywordList: string[] = parsedKeywords.slice()
|
|
1484
|
-
|
|
1485
|
-
logger.debug(`关键词解析结果: [${keywordList.join(', ')}] - 原始输入: "${rawInput}"`)
|
|
1489
|
+
clogV(`关键词解析结果: [${keywordList.join(', ')}] - 原始输入: "${rawInput}"`)
|
|
1486
1490
|
|
|
1487
1491
|
// 如果没有关键词且不是查询/删除操作,则根据现有配置或报错
|
|
1488
1492
|
if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
|
|
@@ -1536,7 +1540,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1536
1540
|
existingConfig.reviewParameters === null ||
|
|
1537
1541
|
isNaN(existingConfig.reviewParameters)) {
|
|
1538
1542
|
reviewParameters = 0 // 默认值
|
|
1539
|
-
|
|
1543
|
+
clog('debug', `检测到老版本数据或无效值,使用默认阈值: 0`)
|
|
1540
1544
|
} else {
|
|
1541
1545
|
reviewParameters = existingConfig.reviewParameters
|
|
1542
1546
|
}
|
|
@@ -1550,10 +1554,10 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1550
1554
|
}
|
|
1551
1555
|
const oldMethod = reviewMethod
|
|
1552
1556
|
reviewMethod = methodNum as 0 | 1 | 2 | 3
|
|
1553
|
-
|
|
1557
|
+
clog('debug', `审核方式明确指定为: ${reviewMethod}`)
|
|
1554
1558
|
var methodChanged = oldMethod !== reviewMethod
|
|
1555
1559
|
} else {
|
|
1556
|
-
|
|
1560
|
+
clog('debug', `未指定审核方式,保持原有值: ${reviewMethod}`)
|
|
1557
1561
|
var methodChanged = false
|
|
1558
1562
|
}
|
|
1559
1563
|
|
|
@@ -1577,7 +1581,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1577
1581
|
updatedBy: session.username || session.userId,
|
|
1578
1582
|
updatedAt: new Date().toISOString()
|
|
1579
1583
|
})
|
|
1580
|
-
|
|
1584
|
+
clog('debug', `自动调整并更新数据库阈值为: ${reviewParameters}`)
|
|
1581
1585
|
}
|
|
1582
1586
|
|
|
1583
1587
|
// 自动调整说明,用于反馈消息
|
|
@@ -1599,8 +1603,6 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1599
1603
|
return keyword.replace(/,/g, '[[COMMA]]')
|
|
1600
1604
|
})
|
|
1601
1605
|
|
|
1602
|
-
logger.debug(`编码后准备存储的关键词: ${JSON.stringify(encodedKeywords)}`)
|
|
1603
|
-
|
|
1604
1606
|
// 保存配置到数据库(使用新的简单格式)
|
|
1605
1607
|
const dbData = {
|
|
1606
1608
|
keywords: encodedKeywords,
|
|
@@ -1618,7 +1620,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1618
1620
|
...dbData,
|
|
1619
1621
|
updatedAt: new Date().toISOString(),
|
|
1620
1622
|
})
|
|
1621
|
-
|
|
1623
|
+
clog('info', `更新配置成功 - 审核方式: ${reviewMethod}, 阈值: ${reviewParameters}`)
|
|
1622
1624
|
} else {
|
|
1623
1625
|
await ctx.database.create('group_verification_config', {
|
|
1624
1626
|
groupId: targetGroupId,
|
|
@@ -1626,7 +1628,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1626
1628
|
createdBy: session.username || session.userId,
|
|
1627
1629
|
createdAt: new Date().toISOString()
|
|
1628
1630
|
})
|
|
1629
|
-
|
|
1631
|
+
clog('info', `创建配置成功 - 审核方式: ${reviewMethod}, 阈值: ${reviewParameters}`)
|
|
1630
1632
|
}
|
|
1631
1633
|
|
|
1632
1634
|
// 读取时解码关键词(修复更新显示中的关键词显示问题)
|
|
@@ -1657,25 +1659,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1657
1659
|
}
|
|
1658
1660
|
|
|
1659
1661
|
|
|
1660
|
-
|
|
1661
|
-
logger.debug(`准备存储到数据库的关键词: ${JSON.stringify(encodedKeywords)}`)
|
|
1662
|
-
|
|
1663
|
-
// 添加详细的处理日志
|
|
1664
|
-
logger.debug(`=== 配置处理详情 ===`)
|
|
1665
|
-
logger.debug(`原始输入: ${keywords || '无关键词'}`)
|
|
1666
|
-
logger.debug(`审核方式: ${reviewMethod} (${['全部同意','按数量','按比例','全部拒绝'][reviewMethod]})`)
|
|
1667
|
-
logger.debug(`阈值参数: ${JSON.stringify(reviewParameters)}`)
|
|
1668
|
-
logger.debug(`关键词列表: [${keywordList.map(k => `"${k}"`).join(', ')}]`)
|
|
1669
|
-
logger.debug(`现有配置: ${existingConfig ? '存在' : '不存在'}`)
|
|
1670
|
-
if (existingConfig) {
|
|
1671
|
-
logger.debug(`原审核方式: ${existingConfig.reviewMethod}`)
|
|
1672
|
-
logger.debug(`原阈值: ${JSON.stringify(existingConfig.reviewParameters)}`)
|
|
1673
|
-
logger.debug(`原关键词数: ${existingConfig.keywords.length}`)
|
|
1674
|
-
logger.debug(`新关键词数: ${keywordList.length}`)
|
|
1675
|
-
}
|
|
1676
|
-
logger.debug(`==================`)
|
|
1677
|
-
|
|
1678
|
-
logger.info(feedbackMessage.replace(/\n/g, '; '))
|
|
1662
|
+
clog('info', feedbackMessage.replace(/\n/g, '; '))
|
|
1679
1663
|
return feedbackMessage
|
|
1680
1664
|
})
|
|
1681
1665
|
|
|
@@ -1719,6 +1703,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1719
1703
|
if (request.requestId) {
|
|
1720
1704
|
try {
|
|
1721
1705
|
await session.bot.handleGuildMemberRequest(request.requestId, true)
|
|
1706
|
+
await updateStats(ctx, groupId, 'manuallyApproved')
|
|
1722
1707
|
approvedCount++
|
|
1723
1708
|
} catch (error: any) {
|
|
1724
1709
|
logger.warn(`处理申请 ${request.id} 时出错:`, error)
|
|
@@ -1750,6 +1735,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1750
1735
|
session.bot.handleGuildMemberRequest(request.requestId, true).catch((e:any) => logger.warn('自动同意失败', e))
|
|
1751
1736
|
// 清除该用户的所有待审核记录(异步也可以,顺序无关)
|
|
1752
1737
|
await ctx.database.remove('group_verification_pending', { groupId, userId: request.userId })
|
|
1738
|
+
await updateStats(ctx, groupId, 'manuallyApproved')
|
|
1753
1739
|
return reply
|
|
1754
1740
|
} catch (error: any) {
|
|
1755
1741
|
return `处理申请时出错: ${error.message}`
|
|
@@ -1776,6 +1762,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
1776
1762
|
const reply = `已同意用户 ${displayName} 的加群申请`
|
|
1777
1763
|
session.bot.handleGuildMemberRequest(request.requestId, true).catch((e:any) => logger.warn('自动同意失败', e))
|
|
1778
1764
|
await ctx.database.remove('group_verification_pending', { groupId, userId })
|
|
1765
|
+
await updateStats(ctx, groupId, 'manuallyApproved')
|
|
1779
1766
|
return reply
|
|
1780
1767
|
} catch (error: any) {
|
|
1781
1768
|
return `处理申请时出错: ${error.message}`
|
|
@@ -2090,7 +2077,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
2090
2077
|
await ctx.database.set('group_verification_pending', { id: p.id }, updates)
|
|
2091
2078
|
}
|
|
2092
2079
|
}
|
|
2093
|
-
|
|
2080
|
+
clog('info', '旧版日期字段迁移完成')
|
|
2094
2081
|
} catch (e) {
|
|
2095
2082
|
logger.warn('迁移旧日期字段时出错', e)
|
|
2096
2083
|
}
|
|
@@ -2107,9 +2094,9 @@ export function apply(ctx: Context, config: Config) {
|
|
|
2107
2094
|
rejected: 0,
|
|
2108
2095
|
lastUpdated: new Date().toISOString()
|
|
2109
2096
|
})
|
|
2110
|
-
|
|
2097
|
+
clog('info', '已创建总计统计行')
|
|
2111
2098
|
} else {
|
|
2112
|
-
|
|
2099
|
+
clog('info', '总计统计行已存在')
|
|
2113
2100
|
}
|
|
2114
2101
|
|
|
2115
2102
|
// 同步现有统计数据到总计行
|