koishi-plugin-group-verification 1.0.34 → 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 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' | 'error';
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
- defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)").default("{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}\n使用 gva 同意或 gvr 拒绝申请"),
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", "error"]).description("日志级别").default("info"),
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)"),
@@ -194,22 +211,22 @@ function usageString() {
194
211
  __name(usageString, "usageString");
195
212
  function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasRealEnableMessageParam, hasRealDisableMessageParam, logger2, defaultMessage) {
196
213
  let reminderEnabled = true;
197
- let reminderMessage = defaultMessage || "{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}";
214
+ let reminderMessage = defaultMessage || "{user}({id}) 申请入群\\n申请理由: {question}\\n匹配情况: {answer}/{threshold}\\n使用 gva 同意或 gvr 拒绝申请";
198
215
  if (existingConfig) {
199
216
  reminderEnabled = existingConfig.reminderEnabled;
200
217
  reminderMessage = existingConfig.reminderMessage || reminderMessage;
201
218
  }
202
219
  if (hasRealDisableMessageParam) {
203
220
  reminderEnabled = false;
204
- logger2.debug("禁用提醒消息功能 (保留现有内容)");
221
+ clog("debug", "禁用提醒消息功能 (保留现有内容)");
205
222
  } else if (hasRealEnableMessageParam) {
206
223
  reminderEnabled = true;
207
- logger2.debug(`启用提醒消息(保留原消息): ${reminderMessage.substring(0, 50)}...`);
224
+ clog("debug", `启用提醒消息(保留原消息): ${reminderMessage.substring(0, 50)}...`);
208
225
  } else if (hasRealMessageParam) {
209
226
  reminderEnabled = true;
210
227
  if (cleanedOptions.message !== void 0) {
211
- reminderMessage = cleanedOptions.message.replace(/\\n/g, "\n");
212
- logger2.debug(`设置自定义提醒消息: ${reminderMessage.substring(0, 50)}...`);
228
+ reminderMessage = cleanedOptions.message;
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
- logger.debug(`总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}, 入群${totalJoined}`);
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
- logger.debug(`权限检查 - Koishi权限等级: ${koishiAuthority || "未获取到"}`);
351
+ clogV(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`);
352
+ clogV(`权限检查 - Koishi权限等级: ${koishiAuthority || "未获取到"}`);
336
353
  if (!session.author) {
337
- logger.debug(`权限检查 - session中可能的权限字段:`, {
354
+ clogV(`权限检查 - session中可能的权限字段:`, {
338
355
  authority: session.authority,
339
356
  permission: session.permission,
340
357
  role: session.role
341
358
  });
342
359
  } else {
343
- logger.debug(`权限检查 - author对象中的字段:`, {
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
- logger.debug(`权限检查 - user对象中的权限信息:`, {
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
- logger.debug(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`);
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
- logger.info(`权限检查 - 获取到成员信息:`, {
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
- logger.info(`权限检查 - 用户是群主`);
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
- logger.info(`权限检查 - 用户是管理员`);
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
- logger.debug("guild-member-request event", session);
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
- logger.info(`配置要求全部拒绝,自动拒绝用户 ${userId}`);
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
- logger.info(`用户 ${userId} 在群 ${guildId} 或全局黑名单中,自动拒绝申请`);
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
- logger.debug(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
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
- logger.debug(`自动同意 requestId=${requestId}`);
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) {
@@ -461,7 +483,7 @@ function parseConfigArgs(raw) {
461
483
  }
462
484
  }
463
485
  tokens = tokens.map(
464
- (t) => t.replace(new RegExp(ESC_QUOTE, "g"), '"').replace(new RegExp(ESC_BACKSLASH, "g"), "\\")
486
+ (t) => t.replace(new RegExp(ESC_QUOTE, "g"), '"').replace(new RegExp(ESC_BACKSLASH, "g"), "\\\\")
465
487
  );
466
488
  for (let i = 0; i < tokens.length; i++) {
467
489
  const tok = tokens[i];
@@ -567,7 +589,11 @@ async function verifyApplication(config, message, session) {
567
589
  isValid = false;
568
590
  requiredThreshold = "null";
569
591
  }
570
- logger.debug(`verifyApplication msg="${message}" keywords=${JSON.stringify(config.keywords)} matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
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
- logger.debug(`处理失败验证 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold}`);
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,31 +633,31 @@ 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
- logger.debug(`群 ${guildId} 的提醒消息已被禁用,跳过发送`);
636
+ clog("debug", `群 ${guildId} 的提醒消息已被禁用,跳过发送`);
607
637
  return;
608
638
  }
609
- let reminderMsg = config.reminderMessage;
639
+ let reminderMsg = config.reminderMessage.replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
610
640
  reminderMsg = reminderMsg.replace(/{user}/g, username).replace(/{id}/g, userId).replace(/{group}/g, guildId).replace(/{gname}/g, groupName).replace(/{question}/g, message).replace(/{answer}/g, matchedCount.toString()).replace(/{threshold}/g, requiredThreshold);
611
641
  const rawChannel = (session.channelId || "").toString().trim();
612
642
  const channel = rawChannel || guildId;
613
643
  const target = rawChannel ? [channel, guildId] : guildId;
614
- logger.debug("broadcast target", { channel, guildId, target });
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);
618
648
  } catch (err) {
619
- logger.warn("bot.broadcast failed, fallback to ctx.broadcast", err);
649
+ logger.warn("bot.broadcast 失败,回退到 ctx.broadcast", err);
620
650
  if (typeof ctx.broadcast === "function") {
621
651
  await ctx.broadcast([target], reminderMsg);
622
652
  } else {
623
- logger.info("ctx.broadcast 不可用,跳过发送");
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
- logger.info("ctx.broadcast 不可用,跳过发送");
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
- logger.info(`已将黑名单用户 ${targetUser} 从群 ${group} 踢出`);
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
- if (config.logLevel) logger.level = config.logLevel;
867
- logger.info("群组验证插件已启动");
868
- logger.info(`默认提醒消息: ${config.defaultReminderMessage}`);
869
- logger.info(`严格群号检查: ${config.enableStrictGroupCheck ? "启用" : "禁用"}`);
870
- logger.info(`日志级别: ${config.logLevel}`);
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",
@@ -875,8 +906,8 @@ function apply(ctx, config) {
875
906
  manuallyApproved: "integer",
876
907
  rejected: "integer",
877
908
  totalJoined: "integer",
878
- // store as string (ISO timestamp) to preserve full date+time;
879
- // Koishi `date` type truncates to day which leads to 00:00:00.
909
+ // 以字符串(ISO 时间戳)格式存储,保留完整日期+时间;
910
+ // Koishi date 类型会截断到天,导致时间显示为 00:00:00
880
911
  lastUpdated: "string"
881
912
  }, {
882
913
  primary: "id",
@@ -890,7 +921,7 @@ function apply(ctx, config) {
890
921
  requestMessage: "string",
891
922
  // 保存 OneBot 事件提供的原始 requestId;用于同意/拒绝操作
892
923
  requestId: "string",
893
- // record full timestamp as string to keep time component
924
+ // 以字符串格式记录完整时间戳,保留时间分量
894
925
  applyTime: "string"
895
926
  }, {
896
927
  primary: "id",
@@ -906,54 +937,36 @@ function apply(ctx, config) {
906
937
  indexes: [["groupId"]]
907
938
  });
908
939
  ctx.on("guild-member-request", async (session) => {
909
- logger.info("收到 guild-member-request 事件,转发给处理函数");
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 set = autoQueue.get(groupId);
920
- if (set && set.has(userId)) {
921
- await updateStats(ctx, groupId, "autoApproved");
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
- logger.debug(`权限检查 - Koishi权限等级: ${koishiAuthority || "未获取到"}`);
960
+ clogV(`权限检查 - 用户ID: ${session.userId}, 群号: ${groupId}`);
961
+ clogV(`权限检查 - Koishi权限等级: ${koishiAuthority || "未获取到"}`);
949
962
  if (!session.author) {
950
- logger.debug(`权限检查 - session中可能的权限字段:`, {
963
+ clogV(`权限检查 - session中可能的权限字段:`, {
951
964
  authority: session.authority,
952
965
  permission: session.permission,
953
966
  role: session.role
954
967
  });
955
968
  } else {
956
- logger.debug(`权限检查 - author对象中的字段:`, {
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
- logger.debug(`权限检查 - user对象中的权限信息:`, {
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
- logger.debug(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`);
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
- logger.debug(`权限检查 - 获取到成员信息:`, {
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
- logger.info(`权限检查 - 用户是群主`);
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
- logger.debug(`权限检查 - 用户是管理员`);
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
- logger.debug(`权限检查 - 权限不足`);
995
- const debugInfo = `调试信息 - 用户ID:${session.userId}, 群号:${groupId}, 权限等级:${koishiAuthority || "未知"}`;
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
- logger.info(`=== 命令解析调试 ===`);
1011
- logger.info(`session内容: guildId=${session.guildId}, userId=${session.userId}`);
1024
+ clogV(`=== 命令解析调试 ===`);
1025
+ clogV(`session内容: guildId=${session.guildId}, userId=${session.userId}`);
1012
1026
  const rawInput = session.content.split(/\s+/).slice(1).join(" ");
1013
- logger.info(`原始命令参数: "${rawInput}"`);
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
- logger.info(`解析结果 flags=${JSON.stringify(flags)}, keywords=[${parsedKeywords.join(", ")}]`);
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
- logger.info(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}`);
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
- logger.info(`配置删除 - 用户ID:${session.userId}, 群号:${targetGroupId}`);
1096
+ clog("info", `配置删除 - 用户ID:${session.userId}, 群号:${targetGroupId}`);
1081
1097
  return `已删除群 ${targetGroupId} 的验证配置`;
1082
1098
  } else {
1083
1099
  return `群 ${targetGroupId} 无验证配置`;
@@ -1113,13 +1129,14 @@ ${debugInfo}`];
1113
1129
  const createTime = new Date(config2.createdAt).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
1114
1130
  const updateTime = new Date(config2.updatedAt).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
1115
1131
  const reminderStatus = config2.reminderEnabled ? "启用" : "禁用";
1132
+ const displayMsg = (config2.reminderMessage || "无").replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
1116
1133
  return `群 ${targetGroupId} 配置:
1117
1134
  关键词: ${decodedKeywords2.join(", ")}
1118
1135
  审核方式: ${methodDesc}
1119
1136
  阈值: ${thresholdInfo}
1120
1137
  提醒消息: ${reminderStatus}
1121
1138
  自定义消息:
1122
- ${config2.reminderMessage || "无"}
1139
+ ${displayMsg}
1123
1140
  创建时间: ${createTime}
1124
1141
  更新时间: ${updateTime}
1125
1142
  创建者: ${config2.createdBy}
@@ -1129,7 +1146,7 @@ ${config2.reminderMessage || "无"}
1129
1146
  }
1130
1147
  }
1131
1148
  let keywordList = parsedKeywords.slice();
1132
- logger.info(`关键词解析结果: [${keywordList.join(", ")}] - 原始输入: "${rawInput}"`);
1149
+ clogV(`关键词解析结果: [${keywordList.join(", ")}] - 原始输入: "${rawInput}"`);
1133
1150
  if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
1134
1151
  const hasConfigParams = cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || hasRealMessageParam || hasRealEnableMessageParam || hasRealDisableMessageParam;
1135
1152
  if (!hasConfigParams) {
@@ -1162,7 +1179,7 @@ ${config2.reminderMessage || "无"}
1162
1179
  reviewMethod = existingConfig.reviewMethod;
1163
1180
  if (existingConfig.reviewParameters === void 0 || existingConfig.reviewParameters === null || isNaN(existingConfig.reviewParameters)) {
1164
1181
  reviewParameters = 0;
1165
- logger.info(`检测到老版本数据或无效值,使用默认阈值: 0`);
1182
+ clog("debug", `检测到老版本数据或无效值,使用默认阈值: 0`);
1166
1183
  } else {
1167
1184
  reviewParameters = existingConfig.reviewParameters;
1168
1185
  }
@@ -1174,10 +1191,10 @@ ${config2.reminderMessage || "无"}
1174
1191
  }
1175
1192
  const oldMethod = reviewMethod;
1176
1193
  reviewMethod = methodNum;
1177
- logger.debug(`审核方式明确指定为: ${reviewMethod}`);
1194
+ clog("debug", `审核方式明确指定为: ${reviewMethod}`);
1178
1195
  var methodChanged = oldMethod !== reviewMethod;
1179
1196
  } else {
1180
- logger.debug(`未指定审核方式,保持原有值: ${reviewMethod}`);
1197
+ clog("debug", `未指定审核方式,保持原有值: ${reviewMethod}`);
1181
1198
  var methodChanged = false;
1182
1199
  }
1183
1200
  const thresholdResult = resolveThreshold(
@@ -1197,7 +1214,7 @@ ${config2.reminderMessage || "无"}
1197
1214
  updatedBy: session.username || session.userId,
1198
1215
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1199
1216
  });
1200
- logger.debug(`自动调整并更新数据库阈值为: ${reviewParameters}`);
1217
+ clog("debug", `自动调整并更新数据库阈值为: ${reviewParameters}`);
1201
1218
  }
1202
1219
  let autoAdjustNote = "";
1203
1220
  if (thresholdResult.autoInfo === "methodChange") {
@@ -1215,7 +1232,6 @@ ${config2.reminderMessage || "无"}
1215
1232
  const encodedKeywords = keywordList.map((keyword) => {
1216
1233
  return keyword.replace(/,/g, "[[COMMA]]");
1217
1234
  });
1218
- logger.info(`编码后准备存储的关键词: ${JSON.stringify(encodedKeywords)}`);
1219
1235
  const dbData = {
1220
1236
  keywords: encodedKeywords,
1221
1237
  reviewMethod,
@@ -1231,7 +1247,7 @@ ${config2.reminderMessage || "无"}
1231
1247
  ...dbData,
1232
1248
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1233
1249
  });
1234
- logger.info(`更新配置成功 - 审核方式: ${reviewMethod}, 阈值: ${reviewParameters}`);
1250
+ clog("info", `更新配置成功 - 审核方式: ${reviewMethod}, 阈值: ${reviewParameters}`);
1235
1251
  } else {
1236
1252
  await ctx.database.create("group_verification_config", {
1237
1253
  groupId: targetGroupId,
@@ -1239,7 +1255,7 @@ ${config2.reminderMessage || "无"}
1239
1255
  createdBy: session.username || session.userId,
1240
1256
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1241
1257
  });
1242
- logger.info(`创建配置成功 - 审核方式: ${reviewMethod}, 阈值: ${reviewParameters}`);
1258
+ clog("info", `创建配置成功 - 审核方式: ${reviewMethod}, 阈值: ${reviewParameters}`);
1243
1259
  }
1244
1260
  const decodedKeywords = keywordList;
1245
1261
  let feedbackMessage = `群 ${targetGroupId} 配置已更新:
@@ -1259,25 +1275,12 @@ ${config2.reminderMessage || "无"}
1259
1275
  feedbackMessage += `提醒状态: ${reminderEnabled ? "启用" : "禁用"}
1260
1276
  `;
1261
1277
  if (reminderMessage && reminderEnabled) {
1278
+ const displayReminder = reminderMessage.replace(/\\\\n/g, "").replace(/\\n/g, "\n").replace(/\x01/g, "\\n");
1262
1279
  feedbackMessage += `提醒消息:
1263
- ${reminderMessage}
1280
+ ${displayReminder}
1264
1281
  `;
1265
1282
  }
1266
- logger.info(`准备存储到数据库的关键词: ${JSON.stringify(encodedKeywords)}`);
1267
- logger.info(`=== 配置处理详情 ===`);
1268
- logger.info(`原始输入: ${keywords || "无关键词"}`);
1269
- logger.info(`审核方式: ${reviewMethod} (${["全部同意", "按数量", "按比例", "全部拒绝"][reviewMethod]})`);
1270
- logger.info(`阈值参数: ${JSON.stringify(reviewParameters)}`);
1271
- logger.info(`关键词列表: [${keywordList.map((k) => `"${k}"`).join(", ")}]`);
1272
- logger.info(`现有配置: ${existingConfig ? "存在" : "不存在"}`);
1273
- if (existingConfig) {
1274
- logger.info(`原审核方式: ${existingConfig.reviewMethod}`);
1275
- logger.info(`原阈值: ${JSON.stringify(existingConfig.reviewParameters)}`);
1276
- logger.info(`原关键词数: ${existingConfig.keywords.length}`);
1277
- logger.info(`新关键词数: ${keywordList.length}`);
1278
- }
1279
- logger.info(`==================`);
1280
- logger.info(feedbackMessage.replace(/\n/g, "; "));
1283
+ clog("info", feedbackMessage.replace(/\n/g, "; "));
1281
1284
  return feedbackMessage;
1282
1285
  });
1283
1286
  groupVerify.subcommand(".approve [userId]", "同意加群申请").alias(
@@ -1313,6 +1316,7 @@ ${reminderMessage}
1313
1316
  if (request2.requestId) {
1314
1317
  try {
1315
1318
  await session.bot.handleGuildMemberRequest(request2.requestId, true);
1319
+ await updateStats(ctx, groupId, "manuallyApproved");
1316
1320
  approvedCount++;
1317
1321
  } catch (error) {
1318
1322
  logger.warn(`处理申请 ${request2.id} 时出错:`, error);
@@ -1340,6 +1344,7 @@ ${reminderMessage}
1340
1344
  const reply = `已同意用户 ${displayName} 的加群申请`;
1341
1345
  session.bot.handleGuildMemberRequest(request2.requestId, true).catch((e) => logger.warn("自动同意失败", e));
1342
1346
  await ctx.database.remove("group_verification_pending", { groupId, userId: request2.userId });
1347
+ await updateStats(ctx, groupId, "manuallyApproved");
1343
1348
  return reply;
1344
1349
  } catch (error) {
1345
1350
  return `处理申请时出错: ${error.message}`;
@@ -1362,6 +1367,7 @@ ${reminderMessage}
1362
1367
  const reply = `已同意用户 ${displayName} 的加群申请`;
1363
1368
  session.bot.handleGuildMemberRequest(request.requestId, true).catch((e) => logger.warn("自动同意失败", e));
1364
1369
  await ctx.database.remove("group_verification_pending", { groupId, userId });
1370
+ await updateStats(ctx, groupId, "manuallyApproved");
1365
1371
  return reply;
1366
1372
  } catch (error) {
1367
1373
  return `处理申请时出错: ${error.message}`;
@@ -1629,7 +1635,7 @@ ${reminderMessage}
1629
1635
  await ctx.database.set("group_verification_pending", { id: p.id }, updates);
1630
1636
  }
1631
1637
  }
1632
- logger.info("旧版日期字段迁移完成");
1638
+ clog("info", "旧版日期字段迁移完成");
1633
1639
  } catch (e) {
1634
1640
  logger.warn("迁移旧日期字段时出错", e);
1635
1641
  }
@@ -1642,9 +1648,9 @@ ${reminderMessage}
1642
1648
  rejected: 0,
1643
1649
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
1644
1650
  });
1645
- logger.info("已创建总计统计行");
1651
+ clog("info", "已创建总计统计行");
1646
1652
  } else {
1647
- logger.info("总计统计行已存在");
1653
+ clog("info", "总计统计行已存在");
1648
1654
  }
1649
1655
  await syncTotalStats(ctx);
1650
1656
  });
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "bugs": {
9
9
  "url": "https://github.com/LHDyx/koishi-plugin-group-verification/issues"
10
10
  },
11
- "version": "1.0.34",
11
+ "version": "1.0.36",
12
12
  "main": "lib/index.js",
13
13
  "typings": "lib/index.d.ts",
14
14
  "files": [
@@ -44,7 +44,7 @@
44
44
  "koishi": "^4.15.0"
45
45
  },
46
46
  "devDependencies": {
47
- "typescript": "^4.9.0",
48
- "@types/node": "^16.0.0"
47
+ "@types/node": "^16.0.0",
48
+ "typescript": "^4.9.0"
49
49
  }
50
50
  }
package/readme.md CHANGED
@@ -138,11 +138,11 @@ group-verify.blacklist i <用户ID> [群号|all]
138
138
 
139
139
  插件启动时会输出运行状态,并根据 `logLevel` 调整输出量。
140
140
  - `debug`:打印所有调试细节,包括权限检查、命令解析等。
141
- - `info`:默认值,记录关键事件(插件启动、配置修改、自动拒绝、黑名单踢人等)。
141
+ - `info`:默认值,记录关键事件(插件启动、配置修改、自动拒绝、黑名单踢人等),包括成功踢人日志。
142
142
  - `warn`:记录可恢复的异常,例如尝试踢出用户失败、数据库操作问题。
143
143
  - `error`:仅在遇到严重错误时输出。
144
144
 
145
- 添加黑名单时会尝试在对应群踢出该用户,成功记为 `info`,失败记为 `warn`。
145
+ 添加黑名单时会尝试在对应群踢出该用户,成功记为 `info`,失败记为 `warn`。权限检查的详细过程则只会在 `debug` 级别输出。
146
146
 
147
147
  ### 严格群号检查
148
148