koishi-plugin-group-verification 1.0.9 → 1.0.10

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
@@ -42,4 +42,5 @@ export interface Config {
42
42
  logLevel?: 'debug' | 'info' | 'warn' | 'error';
43
43
  }
44
44
  export declare const Config: Schema<Config>;
45
+ export declare const inject: string[];
45
46
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -22,6 +22,7 @@ var src_exports = {};
22
22
  __export(src_exports, {
23
23
  Config: () => Config,
24
24
  apply: () => apply,
25
+ inject: () => inject,
25
26
  name: () => name
26
27
  });
27
28
  module.exports = __toCommonJS(src_exports);
@@ -37,6 +38,7 @@ var Config = import_koishi.Schema.object({
37
38
  ).description("默认提醒消息模板"),
38
39
  logLevel: import_koishi.Schema.union(["debug", "info", "warn", "error"]).default("info").description("日志级别")
39
40
  }).description("群组验证插件配置");
41
+ var inject = ["database"];
40
42
  function apply(ctx, config) {
41
43
  ctx.model.extend("group_verification_config", {
42
44
  id: "unsigned",
@@ -97,6 +99,22 @@ function apply(ctx, config) {
97
99
  await handleFailedVerification(ctx, session, config2);
98
100
  }
99
101
  });
102
+ ctx.on("guild-member-added", async (session) => {
103
+ const groupId = session.guildId;
104
+ const userId = session.userId;
105
+ const pendingRecords = await ctx.database.get("group_verification_pending", {
106
+ groupId,
107
+ userId
108
+ });
109
+ if (pendingRecords.length > 0) {
110
+ await updateStats(groupId, "autoApproved");
111
+ await ctx.database.remove("group_verification_pending", { id: pendingRecords[0].id });
112
+ logger.info(`用户 ${userId} 通过验证加入群 ${groupId},统计已更新`);
113
+ } else {
114
+ await updateStats(groupId, "manuallyApproved");
115
+ logger.info(`用户 ${userId} 被手动邀请加入群 ${groupId},手动批准统计已更新`);
116
+ }
117
+ });
100
118
  async function handleFailedVerification(ctx2, session, config2) {
101
119
  const guildId = session.guildId;
102
120
  const userId = session.userId;
@@ -153,39 +171,24 @@ function apply(ctx, config) {
153
171
  return { isValid, matchedCount, requiredThreshold };
154
172
  }
155
173
  __name(verifyApplication, "verifyApplication");
156
- async function updateStats(groupId, statType) {
157
- const groupStats = await ctx.database.get("group_verification_stats", { groupId });
158
- if (groupStats.length > 0) {
159
- const stats = groupStats[0];
174
+ async function updateStats(groupId, action) {
175
+ const existingStats = await ctx.database.get("group_verification_stats", { groupId });
176
+ if (existingStats.length > 0) {
177
+ const stats = existingStats[0];
160
178
  await ctx.database.set("group_verification_stats", { id: stats.id }, {
161
- [statType]: stats[statType] + 1,
179
+ [action]: stats[action] + 1,
162
180
  lastUpdated: /* @__PURE__ */ new Date()
163
181
  });
164
182
  } else {
165
183
  await ctx.database.create("group_verification_stats", {
166
184
  groupId,
167
- autoApproved: statType === "autoApproved" ? 1 : 0,
168
- manuallyApproved: statType === "manuallyApproved" ? 1 : 0,
169
- rejected: statType === "rejected" ? 1 : 0,
170
- lastUpdated: /* @__PURE__ */ new Date()
171
- });
172
- }
173
- const totalStats = await ctx.database.get("group_verification_stats", { groupId: "all" });
174
- if (totalStats.length > 0) {
175
- const stats = totalStats[0];
176
- await ctx.database.set("group_verification_stats", { id: stats.id }, {
177
- [statType]: stats[statType] + 1,
178
- lastUpdated: /* @__PURE__ */ new Date()
179
- });
180
- } else {
181
- await ctx.database.create("group_verification_stats", {
182
- groupId: "all",
183
- autoApproved: statType === "autoApproved" ? 1 : 0,
184
- manuallyApproved: statType === "manuallyApproved" ? 1 : 0,
185
- rejected: statType === "rejected" ? 1 : 0,
185
+ autoApproved: action === "autoApproved" ? 1 : 0,
186
+ manuallyApproved: action === "manuallyApproved" ? 1 : 0,
187
+ rejected: action === "rejected" ? 1 : 0,
186
188
  lastUpdated: /* @__PURE__ */ new Date()
187
189
  });
188
190
  }
191
+ await syncTotalStats(ctx);
189
192
  }
190
193
  __name(updateStats, "updateStats");
191
194
  async function checkPermission(session, targetGroupId) {
@@ -414,24 +417,45 @@ ${debugInfo}`];
414
417
  await updateStats(session.guildId, "rejected");
415
418
  return `已拒绝用户 ${application.userName}(${userId}) 的加群申请`;
416
419
  });
417
- groupVerify.subcommand(".stats [groupId]", "查看验证统计信息").alias("statistics", "统计", "stat", "统计信息").action(async ({ session }, groupId) => {
418
- const targetGroupId = groupId || session.guildId || "all";
419
- const stats = await ctx.database.get("group_verification_stats", { groupId: targetGroupId });
420
- if (stats.length === 0) {
421
- return targetGroupId === "all" ? "暂无统计信息" : `群 ${targetGroupId} 暂无统计信息`;
422
- }
423
- const stat = stats[0];
424
- if (targetGroupId === "all") {
425
- return `总体统计:
426
- 自动通过:${stat.autoApproved}
427
- 手动通过:${stat.manuallyApproved}
428
- 拒绝:${stat.rejected}`;
420
+ groupVerify.subcommand(".stats [groupId]", "查看群组验证统计信息").alias("统计", "stat", "statistics").option("total", "-t 显示总计统计").action(async ({ session, options }, groupId) => {
421
+ const targetGroupId = groupId || session.guildId;
422
+ if (!targetGroupId && !options.total) {
423
+ return "请在群聊中使用此命令或指定群号,或使用 -t 参数查看总计统计";
424
+ }
425
+ let statsList = [];
426
+ if (options.total) {
427
+ const totalStats = await ctx.database.get("group_verification_stats", { groupId: "TOTAL" });
428
+ if (totalStats.length > 0) {
429
+ statsList.push(totalStats[0]);
430
+ }
431
+ if (targetGroupId && targetGroupId !== "TOTAL") {
432
+ const groupStats = await ctx.database.get("group_verification_stats", { groupId: targetGroupId });
433
+ statsList = statsList.concat(groupStats);
434
+ }
429
435
  } else {
430
- return `群 ${targetGroupId} 统计:
431
- 自动通过:${stat.autoApproved}
432
- 手动通过:${stat.manuallyApproved}
433
- 拒绝:${stat.rejected}`;
436
+ const groupStats = await ctx.database.get("group_verification_stats", { groupId: targetGroupId });
437
+ statsList = groupStats;
434
438
  }
439
+ if (statsList.length === 0) {
440
+ if (options.total) {
441
+ return "暂无统计信息";
442
+ } else {
443
+ return `群 ${targetGroupId} 暂无验证统计信息`;
444
+ }
445
+ }
446
+ let result = "";
447
+ for (const stats of statsList) {
448
+ const groupName = stats.groupId === "TOTAL" ? "总计" : `群 ${stats.groupId}`;
449
+ const lastUpdated = new Date(stats.lastUpdated).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
450
+ result += `${groupName} 验证统计:
451
+ 自动批准: ${stats.autoApproved}
452
+ 手动批准: ${stats.manuallyApproved}
453
+ 拒绝: ${stats.rejected}
454
+ 最后更新: ${lastUpdated}
455
+
456
+ `;
457
+ }
458
+ return result.trim();
435
459
  });
436
460
  groupVerify.subcommand(".pending", "查看待审核加群申请").alias("list", "待审", "pend", "待处理").action(async ({ session }) => {
437
461
  if (!session.guildId) {
@@ -488,15 +512,54 @@ ${debugInfo}`];
488
512
 
489
513
  其他命令:
490
514
  .approve <用户ID> - 同意加群申请
491
- .reject <用户ID> - 拒绝加群申请
515
+ .reject <用户ID> - 拒绝加群申请
492
516
  .pending - 查看待审核列表
493
517
  .stats [群号] - 查看统计信息`;
494
518
  });
519
+ ctx.on("ready", async () => {
520
+ const totalStats = await ctx.database.get("group_verification_stats", { groupId: "TOTAL" });
521
+ if (totalStats.length === 0) {
522
+ await ctx.database.create("group_verification_stats", {
523
+ groupId: "TOTAL",
524
+ autoApproved: 0,
525
+ manuallyApproved: 0,
526
+ rejected: 0,
527
+ lastUpdated: /* @__PURE__ */ new Date()
528
+ });
529
+ logger.info("已创建总计统计行");
530
+ } else {
531
+ logger.info("总计统计行已存在");
532
+ }
533
+ await syncTotalStats(ctx);
534
+ });
535
+ async function syncTotalStats(ctx2) {
536
+ try {
537
+ const allStats = await ctx2.database.get("group_verification_stats", {
538
+ groupId: { $ne: "TOTAL" }
539
+ });
540
+ if (allStats.length > 0) {
541
+ const totalAutoApproved = allStats.reduce((sum, stat) => sum + (stat.autoApproved || 0), 0);
542
+ const totalManuallyApproved = allStats.reduce((sum, stat) => sum + (stat.manuallyApproved || 0), 0);
543
+ const totalRejected = allStats.reduce((sum, stat) => sum + (stat.rejected || 0), 0);
544
+ await ctx2.database.set("group_verification_stats", { groupId: "TOTAL" }, {
545
+ autoApproved: totalAutoApproved,
546
+ manuallyApproved: totalManuallyApproved,
547
+ rejected: totalRejected,
548
+ lastUpdated: /* @__PURE__ */ new Date()
549
+ });
550
+ logger.info(`总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}`);
551
+ }
552
+ } catch (error) {
553
+ logger.error("同步总计统计时出错:", error);
554
+ }
555
+ }
556
+ __name(syncTotalStats, "syncTotalStats");
495
557
  }
496
558
  __name(apply, "apply");
497
559
  // Annotate the CommonJS export names for ESM import in node:
498
560
  0 && (module.exports = {
499
561
  Config,
500
562
  apply,
563
+ inject,
501
564
  name
502
565
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-group-verification",
3
3
  "description": "Koishi 群组加群验证插件,支持多关键词匹配审核、多种审核方式和详细统计功能",
4
- "version": "1.0.9",
4
+ "version": "1.0.10",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/src/index.ts CHANGED
@@ -67,6 +67,8 @@ export const Config: Schema<Config> = Schema.object({
67
67
  .description('日志级别')
68
68
  }).description('群组验证插件配置')
69
69
 
70
+ export const inject = ['database']
71
+
70
72
  export function apply(ctx: Context, config: Config) {
71
73
  // 创建数据库表
72
74
  ctx.model.extend('group_verification_config', {
@@ -155,6 +157,30 @@ export function apply(ctx: Context, config: Config) {
155
157
  }
156
158
  })
157
159
 
160
+ // 监听群成员增加事件(包括手动邀请入群)
161
+ ctx.on('guild-member-added', async (session) => {
162
+ const groupId = session.guildId
163
+ const userId = session.userId
164
+
165
+ // 检查是否有待审核记录
166
+ const pendingRecords = await ctx.database.get('group_verification_pending', {
167
+ groupId: groupId,
168
+ userId: userId
169
+ })
170
+
171
+ if (pendingRecords.length > 0) {
172
+ // 通过验证的用户入群,更新统计
173
+ await updateStats(groupId, 'autoApproved')
174
+ // 清除待审核记录
175
+ await ctx.database.remove('group_verification_pending', { id: pendingRecords[0].id })
176
+ logger.info(`用户 ${userId} 通过验证加入群 ${groupId},统计已更新`)
177
+ } else {
178
+ // 手动邀请入群,记录到手动批准统计
179
+ await updateStats(groupId, 'manuallyApproved')
180
+ logger.info(`用户 ${userId} 被手动邀请加入群 ${groupId},手动批准统计已更新`)
181
+ }
182
+ })
183
+
158
184
  // 处理验证失败的情况
159
185
  async function handleFailedVerification(ctx: Context, session: any, config: GroupVerificationConfig) {
160
186
  const guildId = session.guildId
@@ -236,44 +262,28 @@ export function apply(ctx: Context, config: Config) {
236
262
  }
237
263
 
238
264
  // 更新统计信息
239
- async function updateStats(groupId: string, statType: 'autoApproved' | 'manuallyApproved' | 'rejected') {
265
+ async function updateStats(groupId: string, action: 'autoApproved' | 'manuallyApproved' | 'rejected') {
240
266
  // 更新群组统计
241
- const groupStats = await ctx.database.get('group_verification_stats', { groupId })
267
+ const existingStats = await ctx.database.get('group_verification_stats', { groupId })
242
268
 
243
- if (groupStats.length > 0) {
244
- const stats = groupStats[0]
269
+ if (existingStats.length > 0) {
270
+ const stats = existingStats[0]
245
271
  await ctx.database.set('group_verification_stats', { id: stats.id }, {
246
- [statType]: stats[statType] + 1,
272
+ [action]: stats[action] + 1,
247
273
  lastUpdated: new Date()
248
274
  })
249
275
  } else {
250
- // 创建新的统计记录
251
276
  await ctx.database.create('group_verification_stats', {
252
277
  groupId,
253
- autoApproved: statType === 'autoApproved' ? 1 : 0,
254
- manuallyApproved: statType === 'manuallyApproved' ? 1 : 0,
255
- rejected: statType === 'rejected' ? 1 : 0,
278
+ autoApproved: action === 'autoApproved' ? 1 : 0,
279
+ manuallyApproved: action === 'manuallyApproved' ? 1 : 0,
280
+ rejected: action === 'rejected' ? 1 : 0,
256
281
  lastUpdated: new Date()
257
282
  })
258
283
  }
259
284
 
260
- // 更新总统计(groupId为'all')
261
- const totalStats = await ctx.database.get('group_verification_stats', { groupId: 'all' })
262
- if (totalStats.length > 0) {
263
- const stats = totalStats[0]
264
- await ctx.database.set('group_verification_stats', { id: stats.id }, {
265
- [statType]: stats[statType] + 1,
266
- lastUpdated: new Date()
267
- })
268
- } else {
269
- await ctx.database.create('group_verification_stats', {
270
- groupId: 'all',
271
- autoApproved: statType === 'autoApproved' ? 1 : 0,
272
- manuallyApproved: statType === 'manuallyApproved' ? 1 : 0,
273
- rejected: statType === 'rejected' ? 1 : 0,
274
- lastUpdated: new Date()
275
- })
276
- }
285
+ // 同步更新总计统计
286
+ await syncTotalStats(ctx)
277
287
  }
278
288
 
279
289
  // 权限检查函数
@@ -600,23 +610,58 @@ export function apply(ctx: Context, config: Config) {
600
610
 
601
611
  // Subcommand: view statistics
602
612
  groupVerify
603
- .subcommand('.stats [groupId]', '查看验证统计信息')
604
- .alias('statistics', '统计', 'stat', '统计信息')
605
- .action(async ({ session }, groupId) => {
606
- const targetGroupId = groupId || session.guildId || 'all'
613
+ .subcommand('.stats [groupId]', '查看群组验证统计信息')
614
+ .alias('统计', 'stat', 'statistics')
615
+ .option('total', '-t 显示总计统计')
616
+ .action(async ({ session, options }, groupId) => {
617
+ // 权限检查(统计命令相对宽松,任意用户可查看)
618
+ const targetGroupId = groupId || session.guildId
619
+
620
+ if (!targetGroupId && !options.total) {
621
+ return '请在群聊中使用此命令或指定群号,或使用 -t 参数查看总计统计'
622
+ }
607
623
 
608
- const stats = await ctx.database.get('group_verification_stats', { groupId: targetGroupId })
624
+ let statsList: GroupVerificationStats[] = []
609
625
 
610
- if (stats.length === 0) {
611
- return targetGroupId === 'all' ? '暂无统计信息' : `群 ${targetGroupId} 暂无统计信息`
626
+ if (options.total) {
627
+ // 显示总计统计
628
+ const totalStats = await ctx.database.get('group_verification_stats', { groupId: 'TOTAL' })
629
+ if (totalStats.length > 0) {
630
+ statsList.push(totalStats[0])
631
+ }
632
+
633
+ // 如果同时指定了具体群号,也显示该群统计
634
+ if (targetGroupId && targetGroupId !== 'TOTAL') {
635
+ const groupStats = await ctx.database.get('group_verification_stats', { groupId: targetGroupId })
636
+ statsList = statsList.concat(groupStats)
637
+ }
638
+ } else {
639
+ // 显示指定群统计
640
+ const groupStats = await ctx.database.get('group_verification_stats', { groupId: targetGroupId })
641
+ statsList = groupStats
612
642
  }
613
643
 
614
- const stat = stats[0]
615
- if (targetGroupId === 'all') {
616
- return `总体统计:\n自动通过:${stat.autoApproved}\n手动通过:${stat.manuallyApproved}\n拒绝:${stat.rejected}`
617
- } else {
618
- return `群 ${targetGroupId} 统计:\n自动通过:${stat.autoApproved}\n手动通过:${stat.manuallyApproved}\n拒绝:${stat.rejected}`
644
+ if (statsList.length === 0) {
645
+ if (options.total) {
646
+ return '暂无统计信息'
647
+ } else {
648
+ return `群 ${targetGroupId} 暂无验证统计信息`
649
+ }
619
650
  }
651
+
652
+ let result = ''
653
+ for (const stats of statsList) {
654
+ const groupName = stats.groupId === 'TOTAL' ? '总计' : `群 ${stats.groupId}`
655
+ const lastUpdated = new Date(stats.lastUpdated).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
656
+
657
+ result += `${groupName} 验证统计:
658
+ 自动批准: ${stats.autoApproved}
659
+ 手动批准: ${stats.manuallyApproved}
660
+ 拒绝: ${stats.rejected}
661
+ 最后更新: ${lastUpdated}\n\n`
662
+ }
663
+
664
+ return result.trim()
620
665
  })
621
666
 
622
667
  // Subcommand: view pending requests
@@ -687,8 +732,60 @@ export function apply(ctx: Context, config: Config) {
687
732
 
688
733
  其他命令:
689
734
  .approve <用户ID> - 同意加群申请
690
- .reject <用户ID> - 拒绝加群申请
735
+ .reject <用户ID> - 拒绝加群申请
691
736
  .pending - 查看待审核列表
692
737
  .stats [群号] - 查看统计信息`
693
738
  })
739
+
740
+ // 插件初始化时确保总计统计行存在
741
+ ctx.on('ready', async () => {
742
+ // 检查是否已存在总计行(groupId为'TOTAL')
743
+ const totalStats = await ctx.database.get('group_verification_stats', { groupId: 'TOTAL' })
744
+
745
+ if (totalStats.length === 0) {
746
+ // 创建总计统计行
747
+ await ctx.database.create('group_verification_stats', {
748
+ groupId: 'TOTAL',
749
+ autoApproved: 0,
750
+ manuallyApproved: 0,
751
+ rejected: 0,
752
+ lastUpdated: new Date()
753
+ })
754
+ logger.info('已创建总计统计行')
755
+ } else {
756
+ logger.info('总计统计行已存在')
757
+ }
758
+
759
+ // 同步现有统计数据到总计行
760
+ await syncTotalStats(ctx)
761
+ })
762
+
763
+ // 同步统计数据到总计行的函数
764
+ async function syncTotalStats(ctx: Context) {
765
+ try {
766
+ // 获取所有群组统计(排除TOTAL行)
767
+ const allStats = await ctx.database.get('group_verification_stats', {
768
+ groupId: { $ne: 'TOTAL' }
769
+ })
770
+
771
+ if (allStats.length > 0) {
772
+ // 计算总计
773
+ const totalAutoApproved = allStats.reduce((sum, stat) => sum + (stat.autoApproved || 0), 0)
774
+ const totalManuallyApproved = allStats.reduce((sum, stat) => sum + (stat.manuallyApproved || 0), 0)
775
+ const totalRejected = allStats.reduce((sum, stat) => sum + (stat.rejected || 0), 0)
776
+
777
+ // 更新总计行
778
+ await ctx.database.set('group_verification_stats', { groupId: 'TOTAL' }, {
779
+ autoApproved: totalAutoApproved,
780
+ manuallyApproved: totalManuallyApproved,
781
+ rejected: totalRejected,
782
+ lastUpdated: new Date()
783
+ })
784
+
785
+ logger.info(`总计统计已同步: 自动批准${totalAutoApproved}, 手动批准${totalManuallyApproved}, 拒绝${totalRejected}`)
786
+ }
787
+ } catch (error) {
788
+ logger.error('同步总计统计时出错:', error)
789
+ }
790
+ }
694
791
  }