koishi-plugin-chat-analyse 0.3.5 → 0.4.0

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.
@@ -28,6 +28,12 @@ declare module 'koishi' {
28
28
  content: string;
29
29
  timestamp: Date;
30
30
  };
31
+ analyse_at: {
32
+ uid: number;
33
+ target: string;
34
+ content: string;
35
+ timestamp: Date;
36
+ };
31
37
  }
32
38
  }
33
39
  /**
@@ -44,6 +50,7 @@ export declare class Collector {
44
50
  private msgStatBuffer;
45
51
  private cmdStatBuffer;
46
52
  private oriCacheBuffer;
53
+ private whoAtBuffer;
47
54
  private userCache;
48
55
  private pendingUserRequests;
49
56
  private flushInterval;
package/lib/WhoAt.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import { Context, Command } from 'koishi';
2
+ import { Config } from './index';
3
+ /**
4
+ * @class WhoAt
5
+ * @description
6
+ * 负责处理与“谁@我”相关的功能。
7
+ * 该类会注册一个 'whoatme' 子命令,允许用户查询在何时被谁提及。
8
+ * 查询结果将以合并转发的形式发送给用户。
9
+ * 此外,该类还包含一个定时任务,用于定期清理数据库中旧的@记录。
10
+ */
11
+ export declare class WhoAt {
12
+ private ctx;
13
+ private config;
14
+ /**
15
+ * WhoAt 类的构造函数。
16
+ * @param {Context} ctx - Koishi 的插件上下文,用于访问框架核心功能和数据库等服务。
17
+ * @param {Config} config - 插件的配置对象,包含如记录保留天数等设置。
18
+ */
19
+ constructor(ctx: Context, config: Config);
20
+ /**
21
+ * @private
22
+ * @method setupCleanupTask
23
+ * @description 设置一个定时清理任务。
24
+ * 此任务会根据配置中的 `retentionDays` 定期删除过期的@记录,以防止数据库膨胀。
25
+ */
26
+ private setupCleanupTask;
27
+ /**
28
+ * @public
29
+ * @method registerCommand
30
+ * @description 在主 `analyse` 命令下注册 `whoatme` 子命令。
31
+ * @param {Command} analyse - 用户传入的主 `analyse` 命令实例,`whoatme` 将作为其子命令。
32
+ */
33
+ registerCommand(analyse: Command): void;
34
+ }
package/lib/index.d.ts CHANGED
@@ -16,6 +16,8 @@ export interface Config {
16
16
  enableMsgStat: boolean;
17
17
  enableRankStat: boolean;
18
18
  enableOriRecord: boolean;
19
+ enableWhoAt: boolean;
20
+ retentionDays: number;
19
21
  }
20
22
  /**
21
23
  * @const {Schema<Config>} Config
package/lib/index.js CHANGED
@@ -27,7 +27,7 @@ __export(src_exports, {
27
27
  using: () => using
28
28
  });
29
29
  module.exports = __toCommonJS(src_exports);
30
- var import_koishi4 = require("koishi");
30
+ var import_koishi5 = require("koishi");
31
31
 
32
32
  // src/Collector.ts
33
33
  var import_koishi = require("koishi");
@@ -59,6 +59,7 @@ var Collector = class _Collector {
59
59
  msgStatBuffer = /* @__PURE__ */ new Map();
60
60
  cmdStatBuffer = /* @__PURE__ */ new Map();
61
61
  oriCacheBuffer = [];
62
+ whoAtBuffer = [];
62
63
  // 用户缓存
63
64
  userCache = /* @__PURE__ */ new Map();
64
65
  pendingUserRequests = /* @__PURE__ */ new Map();
@@ -97,6 +98,14 @@ var Collector = class _Collector {
97
98
  timestamp: "timestamp"
98
99
  }, { primary: "id", autoInc: true, indexes: ["uid", "timestamp"] });
99
100
  }
101
+ if (this.config.enableWhoAt) {
102
+ this.ctx.model.extend("analyse_at", {
103
+ uid: "unsigned",
104
+ target: "string",
105
+ content: "text",
106
+ timestamp: "timestamp"
107
+ }, { indexes: ["target", "uid"] });
108
+ }
100
109
  }
101
110
  /**
102
111
  * @private
@@ -136,6 +145,16 @@ var Collector = class _Collector {
136
145
  this.msgStatBuffer.set(key, { uid, type, hour: hourStart, count: 1, timestamp: messageTime });
137
146
  }
138
147
  }
148
+ if (this.config.enableWhoAt) {
149
+ const atElements = elements.filter((e) => e.type === "at");
150
+ if (atElements.length > 0) {
151
+ const sanitizedContent = this.sanitizeContent(elements);
152
+ for (const atElement of atElements) {
153
+ const targetId = atElement.attrs.id;
154
+ if (targetId && targetId !== userId) this.whoAtBuffer.push({ uid, target: targetId, content: sanitizedContent, timestamp: messageTime });
155
+ }
156
+ }
157
+ }
139
158
  if (this.config.enableOriRecord) {
140
159
  this.oriCacheBuffer.push({
141
160
  uid,
@@ -225,10 +244,12 @@ var Collector = class _Collector {
225
244
  async flushBuffers() {
226
245
  const cmdBufferToFlush = Array.from(this.cmdStatBuffer.values());
227
246
  const msgBufferToFlush = Array.from(this.msgStatBuffer.values());
228
- const advancedBufferToFlush = this.oriCacheBuffer;
247
+ const oriCacheBufferToFlush = this.oriCacheBuffer;
248
+ const whoAtBufferToFlush = this.whoAtBuffer;
229
249
  this.cmdStatBuffer.clear();
230
250
  this.msgStatBuffer.clear();
231
251
  this.oriCacheBuffer = [];
252
+ this.whoAtBuffer = [];
232
253
  try {
233
254
  if (cmdBufferToFlush.length > 0) {
234
255
  await this.ctx.database.upsert(
@@ -253,7 +274,8 @@ var Collector = class _Collector {
253
274
  }))
254
275
  );
255
276
  }
256
- if (advancedBufferToFlush.length > 0) await this.ctx.database.upsert("analyse_cache", advancedBufferToFlush);
277
+ if (whoAtBufferToFlush.length > 0) await this.ctx.database.upsert("analyse_at", whoAtBufferToFlush);
278
+ if (oriCacheBufferToFlush.length > 0) await this.ctx.database.upsert("analyse_cache", oriCacheBufferToFlush);
257
279
  } catch (error) {
258
280
  this.ctx.logger.error("写入数据出错:", error);
259
281
  }
@@ -298,13 +320,13 @@ var Renderer = class {
298
320
  <style>
299
321
  :root {
300
322
  --card-bg: #ffffff; --text-color: #111827; --header-color: #111827;
301
- --sub-text-color: #6b7280; --border-color: #e5e7eb; --accent-color: #3b82f6;
323
+ --sub-text-color: #6b7280; --border-color: #e5e7eb; --accent-color: #4a6ee0;
302
324
  --chip-bg: #f3f4f6; --stripe-bg: #f9fafb; --gold: #f59e0b; --silver: #9ca3af; --bronze: #a16207;
303
325
  }
304
326
  body {
305
327
  display: inline-block; /* Crucial for shrink-wrapping */
306
328
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
307
- background: transparent; margin: 0; padding: 10px;
329
+ background: transparent; margin: 0; padding: 8px;
308
330
  -webkit-font-smoothing: antialiased;
309
331
  }
310
332
  .container {
@@ -312,22 +334,22 @@ var Renderer = class {
312
334
  border-radius: 12px; padding: 0; overflow: hidden;
313
335
  box-shadow: 0 2px 4px rgba(0,0,0,0.05);
314
336
  }
315
- .header { padding: 12px 16px; }
316
- .header-table { border-collapse: collapse; table-layout: auto; width: 100%; }
337
+ .header { padding: 10px 14px; }
338
+ .header-table { border-collapse: collapse; width: 100%; }
317
339
  .header-table-left, .header-table-right { width: 1%; white-space: nowrap; }
318
340
  .header-table-left { text-align: left; }
319
341
  .header-table-center { text-align: center; }
320
342
  .header-table-right { text-align: right; }
321
343
  .title-text { font-size: 18px; font-weight: 600; color: var(--header-color); margin: 0; }
322
344
  .stat-chip, .time-label {
323
- display: inline-flex; align-items: baseline; padding: 5px 10px; border-radius: 8px;
345
+ display: inline-flex; align-items: baseline; padding: 4px 8px; border-radius: 8px;
324
346
  background: var(--chip-bg); font-size: 13px; color: var(--sub-text-color);
325
347
  }
326
348
  .stat-chip span { font-weight: 600; color: var(--text-color); margin-left: 4px; }
327
349
  .table-container { border-top: 1px solid var(--border-color); }
328
- .main-table { border-collapse: collapse; table-layout: auto; width: 100%; }
350
+ .main-table { border-collapse: collapse; width: 100%; }
329
351
  .main-table th, .main-table td {
330
- padding: 10px 16px;
352
+ padding: 8px 14px;
331
353
  vertical-align: middle;
332
354
  }
333
355
  .main-table th {
@@ -336,10 +358,7 @@ var Renderer = class {
336
358
  }
337
359
  .main-table td { font-size: 14px; color: var(--text-color); }
338
360
  .main-table tbody tr:nth-child(even) { background-color: var(--stripe-bg); }
339
- .main-table .name-cell, .main-table .name-header {
340
- text-align: left;
341
- white-space: normal;
342
- }
361
+ .main-table .name-cell, .main-table .name-header { text-align: left; }
343
362
  .main-table .rank-cell, .main-table .count-cell, .main-table .date-cell, .main-table .percent-cell, .main-table .header-right-align {
344
363
  text-align: right;
345
364
  white-space: nowrap;
@@ -355,7 +374,7 @@ var Renderer = class {
355
374
  .rank-silver { color: var(--silver) !important; }
356
375
  .rank-bronze { color: var(--bronze) !important; }
357
376
  .percent-cell { position: relative; }
358
- .percent-bar { position: absolute; top: 0; left: 0; height: 100%; background-color: var(--accent-color); opacity: 0.1; }
377
+ .percent-bar { position: absolute; top: 0; right: 0; height: 100%; background-color: var(--accent-color); opacity: 0.15; }
359
378
  .percent-text { position: relative; z-index: 1; }
360
379
  </style>
361
380
  </head>
@@ -373,7 +392,6 @@ var Renderer = class {
373
392
  return await page.screenshot({ type: "png", fullPage: true, omitBackground: true });
374
393
  } catch (error) {
375
394
  this.ctx.logger.error("图片渲染出错:", error);
376
- throw new Error(`图片渲染出错: ${error.message || "未知错误"}`);
377
395
  } finally {
378
396
  if (page) await page.close().catch(() => {
379
397
  });
@@ -392,17 +410,17 @@ var Renderer = class {
392
410
  const diff = Date.now() - date.getTime();
393
411
  if (diff < import_koishi2.Time.minute) return "刚刚";
394
412
  if (diff > 365 * import_koishi2.Time.day) {
395
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
413
+ return date.toLocaleDateString("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit" }).replace(/\//g, "-");
396
414
  }
397
415
  const timeUnits = [
398
- { unit: "月", ms: 30 * import_koishi2.Time.day },
399
- { unit: "天", ms: import_koishi2.Time.day },
400
- { unit: "时", ms: import_koishi2.Time.hour },
401
- { unit: "分", ms: import_koishi2.Time.minute }
416
+ ["月", 30 * import_koishi2.Time.day],
417
+ ["天", import_koishi2.Time.day],
418
+ ["时", import_koishi2.Time.hour],
419
+ ["分", import_koishi2.Time.minute]
402
420
  ];
403
421
  let remainingDiff = diff;
404
422
  const parts = [];
405
- for (const { unit, ms } of timeUnits) {
423
+ for (const [unit, ms] of timeUnits) {
406
424
  if (remainingDiff >= ms) {
407
425
  const value = Math.floor(remainingDiff / ms);
408
426
  parts.push(`${value}${unit}`);
@@ -410,7 +428,7 @@ var Renderer = class {
410
428
  }
411
429
  }
412
430
  const result = parts.slice(0, 2).join("");
413
- return result ? `${result}前` : "刚刚";
431
+ return `${result}前`;
414
432
  }
415
433
  /**
416
434
  * @public
@@ -427,16 +445,16 @@ var Renderer = class {
427
445
  const { title, time, list } = data;
428
446
  if (!list?.length) return "暂无数据可供渲染";
429
447
  let totalValueForPercent = 0;
430
- const countHeaderIndex = headers?.findIndex((h2) => ["总计发言", "条数", "次数", "数量"].includes(h2));
448
+ const countHeaderIndex = headers?.findIndex((h3) => ["总计发言", "条数", "次数", "数量"].includes(h3));
431
449
  if (countHeaderIndex > -1) {
432
450
  totalValueForPercent = list.reduce((sum, row) => sum + (Number(row[countHeaderIndex]) || 0), 0);
433
451
  }
434
452
  const totalCount = data.total || totalValueForPercent;
435
- const tableHeadHtml = headers?.length > 0 ? `<thead><tr><th class="rank-cell">#</th>${headers.map((h2, i) => {
453
+ const tableHeadHtml = headers?.length > 0 ? `<thead><tr><th class="rank-cell">#</th>${headers.map((h3, i) => {
436
454
  const firstCell = list[0]?.[i];
437
- const isRightAlign = typeof firstCell === "number" || firstCell instanceof Date || h2.includes("占比");
455
+ const isRightAlign = typeof firstCell === "number" || firstCell instanceof Date || h3.includes("占比");
438
456
  const alignClass = isRightAlign ? "header-right-align" : "name-header";
439
- return `<th class="${alignClass}">${h2}</th>`;
457
+ return `<th class="${alignClass}">${h3}</th>`;
440
458
  }).join("")}</tr></thead>` : "";
441
459
  const tableRowsHtml = list.map((row, index) => {
442
460
  const rank = index + 1;
@@ -514,7 +532,7 @@ var Stat = class {
514
532
  */
515
533
  registerCommands(analyse) {
516
534
  if (this.config.enableCmdStat) {
517
- analyse.subcommand(".cmd", "命令使用统计").option("user", "-u [user:user] 指定用户").option("guild", "-g [guildId:string] 指定群组").option("all", "-a 展示全局统计").action(async ({ session, options }) => {
535
+ analyse.subcommand(".cmd", "命令使用统计").option("user", "-u <user:string> 指定用户").option("guild", "-g <guildId:string> 指定群组").option("all", "-a 展示全局统计").action(async ({ session, options }) => {
518
536
  const scope = this.parseQueryScope(session, options);
519
537
  if (scope.error) return scope.error;
520
538
  try {
@@ -531,7 +549,7 @@ var Stat = class {
531
549
  });
532
550
  }
533
551
  if (this.config.enableMsgStat) {
534
- analyse.subcommand(".msg", "消息发送统计").option("user", "-u [user:user] 指定用户").option("guild", "-g [guildId:string] 指定群组").option("type", "-t <type:string> 指定类型").option("all", "-a 展示全局统计").action(async ({ session, options }) => {
552
+ analyse.subcommand(".msg", "消息发送统计").option("user", "-u <user:string> 指定用户").option("guild", "-g <guildId:string> 指定群组").option("type", "-t <type:string> 指定类型").option("all", "-a 展示全局统计").action(async ({ session, options }) => {
535
553
  const scope = this.parseQueryScope(session, options);
536
554
  if (scope.error) return scope.error;
537
555
  try {
@@ -557,9 +575,8 @@ var Stat = class {
557
575
  });
558
576
  }
559
577
  if (this.config.enableRankStat) {
560
- analyse.subcommand(".rank", "用户发言排行").option("guild", "-g [guildId:string] 指定群组").option("all", "-a 展示全局统计").option("hours", "-h <hours:number> 指定时长", { fallback: 24 }).action(async ({ session, options }) => {
561
- let guildId = options.all ? void 0 : typeof options.guild === "string" ? options.guild : session.guildId;
562
- if (!session.guildId) return "请指定群组 ID";
578
+ analyse.subcommand(".rank", "用户发言排行").option("guild", "-g <guildId:string> 指定群组").option("all", "-a 展示全局统计").option("hours", "-h <hours:number> 指定时长", { fallback: 24 }).action(async ({ session, options }) => {
579
+ const guildId = options.all ? void 0 : options.guild || session.guildId;
563
580
  if (!guildId && !options.all) return "请提供查询范围";
564
581
  try {
565
582
  const stats = await this.getActiveUserStats(options.hours, guildId);
@@ -589,15 +606,20 @@ var Stat = class {
589
606
  */
590
607
  parseQueryScope(session, options) {
591
608
  let userId, guildId;
592
- if (typeof options.user === "string") userId = import_koishi3.h.select(options.user, "user")[0]?.attrs.id;
593
- else if (options.user) userId = session.userId;
594
- if (typeof options.guild === "string") guildId = options.guild;
595
- else if (options.guild) {
596
- if (!session.guildId) return { error: "请指定群组 ID" };
597
- guildId = session.guildId;
609
+ if (options.user) {
610
+ const atElements = import_koishi3.h.select(options.user, "at");
611
+ if (atElements.length > 0) {
612
+ userId = atElements[0].attrs.id;
613
+ } else {
614
+ userId = options.user.trim();
615
+ }
598
616
  }
617
+ if (options.guild) guildId = options.guild;
599
618
  if (options.all) return { userId, guildId: void 0 };
600
- if (!guildId && !userId) return session.guildId ? { guildId: session.guildId } : { error: "请提供查询范围" };
619
+ if (!userId && !guildId) {
620
+ if (session.guildId) return { guildId: session.guildId };
621
+ return { error: "请提供查询范围" };
622
+ }
601
623
  return { userId, guildId };
602
624
  }
603
625
  /**
@@ -643,9 +665,9 @@ var Stat = class {
643
665
  const guild = await this.ctx.database.get("analyse_user", { channelId: guildId }, ["channelName"]);
644
666
  scopeText = guild[0]?.channelName || guildId;
645
667
  }
646
- if (options.main === "排行") return `${scopeText}的${options.timeRange}小时消息排行`;
647
- if (options.main === "消息" && options.subtype) return `${scopeText}"${options.subtype}"消息统计`;
648
- return `${scopeText}的${options.main}统计`;
668
+ if (options.main === "排行") return `${scopeText}${options.timeRange}小时消息排行`;
669
+ if (options.main === "消息" && options.subtype) return `${scopeText}"${options.subtype}"消息统计`;
670
+ return `${scopeText}${options.main}统计`;
649
671
  }
650
672
  /**
651
673
  * @private
@@ -758,6 +780,73 @@ var Stat = class {
758
780
  }
759
781
  };
760
782
 
783
+ // src/WhoAt.ts
784
+ var import_koishi4 = require("koishi");
785
+ var WhoAt = class {
786
+ /**
787
+ * WhoAt 类的构造函数。
788
+ * @param {Context} ctx - Koishi 的插件上下文,用于访问框架核心功能和数据库等服务。
789
+ * @param {Config} config - 插件的配置对象,包含如记录保留天数等设置。
790
+ */
791
+ constructor(ctx, config) {
792
+ this.ctx = ctx;
793
+ this.config = config;
794
+ this.setupCleanupTask();
795
+ }
796
+ static {
797
+ __name(this, "WhoAt");
798
+ }
799
+ /**
800
+ * @private
801
+ * @method setupCleanupTask
802
+ * @description 设置一个定时清理任务。
803
+ * 此任务会根据配置中的 `retentionDays` 定期删除过期的@记录,以防止数据库膨胀。
804
+ */
805
+ setupCleanupTask() {
806
+ if (this.config.retentionDays > 0) {
807
+ this.ctx.cron("0 0 * * *", async () => {
808
+ try {
809
+ const cutoffDate = new Date(Date.now() - this.config.retentionDays * import_koishi4.Time.day);
810
+ await this.ctx.database.remove("analyse_at", { timestamp: { $lt: cutoffDate } });
811
+ } catch (error) {
812
+ this.ctx.logger.error("清理 @ 历史记录出错:", error);
813
+ }
814
+ });
815
+ }
816
+ }
817
+ /**
818
+ * @public
819
+ * @method registerCommand
820
+ * @description 在主 `analyse` 命令下注册 `whoatme` 子命令。
821
+ * @param {Command} analyse - 用户传入的主 `analyse` 命令实例,`whoatme` 将作为其子命令。
822
+ */
823
+ registerCommand(analyse) {
824
+ analyse.subcommand("whoatme", "谁 @ 我").action(async ({ session }) => {
825
+ if (!session.userId) return "无法获取用户信息";
826
+ try {
827
+ const records = await this.ctx.database.select("analyse_at").where({ target: session.userId }).orderBy("timestamp", "asc").limit(100).execute();
828
+ if (records.length === 0) return "暂无 @ 记录";
829
+ const uids = [...new Set(records.map((r) => r.uid))];
830
+ const users = await this.ctx.database.select("analyse_user", { uid: { $in: uids } }).project(["uid", "userName", "userId"]).execute();
831
+ const userInfoMap = new Map(users.map((u) => [u.uid, { name: u.userName, id: u.userId }]));
832
+ const messageElements = records.map((record) => {
833
+ const senderInfo = userInfoMap.get(record.uid);
834
+ const userId = senderInfo?.id;
835
+ const authorElement = (0, import_koishi4.h)("author", { userId, name: userId });
836
+ const contentElement = import_koishi4.h.text(record.content);
837
+ return (0, import_koishi4.h)("message", {}, [authorElement, contentElement]);
838
+ });
839
+ if (messageElements.length === 0) return "暂无有效 @ 记录";
840
+ const forwardMessage = (0, import_koishi4.h)("message", { forward: true }, messageElements);
841
+ await session.send(forwardMessage);
842
+ } catch (error) {
843
+ this.ctx.logger.error("查询 @ 记录时失败:", error);
844
+ return "查询失败,请稍后再试";
845
+ }
846
+ });
847
+ }
848
+ };
849
+
761
850
  // src/index.ts
762
851
  var usage = `
763
852
  <div style="border-radius: 10px; border: 1px solid #ddd; padding: 16px; margin-bottom: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);">
@@ -772,22 +861,27 @@ var usage = `
772
861
  </div>
773
862
  `;
774
863
  var name = "chat-analyse";
775
- var using = ["database", "puppeteer"];
776
- var Config = import_koishi4.Schema.intersect([
777
- import_koishi4.Schema.object({
778
- enableListener: import_koishi4.Schema.boolean().default(true).description("启用消息监听"),
779
- enableOriRecord: import_koishi4.Schema.boolean().default(true).description("启用原始记录")
864
+ var using = ["database", "puppeteer", "cron"];
865
+ var Config = import_koishi5.Schema.intersect([
866
+ import_koishi5.Schema.object({
867
+ enableListener: import_koishi5.Schema.boolean().default(true).description("启用消息监听"),
868
+ enableOriRecord: import_koishi5.Schema.boolean().default(true).description("启用原始记录")
780
869
  }).description("监听配置"),
781
- import_koishi4.Schema.object({
782
- enableCmdStat: import_koishi4.Schema.boolean().default(true).description("启用命令统计"),
783
- enableMsgStat: import_koishi4.Schema.boolean().default(true).description("启用消息统计"),
784
- enableRankStat: import_koishi4.Schema.boolean().default(true).description("启用发言排行")
785
- }).description("命令配置")
870
+ import_koishi5.Schema.object({
871
+ enableCmdStat: import_koishi5.Schema.boolean().default(true).description("启用命令统计"),
872
+ enableMsgStat: import_koishi5.Schema.boolean().default(true).description("启用消息统计"),
873
+ enableRankStat: import_koishi5.Schema.boolean().default(true).description("启用发言排行")
874
+ }).description("命令配置"),
875
+ import_koishi5.Schema.object({
876
+ enableWhoAt: import_koishi5.Schema.boolean().default(true).description("启用 @ 记录"),
877
+ retentionDays: import_koishi5.Schema.number().min(0).default(7).description("保留天数")
878
+ }).description("@ 记录配置")
786
879
  ]);
787
880
  function apply(ctx, config) {
788
881
  if (config.enableListener) new Collector(ctx, config);
789
882
  const analyse = ctx.command("analyse", "聊天记录分析");
790
883
  new Stat(ctx, config).registerCommands(analyse);
884
+ if (config.enableWhoAt) new WhoAt(ctx, config).registerCommand(analyse);
791
885
  }
792
886
  __name(apply, "apply");
793
887
  // Annotate the CommonJS export names for ESM import in node:
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chat-analyse",
3
3
  "description": "聊天记录分析",
4
- "version": "0.3.5",
4
+ "version": "0.4.0",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],