koishi-plugin-group-verification 1.0.18 → 1.0.19
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 +5 -0
- package/lib/index.js +66 -54
- package/package.json +1 -1
- package/src/index.ts +92 -79
package/lib/index.d.ts
CHANGED
|
@@ -95,4 +95,9 @@ export declare function mergeReminder(existingConfig: any | null, cleanedOptions
|
|
|
95
95
|
* 若检测到格式错误(如纯空格分隔关键词),返回 error 字段。
|
|
96
96
|
*/
|
|
97
97
|
export declare function parseConfigArgs(raw: string): ParsedArgs;
|
|
98
|
+
export declare function verifyApplication(config: GroupVerificationConfig, message: string, session: any): Promise<{
|
|
99
|
+
isValid: boolean;
|
|
100
|
+
matchedCount: number;
|
|
101
|
+
requiredThreshold: string;
|
|
102
|
+
}>;
|
|
98
103
|
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/index.js
CHANGED
|
@@ -26,11 +26,13 @@ __export(src_exports, {
|
|
|
26
26
|
mergeReminder: () => mergeReminder,
|
|
27
27
|
name: () => name,
|
|
28
28
|
parseConfigArgs: () => parseConfigArgs,
|
|
29
|
-
tokenize: () => tokenize
|
|
29
|
+
tokenize: () => tokenize,
|
|
30
|
+
verifyApplication: () => verifyApplication
|
|
30
31
|
});
|
|
31
32
|
module.exports = __toCommonJS(src_exports);
|
|
32
33
|
var import_koishi = require("koishi");
|
|
33
34
|
var name = "group-verification";
|
|
35
|
+
var logger = console;
|
|
34
36
|
var Config = import_koishi.Schema.object({
|
|
35
37
|
defaultReminderMessage: import_koishi.Schema.string().description("默认提醒消息模板").default("{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}"),
|
|
36
38
|
enableStrictGroupCheck: import_koishi.Schema.boolean().description("是否启用严格的群号检查(检查群号长度)").default(false),
|
|
@@ -140,7 +142,7 @@ function validateKeywordFormat(raw) {
|
|
|
140
142
|
return true;
|
|
141
143
|
}
|
|
142
144
|
__name(validateKeywordFormat, "validateKeywordFormat");
|
|
143
|
-
function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasRealEnableMessageParam, hasRealDisableMessageParam,
|
|
145
|
+
function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasRealEnableMessageParam, hasRealDisableMessageParam, logger2) {
|
|
144
146
|
let reminderEnabled = true;
|
|
145
147
|
let reminderMessage = "{user}({id}) 申请加入群 {gname}({group})\n申请理由:{question}\n匹配情况:{answer}/{threshold}";
|
|
146
148
|
if (existingConfig) {
|
|
@@ -149,15 +151,15 @@ function mergeReminder(existingConfig, cleanedOptions, hasRealMessageParam, hasR
|
|
|
149
151
|
}
|
|
150
152
|
if (hasRealDisableMessageParam) {
|
|
151
153
|
reminderEnabled = false;
|
|
152
|
-
|
|
154
|
+
logger2.info("禁用提醒消息功能 (保留现有内容)");
|
|
153
155
|
} else if (hasRealEnableMessageParam) {
|
|
154
156
|
reminderEnabled = true;
|
|
155
|
-
|
|
157
|
+
logger2.info(`启用提醒消息(保留原消息): ${reminderMessage.substring(0, 50)}...`);
|
|
156
158
|
} else if (hasRealMessageParam) {
|
|
157
159
|
reminderEnabled = true;
|
|
158
160
|
if (cleanedOptions.message !== void 0) {
|
|
159
161
|
reminderMessage = cleanedOptions.message.replace(/\\n/g, "\n");
|
|
160
|
-
|
|
162
|
+
logger2.info(`设置自定义提醒消息: ${reminderMessage.substring(0, 50)}...`);
|
|
161
163
|
}
|
|
162
164
|
}
|
|
163
165
|
return { reminderEnabled, reminderMessage };
|
|
@@ -247,6 +249,44 @@ function parseConfigArgs(raw) {
|
|
|
247
249
|
return { keywords, flags, error };
|
|
248
250
|
}
|
|
249
251
|
__name(parseConfigArgs, "parseConfigArgs");
|
|
252
|
+
async function verifyApplication(config, message, session) {
|
|
253
|
+
const lowered = message.toLowerCase();
|
|
254
|
+
const matched = /* @__PURE__ */ new Set();
|
|
255
|
+
for (const keyword of config.keywords) {
|
|
256
|
+
if (lowered.includes(keyword.toLowerCase())) {
|
|
257
|
+
matched.add(keyword);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const matchedCount = matched.size;
|
|
261
|
+
let isValid = false;
|
|
262
|
+
let requiredThreshold = "";
|
|
263
|
+
switch (config.reviewMethod) {
|
|
264
|
+
case 0:
|
|
265
|
+
isValid = true;
|
|
266
|
+
requiredThreshold = "null";
|
|
267
|
+
break;
|
|
268
|
+
case 1:
|
|
269
|
+
isValid = matchedCount >= (config.reviewParameters || 1);
|
|
270
|
+
requiredThreshold = `${config.reviewParameters || 1}`;
|
|
271
|
+
break;
|
|
272
|
+
case 2:
|
|
273
|
+
const ratio = matchedCount / config.keywords.length;
|
|
274
|
+
const requiredRatio = (config.reviewParameters || 100) / 100;
|
|
275
|
+
isValid = ratio >= requiredRatio;
|
|
276
|
+
requiredThreshold = `${config.reviewParameters || 100}%`;
|
|
277
|
+
break;
|
|
278
|
+
case 3:
|
|
279
|
+
isValid = false;
|
|
280
|
+
requiredThreshold = "null";
|
|
281
|
+
break;
|
|
282
|
+
default:
|
|
283
|
+
isValid = false;
|
|
284
|
+
requiredThreshold = "null";
|
|
285
|
+
}
|
|
286
|
+
logger.info(`verifyApplication msg="${message}" keywords=${JSON.stringify(config.keywords)} matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
|
|
287
|
+
return { isValid, matchedCount, requiredThreshold };
|
|
288
|
+
}
|
|
289
|
+
__name(verifyApplication, "verifyApplication");
|
|
250
290
|
function apply(ctx, config) {
|
|
251
291
|
ctx.model.extend("group_verification_config", {
|
|
252
292
|
id: "unsigned",
|
|
@@ -265,7 +305,7 @@ function apply(ctx, config) {
|
|
|
265
305
|
primary: "id",
|
|
266
306
|
autoInc: true
|
|
267
307
|
});
|
|
268
|
-
|
|
308
|
+
logger = ctx.logger("group-verification");
|
|
269
309
|
logger.info("群组验证插件已启动");
|
|
270
310
|
logger.info(`默认提醒消息: ${config.defaultReminderMessage}`);
|
|
271
311
|
logger.info(`严格群号检查: ${config.enableStrictGroupCheck ? "启用" : "禁用"}`);
|
|
@@ -292,6 +332,7 @@ function apply(ctx, config) {
|
|
|
292
332
|
primary: "id",
|
|
293
333
|
autoInc: true
|
|
294
334
|
});
|
|
335
|
+
const autoQueue = /* @__PURE__ */ new Map();
|
|
295
336
|
ctx.on("guild-member-request", async (session) => {
|
|
296
337
|
logger.debug("guild-member-request event", session);
|
|
297
338
|
let guildId = (session.guildId || session.channelId || "").toString().trim();
|
|
@@ -302,16 +343,6 @@ function apply(ctx, config) {
|
|
|
302
343
|
return;
|
|
303
344
|
}
|
|
304
345
|
const requestId = session.event?.requestId || session.messageId || "";
|
|
305
|
-
if (requestId) {
|
|
306
|
-
try {
|
|
307
|
-
await session.bot.handleGuildMemberRequest(requestId, true);
|
|
308
|
-
logger.info(`自动同意申请 requestId=${requestId}`);
|
|
309
|
-
await updateStats(guildId, "autoApproved");
|
|
310
|
-
return;
|
|
311
|
-
} catch (e) {
|
|
312
|
-
logger.warn("自动同意失败", e);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
346
|
const groupConfig = await ctx.database.get("group_verification_config", {
|
|
316
347
|
groupId: guildId
|
|
317
348
|
});
|
|
@@ -320,16 +351,18 @@ function apply(ctx, config) {
|
|
|
320
351
|
}
|
|
321
352
|
const config2 = groupConfig[0];
|
|
322
353
|
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config2, message, session);
|
|
354
|
+
logger.info(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
|
|
323
355
|
if (isValid) {
|
|
324
356
|
if (requestId) {
|
|
325
357
|
try {
|
|
326
358
|
await session.bot.handleGuildMemberRequest(requestId, true);
|
|
327
|
-
logger.info(
|
|
359
|
+
logger.info(`自动同意 requestId=${requestId}`);
|
|
360
|
+
if (!autoQueue.has(guildId)) autoQueue.set(guildId, /* @__PURE__ */ new Set());
|
|
361
|
+
autoQueue.get(guildId).add(userId);
|
|
328
362
|
} catch (e) {
|
|
329
363
|
logger.warn("自动同意失败", e);
|
|
330
364
|
}
|
|
331
365
|
}
|
|
332
|
-
await updateStats(guildId, "autoApproved");
|
|
333
366
|
} else {
|
|
334
367
|
await handleFailedVerification(ctx, session, config2);
|
|
335
368
|
}
|
|
@@ -337,6 +370,13 @@ function apply(ctx, config) {
|
|
|
337
370
|
ctx.on("guild-member-added", async (session) => {
|
|
338
371
|
const groupId = session.guildId;
|
|
339
372
|
const userId = session.userId;
|
|
373
|
+
const set = autoQueue.get(groupId);
|
|
374
|
+
if (set && set.has(userId)) {
|
|
375
|
+
await updateStats(groupId, "autoApproved");
|
|
376
|
+
set.delete(userId);
|
|
377
|
+
logger.info(`用户 ${userId} 通过机器人审批加入群 ${groupId}(autoQueue),统计已更新`);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
340
380
|
const pendingRecords = await ctx.database.get("group_verification_pending", {
|
|
341
381
|
groupId,
|
|
342
382
|
userId
|
|
@@ -382,38 +422,6 @@ function apply(ctx, config) {
|
|
|
382
422
|
await ctx2.broadcast([guildId], reminderMsg);
|
|
383
423
|
}
|
|
384
424
|
__name(handleFailedVerification, "handleFailedVerification");
|
|
385
|
-
async function verifyApplication(config2, message, session) {
|
|
386
|
-
const matchedCount = config2.keywords.filter(
|
|
387
|
-
(keyword) => message.toLowerCase().includes(keyword.toLowerCase())
|
|
388
|
-
).length;
|
|
389
|
-
let isValid = false;
|
|
390
|
-
let requiredThreshold = "";
|
|
391
|
-
switch (config2.reviewMethod) {
|
|
392
|
-
case 0:
|
|
393
|
-
isValid = true;
|
|
394
|
-
requiredThreshold = "null";
|
|
395
|
-
break;
|
|
396
|
-
case 1:
|
|
397
|
-
isValid = matchedCount >= (config2.reviewParameters || 1);
|
|
398
|
-
requiredThreshold = `${config2.reviewParameters || 1}`;
|
|
399
|
-
break;
|
|
400
|
-
case 2:
|
|
401
|
-
const ratio = matchedCount / config2.keywords.length;
|
|
402
|
-
const requiredRatio = (config2.reviewParameters || 100) / 100;
|
|
403
|
-
isValid = ratio >= requiredRatio;
|
|
404
|
-
requiredThreshold = `${config2.reviewParameters || 100}%`;
|
|
405
|
-
break;
|
|
406
|
-
case 3:
|
|
407
|
-
isValid = false;
|
|
408
|
-
requiredThreshold = "null";
|
|
409
|
-
break;
|
|
410
|
-
default:
|
|
411
|
-
isValid = false;
|
|
412
|
-
requiredThreshold = "null";
|
|
413
|
-
}
|
|
414
|
-
return { isValid, matchedCount, requiredThreshold };
|
|
415
|
-
}
|
|
416
|
-
__name(verifyApplication, "verifyApplication");
|
|
417
425
|
async function updateStats(groupId, action) {
|
|
418
426
|
const existingStats = await ctx.database.get("group_verification_stats", { groupId });
|
|
419
427
|
if (existingStats.length > 0) {
|
|
@@ -805,7 +813,8 @@ gvc -r # 删除配置`;
|
|
|
805
813
|
for (const request2 of pendingRequests2) {
|
|
806
814
|
try {
|
|
807
815
|
await ctx.database.remove("group_verification_pending", { id: request2.id });
|
|
808
|
-
|
|
816
|
+
if (!autoQueue.has(groupId)) autoQueue.set(groupId, /* @__PURE__ */ new Set());
|
|
817
|
+
autoQueue.get(groupId).add(request2.userId);
|
|
809
818
|
approvedCount++;
|
|
810
819
|
} catch (error) {
|
|
811
820
|
logger.warn(`处理申请 ${request2.id} 时出错:`, error);
|
|
@@ -821,7 +830,8 @@ gvc -r # 删除配置`;
|
|
|
821
830
|
try {
|
|
822
831
|
await session.bot.handleGuildMemberRequest(request2.userId, true);
|
|
823
832
|
await ctx.database.remove("group_verification_pending", { id: request2.id });
|
|
824
|
-
|
|
833
|
+
if (!autoQueue.has(groupId)) autoQueue.set(groupId, /* @__PURE__ */ new Set());
|
|
834
|
+
autoQueue.get(groupId).add(request2.userId);
|
|
825
835
|
return `已同意用户 ${request2.userName}(${request2.userId}) 的加群申请`;
|
|
826
836
|
} catch (error) {
|
|
827
837
|
return `处理申请时出错: ${error.message}`;
|
|
@@ -839,7 +849,8 @@ gvc -r # 删除配置`;
|
|
|
839
849
|
try {
|
|
840
850
|
await session.bot.handleGuildMemberRequest(request.userId, true);
|
|
841
851
|
await ctx.database.remove("group_verification_pending", { id: request.id });
|
|
842
|
-
|
|
852
|
+
if (!autoQueue.has(groupId)) autoQueue.set(groupId, /* @__PURE__ */ new Set());
|
|
853
|
+
autoQueue.get(groupId).add(userId);
|
|
843
854
|
return `已同意用户 ${request.userName}(${userId}) 的加群申请`;
|
|
844
855
|
} catch (error) {
|
|
845
856
|
return `处理申请时出错: ${error.message}`;
|
|
@@ -1111,5 +1122,6 @@ __name(apply, "apply");
|
|
|
1111
1122
|
mergeReminder,
|
|
1112
1123
|
name,
|
|
1113
1124
|
parseConfigArgs,
|
|
1114
|
-
tokenize
|
|
1125
|
+
tokenize,
|
|
1126
|
+
verifyApplication
|
|
1115
1127
|
});
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { Context, Schema, Session } from 'koishi'
|
|
|
2
2
|
|
|
3
3
|
export const name = 'group-verification'
|
|
4
4
|
|
|
5
|
+
// 模块级日志器,测试时使用 console
|
|
6
|
+
let logger: any = console
|
|
7
|
+
|
|
5
8
|
// 数据库模型定义
|
|
6
9
|
declare module 'koishi' {
|
|
7
10
|
interface Tables {
|
|
@@ -358,6 +361,50 @@ export function parseConfigArgs(raw: string): ParsedArgs {
|
|
|
358
361
|
|
|
359
362
|
return { keywords, flags, error };
|
|
360
363
|
}
|
|
364
|
+
|
|
365
|
+
// 验证申请(提取到外层,供测试调用)
|
|
366
|
+
export async function verifyApplication(config: GroupVerificationConfig, message: string, session: any): Promise<{isValid: boolean, matchedCount: number, requiredThreshold: string}> {
|
|
367
|
+
// 统计匹配的关键词数量(允许相互重叠)
|
|
368
|
+
const lowered = message.toLowerCase()
|
|
369
|
+
const matched = new Set<string>()
|
|
370
|
+
for (const keyword of config.keywords) {
|
|
371
|
+
if (lowered.includes(keyword.toLowerCase())) {
|
|
372
|
+
matched.add(keyword)
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const matchedCount = matched.size
|
|
376
|
+
|
|
377
|
+
let isValid = false
|
|
378
|
+
let requiredThreshold = ''
|
|
379
|
+
|
|
380
|
+
switch (config.reviewMethod) {
|
|
381
|
+
case 0: // 全部同意
|
|
382
|
+
isValid = true
|
|
383
|
+
requiredThreshold = 'null'
|
|
384
|
+
break
|
|
385
|
+
case 1: // 按数量同意
|
|
386
|
+
isValid = matchedCount >= (config.reviewParameters || 1)
|
|
387
|
+
requiredThreshold = `${config.reviewParameters || 1}`
|
|
388
|
+
break
|
|
389
|
+
case 2: // 按比例同意
|
|
390
|
+
const ratio = matchedCount / config.keywords.length
|
|
391
|
+
const requiredRatio = (config.reviewParameters || 100) / 100
|
|
392
|
+
isValid = ratio >= requiredRatio
|
|
393
|
+
requiredThreshold = `${config.reviewParameters || 100}%`
|
|
394
|
+
break
|
|
395
|
+
case 3: // 全部拒绝
|
|
396
|
+
isValid = false
|
|
397
|
+
requiredThreshold = 'null'
|
|
398
|
+
break
|
|
399
|
+
default:
|
|
400
|
+
isValid = false
|
|
401
|
+
requiredThreshold = 'null'
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
logger.info(`verifyApplication msg="${message}" keywords=${JSON.stringify(config.keywords)} matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`)
|
|
405
|
+
return { isValid, matchedCount, requiredThreshold }
|
|
406
|
+
}
|
|
407
|
+
|
|
361
408
|
export function apply(ctx: Context, config: Config) {
|
|
362
409
|
// 创建数据库表
|
|
363
410
|
ctx.model.extend('group_verification_config', {
|
|
@@ -377,8 +424,8 @@ export function apply(ctx: Context, config: Config) {
|
|
|
377
424
|
autoInc: true
|
|
378
425
|
})
|
|
379
426
|
|
|
380
|
-
// 获取logger
|
|
381
|
-
|
|
427
|
+
// 获取logger实例并保存到模块级变量
|
|
428
|
+
logger = ctx.logger('group-verification')
|
|
382
429
|
|
|
383
430
|
// 设置日志级别
|
|
384
431
|
// 注意:Koishi logger的level设置可能需要不同的方式
|
|
@@ -413,80 +460,78 @@ export function apply(ctx: Context, config: Config) {
|
|
|
413
460
|
autoInc: true
|
|
414
461
|
})
|
|
415
462
|
|
|
463
|
+
// 缓存正在审批的用户,用于避免 guild-member-added 重复计数
|
|
464
|
+
const autoQueue = new Map<string, Set<string>>();
|
|
465
|
+
|
|
416
466
|
// 监听加群申请事件
|
|
417
467
|
ctx.on('guild-member-request', async (session) => {
|
|
418
468
|
// debug: 输出整个 session 以便定位字段名
|
|
419
469
|
logger.debug('guild-member-request event', session)
|
|
420
470
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
const message = session.content || ''
|
|
471
|
+
let guildId = (session.guildId || session.channelId || '').toString().trim();
|
|
472
|
+
const userId = session.userId;
|
|
473
|
+
const message = session.content || '';
|
|
425
474
|
|
|
426
475
|
if (!guildId) {
|
|
427
|
-
logger.warn('guild-member-request 没有 guildId,跳过处理')
|
|
428
|
-
return
|
|
476
|
+
logger.warn('guild-member-request 没有 guildId,跳过处理');
|
|
477
|
+
return;
|
|
429
478
|
}
|
|
430
479
|
|
|
431
|
-
//
|
|
432
|
-
const requestId = ((session.event as any)?.requestId) || session.messageId || ''
|
|
433
|
-
if (requestId) {
|
|
434
|
-
try {
|
|
435
|
-
await session.bot.handleGuildMemberRequest(requestId, true)
|
|
436
|
-
logger.info(`自动同意申请 requestId=${requestId}`)
|
|
437
|
-
await updateStats(guildId, 'autoApproved')
|
|
438
|
-
// 不再执行后续验证或提醒
|
|
439
|
-
return
|
|
440
|
-
} catch (e) {
|
|
441
|
-
logger.warn('自动同意失败', e)
|
|
442
|
-
// 继续下面的验证逻辑
|
|
443
|
-
}
|
|
444
|
-
}
|
|
480
|
+
// 获取 requestId(不同平台字段不同)
|
|
481
|
+
const requestId = ((session.event as any)?.requestId) || session.messageId || '';
|
|
445
482
|
|
|
446
483
|
// 获取群组配置
|
|
447
484
|
const groupConfig = await ctx.database.get('group_verification_config', {
|
|
448
485
|
groupId: guildId
|
|
449
|
-
})
|
|
486
|
+
});
|
|
450
487
|
|
|
451
488
|
if (!groupConfig || groupConfig.length === 0) {
|
|
452
|
-
//
|
|
453
|
-
return
|
|
489
|
+
// 无配置直接放行(需要有 requestId)
|
|
490
|
+
return;
|
|
454
491
|
}
|
|
492
|
+
const config = groupConfig[0];
|
|
455
493
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session)
|
|
494
|
+
// 执行验证,记录详细情况
|
|
495
|
+
const { isValid, matchedCount, requiredThreshold } = await verifyApplication(config, message, session);
|
|
496
|
+
logger.info(`验证结果 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold} valid=${isValid}`);
|
|
460
497
|
|
|
461
498
|
if (isValid) {
|
|
462
|
-
// 验证成功,自动同意入群(同样需要 requestId)
|
|
463
499
|
if (requestId) {
|
|
464
500
|
try {
|
|
465
|
-
await session.bot.handleGuildMemberRequest(requestId, true)
|
|
466
|
-
logger.info(
|
|
501
|
+
await session.bot.handleGuildMemberRequest(requestId, true);
|
|
502
|
+
logger.info(`自动同意 requestId=${requestId}`);
|
|
503
|
+
// 将此用户标记为自动批准,等待 guild-member-added 更新统计
|
|
504
|
+
if (!autoQueue.has(guildId)) autoQueue.set(guildId, new Set());
|
|
505
|
+
autoQueue.get(guildId)!.add(userId);
|
|
467
506
|
} catch (e) {
|
|
468
|
-
logger.warn('自动同意失败', e)
|
|
507
|
+
logger.warn('自动同意失败', e);
|
|
469
508
|
}
|
|
470
509
|
}
|
|
471
|
-
// 更新统计信息
|
|
472
|
-
await updateStats(guildId, 'autoApproved')
|
|
473
510
|
} else {
|
|
474
|
-
|
|
475
|
-
await handleFailedVerification(ctx, session, config)
|
|
511
|
+
await handleFailedVerification(ctx, session, config);
|
|
476
512
|
}
|
|
477
|
-
})
|
|
513
|
+
});
|
|
478
514
|
|
|
479
515
|
// 监听群成员增加事件(包括手动邀请入群)
|
|
480
516
|
ctx.on('guild-member-added', async (session) => {
|
|
481
517
|
const groupId = session.guildId
|
|
482
518
|
const userId = session.userId
|
|
483
|
-
|
|
519
|
+
|
|
520
|
+
// 先检查 autoQueue
|
|
521
|
+
const set = autoQueue.get(groupId)
|
|
522
|
+
if (set && set.has(userId)) {
|
|
523
|
+
await updateStats(groupId, 'autoApproved')
|
|
524
|
+
set.delete(userId)
|
|
525
|
+
logger.info(`用户 ${userId} 通过机器人审批加入群 ${groupId}(autoQueue),统计已更新`)
|
|
526
|
+
return
|
|
527
|
+
}
|
|
528
|
+
|
|
484
529
|
// 检查是否有待审核记录
|
|
485
530
|
const pendingRecords = await ctx.database.get('group_verification_pending', {
|
|
486
531
|
groupId: groupId,
|
|
487
532
|
userId: userId
|
|
488
533
|
})
|
|
489
|
-
|
|
534
|
+
|
|
490
535
|
if (pendingRecords.length > 0) {
|
|
491
536
|
// 通过验证的用户入群,更新统计
|
|
492
537
|
await updateStats(groupId, 'autoApproved')
|
|
@@ -554,42 +599,6 @@ export function apply(ctx: Context, config: Config) {
|
|
|
554
599
|
await ctx.broadcast([guildId], reminderMsg)
|
|
555
600
|
}
|
|
556
601
|
|
|
557
|
-
// 验证申请
|
|
558
|
-
async function verifyApplication(config: GroupVerificationConfig, message: string, session: any): Promise<{isValid: boolean, matchedCount: number, requiredThreshold: string}> {
|
|
559
|
-
// 统计匹配的关键词数量
|
|
560
|
-
const matchedCount = config.keywords.filter(keyword =>
|
|
561
|
-
message.toLowerCase().includes(keyword.toLowerCase())
|
|
562
|
-
).length
|
|
563
|
-
|
|
564
|
-
let isValid = false
|
|
565
|
-
let requiredThreshold = ''
|
|
566
|
-
|
|
567
|
-
switch (config.reviewMethod) {
|
|
568
|
-
case 0: // 全部同意
|
|
569
|
-
isValid = true
|
|
570
|
-
requiredThreshold = 'null'
|
|
571
|
-
break
|
|
572
|
-
case 1: // 按数量同意
|
|
573
|
-
isValid = matchedCount >= (config.reviewParameters || 1)
|
|
574
|
-
requiredThreshold = `${config.reviewParameters || 1}`
|
|
575
|
-
break
|
|
576
|
-
case 2: // 按比例同意
|
|
577
|
-
const ratio = matchedCount / config.keywords.length
|
|
578
|
-
const requiredRatio = (config.reviewParameters || 100) / 100
|
|
579
|
-
isValid = ratio >= requiredRatio
|
|
580
|
-
requiredThreshold = `${config.reviewParameters || 100}%`
|
|
581
|
-
break
|
|
582
|
-
case 3: // 全部拒绝
|
|
583
|
-
isValid = false
|
|
584
|
-
requiredThreshold = 'null'
|
|
585
|
-
break
|
|
586
|
-
default:
|
|
587
|
-
isValid = false
|
|
588
|
-
requiredThreshold = 'null'
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
return { isValid, matchedCount, requiredThreshold }
|
|
592
|
-
}
|
|
593
602
|
|
|
594
603
|
// 更新统计信息
|
|
595
604
|
async function updateStats(groupId: string, action: 'autoApproved' | 'manuallyApproved' | 'rejected') {
|
|
@@ -1117,7 +1126,9 @@ gvc -r # 删除配置`
|
|
|
1117
1126
|
// TODO: 需要获取实际的requestId来进行审批
|
|
1118
1127
|
// await session.bot.handleGuildMemberRequest(request.requestId, true)
|
|
1119
1128
|
await ctx.database.remove('group_verification_pending', { id: request.id })
|
|
1120
|
-
|
|
1129
|
+
// 标记为自动批准,实际统计在 guild-member-added 处理
|
|
1130
|
+
if (!autoQueue.has(groupId)) autoQueue.set(groupId, new Set())
|
|
1131
|
+
autoQueue.get(groupId)!.add(request.userId)
|
|
1121
1132
|
approvedCount++
|
|
1122
1133
|
} catch (error) {
|
|
1123
1134
|
logger.warn(`处理申请 ${request.id} 时出错:`, error)
|
|
@@ -1137,7 +1148,8 @@ gvc -r # 删除配置`
|
|
|
1137
1148
|
// 这里需要获取实际的requestId,暂时用userId作为示例
|
|
1138
1149
|
await session.bot.handleGuildMemberRequest(request.userId, true)
|
|
1139
1150
|
await ctx.database.remove('group_verification_pending', { id: request.id })
|
|
1140
|
-
|
|
1151
|
+
if (!autoQueue.has(groupId)) autoQueue.set(groupId, new Set())
|
|
1152
|
+
autoQueue.get(groupId)!.add(request.userId)
|
|
1141
1153
|
return `已同意用户 ${request.userName}(${request.userId}) 的加群申请`
|
|
1142
1154
|
} catch (error) {
|
|
1143
1155
|
return `处理申请时出错: ${error.message}`
|
|
@@ -1160,7 +1172,8 @@ gvc -r # 删除配置`
|
|
|
1160
1172
|
// 这里需要获取实际的requestId来进行审批
|
|
1161
1173
|
await session.bot.handleGuildMemberRequest(request.userId, true)
|
|
1162
1174
|
await ctx.database.remove('group_verification_pending', { id: request.id })
|
|
1163
|
-
|
|
1175
|
+
if (!autoQueue.has(groupId)) autoQueue.set(groupId, new Set())
|
|
1176
|
+
autoQueue.get(groupId)!.add(userId)
|
|
1164
1177
|
return `已同意用户 ${request.userName}(${userId}) 的加群申请`
|
|
1165
1178
|
} catch (error) {
|
|
1166
1179
|
return `处理申请时出错: ${error.message}`
|