koishi-plugin-group-verification 1.0.31 → 1.0.33

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
@@ -348,7 +348,7 @@ async function checkPermission(session, targetGroupId) {
348
348
  });
349
349
  }
350
350
  if (koishiAuthority && koishiAuthority >= 3) {
351
- logger.info(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`);
351
+ logger.debug(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`);
352
352
  return [true];
353
353
  }
354
354
  try {
@@ -661,7 +661,9 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
661
661
  targetUser = parts[1];
662
662
  if (!targetUser) return "请提供用户ID";
663
663
  const rest = parts.slice(2);
664
- if (rest.length > 0) {
664
+ if (rest.length === 1) {
665
+ reason = rest[0];
666
+ } else if (rest.length > 1) {
665
667
  const last = rest[rest.length - 1];
666
668
  if (/^\d+$/.test(last) || last.toLowerCase() === "all") {
667
669
  group = last;
@@ -700,6 +702,14 @@ async function processBlacklistCommand(ctx, session, rawArgs, config) {
700
702
  entries[targetUser] = storedReason;
701
703
  await ctx.database.create("group_verification_blacklist", { groupId: group, entries });
702
704
  }
705
+ if (session.bot && typeof session.bot.kickGuildMember === "function") {
706
+ try {
707
+ await session.bot.kickGuildMember(group, targetUser);
708
+ logger.info(`已将黑名单用户 ${targetUser} 从群 ${group} 踢出`);
709
+ } catch (e) {
710
+ logger.warn(`踢出用户 ${targetUser} 失败`, e);
711
+ }
712
+ }
703
713
  const tmpl = config && config.blacklistAddSuccess || "已将用户 {user} 加入群 {group} 黑名单{reason}";
704
714
  return tmpl.replace("{user}", targetUser).replace("{group}", group).replace("{reason}", reason ? `,原因:${reason}` : "");
705
715
  }
@@ -872,7 +882,7 @@ function apply(ctx, config) {
872
882
  userId: "string",
873
883
  userName: "string",
874
884
  requestMessage: "string",
875
- // store the raw requestId if provided by OneBot event; used for approving/rejecting
885
+ // 保存 OneBot 事件提供的原始 requestId;用于同意/拒绝操作
876
886
  requestId: "string",
877
887
  // record full timestamp as string to keep time component
878
888
  applyTime: "string"
package/package.json CHANGED
@@ -1,51 +1,50 @@
1
- {
2
- "name": "koishi-plugin-group-verification",
3
- "description": "Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式和详细统计功能",
4
-
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/LHDyx/koishi-plugin-group-verification.git"
8
- },
9
- "bugs": {
10
- "url": "https://github.com/LHDyx/koishi-plugin-group-verification/issues"
11
- },
12
- "version": "1.0.31",
13
- "main": "lib/index.js",
14
- "typings": "lib/index.d.ts",
15
- "files": [
16
- "lib",
17
- "dist",
18
- "src",
19
- "README.md",
20
- "USAGE_EXAMPLE.md"
21
- ],
22
- "scripts": {
23
- "build": "tsc",
24
- "dev": "tsc -w",
25
- "test": "ts-node test/basic-test.ts"
26
- },
27
- "license": "MIT",
28
- "keywords": [
29
- "chatbot",
30
- "koishi",
31
- "plugin",
32
- "group-verification",
33
- "guild-management",
34
- "join-request",
35
- "moderation"
36
- ],
37
- "koishi": {
38
- "service": {
39
- "required": [
40
- "database"
41
- ]
42
- }
43
- },
44
- "peerDependencies": {
45
- "koishi": "^4.15.0"
46
- },
47
- "devDependencies": {
48
- "typescript": "^4.9.0",
49
- "@types/node": "^16.0.0"
50
- }
51
- }
1
+ {
2
+ "name": "koishi-plugin-group-verification",
3
+ "description": "Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式和详细统计功能",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/LHDyx/koishi-plugin-group-verification.git"
7
+ },
8
+ "bugs": {
9
+ "url": "https://github.com/LHDyx/koishi-plugin-group-verification/issues"
10
+ },
11
+ "version": "1.0.33",
12
+ "main": "lib/index.js",
13
+ "typings": "lib/index.d.ts",
14
+ "files": [
15
+ "lib",
16
+ "dist",
17
+ "src",
18
+ "README.md",
19
+ "USAGE_EXAMPLE.md"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "dev": "tsc -w",
24
+ "test": "ts-node test/basic-test.ts"
25
+ },
26
+ "license": "MIT",
27
+ "keywords": [
28
+ "chatbot",
29
+ "koishi",
30
+ "plugin",
31
+ "group-verification",
32
+ "guild-management",
33
+ "join-request",
34
+ "moderation"
35
+ ],
36
+ "koishi": {
37
+ "service": {
38
+ "required": [
39
+ "database"
40
+ ]
41
+ }
42
+ },
43
+ "peerDependencies": {
44
+ "koishi": "^4.15.0"
45
+ },
46
+ "devDependencies": {
47
+ "typescript": "^4.9.0",
48
+ "@types/node": "^16.0.0"
49
+ }
50
+ }
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
@@ -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
  }
@@ -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++;
@@ -226,7 +226,7 @@ export interface ParsedArgs {
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;
@@ -403,7 +403,8 @@ export async function incrementTotal(ctx: Context, groupId: string) {
403
403
  await syncTotalStats(ctx)
404
404
  }
405
405
 
406
- // helper to decide reviewParameters based on existing configuration, keyword list, user inputs,
406
+ // 根据已有配置、关键词列表及用户输入确定阈值参数的辅助函数,
407
+ // 并处理审核方式更改所致的自动调整
407
408
  // and whether the audit method has been changed by the command.
408
409
  export interface ThresholdResult {
409
410
  reviewParameters: number
@@ -498,7 +499,7 @@ export async function checkPermission(session: any, targetGroupId?: string): Pro
498
499
  }
499
500
 
500
501
  if (koishiAuthority && koishiAuthority >= 3) {
501
- logger.info(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`)
502
+ logger.debug(`权限检查 - 通过koishi权限检查: ${koishiAuthority}`)
502
503
  return [true]
503
504
  }
504
505
 
@@ -603,7 +604,7 @@ export function parseConfigArgs(raw: string): ParsedArgs {
603
604
 
604
605
  const isFlag = (tok: string) => /^-(?:i|m|t|msg|nomsg|\?|r)$/.test(tok);
605
606
 
606
- // catch unescaped stray quotes before we unescape the sentinel symbols
607
+ // 在还原哨兵字符之前处理未转义的杂散引号
607
608
  for (const tok of tokens) {
608
609
  if (tok.includes('"')) {
609
610
  error = '存在未转义的引号';
@@ -611,7 +612,7 @@ export function parseConfigArgs(raw: string): ParsedArgs {
611
612
  }
612
613
  }
613
614
 
614
- // unescape sentinel placeholders back to real characters
615
+ // 将哨兵占位符还原为真实字符
615
616
  tokens = tokens.map(t =>
616
617
  t.replace(new RegExp(ESC_QUOTE, 'g'), '"').replace(new RegExp(ESC_BACKSLASH, 'g'), '\\')
617
618
  );
@@ -781,7 +782,7 @@ export async function handleFailedVerification(
781
782
  // 无法获取群名称时使用默认值
782
783
  }
783
784
 
784
- // extract requestId if available (OneBot event attaches it)
785
+ // 如果可用则提取 requestIdOneBot 事件会附带)
785
786
  const requestId = ((session.event as any)?.requestId) || session.messageId || ''
786
787
 
787
788
  // 删除同一用户在该群之前的所有待审核记录,保留最新一个
@@ -886,13 +887,17 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
886
887
  targetUser = parts[1]
887
888
  if (!targetUser) return '请提供用户ID'
888
889
  // handle optional reason and group at end
890
+ // 规则:如果只有一个附加参数,则作为 reason;两个及以上时最后一个为群号,其余拼成 reason
889
891
  const rest = parts.slice(2)
890
- if (rest.length > 0) {
892
+ if (rest.length === 1) {
893
+ reason = rest[0]
894
+ } else if (rest.length > 1) {
891
895
  const last = rest[rest.length - 1]
892
896
  if (/^\d+$/.test(last) || last.toLowerCase() === 'all') {
893
897
  group = last
894
898
  reason = rest.slice(0, -1).join(' ')
895
899
  } else {
900
+ // 虽然数量>=2,但最后一个不是数字,全部作为reason
896
901
  reason = rest.join(' ')
897
902
  }
898
903
  }
@@ -930,6 +935,15 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
930
935
  entries[targetUser] = storedReason
931
936
  await ctx.database.create('group_verification_blacklist', { groupId: group, entries })
932
937
  }
938
+ // 添加成功后尝试踢人
939
+ if (session.bot && typeof session.bot.kickGuildMember === 'function') {
940
+ try {
941
+ await session.bot.kickGuildMember(group, targetUser)
942
+ logger.info(`已将黑名单用户 ${targetUser} 从群 ${group} 踢出`)
943
+ } catch (e) {
944
+ logger.warn(`踢出用户 ${targetUser} 失败`, e)
945
+ }
946
+ }
933
947
  const tmpl = (config && config.blacklistAddSuccess) || '已将用户 {user} 加入群 {group} 黑名单{reason}'
934
948
  return tmpl.replace('{user}', targetUser).replace('{group}', group).replace('{reason}', reason ? `,原因:${reason}` : '')
935
949
  }
@@ -1001,7 +1015,7 @@ export async function processBlacklistCommand(ctx: Context, session: any, rawArg
1001
1015
  const globalRows = await ctx.database.get('group_verification_blacklist', { groupId: 'all' })
1002
1016
  const globalHit = globalRows.length > 0 && (globalRows[0].entries || {})[targetUser] !== undefined
1003
1017
 
1004
- // helper to format reply using template
1018
+ // 使用模板格式化回复的辅助函数
1005
1019
  const tmpl = (config && config.blacklistInfoTemplate) || '全局黑名单: {global}\n本群黑名单: {group}'
1006
1020
  const formatReply = (localHit: boolean, groupsList?: string[]) => {
1007
1021
  if (groupsList) {
@@ -1119,7 +1133,7 @@ export function apply(ctx: Context, config: Config) {
1119
1133
  userId: 'string',
1120
1134
  userName: 'string',
1121
1135
  requestMessage: 'string',
1122
- // store the raw requestId if provided by OneBot event; used for approving/rejecting
1136
+ // 保存 OneBot 事件提供的原始 requestId;用于同意/拒绝操作
1123
1137
  requestId: 'string',
1124
1138
  // record full timestamp as string to keep time component
1125
1139
  applyTime: 'string'