koishi-plugin-group-verification 1.0.19 → 1.0.20
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 +4 -4
- package/lib/index.js +21 -17
- package/package.json +1 -1
- package/readme.md +9 -0
- package/src/index.ts +41 -32
package/lib/index.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ export interface GroupVerificationConfig {
|
|
|
17
17
|
reminderMessage: string;
|
|
18
18
|
createdBy: string;
|
|
19
19
|
updatedBy: string;
|
|
20
|
-
createdAt: Date;
|
|
21
|
-
updatedAt: Date;
|
|
20
|
+
createdAt: string | Date;
|
|
21
|
+
updatedAt: string | Date;
|
|
22
22
|
}
|
|
23
23
|
export interface GroupVerificationStats {
|
|
24
24
|
id: number;
|
|
@@ -26,7 +26,7 @@ export interface GroupVerificationStats {
|
|
|
26
26
|
autoApproved: number;
|
|
27
27
|
manuallyApproved: number;
|
|
28
28
|
rejected: number;
|
|
29
|
-
lastUpdated: Date;
|
|
29
|
+
lastUpdated: string | Date;
|
|
30
30
|
}
|
|
31
31
|
export interface PendingVerification {
|
|
32
32
|
id: number;
|
|
@@ -34,7 +34,7 @@ export interface PendingVerification {
|
|
|
34
34
|
userId: string;
|
|
35
35
|
userName: string;
|
|
36
36
|
requestMessage: string;
|
|
37
|
-
applyTime: Date;
|
|
37
|
+
applyTime: string | Date;
|
|
38
38
|
}
|
|
39
39
|
export interface Config {
|
|
40
40
|
defaultReminderMessage?: string;
|
package/lib/index.js
CHANGED
|
@@ -299,8 +299,8 @@ function apply(ctx, config) {
|
|
|
299
299
|
reminderMessage: "string",
|
|
300
300
|
createdBy: "string",
|
|
301
301
|
updatedBy: "string",
|
|
302
|
-
createdAt: "
|
|
303
|
-
updatedAt: "
|
|
302
|
+
createdAt: "string",
|
|
303
|
+
updatedAt: "string"
|
|
304
304
|
}, {
|
|
305
305
|
primary: "id",
|
|
306
306
|
autoInc: true
|
|
@@ -316,7 +316,9 @@ function apply(ctx, config) {
|
|
|
316
316
|
autoApproved: "integer",
|
|
317
317
|
manuallyApproved: "integer",
|
|
318
318
|
rejected: "integer",
|
|
319
|
-
|
|
319
|
+
// store as string (ISO timestamp) to preserve full date+time;
|
|
320
|
+
// Koishi `date` type truncates to day which leads to 00:00:00.
|
|
321
|
+
lastUpdated: "string"
|
|
320
322
|
}, {
|
|
321
323
|
primary: "id",
|
|
322
324
|
autoInc: true
|
|
@@ -327,7 +329,8 @@ function apply(ctx, config) {
|
|
|
327
329
|
userId: "string",
|
|
328
330
|
userName: "string",
|
|
329
331
|
requestMessage: "string",
|
|
330
|
-
|
|
332
|
+
// record full timestamp as string to keep time component
|
|
333
|
+
applyTime: "string"
|
|
331
334
|
}, {
|
|
332
335
|
primary: "id",
|
|
333
336
|
autoInc: true
|
|
@@ -390,29 +393,30 @@ function apply(ctx, config) {
|
|
|
390
393
|
logger.info(`用户 ${userId} 被手动邀请加入群 ${groupId},手动批准统计已更新`);
|
|
391
394
|
}
|
|
392
395
|
});
|
|
393
|
-
async function handleFailedVerification(ctx2, session, config2) {
|
|
394
|
-
const guildId = session.guildId;
|
|
396
|
+
async function handleFailedVerification(ctx2, session, config2, matchedCount, requiredThreshold) {
|
|
397
|
+
const guildId = (session.guildId || session.channelId || "").toString().trim();
|
|
395
398
|
const userId = session.userId;
|
|
396
399
|
const username = session.username || "未知用户";
|
|
397
400
|
const message = session.content || "";
|
|
401
|
+
logger.info(`处理失败验证 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold}`);
|
|
402
|
+
if (matchedCount === void 0 || requiredThreshold === void 0) {
|
|
403
|
+
const result = await verifyApplication(config2, message, session);
|
|
404
|
+
matchedCount = result.matchedCount;
|
|
405
|
+
requiredThreshold = result.requiredThreshold;
|
|
406
|
+
}
|
|
398
407
|
let groupName = "未知群组";
|
|
399
408
|
try {
|
|
400
409
|
const guild = await session.bot.getGuild(guildId);
|
|
401
410
|
groupName = guild.name || groupName;
|
|
402
411
|
} catch (error) {
|
|
403
412
|
}
|
|
404
|
-
const { matchedCount, requiredThreshold } = await verifyApplication(config2, message, session);
|
|
405
413
|
await ctx2.database.create("group_verification_pending", {
|
|
406
414
|
groupId: guildId,
|
|
407
415
|
userId,
|
|
408
416
|
userName: username,
|
|
409
417
|
requestMessage: message,
|
|
410
|
-
applyTime: /* @__PURE__ */ new Date()
|
|
418
|
+
applyTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
411
419
|
});
|
|
412
|
-
if (!guildId) {
|
|
413
|
-
logger.warn("handleFailedVerification 收到无效 guildId,已放弃发送");
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
420
|
if (!config2.reminderEnabled || !config2.reminderMessage || config2.reminderMessage === "") {
|
|
417
421
|
logger.info(`群 ${guildId} 的提醒消息已被禁用,跳过发送`);
|
|
418
422
|
return;
|
|
@@ -428,7 +432,7 @@ function apply(ctx, config) {
|
|
|
428
432
|
const stats = existingStats[0];
|
|
429
433
|
await ctx.database.set("group_verification_stats", { id: stats.id }, {
|
|
430
434
|
[action]: stats[action] + 1,
|
|
431
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
435
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
432
436
|
});
|
|
433
437
|
} else {
|
|
434
438
|
await ctx.database.create("group_verification_stats", {
|
|
@@ -436,7 +440,7 @@ function apply(ctx, config) {
|
|
|
436
440
|
autoApproved: action === "autoApproved" ? 1 : 0,
|
|
437
441
|
manuallyApproved: action === "manuallyApproved" ? 1 : 0,
|
|
438
442
|
rejected: action === "rejected" ? 1 : 0,
|
|
439
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
443
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
440
444
|
});
|
|
441
445
|
}
|
|
442
446
|
await syncTotalStats(ctx);
|
|
@@ -533,8 +537,8 @@ ${debugInfo}`];
|
|
|
533
537
|
remove: flags.remove || options.remove
|
|
534
538
|
};
|
|
535
539
|
logger.info(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}`);
|
|
536
|
-
if ((cleanedOptions.query || cleanedOptions.remove) && (parsedKeywords.length > 0 || cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || cleanedOptions.message !== void 0 || cleanedOptions.enableMessage || cleanedOptions.disableMessage
|
|
537
|
-
return "参数冲突:-? 或 -r
|
|
540
|
+
if ((cleanedOptions.query || cleanedOptions.remove) && (parsedKeywords.length > 0 || cleanedOptions.method !== void 0 || cleanedOptions.threshold !== void 0 || cleanedOptions.message !== void 0 || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
|
|
541
|
+
return "参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)";
|
|
538
542
|
}
|
|
539
543
|
const hasRealMessageParam = cleanedOptions.message !== void 0;
|
|
540
544
|
const hasRealEnableMessageParam = cleanedOptions.enableMessage === true;
|
|
@@ -1103,7 +1107,7 @@ gvc -r # 删除配置`;
|
|
|
1103
1107
|
autoApproved: totalAutoApproved,
|
|
1104
1108
|
manuallyApproved: totalManuallyApproved,
|
|
1105
1109
|
rejected: totalRejected,
|
|
1106
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
1110
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1107
1111
|
});
|
|
1108
1112
|
logger.info(`总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}`);
|
|
1109
1113
|
}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -48,6 +48,9 @@ group-verify.config -r
|
|
|
48
48
|
|
|
49
49
|
# 指定群号配置
|
|
50
50
|
group-verify.config -i 123456789 关键词1,关键词2 -m 1 -t 1
|
|
51
|
+
|
|
52
|
+
> **提示**: `-?`(查询)和 `-r`(删除)仍然与所有其它参数 / 关键字互斥,
|
|
53
|
+
> 但可以和 `-i` 组合使用,例如 `gvc -i 123 -?` 或 `gvc -i 123 -r`。这样便于在私聊环境下查询或删除指定群的设置。
|
|
51
54
|
```
|
|
52
55
|
|
|
53
56
|
### 审核命令
|
|
@@ -106,6 +109,12 @@ group-verify.stats total
|
|
|
106
109
|
- `-nomsg` - 禁用提醒消息功能
|
|
107
110
|
- 不带参数的 `-msg` 会显示帮助信息
|
|
108
111
|
|
|
112
|
+
### 提醒消息变量
|
|
113
|
+
|
|
114
|
+
### 其他注意事项
|
|
115
|
+
|
|
116
|
+
- 统计数据的 **最后更新时间** 使用完整的日期+时间存储,升级到最新版后若看到 `00:00:00`,请手动重建或清除旧的统计记录以便记录最新时间。
|
|
117
|
+
|
|
109
118
|
### 提醒消息变量
|
|
110
119
|
```
|
|
111
120
|
{user} - 用户名
|
package/src/index.ts
CHANGED
|
@@ -25,8 +25,8 @@ export interface GroupVerificationConfig {
|
|
|
25
25
|
reminderMessage: string
|
|
26
26
|
createdBy: string
|
|
27
27
|
updatedBy: string
|
|
28
|
-
createdAt: Date
|
|
29
|
-
updatedAt: Date
|
|
28
|
+
createdAt: string | Date
|
|
29
|
+
updatedAt: string | Date
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// 群组统计信息表
|
|
@@ -36,7 +36,7 @@ export interface GroupVerificationStats {
|
|
|
36
36
|
autoApproved: number
|
|
37
37
|
manuallyApproved: number
|
|
38
38
|
rejected: number
|
|
39
|
-
lastUpdated: Date
|
|
39
|
+
lastUpdated: string | Date
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// 待审核申请表
|
|
@@ -46,7 +46,7 @@ export interface PendingVerification {
|
|
|
46
46
|
userId: string
|
|
47
47
|
userName: string
|
|
48
48
|
requestMessage: string
|
|
49
|
-
applyTime: Date
|
|
49
|
+
applyTime: string | Date
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export interface Config {
|
|
@@ -417,8 +417,8 @@ export function apply(ctx: Context, config: Config) {
|
|
|
417
417
|
reminderMessage: 'string',
|
|
418
418
|
createdBy: 'string',
|
|
419
419
|
updatedBy: 'string',
|
|
420
|
-
createdAt: '
|
|
421
|
-
updatedAt: '
|
|
420
|
+
createdAt: 'string',
|
|
421
|
+
updatedAt: 'string'
|
|
422
422
|
}, {
|
|
423
423
|
primary: 'id',
|
|
424
424
|
autoInc: true
|
|
@@ -442,7 +442,9 @@ export function apply(ctx: Context, config: Config) {
|
|
|
442
442
|
autoApproved: 'integer',
|
|
443
443
|
manuallyApproved: 'integer',
|
|
444
444
|
rejected: 'integer',
|
|
445
|
-
|
|
445
|
+
// store as string (ISO timestamp) to preserve full date+time;
|
|
446
|
+
// Koishi `date` type truncates to day which leads to 00:00:00.
|
|
447
|
+
lastUpdated: 'string'
|
|
446
448
|
}, {
|
|
447
449
|
primary: 'id',
|
|
448
450
|
autoInc: true
|
|
@@ -454,7 +456,8 @@ export function apply(ctx: Context, config: Config) {
|
|
|
454
456
|
userId: 'string',
|
|
455
457
|
userName: 'string',
|
|
456
458
|
requestMessage: 'string',
|
|
457
|
-
|
|
459
|
+
// record full timestamp as string to keep time component
|
|
460
|
+
applyTime: 'string'
|
|
458
461
|
}, {
|
|
459
462
|
primary: 'id',
|
|
460
463
|
autoInc: true
|
|
@@ -546,12 +549,27 @@ export function apply(ctx: Context, config: Config) {
|
|
|
546
549
|
})
|
|
547
550
|
|
|
548
551
|
// 处理验证失败的情况
|
|
549
|
-
|
|
550
|
-
|
|
552
|
+
// 处理验证失败的情况并发送提醒消息
|
|
553
|
+
// matchedCount/requiredThreshold 可选,为空时内部重新计算(兼容旧调用)
|
|
554
|
+
async function handleFailedVerification(
|
|
555
|
+
ctx: Context,
|
|
556
|
+
session: any,
|
|
557
|
+
config: GroupVerificationConfig,
|
|
558
|
+
matchedCount?: number,
|
|
559
|
+
requiredThreshold?: string
|
|
560
|
+
) {
|
|
561
|
+
const guildId = (session.guildId || session.channelId || '').toString().trim();
|
|
551
562
|
const userId = session.userId
|
|
552
563
|
const username = session.username || '未知用户'
|
|
553
564
|
const message = session.content || ''
|
|
554
|
-
|
|
565
|
+
logger.info(`处理失败验证 guild=${guildId} user=${userId} msg="${message}" matched=${matchedCount} threshold=${requiredThreshold}`)
|
|
566
|
+
// 如果未传入匹配信息,则重新计算一次(老调用)
|
|
567
|
+
if (matchedCount === undefined || requiredThreshold === undefined) {
|
|
568
|
+
const result = await verifyApplication(config, message, session)
|
|
569
|
+
matchedCount = result.matchedCount
|
|
570
|
+
requiredThreshold = result.requiredThreshold
|
|
571
|
+
}
|
|
572
|
+
|
|
555
573
|
// 获取群信息
|
|
556
574
|
let groupName = '未知群组'
|
|
557
575
|
try {
|
|
@@ -560,30 +578,21 @@ export function apply(ctx: Context, config: Config) {
|
|
|
560
578
|
} catch (error) {
|
|
561
579
|
// 无法获取群名称时使用默认值
|
|
562
580
|
}
|
|
563
|
-
|
|
564
|
-
// 执行验证获取详细信息
|
|
565
|
-
const { matchedCount, requiredThreshold } = await verifyApplication(config, message, session)
|
|
566
|
-
|
|
581
|
+
|
|
567
582
|
// 将申请加入待审核列表
|
|
568
583
|
await ctx.database.create('group_verification_pending', {
|
|
569
584
|
groupId: guildId,
|
|
570
585
|
userId: userId,
|
|
571
586
|
userName: username,
|
|
572
587
|
requestMessage: message,
|
|
573
|
-
applyTime: new Date()
|
|
588
|
+
applyTime: new Date().toISOString()
|
|
574
589
|
})
|
|
575
|
-
|
|
576
|
-
// 如果 guildId 不合法,跳过
|
|
577
|
-
if (!guildId) {
|
|
578
|
-
logger.warn('handleFailedVerification 收到无效 guildId,已放弃发送')
|
|
579
|
-
return
|
|
580
|
-
}
|
|
581
590
|
// 如果提醒消息被禁用,直接返回
|
|
582
591
|
if (!config.reminderEnabled || !config.reminderMessage || config.reminderMessage === '') {
|
|
583
592
|
logger.info(`群 ${guildId} 的提醒消息已被禁用,跳过发送`)
|
|
584
593
|
return
|
|
585
594
|
}
|
|
586
|
-
|
|
595
|
+
|
|
587
596
|
// 替换提醒消息中的变量
|
|
588
597
|
let reminderMsg = config.reminderMessage
|
|
589
598
|
reminderMsg = reminderMsg
|
|
@@ -592,9 +601,9 @@ export function apply(ctx: Context, config: Config) {
|
|
|
592
601
|
.replace(/{group}/g, guildId)
|
|
593
602
|
.replace(/{gname}/g, groupName)
|
|
594
603
|
.replace(/{question}/g, message)
|
|
595
|
-
.replace(/{answer}/g, matchedCount
|
|
596
|
-
.replace(/{threshold}/g, requiredThreshold)
|
|
597
|
-
|
|
604
|
+
.replace(/{answer}/g, matchedCount!.toString())
|
|
605
|
+
.replace(/{threshold}/g, requiredThreshold!)
|
|
606
|
+
|
|
598
607
|
// 发送提醒消息到群内
|
|
599
608
|
await ctx.broadcast([guildId], reminderMsg)
|
|
600
609
|
}
|
|
@@ -609,7 +618,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
609
618
|
const stats = existingStats[0]
|
|
610
619
|
await ctx.database.set('group_verification_stats', { id: stats.id }, {
|
|
611
620
|
[action]: stats[action] + 1,
|
|
612
|
-
lastUpdated: new Date()
|
|
621
|
+
lastUpdated: new Date().toISOString()
|
|
613
622
|
})
|
|
614
623
|
} else {
|
|
615
624
|
await ctx.database.create('group_verification_stats', {
|
|
@@ -617,7 +626,7 @@ export function apply(ctx: Context, config: Config) {
|
|
|
617
626
|
autoApproved: action === 'autoApproved' ? 1 : 0,
|
|
618
627
|
manuallyApproved: action === 'manuallyApproved' ? 1 : 0,
|
|
619
628
|
rejected: action === 'rejected' ? 1 : 0,
|
|
620
|
-
lastUpdated: new Date()
|
|
629
|
+
lastUpdated: new Date().toISOString()
|
|
621
630
|
})
|
|
622
631
|
}
|
|
623
632
|
|
|
@@ -755,11 +764,11 @@ export function apply(ctx: Context, config: Config) {
|
|
|
755
764
|
}
|
|
756
765
|
logger.info(`合并后options: ${JSON.stringify(cleanedOptions, null, 2)}`)
|
|
757
766
|
|
|
758
|
-
// 检查 -? 和 -r
|
|
767
|
+
// 检查 -? 和 -r 的独占性;允许与 -i 并存
|
|
759
768
|
if ((cleanedOptions.query || cleanedOptions.remove) &&
|
|
760
769
|
(parsedKeywords.length > 0 || cleanedOptions.method !== undefined || cleanedOptions.threshold !== undefined ||
|
|
761
|
-
cleanedOptions.message !== undefined || cleanedOptions.enableMessage || cleanedOptions.disableMessage
|
|
762
|
-
return '参数冲突:-? 或 -r
|
|
770
|
+
cleanedOptions.message !== undefined || cleanedOptions.enableMessage || cleanedOptions.disableMessage)) {
|
|
771
|
+
return '参数冲突:-? 或 -r 不能与其他参数或关键词一起使用(仅可搭配 -i)'
|
|
763
772
|
}
|
|
764
773
|
|
|
765
774
|
// 检查消息参数冲突
|
|
@@ -1495,7 +1504,7 @@ gvc -r # 删除配置`
|
|
|
1495
1504
|
autoApproved: totalAutoApproved,
|
|
1496
1505
|
manuallyApproved: totalManuallyApproved,
|
|
1497
1506
|
rejected: totalRejected,
|
|
1498
|
-
lastUpdated: new Date()
|
|
1507
|
+
lastUpdated: new Date().toISOString()
|
|
1499
1508
|
})
|
|
1500
1509
|
|
|
1501
1510
|
logger.info(`总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}`)
|