koishi-plugin-group-verification 1.0.32 → 1.0.34

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.js CHANGED
@@ -45,12 +45,12 @@ var import_koishi = require("koishi");
45
45
  var name = "group-verification";
46
46
  var logger = console;
47
47
  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 拒绝申请"),
48
+ defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)").default("{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}\n使用 gva 同意或 gvr 拒绝申请"),
49
49
  enableStrictGroupCheck: import_koishi.Schema.boolean().description("是否启用严格的群号检查(检查群号长度)").default(false),
50
50
  logLevel: import_koishi.Schema.union(["debug", "info", "warn", "error"]).description("日志级别").default("info"),
51
- permissionDeniedMessage: import_koishi.Schema.string().description("权限不足时返回给调用者的提示").default("权限不足:需要群主/管理员权限或koishi三级以上权限"),
51
+ permissionDeniedMessage: import_koishi.Schema.string().description("权限不足时返回给调用者的提示").default("权限不足: 需要群主/管理员权限或koishi三级以上权限"),
52
52
  invalidGroupMessage: import_koishi.Schema.string().description("无效群号或机器人未在该群时的提示").default("群号 {group} 格式不合法或机器人不在该群中"),
53
- parameterConflictMessage: import_koishi.Schema.string().description("参数冲突时提示").default("参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)"),
53
+ parameterConflictMessage: import_koishi.Schema.string().description("参数冲突时提示").default("参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)"),
54
54
  noKeywordsMessage: import_koishi.Schema.string().description("未提供关键词且无法从现有配置继承时的提示").default("请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置"),
55
55
  blacklistAddSuccess: import_koishi.Schema.string().description("将用户加入黑名单后的提示,可使用 {user},{group},{reason}").default("已将用户 {user} 加入群 {group} 黑名单{reason}"),
56
56
  blacklistRemoveSuccess: import_koishi.Schema.string().description("从黑名单移除用户后的提示,可使用 {user},{group}").default("已从群 {group} 的黑名单中移除用户 {user}"),
@@ -162,25 +162,31 @@ function validateKeywordFormat(raw) {
162
162
  }
163
163
  __name(validateKeywordFormat, "validateKeywordFormat");
164
164
  function usageString() {
165
- return `用法:
166
- # 创建/修改配置
167
- gvc 关键词1,关键词2 -m 1 -t 2 # 创建配置
168
- gvc -m 1 -t 2 # 修改审核参数
165
+ return `用法:
166
+ -i 群号
167
+ -m (0|1|2|3)审核方式
168
+ -t 阈值
169
+ -msg [可选,消息内容] 并打开提醒消息
170
+ -nomsg 取消提醒消息(与-msg冲突)
171
+ -r 删除配置
172
+ -? 查询配置
169
173
 
170
- # 提醒消息控制(可用变量详见下方)
171
- gvc -msg "消息内容" # 修改提醒消息
174
+ 对于存在空格的关键词/提醒消息用双引号""包裹
175
+
176
+ # 例
177
+ gvc 关键词1,关键词2 -m 1 -t 2 # 创建配置
178
+ gvc -msg "消息内容" # 修改提醒消息
172
179
  gvc -nomsg # 禁用提醒消息
173
- # 查询/删除
174
180
  gvc -? # 查询配置
175
181
  gvc -r # 删除配置
176
182
 
177
- 审核方式说明(使用 -m 参数):
183
+ 审核方式说明(使用 -m 参数):
178
184
  0 全部同意(默认)
179
- 1 按数量同意,需要 -t 指定数量
185
+ 1 按数量同意,需要 -t 指定数量
180
186
  2 按比例同意,需要 -t 指定百分比
181
- 3 全部拒绝(拒绝后系统会自动阻止任何通过)
187
+ 3 全部拒绝
182
188
 
183
- 提醒消息可用变量:{user} 用户名 {id} 用户ID
189
+ 提醒消息可用变量: {user} 用户名 {id} 用户ID
184
190
  {group} 群号 {gname} 群名称
185
191
  {question} 申请理由 {answer} 匹配情况 {threshold} 阈值
186
192
  使用 \\n 换行`;
@@ -188,7 +194,7 @@ function usageString() {
188
194
  __name(usageString, "usageString");
189
195
  function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasRealEnableMessageParam, hasRealDisableMessageParam, logger2, defaultMessage) {
190
196
  let reminderEnabled = true;
191
- let reminderMessage = defaultMessage || "{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}";
197
+ let reminderMessage = defaultMessage || "{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}";
192
198
  if (existingConfig) {
193
199
  reminderEnabled = existingConfig.reminderEnabled;
194
200
  reminderMessage = existingConfig.reminderMessage || reminderMessage;
@@ -516,7 +522,7 @@ function parseConfigArgs(raw) {
516
522
  }
517
523
  const keywordSection = raw.split(/(?:^|\s+)-(?:i|m|t|msg|nomsg|\?|r)\b/)[0].trim();
518
524
  if (keywordSection && !validateKeywordFormat(keywordSection)) {
519
- error = '关键词应使用逗号分隔或引号框起(如:k1,k2,k3 或 "k1","k2" 或 "k1,k2",k3)';
525
+ error = '关键词应使用逗号分隔或引号框起(如: k1,k2,k3 或 "k1","k2" 或 "k1,k2",k3)';
520
526
  }
521
527
  return { keywords, flags, error };
522
528
  }
@@ -645,7 +651,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
645
651
  const op = parts[0]?.toLowerCase();
646
652
  if (!op || !["a", "r", "l", "i"].includes(op)) {
647
653
  return [
648
- "用法:",
654
+ "用法: ",
649
655
  " gvb a id [reason] [group] 将用户加入黑名单",
650
656
  " gvb r id [group] 将用户移出黑名单",
651
657
  " gvb l [group] 查询群黑名单",
@@ -693,7 +699,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
693
699
  const row = rows[0];
694
700
  const entries = row.entries || {};
695
701
  if (entries[targetUser] !== void 0) {
696
- return `用户 ${targetUser} 已在群 ${group} 的黑名单中:${entries[targetUser]}`;
702
+ return `用户 ${targetUser} 已在群 ${group} 的黑名单中: ${entries[targetUser]}`;
697
703
  }
698
704
  entries[targetUser] = storedReason;
699
705
  await ctx.database.set("group_verification_blacklist", { id: row.id }, { entries });
@@ -711,7 +717,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
711
717
  }
712
718
  }
713
719
  const tmpl = config && config.blacklistAddSuccess || "已将用户 {user} 加入群 {group} 黑名单{reason}";
714
- return tmpl.replace("{user}", targetUser).replace("{group}", group).replace("{reason}", reason ? `,原因:${reason}` : "");
720
+ return tmpl.replace("{user}", targetUser).replace("{group}", group).replace("{reason}", reason ? `,原因: ${reason}` : "");
715
721
  }
716
722
  if (op === "r") {
717
723
  targetUser = parts[1];
@@ -768,7 +774,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
768
774
  `;
769
775
  let msg = prefix;
770
776
  for (const uid in entries) {
771
- msg += `${uid}${entries[uid] ? `:${entries[uid]}` : ""}
777
+ msg += `${uid}${entries[uid] ? `: ${entries[uid]}` : ""}
772
778
  `;
773
779
  }
774
780
  return msg;
@@ -802,7 +808,7 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
802
808
  }
803
809
  if (groupArg.toLowerCase() === "all") {
804
810
  const auth = session.author?.authority || session.user?.authority;
805
- if (!(auth && auth >= 3)) return "权限不足:查看全局/所有群黑名单需要 koishi 3 级以上权限";
811
+ if (!(auth && auth >= 3)) return "权限不足: 查看全局/所有群黑名单需要 koishi 3 级以上权限";
806
812
  const rows2 = await ctx.database.get("group_verification_blacklist", {});
807
813
  const globalReason2 = globalHit ? globalRows[0].entries[targetUser] : "无";
808
814
  const lines = [];
@@ -882,7 +888,7 @@ function apply(ctx, config) {
882
888
  userId: "string",
883
889
  userName: "string",
884
890
  requestMessage: "string",
885
- // store the raw requestId if provided by OneBot event; used for approving/rejecting
891
+ // 保存 OneBot 事件提供的原始 requestId;用于同意/拒绝操作
886
892
  requestId: "string",
887
893
  // record full timestamp as string to keep time component
888
894
  applyTime: "string"
@@ -987,7 +993,7 @@ function apply(ctx, config) {
987
993
  }
988
994
  logger.debug(`权限检查 - 权限不足`);
989
995
  const debugInfo = `调试信息 - 用户ID:${session.userId}, 群号:${groupId}, 权限等级:${koishiAuthority || "未知"}`;
990
- return [false, (config.permissionDeniedMessage || "权限不足:需要群主/管理员权限或koishi三级以上权限") + `
996
+ return [false, (config.permissionDeniedMessage || "权限不足: 需要群主/管理员权限或koishi三级以上权限") + `
991
997
  ${debugInfo}`];
992
998
  }
993
999
  __name(checkPermission2, "checkPermission");
@@ -1017,14 +1023,14 @@ ${debugInfo}`];
1017
1023
  threshold: flags.threshold || options.threshold,
1018
1024
  message: flags.message !== void 0 ? flags.message : options.message,
1019
1025
  enableMessage: flags.enableMessage,
1020
- // 新增:-msg 裸调用标记
1026
+ // 新增: -msg 裸调用标记
1021
1027
  disableMessage: flags.disableMessage || options.disableMessage,
1022
1028
  query: flags.query || options.query,
1023
1029
  remove: flags.remove || options.remove
1024
1030
  };
1025
1031
  logger.info(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}`);
1026
1032
  if ((cleanedOptions.query || cleanedOptions.remove) && (parsedKeywords.length > 0 || cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || cleanedOptions.message !== void 0 || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
1027
- return config.parameterConflictMessage || "参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)";
1033
+ return config.parameterConflictMessage || "参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)";
1028
1034
  }
1029
1035
  const hasRealMessageParam = cleanedOptions.message !== void 0;
1030
1036
  const hasRealEnableMessageParam = cleanedOptions.enableMessage === true;
@@ -1049,7 +1055,7 @@ ${debugInfo}`];
1049
1055
  return "请在群聊中使用此命令或指定群号(-i参数)";
1050
1056
  }
1051
1057
  if ((hasRealMessageParam || hasRealEnableMessageParam) && hasRealDisableMessageParam) {
1052
- return "参数冲突:不能同时使用 -msg 和 -nomsg";
1058
+ return "参数冲突: 不能同时使用 -msg 和 -nomsg";
1053
1059
  }
1054
1060
  const usedOptions = [];
1055
1061
  if (cleanedOptions.method !== void 0) usedOptions.push("-m");
@@ -1107,7 +1113,7 @@ ${debugInfo}`];
1107
1113
  const createTime = new Date(config2.createdAt).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
1108
1114
  const updateTime = new Date(config2.updatedAt).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
1109
1115
  const reminderStatus = config2.reminderEnabled ? "启用" : "禁用";
1110
- return `群 ${targetGroupId} 配置:
1116
+ return `群 ${targetGroupId} 配置:
1111
1117
  关键词: ${decodedKeywords2.join(", ")}
1112
1118
  审核方式: ${methodDesc}
1113
1119
  阈值: ${thresholdInfo}
@@ -1164,7 +1170,7 @@ ${config2.reminderMessage || "无"}
1164
1170
  if (cleanedOptions.method !== void 0 && cleanedOptions.method !== "") {
1165
1171
  const methodNum = parseInt(cleanedOptions.method);
1166
1172
  if (isNaN(methodNum) || methodNum < 0 || methodNum > 3) {
1167
- return "审核方式参数错误:0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝";
1173
+ return "审核方式参数错误: 0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝";
1168
1174
  }
1169
1175
  const oldMethod = reviewMethod;
1170
1176
  reviewMethod = methodNum;
@@ -1455,7 +1461,7 @@ ${reminderMessage}
1455
1461
  const isGroupId = target && /^\d+$/.test(target);
1456
1462
  const isSpecialTarget = target && validTargets.includes(target.toLowerCase());
1457
1463
  if (target && !isGroupId && !isSpecialTarget) {
1458
- return "参数错误:只能指定群号、all、total或留空";
1464
+ return "参数错误: 只能指定群号、all、total或留空";
1459
1465
  }
1460
1466
  if (target?.toLowerCase() === "total" || target?.toLowerCase() === "all") {
1461
1467
  const koishiAuthority = session.author?.authority || session.user?.authority;
@@ -1496,11 +1502,11 @@ ${reminderMessage}
1496
1502
  if (pendingApplications.length === 0) {
1497
1503
  return "当前没有待审核的加群申请";
1498
1504
  }
1499
- let result = "待审核申请列表:\n";
1505
+ let result = "待审核申请列表: \n";
1500
1506
  pendingApplications.forEach((app, index) => {
1501
1507
  result += `${index + 1}. ${app.userName}(${app.userId})
1502
- 申请时间:${app.applyTime.toLocaleString()}
1503
- 申请理由:${app.requestMessage}
1508
+ 申请时间: ${app.applyTime.toLocaleString()}
1509
+ 申请理由: ${app.requestMessage}
1504
1510
 
1505
1511
  `;
1506
1512
  });
@@ -1518,19 +1524,19 @@ ${reminderMessage}
1518
1524
  return await processBlacklistCommand(ctx, session, args || "", config);
1519
1525
  });
1520
1526
  groupVerify.subcommand(".help", "显示帮助信息").alias("gv.帮助", "gverify.帮助", "group-verify.帮助", "帮助", "hlp", "帮助信息").action(() => {
1521
- return `群组验证命令帮助:
1522
- 主指令别名:gv, gverify
1527
+ return `群组验证命令帮助:
1528
+ 主指令别名: gv, gverify
1523
1529
 
1524
1530
  配置命令 (.config/.cfg):
1525
- 用法:
1526
- 1. 创建新配置:gv.cfg 关键词1,关键词2 -m 1 -t 2
1527
- 2. 修改现有配置:gv.cfg -m 1 -t 2 (不提供关键词)
1528
- 3. 启用提醒消息:gv.cfg -msg "消息内容"
1529
- 4. 禁用提醒消息:gv.cfg -nomsg
1530
- 5. 查询配置:gv.cfg -?
1531
- 6. 删除配置:gv.cfg -r
1531
+ 用法:
1532
+ 1. 创建新配置: gv.cfg 关键词1,关键词2 -m 1 -t 2
1533
+ 2. 修改现有配置: gv.cfg -m 1 -t 2 (不提供关键词)
1534
+ 3. 启用提醒消息: gv.cfg -msg "消息内容"
1535
+ 4. 禁用提醒消息: gv.cfg -nomsg
1536
+ 5. 查询配置: gv.cfg -?
1537
+ 6. 删除配置: gv.cfg -r
1532
1538
 
1533
- 参数说明:
1539
+ 参数说明:
1534
1540
  -i <群号> 指定群号(私聊时必需)
1535
1541
  -m <方式> 审核方式 (0=全部同意, 1=按数量, 2=按比例, 3=全部拒绝)
1536
1542
  -t <阈值> 阈值参数(方式1:0-关键词数, 方式2:0-100)
@@ -1542,19 +1548,19 @@ ${reminderMessage}
1542
1548
  -? 查询当前配置
1543
1549
  -r 删除配置
1544
1550
 
1545
- 引号使用规则:
1546
- 关键词包含空格:gv.cfg "关键词1,关键词 2,关键词3"
1547
- 提醒消息包含空格:gv.cfg -msg "这是包含空格的消息"
1548
- 内部引号转义:gv.cfg -msg "包含\\"引号\\"的内容"
1549
- 换行符:gv.cfg -msg "第一行\\n第二行"
1551
+ 引号使用规则:
1552
+ 关键词包含空格: gv.cfg "关键词1,关键词 2,关键词3"
1553
+ 提醒消息包含空格: gv.cfg -msg "这是包含空格的消息"
1554
+ 内部引号转义: gv.cfg -msg "包含\\"引号\\"的内容"
1555
+ 换行符: gv.cfg -msg "第一行\\n第二行"
1550
1556
 
1551
- 特殊说明:
1557
+ 特殊说明:
1552
1558
  • 阈值可设为0表示全部同意
1553
1559
  • 关键词数量变化时阈值会自动调整
1554
1560
  • 重复参数会使用最后出现的值并提醒
1555
1561
  • 群号检查可在插件配置中开关
1556
1562
 
1557
- 提醒消息变量:
1563
+ 提醒消息变量:
1558
1564
  {user} - 用户名
1559
1565
  {id} - 用户ID
1560
1566
  {group} - 群号
@@ -1563,9 +1569,9 @@ ${reminderMessage}
1563
1569
  {answer} - 答对数量/比例
1564
1570
  {threshold} - 阈值要求
1565
1571
 
1566
- 使用示例:
1572
+ 使用示例:
1567
1573
  gv.cfg "关键词1,关键词2" -m 1 -t 2
1568
- gv.cfg -msg "用户 {user}\\n申请理由:{question}"
1574
+ gv.cfg -msg "用户 {user}\\n申请理由: {question}"
1569
1575
  gv.cfg -m 2 -t 80
1570
1576
  gv.cfg -nomsg
1571
1577
 
@@ -1575,21 +1581,21 @@ ${reminderMessage}
1575
1581
  l [group] 查询群黑名单;传入 all 时查看全局黑名单
1576
1582
  i id [group] 查询账号黑名单;不带参数时查询本群与全局,
1577
1583
  指定群号查询该群与全局,传入 all 则列出所有群及全局(需Koishi 3级)
1578
- 使用示例:
1584
+ 使用示例:
1579
1585
  gvb a 12345 作弊记录
1580
1586
  gvb r 12345 67890
1581
1587
  gvb l
1582
1588
  gvb l all
1583
1589
  gvb i 12345
1584
1590
 
1585
- 快捷命令:
1591
+ 快捷命令:
1586
1592
  gvc - 配置命令快捷方式
1587
1593
  gva - 同意申请快捷命令
1588
1594
  gvr - 拒绝申请快捷命令
1589
1595
  gvp - 查看待审核列表快捷命令
1590
1596
  gvs - 查看统计信息快捷命令
1591
1597
 
1592
- 权限说明:
1598
+ 权限说明:
1593
1599
  - 群主/管理员权限
1594
1600
  - koishi三级以上权限
1595
1601
  - 私聊时必须指定群号(-i参数)`;
@@ -1649,7 +1655,7 @@ ${reminderMessage}
1649
1655
  }
1650
1656
  const stat = stats[0];
1651
1657
  const lastUpdated = new Date(stat.lastUpdated).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
1652
- return `群 ${groupId} 验证统计:
1658
+ return `群 ${groupId} 验证统计:
1653
1659
  自动批准: ${stat.autoApproved}
1654
1660
  手动批准: ${stat.manuallyApproved}
1655
1661
  拒绝: ${stat.rejected}
@@ -1663,7 +1669,7 @@ ${reminderMessage}
1663
1669
  }
1664
1670
  const stat = stats[0];
1665
1671
  const lastUpdated = new Date(stat.lastUpdated).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
1666
- return `总计验证统计:
1672
+ return `总计验证统计:
1667
1673
  自动批准: ${stat.autoApproved}
1668
1674
  手动批准: ${stat.manuallyApproved}
1669
1675
  拒绝: ${stat.rejected}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-group-verification",
3
- "description": "Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式和详细统计功能",
3
+ "description": "Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式、黑名单和详细统计功能",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/LHDyx/koishi-plugin-group-verification.git"
@@ -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.32",
11
+ "version": "1.0.34",
12
12
  "main": "lib/index.js",
13
13
  "typings": "lib/index.d.ts",
14
14
  "files": [
package/readme.md CHANGED
@@ -134,6 +134,22 @@ group-verify.blacklist i <用户ID> [群号|all]
134
134
 
135
135
  ## ⚙️ 参数说明
136
136
 
137
+ ### 日志级别配置
138
+
139
+ 插件启动时会输出运行状态,并根据 `logLevel` 调整输出量。
140
+ - `debug`:打印所有调试细节,包括权限检查、命令解析等。
141
+ - `info`:默认值,记录关键事件(插件启动、配置修改、自动拒绝、黑名单踢人等)。
142
+ - `warn`:记录可恢复的异常,例如尝试踢出用户失败、数据库操作问题。
143
+ - `error`:仅在遇到严重错误时输出。
144
+
145
+ 添加黑名单时会尝试在对应群踢出该用户,成功记为 `info`,失败记为 `warn`。
146
+
147
+ ### 严格群号检查
148
+
149
+ `enableStrictGroupCheck` 可开启简单群号格式验证(长度 5‑15 位),
150
+ 影响所有需要群号的命令。
151
+
152
+
137
153
  ### 审核方式 (-m)
138
154
 
139
155
  *如果改变审核方式而未提供 `-t`,阈值会自动设置为最大值(方式1为关键词数量,方式2为100)。*
package/src/index.ts CHANGED
@@ -21,7 +21,7 @@ export interface GroupVerificationConfig {
21
21
  groupId: string
22
22
  keywords: string[]
23
23
  reviewMethod: 0 | 1 | 2 | 3 // 0:全部同意, 1:按数量同意, 2:按比例同意, 3:全部拒绝
24
- reviewParameters: number // 直接存储数字:0表示无阈值,其他表示具体阈值
24
+ reviewParameters: number // 直接存储数字: 0表示无阈值,其他表示具体阈值
25
25
  reminderEnabled: boolean // 是否启用提醒消息
26
26
  reminderMessage: string
27
27
  createdBy: string
@@ -37,12 +37,12 @@ export interface GroupVerificationStats {
37
37
  autoApproved: number
38
38
  manuallyApproved: number
39
39
  rejected: number
40
- // 新增:总入群人数(不论方式,只要检测到成员加入则增加)
40
+ // 新增: 总入群人数(不论方式,只要检测到成员加入则增加)
41
41
  totalJoined: number
42
42
  lastUpdated: string | Date
43
43
  }
44
44
 
45
- // 黑名单行:每个群/全局对应一条记录,entries 保存 userId->reason 对象
45
+ // 黑名单行: 每个群/全局对应一条记录,entries 保存 userId->reason 对象
46
46
  export interface GroupBlacklistEntry {
47
47
  id: number
48
48
  groupId: string // 群号或 "all" 表示全局
@@ -57,7 +57,7 @@ export interface PendingVerification {
57
57
  userId: string
58
58
  userName: string
59
59
  requestMessage: string
60
- // raw OneBot requestId (may be empty string)
60
+ // 原始 OneBot requestId(字符串可能为空)
61
61
  requestId?: string
62
62
  applyTime: string | Date
63
63
  }
@@ -81,12 +81,12 @@ export interface Config {
81
81
  export const Config: Schema<Config> = Schema.object({
82
82
  defaultReminderMessage: Schema.string()
83
83
  .description('默认提醒消息模板(使用 \\n 表示换行,可包含下方变量)')
84
- .default('{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}\n使用 gva 同意或 gvr 拒绝申请'),
84
+ .default('{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}\n使用 gva 同意或 gvr 拒绝申请'),
85
85
  enableStrictGroupCheck: Schema.boolean().description('是否启用严格的群号检查(检查群号长度)').default(false),
86
86
  logLevel: Schema.union(['debug', 'info', 'warn', 'error']).description('日志级别').default('info'),
87
- permissionDeniedMessage: Schema.string().description('权限不足时返回给调用者的提示').default('权限不足:需要群主/管理员权限或koishi三级以上权限'),
87
+ permissionDeniedMessage: Schema.string().description('权限不足时返回给调用者的提示').default('权限不足: 需要群主/管理员权限或koishi三级以上权限'),
88
88
  invalidGroupMessage: Schema.string().description('无效群号或机器人未在该群时的提示').default('群号 {group} 格式不合法或机器人不在该群中'),
89
- parameterConflictMessage: Schema.string().description('参数冲突时提示').default('参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'),
89
+ parameterConflictMessage: Schema.string().description('参数冲突时提示').default('参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'),
90
90
  noKeywordsMessage: Schema.string().description('未提供关键词且无法从现有配置继承时的提示').default('请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'),
91
91
  blacklistAddSuccess: Schema.string().description('将用户加入黑名单后的提示,可使用 {user},{group},{reason}').default('已将用户 {user} 加入群 {group} 黑名单{reason}'),
92
92
  blacklistRemoveSuccess: Schema.string().description('从黑名单移除用户后的提示,可使用 {user},{group}').default('已从群 {group} 的黑名单中移除用户 {user}'),
@@ -104,11 +104,11 @@ export const inject = ['database']
104
104
  */
105
105
  export interface TokenizeResult {
106
106
  tokens: string[];
107
- seps: string[]; // separator that preceded each token: ' ' or ',' or '' (start)
107
+ seps: string[]; // 每个令牌前面的分隔符: ' ' ',' ''(表示开始)
108
108
  error?: string;
109
109
  }
110
110
 
111
- // Sentinel characters used internally to distinguish escaped quotes/backslashes
111
+ // 内部哨兵字符,用于区分转义的引号或反斜杠
112
112
  const ESC_QUOTE = '\u0000';
113
113
  const ESC_BACKSLASH = '\u0001';
114
114
 
@@ -116,7 +116,7 @@ export function tokenize(input: string): TokenizeResult {
116
116
  const tokens: string[] = [];
117
117
  const seps: string[] = [];
118
118
  let cur = '';
119
- let lastSep = ''; // separator before current token
119
+ let lastSep = ''; // 当前令牌之前的分隔符
120
120
  let i = 0;
121
121
 
122
122
  const flush = () => {
@@ -131,7 +131,7 @@ export function tokenize(input: string): TokenizeResult {
131
131
  while (i < input.length) {
132
132
  const ch = input[i];
133
133
  if (ch === ' ' || ch === ',') {
134
- // record separator for next token
134
+ // 记录下一个令牌的分隔符
135
135
  lastSep = ch;
136
136
  flush();
137
137
  i++;
@@ -224,9 +224,9 @@ export interface ParsedArgs {
224
224
  }
225
225
 
226
226
  /**
227
- * 验证关键词格式:仅允许用逗号和引号分隔,禁止纯空格分隔
227
+ * 验证关键词格式: 仅允许用逗号和引号分隔,禁止纯空格分隔
228
228
  */
229
- // helper used during parsing
229
+ // 解析过程中使用的辅助函数
230
230
  function validateKeywordFormat(raw: string): boolean {
231
231
  if (raw.includes(',') || raw.includes('"')) {
232
232
  return true;
@@ -251,25 +251,31 @@ function validateKeywordFormat(raw: string): boolean {
251
251
  * 对于 -nomsg 不会清除已保存的 message,便于后续再次启用时恢复。
252
252
  */
253
253
  export function usageString(): string {
254
- return `用法:
255
- # 创建/修改配置
256
- gvc 关键词1,关键词2 -m 1 -t 2 # 创建配置
257
- gvc -m 1 -t 2 # 修改审核参数
258
-
259
- # 提醒消息控制(可用变量详见下方)
260
- gvc -msg "消息内容" # 修改提醒消息
254
+ return `用法:
255
+ -i 群号
256
+ -m (0|1|2|3)审核方式
257
+ -t 阈值
258
+ -msg [可选,消息内容] 并打开提醒消息
259
+ -nomsg 取消提醒消息(与-msg冲突)
260
+ -r 删除配置
261
+ -? 查询配置
262
+
263
+ 对于存在空格的关键词/提醒消息用双引号""包裹
264
+
265
+ # 例
266
+ gvc 关键词1,关键词2 -m 1 -t 2 # 创建配置
267
+ gvc -msg "消息内容" # 修改提醒消息
261
268
  gvc -nomsg # 禁用提醒消息
262
- # 查询/删除
263
269
  gvc -? # 查询配置
264
270
  gvc -r # 删除配置
265
271
 
266
- 审核方式说明(使用 -m 参数):
272
+ 审核方式说明(使用 -m 参数):
267
273
  0 全部同意(默认)
268
- 1 按数量同意,需要 -t 指定数量
274
+ 1 按数量同意,需要 -t 指定数量
269
275
  2 按比例同意,需要 -t 指定百分比
270
- 3 全部拒绝(拒绝后系统会自动阻止任何通过)
276
+ 3 全部拒绝
271
277
 
272
- 提醒消息可用变量:{user} 用户名 {id} 用户ID
278
+ 提醒消息可用变量: {user} 用户名 {id} 用户ID
273
279
  {group} 群号 {gname} 群名称
274
280
  {question} 申请理由 {answer} 匹配情况 {threshold} 阈值
275
281
  使用 \\n 换行`;
@@ -290,7 +296,7 @@ export function mergeReminder(
290
296
  ) {
291
297
  let reminderEnabled = true;
292
298
  // 优先使用传入的默认模板,其次使用已有配置,再 fallback 到老写死的样式
293
- let reminderMessage = defaultMessage || '{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}';
299
+ let reminderMessage = defaultMessage || '{user}({id}) 申请加入群 {gname}({group})\n申请理由: {question}\n匹配情况: {answer}/{threshold}';
294
300
 
295
301
  if (existingConfig) {
296
302
  reminderEnabled = existingConfig.reminderEnabled;
@@ -298,7 +304,7 @@ export function mergeReminder(
298
304
  reminderMessage = existingConfig.reminderMessage || reminderMessage;
299
305
  }
300
306
 
301
- // 优先级:disable > bare enable > new message content
307
+ // 优先级: disable > bare enable > new message content
302
308
  if (hasRealDisableMessageParam) {
303
309
  reminderEnabled = false;
304
310
  logger.debug('禁用提醒消息功能 (保留现有内容)');
@@ -321,7 +327,7 @@ export function mergeReminder(
321
327
  * 返回关键词数组和各类 flag 的值,未出现的 flag 保持 undefined。
322
328
  * 若检测到格式错误(如纯空格分隔关键词),返回 error 字段。
323
329
  */
324
- // 全局缓存:记录通过机器人自动批准的用户,供 guild-member-added 事件使用
330
+ // 全局缓存: 记录通过机器人自动批准的用户,供 guild-member-added 事件使用
325
331
  const autoQueue = new Map<string, Set<string>>();
326
332
 
327
333
  // 更新统计信息函数,提取到模块层供多个位置调用
@@ -381,7 +387,7 @@ export async function updateStats(ctx: Context, groupId: string, action: 'autoAp
381
387
  await syncTotalStats(ctx)
382
388
  }
383
389
 
384
- // 提取成独立函数:增加总入群计数,供事件统一调用
390
+ // 提取成独立函数: 增加总入群计数,供事件统一调用
385
391
  export async function incrementTotal(ctx: Context, groupId: string) {
386
392
  const existingStats = await ctx.database.get('group_verification_stats', { groupId })
387
393
  if (existingStats.length > 0) {
@@ -403,7 +409,8 @@ export async function incrementTotal(ctx: Context, groupId: string) {
403
409
  await syncTotalStats(ctx)
404
410
  }
405
411
 
406
- // helper to decide reviewParameters based on existing configuration, keyword list, user inputs,
412
+ // 根据已有配置、关键词列表及用户输入确定阈值参数的辅助函数,
413
+ // 并处理审核方式更改所致的自动调整
407
414
  // and whether the audit method has been changed by the command.
408
415
  export interface ThresholdResult {
409
416
  reviewParameters: number
@@ -525,7 +532,7 @@ export async function checkPermission(session: any, targetGroupId?: string): Pro
525
532
  return [false, '权限不足']
526
533
  }
527
534
 
528
- // 提供给测试的辅助函数:处理 guild-member-request 事件的逻辑
535
+ // 提供给测试的辅助函数: 处理 guild-member-request 事件的逻辑
529
536
  export async function handleGuildMemberRequestEvent(ctx: Context, session: any) {
530
537
  logger.debug('guild-member-request event', session)
531
538
  let guildId = (session.guildId || session.channelId || '').toString().trim();
@@ -603,7 +610,7 @@ export function parseConfigArgs(raw: string): ParsedArgs {
603
610
 
604
611
  const isFlag = (tok: string) => /^-(?:i|m|t|msg|nomsg|\?|r)$/.test(tok);
605
612
 
606
- // catch unescaped stray quotes before we unescape the sentinel symbols
613
+ // 在还原哨兵字符之前处理未转义的杂散引号
607
614
  for (const tok of tokens) {
608
615
  if (tok.includes('"')) {
609
616
  error = '存在未转义的引号';
@@ -611,7 +618,7 @@ export function parseConfigArgs(raw: string): ParsedArgs {
611
618
  }
612
619
  }
613
620
 
614
- // unescape sentinel placeholders back to real characters
621
+ // 将哨兵占位符还原为真实字符
615
622
  tokens = tokens.map(t =>
616
623
  t.replace(new RegExp(ESC_QUOTE, 'g'), '"').replace(new RegExp(ESC_BACKSLASH, 'g'), '\\')
617
624
  );
@@ -686,7 +693,7 @@ export function parseConfigArgs(raw: string): ParsedArgs {
686
693
  // remove everything from the first flag onward, including when flag sits at start
687
694
  const keywordSection = raw.split(/(?:^|\s+)-(?:i|m|t|msg|nomsg|\?|r)\b/)[0].trim();
688
695
  if (keywordSection && !validateKeywordFormat(keywordSection)) {
689
- error = '关键词应使用逗号分隔或引号框起(如:k1,k2,k3 或 "k1","k2" 或 "k1,k2",k3)';
696
+ error = '关键词应使用逗号分隔或引号框起(如: k1,k2,k3 或 "k1","k2" 或 "k1,k2",k3)';
690
697
  }
691
698
 
692
699
  return { keywords, flags, error };
@@ -781,7 +788,7 @@ export async function handleFailedVerification(
781
788
  // 无法获取群名称时使用默认值
782
789
  }
783
790
 
784
- // extract requestId if available (OneBot event attaches it)
791
+ // 如果可用则提取 requestIdOneBot 事件会附带)
785
792
  const requestId = ((session.event as any)?.requestId) || session.messageId || ''
786
793
 
787
794
  // 删除同一用户在该群之前的所有待审核记录,保留最新一个
@@ -868,7 +875,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
868
875
  if (!op || !['a','r','l','i'].includes(op)) {
869
876
  // present a multiline Chinese usage guide without angle brackets
870
877
  return [
871
- '用法:',
878
+ '用法: ',
872
879
  ' gvb a id [reason] [group] 将用户加入黑名单',
873
880
  ' gvb r id [group] 将用户移出黑名单',
874
881
  ' gvb l [group] 查询群黑名单',
@@ -886,7 +893,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
886
893
  targetUser = parts[1]
887
894
  if (!targetUser) return '请提供用户ID'
888
895
  // handle optional reason and group at end
889
- // 规则:如果只有一个附加参数,则作为 reason;两个及以上时最后一个为群号,其余拼成 reason
896
+ // 规则: 如果只有一个附加参数,则作为 reason;两个及以上时最后一个为群号,其余拼成 reason
890
897
  const rest = parts.slice(2)
891
898
  if (rest.length === 1) {
892
899
  reason = rest[0]
@@ -925,7 +932,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
925
932
  const row = rows[0]
926
933
  const entries = row.entries || {}
927
934
  if (entries[targetUser] !== undefined) {
928
- return `用户 ${targetUser} 已在群 ${group} 的黑名单中:${entries[targetUser]}`
935
+ return `用户 ${targetUser} 已在群 ${group} 的黑名单中: ${entries[targetUser]}`
929
936
  }
930
937
  entries[targetUser] = storedReason
931
938
  await ctx.database.set('group_verification_blacklist', { id: row.id }, { entries })
@@ -944,7 +951,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
944
951
  }
945
952
  }
946
953
  const tmpl = (config && config.blacklistAddSuccess) || '已将用户 {user} 加入群 {group} 黑名单{reason}'
947
- return tmpl.replace('{user}', targetUser).replace('{group}', group).replace('{reason}', reason ? `,原因:${reason}` : '')
954
+ return tmpl.replace('{user}', targetUser).replace('{group}', group).replace('{reason}', reason ? `,原因: ${reason}` : '')
948
955
  }
949
956
  if (op === 'r') {
950
957
  targetUser = parts[1]
@@ -1003,7 +1010,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1003
1010
  let prefix = group && group.toLowerCase() === 'all' ? '全局黑名单: \n' : `群 ${group} 黑名单: \n`
1004
1011
  let msg = prefix
1005
1012
  for (const uid in entries) {
1006
- msg += `${uid}${entries[uid] ? `:${entries[uid]}` : ''}\n`
1013
+ msg += `${uid}${entries[uid] ? `: ${entries[uid]}` : ''}\n`
1007
1014
  }
1008
1015
  return msg
1009
1016
  }
@@ -1014,7 +1021,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1014
1021
  const globalRows = await ctx.database.get('group_verification_blacklist', { groupId: 'all' })
1015
1022
  const globalHit = globalRows.length > 0 && (globalRows[0].entries || {})[targetUser] !== undefined
1016
1023
 
1017
- // helper to format reply using template
1024
+ // 使用模板格式化回复的辅助函数
1018
1025
  const tmpl = (config && config.blacklistInfoTemplate) || '全局黑名单: {global}\n本群黑名单: {group}'
1019
1026
  const formatReply = (localHit: boolean, groupsList?: string[]) => {
1020
1027
  if (groupsList) {
@@ -1040,7 +1047,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1040
1047
  // groupArg provided
1041
1048
  if (groupArg.toLowerCase() === 'all') {
1042
1049
  const auth = session.author?.authority || session.user?.authority
1043
- if (!(auth && auth >= 3)) return '权限不足:查看全局/所有群黑名单需要 koishi 3 级以上权限'
1050
+ if (!(auth && auth >= 3)) return '权限不足: 查看全局/所有群黑名单需要 koishi 3 级以上权限'
1044
1051
  const rows = await ctx.database.get('group_verification_blacklist', {})
1045
1052
  const globalReason = globalHit ? globalRows[0].entries[targetUser] : '无'
1046
1053
  const lines: string[] = []
@@ -1102,7 +1109,7 @@ export function apply(ctx: Context, config: Config) {
1102
1109
  if (config.logLevel) logger.level = config.logLevel
1103
1110
 
1104
1111
  // 设置日志级别
1105
- // 注意:Koishi logger的level设置可能需要不同的方式
1112
+ // 注意: Koishi logger的level设置可能需要不同的方式
1106
1113
 
1107
1114
  // 记录插件启动信息
1108
1115
  logger.info('群组验证插件已启动')
@@ -1132,7 +1139,7 @@ export function apply(ctx: Context, config: Config) {
1132
1139
  userId: 'string',
1133
1140
  userName: 'string',
1134
1141
  requestMessage: 'string',
1135
- // store the raw requestId if provided by OneBot event; used for approving/rejecting
1142
+ // 保存 OneBot 事件提供的原始 requestId;用于同意/拒绝操作
1136
1143
  requestId: 'string',
1137
1144
  // record full timestamp as string to keep time component
1138
1145
  applyTime: 'string'
@@ -1141,7 +1148,7 @@ export function apply(ctx: Context, config: Config) {
1141
1148
  autoInc: true
1142
1149
  })
1143
1150
 
1144
- // 黑名单表:每条记录对应一个群(或全局),entries 存储 userId->reason 键值对
1151
+ // 黑名单表: 每条记录对应一个群(或全局),entries 存储 userId->reason 键值对
1145
1152
  ctx.model.extend('group_verification_blacklist', {
1146
1153
  id: 'unsigned',
1147
1154
  groupId: 'string',
@@ -1281,7 +1288,7 @@ export function apply(ctx: Context, config: Config) {
1281
1288
 
1282
1289
  logger.debug(`权限检查 - 权限不足`)
1283
1290
  const debugInfo = `调试信息 - 用户ID:${session.userId}, 群号:${groupId}, 权限等级:${koishiAuthority || '未知'}`
1284
- return [false, (config.permissionDeniedMessage || '权限不足:需要群主/管理员权限或koishi三级以上权限') + `\n${debugInfo}`]
1291
+ return [false, (config.permissionDeniedMessage || '权限不足: 需要群主/管理员权限或koishi三级以上权限') + `\n${debugInfo}`]
1285
1292
  }
1286
1293
 
1287
1294
  // Create main command with aliases
@@ -1304,7 +1311,7 @@ export function apply(ctx: Context, config: Config) {
1304
1311
  .option('query', '-? 查询当前配置')
1305
1312
  .option('remove', '-r 删除配置')
1306
1313
  .action(async ({ session, options }: any, keywords: any) => {
1307
- // 详细调试:记录所有输入信息
1314
+ // 详细调试: 记录所有输入信息
1308
1315
  logger.info(`=== 命令解析调试 ===`)
1309
1316
  logger.info(`session内容: guildId=${session.guildId}, userId=${session.userId}`)
1310
1317
 
@@ -1329,7 +1336,7 @@ export function apply(ctx: Context, config: Config) {
1329
1336
  method: flags.method || (options.method === '' ? undefined : options.method),
1330
1337
  threshold: flags.threshold || options.threshold,
1331
1338
  message: flags.message !== undefined ? flags.message : options.message,
1332
- enableMessage: flags.enableMessage, // 新增:-msg 裸调用标记
1339
+ enableMessage: flags.enableMessage, // 新增: -msg 裸调用标记
1333
1340
  disableMessage: flags.disableMessage || options.disableMessage,
1334
1341
  query: flags.query || options.query,
1335
1342
  remove: flags.remove || options.remove,
@@ -1340,7 +1347,7 @@ export function apply(ctx: Context, config: Config) {
1340
1347
  if ((cleanedOptions.query || cleanedOptions.remove) &&
1341
1348
  (parsedKeywords.length > 0 || cleanedOptions.method !== undefined || cleanedOptions.threshold !== undefined ||
1342
1349
  cleanedOptions.message !== undefined || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
1343
- return config.parameterConflictMessage || '参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'
1350
+ return config.parameterConflictMessage || '参数冲突: -? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'
1344
1351
  }
1345
1352
 
1346
1353
  // 检查消息参数冲突
@@ -1377,7 +1384,7 @@ export function apply(ctx: Context, config: Config) {
1377
1384
 
1378
1385
  // 参数冲突检查
1379
1386
  if ((hasRealMessageParam || hasRealEnableMessageParam) && hasRealDisableMessageParam) {
1380
- return '参数冲突:不能同时使用 -msg 和 -nomsg'
1387
+ return '参数冲突: 不能同时使用 -msg 和 -nomsg'
1381
1388
  }
1382
1389
 
1383
1390
  // 重复参数检测
@@ -1450,7 +1457,7 @@ export function apply(ctx: Context, config: Config) {
1450
1457
  const updateTime = new Date(config.updatedAt).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
1451
1458
  const reminderStatus = config.reminderEnabled ? '启用' : '禁用'
1452
1459
 
1453
- return `群 ${targetGroupId} 配置:
1460
+ return `群 ${targetGroupId} 配置:
1454
1461
  关键词: ${decodedKeywords.join(', ')}
1455
1462
  审核方式: ${methodDesc}
1456
1463
  阈值: ${thresholdInfo}
@@ -1490,7 +1497,7 @@ export function apply(ctx: Context, config: Config) {
1490
1497
  existingConfig = existingConfigs[0]
1491
1498
  }
1492
1499
 
1493
- // 处理关键词:如果没有关键词但有其他参数,从现有配置中获取
1500
+ // 处理关键词: 如果没有关键词但有其他参数,从现有配置中获取
1494
1501
  if (keywordList.length === 0 && !cleanedOptions.query && !cleanedOptions.remove) {
1495
1502
  if (!existingConfig) {
1496
1503
  return config.noKeywordsMessage || '请先提供关键词创建配置,或使用 -? 查询配置,-r 删除配置'
@@ -1511,13 +1518,13 @@ export function apply(ctx: Context, config: Config) {
1511
1518
 
1512
1519
  // 解析审核方式和阈值(基于新的数据库模型设计)
1513
1520
  let reviewMethod: 0 | 1 | 2 | 3 = 0 // 默认值
1514
- let reviewParameters: number = 0 // 直接存储数字:0表示无阈值
1521
+ let reviewParameters: number = 0 // 直接存储数字: 0表示无阈值
1515
1522
 
1516
1523
  if (existingConfig) {
1517
1524
  // 默认使用现有配置的参数
1518
1525
  reviewMethod = existingConfig.reviewMethod
1519
1526
 
1520
- // 老版本兼容性处理:处理可能的NaN或无效值
1527
+ // 老版本兼容性处理: 处理可能的NaN或无效值
1521
1528
  if (existingConfig.reviewParameters === undefined ||
1522
1529
  existingConfig.reviewParameters === null ||
1523
1530
  isNaN(existingConfig.reviewParameters)) {
@@ -1532,7 +1539,7 @@ export function apply(ctx: Context, config: Config) {
1532
1539
  if (cleanedOptions.method !== undefined && cleanedOptions.method !== '') {
1533
1540
  const methodNum = parseInt(cleanedOptions.method)
1534
1541
  if (isNaN(methodNum) || methodNum < 0 || methodNum > 3) {
1535
- return '审核方式参数错误:0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝'
1542
+ return '审核方式参数错误: 0-全部同意, 1-按数量同意, 2-按比例同意, 3-全部拒绝'
1536
1543
  }
1537
1544
  const oldMethod = reviewMethod
1538
1545
  reviewMethod = methodNum as 0 | 1 | 2 | 3
@@ -1872,16 +1879,16 @@ export function apply(ctx: Context, config: Config) {
1872
1879
  'gvs'
1873
1880
  )
1874
1881
  .action(async ({ session }: any, target: any) => {
1875
- // 参数验证:只能是群号、all、total或空
1882
+ // 参数验证: 只能是群号、all、total或空
1876
1883
  const validTargets = ['all', 'total']
1877
1884
  const isGroupId = target && /^\d+$/.test(target)
1878
1885
  const isSpecialTarget = target && validTargets.includes(target.toLowerCase())
1879
1886
 
1880
1887
  if (target && !isGroupId && !isSpecialTarget) {
1881
- return '参数错误:只能指定群号、all、total或留空'
1888
+ return '参数错误: 只能指定群号、all、total或留空'
1882
1889
  }
1883
1890
 
1884
- // 权限检查:总计统计需要3级以上权限
1891
+ // 权限检查: 总计统计需要3级以上权限
1885
1892
  if (target?.toLowerCase() === 'total' || target?.toLowerCase() === 'all') {
1886
1893
  // 检查是否为koishi 3级以上权限
1887
1894
  const koishiAuthority = (session as any).author?.authority || (session as any).user?.authority
@@ -1894,7 +1901,7 @@ export function apply(ctx: Context, config: Config) {
1894
1901
 
1895
1902
  // 处理不同参数情况
1896
1903
  if (!target) {
1897
- // 无参数:显示当前群统计
1904
+ // 无参数: 显示当前群统计
1898
1905
  if (!groupId) {
1899
1906
  return '请在群聊中使用此命令或指定群号'
1900
1907
  }
@@ -1935,11 +1942,11 @@ export function apply(ctx: Context, config: Config) {
1935
1942
  return '当前没有待审核的加群申请'
1936
1943
  }
1937
1944
 
1938
- let result = '待审核申请列表:\n'
1945
+ let result = '待审核申请列表: \n'
1939
1946
  pendingApplications.forEach((app, index) => {
1940
1947
  result += `${index + 1}. ${app.userName}(${app.userId})
1941
- 申请时间:${app.applyTime.toLocaleString()}
1942
- 申请理由:${app.requestMessage}
1948
+ 申请时间: ${app.applyTime.toLocaleString()}
1949
+ 申请理由: ${app.requestMessage}
1943
1950
 
1944
1951
  `
1945
1952
  })
@@ -1964,19 +1971,19 @@ export function apply(ctx: Context, config: Config) {
1964
1971
  .subcommand('.help', '显示帮助信息')
1965
1972
  .alias('gv.帮助', 'gverify.帮助', 'group-verify.帮助', '帮助', 'hlp', '帮助信息')
1966
1973
  .action(() => {
1967
- return `群组验证命令帮助:
1968
- 主指令别名:gv, gverify
1974
+ return `群组验证命令帮助:
1975
+ 主指令别名: gv, gverify
1969
1976
 
1970
1977
  配置命令 (.config/.cfg):
1971
- 用法:
1972
- 1. 创建新配置:gv.cfg 关键词1,关键词2 -m 1 -t 2
1973
- 2. 修改现有配置:gv.cfg -m 1 -t 2 (不提供关键词)
1974
- 3. 启用提醒消息:gv.cfg -msg "消息内容"
1975
- 4. 禁用提醒消息:gv.cfg -nomsg
1976
- 5. 查询配置:gv.cfg -?
1977
- 6. 删除配置:gv.cfg -r
1978
+ 用法:
1979
+ 1. 创建新配置: gv.cfg 关键词1,关键词2 -m 1 -t 2
1980
+ 2. 修改现有配置: gv.cfg -m 1 -t 2 (不提供关键词)
1981
+ 3. 启用提醒消息: gv.cfg -msg "消息内容"
1982
+ 4. 禁用提醒消息: gv.cfg -nomsg
1983
+ 5. 查询配置: gv.cfg -?
1984
+ 6. 删除配置: gv.cfg -r
1978
1985
 
1979
- 参数说明:
1986
+ 参数说明:
1980
1987
  -i <群号> 指定群号(私聊时必需)
1981
1988
  -m <方式> 审核方式 (0=全部同意, 1=按数量, 2=按比例, 3=全部拒绝)
1982
1989
  -t <阈值> 阈值参数(方式1:0-关键词数, 方式2:0-100)
@@ -1988,19 +1995,19 @@ export function apply(ctx: Context, config: Config) {
1988
1995
  -? 查询当前配置
1989
1996
  -r 删除配置
1990
1997
 
1991
- 引号使用规则:
1992
- 关键词包含空格:gv.cfg "关键词1,关键词 2,关键词3"
1993
- 提醒消息包含空格:gv.cfg -msg "这是包含空格的消息"
1994
- 内部引号转义:gv.cfg -msg "包含\\"引号\\"的内容"
1995
- 换行符:gv.cfg -msg "第一行\\n第二行"
1998
+ 引号使用规则:
1999
+ 关键词包含空格: gv.cfg "关键词1,关键词 2,关键词3"
2000
+ 提醒消息包含空格: gv.cfg -msg "这是包含空格的消息"
2001
+ 内部引号转义: gv.cfg -msg "包含\\"引号\\"的内容"
2002
+ 换行符: gv.cfg -msg "第一行\\n第二行"
1996
2003
 
1997
- 特殊说明:
2004
+ 特殊说明:
1998
2005
  • 阈值可设为0表示全部同意
1999
2006
  • 关键词数量变化时阈值会自动调整
2000
2007
  • 重复参数会使用最后出现的值并提醒
2001
2008
  • 群号检查可在插件配置中开关
2002
2009
 
2003
- 提醒消息变量:
2010
+ 提醒消息变量:
2004
2011
  {user} - 用户名
2005
2012
  {id} - 用户ID
2006
2013
  {group} - 群号
@@ -2009,9 +2016,9 @@ export function apply(ctx: Context, config: Config) {
2009
2016
  {answer} - 答对数量/比例
2010
2017
  {threshold} - 阈值要求
2011
2018
 
2012
- 使用示例:
2019
+ 使用示例:
2013
2020
  gv.cfg "关键词1,关键词2" -m 1 -t 2
2014
- gv.cfg -msg "用户 {user}\\n申请理由:{question}"
2021
+ gv.cfg -msg "用户 {user}\\n申请理由: {question}"
2015
2022
  gv.cfg -m 2 -t 80
2016
2023
  gv.cfg -nomsg
2017
2024
 
@@ -2021,21 +2028,21 @@ export function apply(ctx: Context, config: Config) {
2021
2028
  l [group] 查询群黑名单;传入 all 时查看全局黑名单
2022
2029
  i id [group] 查询账号黑名单;不带参数时查询本群与全局,
2023
2030
  指定群号查询该群与全局,传入 all 则列出所有群及全局(需Koishi 3级)
2024
- 使用示例:
2031
+ 使用示例:
2025
2032
  gvb a 12345 作弊记录
2026
2033
  gvb r 12345 67890
2027
2034
  gvb l
2028
2035
  gvb l all
2029
2036
  gvb i 12345
2030
2037
 
2031
- 快捷命令:
2038
+ 快捷命令:
2032
2039
  gvc - 配置命令快捷方式
2033
2040
  gva - 同意申请快捷命令
2034
2041
  gvr - 拒绝申请快捷命令
2035
2042
  gvp - 查看待审核列表快捷命令
2036
2043
  gvs - 查看统计信息快捷命令
2037
2044
 
2038
- 权限说明:
2045
+ 权限说明:
2039
2046
  - 群主/管理员权限
2040
2047
  - koishi三级以上权限
2041
2048
  - 私聊时必须指定群号(-i参数)`
@@ -2043,7 +2050,7 @@ export function apply(ctx: Context, config: Config) {
2043
2050
 
2044
2051
  // 插件初始化时确保总计统计行存在
2045
2052
  ctx.on('ready', async () => {
2046
- // 迁移:将旧版保留的 Date 对象字段转换为 ISO 字符串,以避免绑定错误
2053
+ // 迁移: 将旧版保留的 Date 对象字段转换为 ISO 字符串,以避免绑定错误
2047
2054
  try {
2048
2055
  const configs = await ctx.database.get('group_verification_config', {})
2049
2056
  for (const cfg of configs) {
@@ -2100,7 +2107,7 @@ export function apply(ctx: Context, config: Config) {
2100
2107
  await syncTotalStats(ctx)
2101
2108
  })
2102
2109
 
2103
- // 辅助函数:获取群组统计
2110
+ // 辅助函数: 获取群组统计
2104
2111
  async function getGroupStats(groupId: string): Promise<string> {
2105
2112
  const stats = await ctx.database.get('group_verification_stats', { groupId })
2106
2113
 
@@ -2111,14 +2118,14 @@ export function apply(ctx: Context, config: Config) {
2111
2118
  const stat = stats[0]
2112
2119
  const lastUpdated = new Date(stat.lastUpdated).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
2113
2120
 
2114
- return `群 ${groupId} 验证统计:
2121
+ return `群 ${groupId} 验证统计:
2115
2122
  自动批准: ${stat.autoApproved}
2116
2123
  手动批准: ${stat.manuallyApproved}
2117
2124
  拒绝: ${stat.rejected}
2118
2125
  最后更新: ${lastUpdated}`
2119
2126
  }
2120
2127
 
2121
- // 辅助函数:获取总计统计
2128
+ // 辅助函数: 获取总计统计
2122
2129
  async function getTotalStats(): Promise<string> {
2123
2130
  const stats = await ctx.database.get('group_verification_stats', { groupId: 'TOTAL' })
2124
2131
 
@@ -2129,7 +2136,7 @@ export function apply(ctx: Context, config: Config) {
2129
2136
  const stat = stats[0]
2130
2137
  const lastUpdated = new Date(stat.lastUpdated).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
2131
2138
 
2132
- return `总计验证统计:
2139
+ return `总计验证统计:
2133
2140
  自动批准: ${stat.autoApproved}
2134
2141
  手动批准: ${stat.manuallyApproved}
2135
2142
  拒绝: ${stat.rejected}