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.
- package/lib/Collector.d.ts +7 -0
- package/lib/WhoAt.d.ts +34 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +146 -52
- package/package.json +1 -1
package/lib/Collector.d.ts
CHANGED
|
@@ -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
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
|
|
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
|
|
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 (
|
|
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: #
|
|
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:
|
|
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:
|
|
316
|
-
.header-table { border-collapse: collapse;
|
|
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:
|
|
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;
|
|
350
|
+
.main-table { border-collapse: collapse; width: 100%; }
|
|
329
351
|
.main-table th, .main-table td {
|
|
330
|
-
padding:
|
|
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;
|
|
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
|
|
413
|
+
return date.toLocaleDateString("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit" }).replace(/\//g, "-");
|
|
396
414
|
}
|
|
397
415
|
const timeUnits = [
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
|
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
|
|
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((
|
|
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((
|
|
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 ||
|
|
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}">${
|
|
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
|
|
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
|
|
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
|
|
561
|
-
|
|
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 (
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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 (!
|
|
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}
|
|
647
|
-
if (options.main === "消息" && options.subtype) return `${scopeText}
|
|
648
|
-
return `${scopeText}
|
|
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 =
|
|
777
|
-
|
|
778
|
-
enableListener:
|
|
779
|
-
enableOriRecord:
|
|
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
|
-
|
|
782
|
-
enableCmdStat:
|
|
783
|
-
enableMsgStat:
|
|
784
|
-
enableRankStat:
|
|
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:
|