koishi-plugin-chat-analyse 0.6.4 → 1.0.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/index.d.ts +1 -1
- package/lib/index.js +54 -91
- package/package.json +3 -4
- package/readme.md +77 -1
- package/lib/Analyse.d.ts +0 -22
- package/lib/Collector.d.ts +0 -86
- package/lib/Data.d.ts +0 -17
- package/lib/Renderer.d.ts +0 -87
- package/lib/Stat.d.ts +0 -23
- package/lib/WhoAt.d.ts +0 -21
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -553,7 +553,7 @@ var Stat = class {
|
|
|
553
553
|
};
|
|
554
554
|
}, "createHandler");
|
|
555
555
|
if (this.config.enableCmdStat) {
|
|
556
|
-
cmd.subcommand("cmdstat", "命令统计").option("user", "-u <user:string> 指定用户").option("guild", "-g <guildId:string> 指定群组").option("separate", "-h 分离展示").option("all", "-a 全局").action(createHandler(async (scope, options) => {
|
|
556
|
+
cmd.subcommand("cmdstat", "命令统计").usage("查询命令统计,可指定查询范围,默认当前群组。").option("user", "-u <user:string> 指定用户").option("guild", "-g <guildId:string> 指定群组").option("separate", "-h 分离展示").option("all", "-a 全局").action(createHandler(async (scope, options) => {
|
|
557
557
|
const stats = await this.ctx.database.select("analyse_cmd").where({ uid: { $in: scope.uids } }).groupBy("command", { count: /* @__PURE__ */ __name((row) => import_koishi3.$.sum(row.count), "count"), lastUsed: /* @__PURE__ */ __name((row) => import_koishi3.$.max(row.timestamp), "lastUsed") }).orderBy("count", "desc").execute();
|
|
558
558
|
if (stats.length === 0) return "暂无统计数据";
|
|
559
559
|
let processedStats;
|
|
@@ -581,7 +581,7 @@ var Stat = class {
|
|
|
581
581
|
}));
|
|
582
582
|
}
|
|
583
583
|
if (this.config.enableMsgStat) {
|
|
584
|
-
cmd.subcommand("msgstat", "发言统计").option("user", "-u <user:string> 指定用户").option("guild", "-g <guildId:string> 指定群组").option("type", "-t <type:string> 指定类型").option("all", "-a 全局").action(createHandler(async (scope, options) => {
|
|
584
|
+
cmd.subcommand("msgstat", "发言统计").usage("查询发言统计,可指定查询范围,默认当前群组。").option("user", "-u <user:string> 指定用户").option("guild", "-g <guildId:string> 指定群组").option("type", "-t <type:string> 指定类型").option("all", "-a 全局").action(createHandler(async (scope, options) => {
|
|
585
585
|
const { type } = options;
|
|
586
586
|
const query = { uid: { $in: scope.uids } };
|
|
587
587
|
if (type) query.type = type;
|
|
@@ -597,7 +597,7 @@ var Stat = class {
|
|
|
597
597
|
}));
|
|
598
598
|
}
|
|
599
599
|
if (this.config.enableRankStat) {
|
|
600
|
-
cmd.subcommand("rankstat", "发言排行").option("guild", "-g <guildId:string> 指定群组").option("type", "-t <type:string> 指定类型").option("hours", "-h <hours:number> 指定时长", { fallback: 24 }).option("all", "-a 全局").action(createHandler(async (scope, options) => {
|
|
600
|
+
cmd.subcommand("rankstat", "发言排行").usage("查询发言排行,可指定查询范围,默认当前群组。").option("guild", "-g <guildId:string> 指定群组").option("type", "-t <type:string> 指定类型").option("hours", "-h <hours:number> 指定时长", { fallback: 24 }).option("all", "-a 全局").action(createHandler(async (scope, options) => {
|
|
601
601
|
const { hours, type } = options;
|
|
602
602
|
const since = new Date(Date.now() - hours * import_koishi3.Time.hour);
|
|
603
603
|
const query = { uid: { $in: scope.uids }, timestamp: { $gte: since } };
|
|
@@ -614,7 +614,7 @@ var Stat = class {
|
|
|
614
614
|
}));
|
|
615
615
|
}
|
|
616
616
|
if (this.config.enableActivity) {
|
|
617
|
-
cmd.subcommand("activity", "活跃统计").option("user", "-u <user:string> 指定用户").option("guild", "-g <guildId:string> 指定群组").option("hours", "-h <hours:number> 指定偏移时长").option("all", "-a 全局").option("days", "-d 切换至天数").action(createHandler(async (scope, options) => {
|
|
617
|
+
cmd.subcommand("activity", "活跃统计").usage("查询活跃统计,可指定查询范围,默认当前群组。").option("user", "-u <user:string> 指定用户").option("guild", "-g <guildId:string> 指定群组").option("hours", "-h <hours:number> 指定偏移时长").option("all", "-a 全局").option("days", "-d 切换至天数").action(createHandler(async (scope, options) => {
|
|
618
618
|
const { days, hours } = options;
|
|
619
619
|
if (days) {
|
|
620
620
|
const timeRangeInDays = 24;
|
|
@@ -798,7 +798,7 @@ var Data = class {
|
|
|
798
798
|
return "数据恢复失败";
|
|
799
799
|
}
|
|
800
800
|
});
|
|
801
|
-
cmd.subcommand(".clear", "清除数据", { authority: 4 }).option("table", "-t <table:string> 指定表名").option("guild", "-g <guildId:string> 指定群组").option("user", "-u <user:string> 指定用户").option("days", "-d <days:number> 指定天数").option("command", "-c <command:string> 指定命令").option("all", "-a 清除全部").
|
|
801
|
+
cmd.subcommand(".clear", "清除数据", { authority: 4 }).usage(`清除指定统计数据,可精确控制清除范围。`).option("table", "-t <table:string> 指定表名").option("guild", "-g <guildId:string> 指定群组").option("user", "-u <user:string> 指定用户").option("days", "-d <days:number> 指定天数").option("command", "-c <command:string> 指定命令").option("all", "-a 清除全部").action(async ({ options }) => {
|
|
802
802
|
if (Object.keys(options).length === 0) return "请指定清除条件";
|
|
803
803
|
if (options.table && !ALL_TABLES.includes(options.table)) return `表名 ${options.table} 无效`;
|
|
804
804
|
try {
|
|
@@ -871,107 +871,70 @@ ${commandOutput}`;
|
|
|
871
871
|
|
|
872
872
|
// src/Analyse.ts
|
|
873
873
|
var import_koishi6 = require("koishi");
|
|
874
|
-
var
|
|
875
|
-
var
|
|
874
|
+
var import_jieba = require("@node-rs/jieba");
|
|
875
|
+
var import_dict = require("@node-rs/jieba/dict");
|
|
876
876
|
var Analyse = class {
|
|
877
877
|
constructor(ctx, config) {
|
|
878
878
|
this.ctx = ctx;
|
|
879
879
|
this.config = config;
|
|
880
880
|
this.renderer = new Renderer(ctx);
|
|
881
|
-
this.
|
|
882
|
-
this.
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
881
|
+
if (config.enableWordCloud) this.jieba = import_jieba.Jieba.withDict(import_dict.dict);
|
|
882
|
+
if (this.config.enableOriRecord && this.config.cacheRetentionDays > 0) {
|
|
883
|
+
this.ctx.cron("0 0 * * *", async () => {
|
|
884
|
+
const cutoffDate = new Date(Date.now() - this.config.cacheRetentionDays * import_koishi6.Time.day);
|
|
885
|
+
await this.ctx.database.remove("analyse_cache", { timestamp: { $lt: cutoffDate } }).catch((e) => this.ctx.logger.error("清理原始记录缓存失败:", e));
|
|
886
|
+
});
|
|
887
|
+
}
|
|
887
888
|
}
|
|
888
889
|
static {
|
|
889
890
|
__name(this, "Analyse");
|
|
890
891
|
}
|
|
891
892
|
renderer;
|
|
892
|
-
|
|
893
|
-
isNlpReady = false;
|
|
893
|
+
jieba = null;
|
|
894
894
|
/**
|
|
895
|
-
* @
|
|
896
|
-
* @
|
|
897
|
-
* @
|
|
895
|
+
* @public @method registerCommands
|
|
896
|
+
* @description 在主命令下注册子命令。
|
|
897
|
+
* @param cmd - 主命令实例。
|
|
898
898
|
*/
|
|
899
|
-
async initializeNlp() {
|
|
900
|
-
await this.nlp.train();
|
|
901
|
-
this.isNlpReady = true;
|
|
902
|
-
}
|
|
903
899
|
registerCommands(cmd) {
|
|
904
900
|
if (this.config.enableWordCloud) {
|
|
905
|
-
cmd.subcommand("
|
|
906
|
-
if (!this.
|
|
901
|
+
cmd.subcommand("wordcloud", "生成词云").usage("基于聊天记录生成词云图,可指定范围,默认当前群组。").option("guild", "-g <guildId:string> 指定群组").option("user", "-u <user:string> 指定用户").option("hours", "-h <hours:number> 指定时长", { fallback: 24 }).option("all", "-a 全局").action(async ({ session, options }) => {
|
|
902
|
+
if (!this.jieba) return "Jieba 分词服务未就绪";
|
|
907
903
|
const scope = await parseQueryScope(this.ctx, session, options);
|
|
908
904
|
if (scope.error) return scope.error;
|
|
905
|
+
scope.uids ??= (await this.ctx.database.get("analyse_user", {}, ["uid"])).map((u) => u.uid);
|
|
906
|
+
if (!scope.uids?.length) return "暂无用户数据";
|
|
909
907
|
const since = new Date(Date.now() - options.hours * import_koishi6.Time.hour);
|
|
910
|
-
const records = await this.ctx.database.
|
|
911
|
-
if (records.length
|
|
908
|
+
const records = await this.ctx.database.get("analyse_cache", { uid: { $in: scope.uids }, timestamp: { $gte: since } }, ["content"]);
|
|
909
|
+
if (!records.length) return "暂无统计数据";
|
|
912
910
|
const allText = records.map((r) => r.content).join(" ");
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
if (Array.isArray(renderResult) && renderResult.length > 0) {
|
|
925
|
-
for (const buffer of renderResult) await session.sendQueued(import_koishi6.h.image(buffer, "image/png"));
|
|
926
|
-
}
|
|
927
|
-
});
|
|
928
|
-
}
|
|
929
|
-
if (this.config.enableVocabulary) {
|
|
930
|
-
cmd.subcommand(".vocabulary", "词汇排行").usage("根据不重复词汇量占总词汇量的比例进行排行。").option("guild", "-g <guildId:string> 指定群组").option("all", "-a 全局").option("hours", "-h <hours:number> 指定时长", { fallback: 24 }).action(async ({ session, options }) => {
|
|
931
|
-
if (!this.isNlpReady) return "文本分析尚未就绪,请稍后再试";
|
|
932
|
-
const scope = await parseQueryScope(this.ctx, session, options);
|
|
933
|
-
if (scope.error) return scope.error;
|
|
934
|
-
const users = await this.ctx.database.get("analyse_user", { uid: { $in: scope.uids } }, ["uid", "userName"]);
|
|
935
|
-
const userNameMap = new Map(users.map((u) => [u.uid, u.userName]));
|
|
936
|
-
const since = new Date(Date.now() - options.hours * import_koishi6.Time.hour);
|
|
937
|
-
const allRecords = await this.ctx.database.get("analyse_cache", { uid: { $in: scope.uids }, timestamp: { $gte: since } }, ["uid", "content"]);
|
|
938
|
-
if (allRecords.length === 0) return "暂无统计数据";
|
|
939
|
-
const messagesByUid = /* @__PURE__ */ new Map();
|
|
940
|
-
for (const record of allRecords) {
|
|
941
|
-
if (!messagesByUid.has(record.uid)) messagesByUid.set(record.uid, []);
|
|
942
|
-
messagesByUid.get(record.uid).push(record.content);
|
|
943
|
-
}
|
|
944
|
-
const richnessData = [];
|
|
945
|
-
for (const [uid, messages] of messagesByUid.entries()) {
|
|
946
|
-
const allText = messages.join(" ");
|
|
947
|
-
const result = await this.nlp.process("zh", allText);
|
|
948
|
-
const words = (result.stems || []).filter((stem) => stem.length > 1);
|
|
949
|
-
if (words.length < 50) continue;
|
|
950
|
-
const uniqueWords = new Set(words);
|
|
951
|
-
const richness = uniqueWords.size / words.length;
|
|
952
|
-
richnessData.push({
|
|
953
|
-
name: userNameMap.get(uid) || `UID ${uid}`,
|
|
954
|
-
total: words.length,
|
|
955
|
-
unique: uniqueWords.size,
|
|
956
|
-
richness
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
if (richnessData.length === 0) return "暂无有效词语";
|
|
960
|
-
richnessData.sort((a, b) => b.richness - a.richness);
|
|
961
|
-
const list = richnessData.map((item) => [
|
|
962
|
-
item.name,
|
|
963
|
-
item.unique,
|
|
964
|
-
item.total,
|
|
965
|
-
`${(item.richness * 100).toFixed(2)}%`
|
|
911
|
+
const exclusionSet = /* @__PURE__ */ new Set([
|
|
912
|
+
"[face]",
|
|
913
|
+
"[file]",
|
|
914
|
+
"[forward]",
|
|
915
|
+
"[img]",
|
|
916
|
+
"[audio]",
|
|
917
|
+
"[video]",
|
|
918
|
+
"[json]",
|
|
919
|
+
"[rps]",
|
|
920
|
+
"[markdown]",
|
|
921
|
+
"[dice]"
|
|
966
922
|
]);
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
923
|
+
const words = this.jieba.cut(allText).filter((w) => {
|
|
924
|
+
if (w.length <= 1) return false;
|
|
925
|
+
if (/^\d+$/.test(w)) return false;
|
|
926
|
+
if (exclusionSet.has(w)) return false;
|
|
927
|
+
if (/^\[at:.*?\]$/.test(w)) return false;
|
|
928
|
+
return true;
|
|
929
|
+
});
|
|
930
|
+
if (!words.length) return "暂无有效词语";
|
|
931
|
+
const wordCounts = words.reduce((map, word) => map.set(word, (map.get(word) || 0) + 1), /* @__PURE__ */ new Map());
|
|
932
|
+
const wordList = Array.from(wordCounts.entries()).sort((a, b) => b[1] - a[1]);
|
|
933
|
+
const title = await generateTitle(this.ctx, scope.scopeDesc, { main: "词云" });
|
|
934
|
+
const result = await this.renderer.renderWordCloud({ title, time: /* @__PURE__ */ new Date(), words: wordList });
|
|
935
|
+
if (typeof result === "string") return result;
|
|
936
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
937
|
+
for (const buffer of result) await session.sendQueued(import_koishi6.h.image(buffer, "image/png"));
|
|
975
938
|
}
|
|
976
939
|
});
|
|
977
940
|
}
|
|
@@ -1003,14 +966,14 @@ var Config3 = import_koishi7.Schema.intersect([
|
|
|
1003
966
|
enableMsgStat: import_koishi7.Schema.boolean().default(true).description("启用消息统计"),
|
|
1004
967
|
enableActivity: import_koishi7.Schema.boolean().default(true).description("启用活跃统计"),
|
|
1005
968
|
enableRankStat: import_koishi7.Schema.boolean().default(true).description("启用发言排行"),
|
|
1006
|
-
rankRetentionDays: import_koishi7.Schema.number().min(0).default(
|
|
969
|
+
rankRetentionDays: import_koishi7.Schema.number().min(0).default(365).description("排行保留天数"),
|
|
1007
970
|
enableWhoAt: import_koishi7.Schema.boolean().default(true).description("启用提及记录"),
|
|
1008
971
|
atRetentionDays: import_koishi7.Schema.number().min(0).default(7).description("提及保留天数")
|
|
1009
972
|
}).description("基础分析配置"),
|
|
1010
973
|
import_koishi7.Schema.object({
|
|
1011
974
|
enableOriRecord: import_koishi7.Schema.boolean().default(true).description("启用原始记录"),
|
|
1012
|
-
|
|
1013
|
-
|
|
975
|
+
cacheRetentionDays: import_koishi7.Schema.number().min(0).default(180).description("记录保留天数"),
|
|
976
|
+
enableWordCloud: import_koishi7.Schema.boolean().default(true).description("启用词云生成")
|
|
1014
977
|
}).description("高级分析配置")
|
|
1015
978
|
]);
|
|
1016
979
|
async function parseQueryScope(ctx, session, options) {
|
|
@@ -1057,7 +1020,7 @@ function apply(ctx, config) {
|
|
|
1057
1020
|
new Stat(ctx, config).registerCommands(analyse);
|
|
1058
1021
|
if (config.enableWhoAt) new WhoAt(ctx, config).registerCommand(analyse);
|
|
1059
1022
|
if (config.enableDataIO) new Data(ctx).registerCommands(analyse);
|
|
1060
|
-
if (config.enableOriRecord &&
|
|
1023
|
+
if (config.enableOriRecord && config.enableWordCloud) new Analyse(ctx, config).registerCommands(analyse);
|
|
1061
1024
|
}
|
|
1062
1025
|
__name(apply, "apply");
|
|
1063
1026
|
// 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
|
-
"description": "
|
|
4
|
-
"version": "0.
|
|
3
|
+
"description": "强大而全面的聊天数据分析,支持统计命令,发言,消息类型,活跃度,支持发言排行和生成词云",
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"contributors": [
|
|
6
6
|
"Yis_Rime <yis_rime@outlook.com>"
|
|
7
7
|
],
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"koishi": "4.18.8"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@nlpjs/lang-zh": "^4.26.1"
|
|
42
|
+
"@node-rs/jieba": "^2.0.1"
|
|
44
43
|
}
|
|
45
44
|
}
|
package/readme.md
CHANGED
|
@@ -2,4 +2,80 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/koishi-plugin-chat-analyse)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
强大而全面的聊天数据分析,支持统计命令,发言,消息类型,活跃度,支持发言排行和生成词云
|
|
6
|
+
|
|
7
|
+
## ✨ 功能特性
|
|
8
|
+
|
|
9
|
+
- **高效数据收集**:采用异步、高并发和缓存机制,在不影响机器人性能的前提下,精确高效地收集聊天数据。
|
|
10
|
+
- **多维度统计分析**:
|
|
11
|
+
- **命令统计** (`cmdstat`):追踪指令使用频率,了解用户最常用的功能。
|
|
12
|
+
- **发言统计** (`msgstat`):分析用户发言类型与数量,掌握核心用户群体。
|
|
13
|
+
- **发言排行** (`rankstat`):生成指定时间范围内的用户发言排行榜。
|
|
14
|
+
- **活跃度分析** (`activity`):以小时或天为单位,生成直观的周期性活跃度图表。
|
|
15
|
+
- **高级文本分析**:
|
|
16
|
+
- **词云生成** (`wordcloud`):基于聊天记录,利用 Jieba 分词生成热门话题词云图。
|
|
17
|
+
- **提及追踪** (`whoatme`):轻松查询谁在什么时候因为什么内容提及了您。
|
|
18
|
+
- **强大的数据管理**:
|
|
19
|
+
- **备份与恢复** (`backup`/`restore`):一键备份所有统计数据至本地,并可随时恢复,保障数据安全。
|
|
20
|
+
- **精确清理** (`clear`):提供多维度的筛选条件(如按时间、用户、群组、命令),精确清理不再需要的数据。
|
|
21
|
+
- **精美图表渲染**:借助 Puppeteer 服务,将复杂的统计数据渲染成美观、易读的图片,方便在聊天中分享。
|
|
22
|
+
- **高度可配置**:所有功能模块均可独立开关,并可自定义数据保留时长等核心参数,以适应不同场景的需求。
|
|
23
|
+
|
|
24
|
+
### 前置服务
|
|
25
|
+
|
|
26
|
+
本插件依赖以下 Koishi 服务,请确保您已正确安装并启用了它们:
|
|
27
|
+
|
|
28
|
+
- `database`:用于存储所有统计分析数据。
|
|
29
|
+
- `puppeteer`:用于将统计结果渲染成图片。
|
|
30
|
+
- `cron`:用于执行数据定时清理任务。
|
|
31
|
+
|
|
32
|
+
## 📝 使用说明
|
|
33
|
+
|
|
34
|
+
所有功能都集成在主指令 `analyse` 之下。
|
|
35
|
+
|
|
36
|
+
### 指令列表
|
|
37
|
+
|
|
38
|
+
| 指令 | 别名 | 描述 | 选项 |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `analyse.cmdstat` | 命令统计 | 查询命令使用情况 | `-u <user>` (指定用户), `-g <guild>` (指定群组), `-a` (全局), `-h` (分离子指令) |
|
|
41
|
+
| `analyse.msgstat` | 发言统计 | 查询用户发言统计 | `-u <user>`, `-g <guild>`, `-a` (全局), `-t <type>` (指定消息类型) |
|
|
42
|
+
| `analyse.rankstat` | 发言排行 | 查询指定时间内的发言排行 | `-g <guild>`, `-a` (全局), `-t <type>`, `-h <hours>` (默认24小时) |
|
|
43
|
+
| `analyse.activity` | 活跃统计 | 查询周期性活跃度图表 | `-u <user>`, `-g <guild>`, `-a` (全局), `-d` (按天), `-h <hours>` (小时偏移) |
|
|
44
|
+
| `analyse.wordcloud` | 生成词云 | 基于聊天记录生成词云图 | `-u <user>`, `-g <guild>`, `-a` (全局), `-h <hours>` (默认24小时) |
|
|
45
|
+
| `analyse.whoatme` | 谁提及我 | 查看最近谁提及了您 | (无) |
|
|
46
|
+
| `analyse.list` | 列出数据 | 列出已记录的频道和命令 | (无) |
|
|
47
|
+
| `analyse.backup` | 备份数据 | 将所有数据备份为本地 JSON 文件 | (无) |
|
|
48
|
+
| `analyse.restore` | 恢复数据 | 从本地 JSON 文件恢复数据 | (无) |
|
|
49
|
+
| `analyse.clear` | 清除数据 | 根据条件精确清理数据 | `-t <table>`, `-g <guild>`, `-u <user>`, `-d <days>`, `-c <command>`, `-a` (全部) |
|
|
50
|
+
|
|
51
|
+
**通用查询范围说明:**
|
|
52
|
+
|
|
53
|
+
- 不带任何范围选项时,默认查询**当前群组**。
|
|
54
|
+
- 使用 `-u <user>` 可指定用户 (支持 @某人)。
|
|
55
|
+
- 使用 `-g <guild>` 可指定群组 ID。
|
|
56
|
+
- 使用 `-a` 将忽略其他范围限制,查询**全局**数据。
|
|
57
|
+
|
|
58
|
+
### 配置项
|
|
59
|
+
|
|
60
|
+
您可以在插件的配置页面中调整以下选项:
|
|
61
|
+
|
|
62
|
+
#### 杂项配置
|
|
63
|
+
|
|
64
|
+
- `enableListener`: **启用消息监听**。总开关,关闭后插件将停止所有数据收集。 (默认: `true`)
|
|
65
|
+
- `enableDataIO`: **启用数据管理**。控制 `.backup`, `.restore`, `.clear`, `.list` 等指令的可用性。 (默认: `true`)
|
|
66
|
+
|
|
67
|
+
#### 基础分析配置
|
|
68
|
+
|
|
69
|
+
- `enableCmdStat`: **启用命令统计**。 (默认: `true`)
|
|
70
|
+
- `enableMsgStat`: **启用消息统计**。 (默认: `true`)
|
|
71
|
+
- `enableActivity`: **启用活跃统计**。 (默认: `true`)
|
|
72
|
+
- `enableRankStat`: **启用发言排行**。 (默认: `true`)
|
|
73
|
+
- `rankRetentionDays`: **排行保留天数**。发言排行数据的保留时长(天),`0` 为永久保留。 (默认: `31`)
|
|
74
|
+
- `enableWhoAt`: **启用提及记录**。 (默认: `true`)
|
|
75
|
+
- `atRetentionDays`: **提及保留天数**。`whoatme` 数据的保留时长(天),`0` 为永久保留。 (默认: `7`)
|
|
76
|
+
|
|
77
|
+
#### 高级分析配置
|
|
78
|
+
|
|
79
|
+
- `enableOriRecord`: **启用原始记录**。是否记录原始消息内容以供词云等功能使用。 (默认: `true`)
|
|
80
|
+
- `enableWordCloud`: **启用词云生成**。 (默认: `true`)
|
|
81
|
+
- `cacheRetentionDays`: **原始记录保留天数**。原始消息记录的保留时长(天),`0` 为永久保留。 (默认: `180`)
|
package/lib/Analyse.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Context, Command } from 'koishi';
|
|
2
|
-
import { Config } from './index';
|
|
3
|
-
export interface WordCloudData {
|
|
4
|
-
title: string;
|
|
5
|
-
time: Date;
|
|
6
|
-
words: [string, number][];
|
|
7
|
-
}
|
|
8
|
-
export declare class Analyse {
|
|
9
|
-
private ctx;
|
|
10
|
-
private config;
|
|
11
|
-
private renderer;
|
|
12
|
-
private nlp;
|
|
13
|
-
private isNlpReady;
|
|
14
|
-
constructor(ctx: Context, config: Config);
|
|
15
|
-
/**
|
|
16
|
-
* @private
|
|
17
|
-
* @method initializeNlp
|
|
18
|
-
* @description 异步加载并训练 NLP 模型。
|
|
19
|
-
*/
|
|
20
|
-
private initializeNlp;
|
|
21
|
-
registerCommands(cmd: Command): void;
|
|
22
|
-
}
|
package/lib/Collector.d.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import { Config } from './index';
|
|
3
|
-
declare module 'koishi' {
|
|
4
|
-
interface Tables {
|
|
5
|
-
analyse_user: {
|
|
6
|
-
uid: number;
|
|
7
|
-
channelId: string;
|
|
8
|
-
userId: string;
|
|
9
|
-
channelName: string;
|
|
10
|
-
userName: string;
|
|
11
|
-
};
|
|
12
|
-
analyse_cmd: {
|
|
13
|
-
uid: number;
|
|
14
|
-
command: string;
|
|
15
|
-
count: number;
|
|
16
|
-
timestamp: Date;
|
|
17
|
-
};
|
|
18
|
-
analyse_msg: {
|
|
19
|
-
uid: number;
|
|
20
|
-
type: string;
|
|
21
|
-
count: number;
|
|
22
|
-
timestamp: Date;
|
|
23
|
-
};
|
|
24
|
-
analyse_rank: {
|
|
25
|
-
uid: number;
|
|
26
|
-
type: string;
|
|
27
|
-
count: number;
|
|
28
|
-
timestamp: Date;
|
|
29
|
-
};
|
|
30
|
-
analyse_cache: {
|
|
31
|
-
id: number;
|
|
32
|
-
uid: number;
|
|
33
|
-
content: string;
|
|
34
|
-
timestamp: Date;
|
|
35
|
-
};
|
|
36
|
-
analyse_at: {
|
|
37
|
-
id: number;
|
|
38
|
-
uid: number;
|
|
39
|
-
target: string;
|
|
40
|
-
content: string;
|
|
41
|
-
timestamp: Date;
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* @class Collector
|
|
47
|
-
* @description 核心数据收集器。根据配置,高效地监听、收集、缓冲并持久化聊天数据。
|
|
48
|
-
*/
|
|
49
|
-
export declare class Collector {
|
|
50
|
-
private ctx;
|
|
51
|
-
private config;
|
|
52
|
-
private static readonly FLUSH_INTERVAL;
|
|
53
|
-
private static readonly BUFFER_THRESHOLD;
|
|
54
|
-
private msgStatBuffer;
|
|
55
|
-
private rankStatBuffer;
|
|
56
|
-
private cmdStatBuffer;
|
|
57
|
-
private oriCacheBuffer;
|
|
58
|
-
private whoAtBuffer;
|
|
59
|
-
private userCache;
|
|
60
|
-
private channelCache;
|
|
61
|
-
private pendingRequests;
|
|
62
|
-
private flushInterval;
|
|
63
|
-
/**
|
|
64
|
-
* @param ctx - Koishi 的插件上下文。
|
|
65
|
-
* @param config - 插件的配置对象。
|
|
66
|
-
*/
|
|
67
|
-
constructor(ctx: Context, config: Config);
|
|
68
|
-
/**
|
|
69
|
-
* @private @method onMessage
|
|
70
|
-
* @description 统一的消息事件处理器,解析消息并更新各类统计数据的缓冲区。
|
|
71
|
-
* @param session - Koishi 的会话对象。
|
|
72
|
-
*/
|
|
73
|
-
private onMessage;
|
|
74
|
-
/**
|
|
75
|
-
* @private @method sanitizeContent
|
|
76
|
-
* @description 将 Koishi 消息元素数组净化为纯文本字符串。
|
|
77
|
-
* @param elements - 消息元素数组。
|
|
78
|
-
* @returns 净化后的纯文本。
|
|
79
|
-
*/
|
|
80
|
-
private sanitizeContent;
|
|
81
|
-
/**
|
|
82
|
-
* @private @method flushBuffers
|
|
83
|
-
* @description 将所有内存中的数据缓冲区批量写入数据库,并清空缓冲区。
|
|
84
|
-
*/
|
|
85
|
-
private flushBuffers;
|
|
86
|
-
}
|
package/lib/Data.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Context, Command } from 'koishi';
|
|
2
|
-
/**
|
|
3
|
-
* @class Data
|
|
4
|
-
* @description 提供数据备份、恢复和清理等高级管理功能。
|
|
5
|
-
*/
|
|
6
|
-
export declare class Data {
|
|
7
|
-
private ctx;
|
|
8
|
-
private dataDir;
|
|
9
|
-
constructor(ctx: Context);
|
|
10
|
-
/**
|
|
11
|
-
* @public
|
|
12
|
-
* @method registerCommands
|
|
13
|
-
* @description 在主命令下注册所有数据管理相关的子命令。
|
|
14
|
-
* @param cmd - 主命令实例。
|
|
15
|
-
*/
|
|
16
|
-
registerCommands(cmd: Command): void;
|
|
17
|
-
}
|
package/lib/Renderer.d.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import { WordCloudData } from './Analyse';
|
|
3
|
-
/**
|
|
4
|
-
* @interface ListRenderData
|
|
5
|
-
* @description 定义了调用 `renderList` 方法所需的数据结构。
|
|
6
|
-
*/
|
|
7
|
-
export interface ListRenderData {
|
|
8
|
-
title: string;
|
|
9
|
-
time: Date;
|
|
10
|
-
total?: string | number;
|
|
11
|
-
list: (string | number | Date)[][];
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* @interface CircadianChartData
|
|
15
|
-
* @description 定义了调用 `renderCircadianChart` 方法所需的数据结构。
|
|
16
|
-
*/
|
|
17
|
-
export interface CircadianChartData {
|
|
18
|
-
title: string;
|
|
19
|
-
time: Date;
|
|
20
|
-
total: string | number;
|
|
21
|
-
data: number[];
|
|
22
|
-
labels?: string[];
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* @class Renderer
|
|
26
|
-
* @description 负责将结构化的数据渲染为设计精美的 PNG 图片。
|
|
27
|
-
*/
|
|
28
|
-
export declare class Renderer {
|
|
29
|
-
private ctx;
|
|
30
|
-
private readonly COMMON_STYLE;
|
|
31
|
-
/**
|
|
32
|
-
* @constructor
|
|
33
|
-
* @description Renderer 类的构造函数。
|
|
34
|
-
* @param {Context} ctx - Koishi 的插件上下文,用于访问 logger 和 puppeteer 服务。
|
|
35
|
-
*/
|
|
36
|
-
constructor(ctx: Context);
|
|
37
|
-
/**
|
|
38
|
-
* @private
|
|
39
|
-
* @method generateFullHtml
|
|
40
|
-
* @description 将卡片内容和特定样式组合成一个完整的 HTML 文档,以便进行渲染。
|
|
41
|
-
* @param {string} cardContent - 卡片主体部分的 HTML 字符串。
|
|
42
|
-
* @param {string} specificStyles - 针对该卡片类型的特定 CSS 样式字符串。
|
|
43
|
-
* @returns {string} - 一个完整的、可被浏览器渲染的 HTML 字符串。
|
|
44
|
-
*/
|
|
45
|
-
private generateFullHtml;
|
|
46
|
-
/**
|
|
47
|
-
* @private
|
|
48
|
-
* @method htmlToImage
|
|
49
|
-
* @description 使用 puppeteer 将给定的 HTML 字符串内容渲染成 PNG 图片的 Buffer。
|
|
50
|
-
* @param {string} fullHtmlContent - 完整的 HTML 内容字符串。
|
|
51
|
-
* @returns {Promise<Buffer | null>} - 成功时返回包含 PNG 图片数据的 Buffer,失败则返回 null。
|
|
52
|
-
*/
|
|
53
|
-
private htmlToImage;
|
|
54
|
-
/**
|
|
55
|
-
* @private
|
|
56
|
-
* @method formatDate
|
|
57
|
-
* @description 将 Date 对象格式化为易于理解的相对时间字符串(如“刚刚”,“5分钟前”)。
|
|
58
|
-
* @param {Date} date - 需要格式化的日期对象。
|
|
59
|
-
* @returns {string} - 格式化后的时间字符串。
|
|
60
|
-
*/
|
|
61
|
-
private formatDate;
|
|
62
|
-
/**
|
|
63
|
-
* @public
|
|
64
|
-
* @method renderList
|
|
65
|
-
* @description 将表格型数据渲染成一个或多个列表形式的图片。如果数据过多,会自动进行分页渲染。
|
|
66
|
-
* @param {ListRenderData} data - 包含标题、时间、总计和列表数据的对象。
|
|
67
|
-
* @param {string[]} [headers] - (可选)列表的表头数组。
|
|
68
|
-
* @returns {Promise<string | Buffer[]>} - 成功时返回包含图片 Buffer 的数组,失败或无数据时返回提示字符串。
|
|
69
|
-
*/
|
|
70
|
-
renderList(data: ListRenderData, headers?: string[]): Promise<string | Buffer[]>;
|
|
71
|
-
/**
|
|
72
|
-
* @public
|
|
73
|
-
* @method renderCircadianChart
|
|
74
|
-
* @description 将 24 小时制的活跃度数据渲染成一张柱状图图片。
|
|
75
|
-
* @param {CircadianChartData} data - 包含标题、时间、总计和 24 小时数据数组的对象。
|
|
76
|
-
* @returns {Promise<string | Buffer[]>} - 成功时返回包含图片 Buffer 的数组,失败或无数据时返回提示字符串。
|
|
77
|
-
*/
|
|
78
|
-
renderCircadianChart(data: CircadianChartData): Promise<string | Buffer[]>;
|
|
79
|
-
/**
|
|
80
|
-
* @public
|
|
81
|
-
* @method renderWordCloud
|
|
82
|
-
* @description 将词频数据渲染成一张词云图片,使用 Puppeteer 和 wordcloud2.js。
|
|
83
|
-
* @param {WordCloudData} data - 包含标题、时间和词汇列表的对象。
|
|
84
|
-
* @returns {Promise<string | Buffer[]>} - 成功时返回图片 Buffer 数组,否则返回提示。
|
|
85
|
-
*/
|
|
86
|
-
renderWordCloud(data: WordCloudData): Promise<string | Buffer[]>;
|
|
87
|
-
}
|
package/lib/Stat.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Context, Command } from 'koishi';
|
|
2
|
-
import { Renderer } from './Renderer';
|
|
3
|
-
import { Config } from './index';
|
|
4
|
-
/**
|
|
5
|
-
* @class Stat
|
|
6
|
-
* @description 提供统一的统计查询服务。负责注册查询命令,从数据库获取数据,并调用渲染器生成图表。
|
|
7
|
-
*/
|
|
8
|
-
export declare class Stat {
|
|
9
|
-
private ctx;
|
|
10
|
-
private config;
|
|
11
|
-
renderer: Renderer;
|
|
12
|
-
/**
|
|
13
|
-
* @param ctx - Koishi 的插件上下文。
|
|
14
|
-
* @param config - 插件的配置对象。
|
|
15
|
-
*/
|
|
16
|
-
constructor(ctx: Context, config: Config);
|
|
17
|
-
/**
|
|
18
|
-
* @public @method registerCommands
|
|
19
|
-
* @description 根据配置,动态地将子命令注册到主命令下。
|
|
20
|
-
* @param cmd - 主命令实例。
|
|
21
|
-
*/
|
|
22
|
-
registerCommands(cmd: Command): void;
|
|
23
|
-
}
|
package/lib/WhoAt.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Context, Command } from 'koishi';
|
|
2
|
-
import { Config } from './index';
|
|
3
|
-
/**
|
|
4
|
-
* @class WhoAt
|
|
5
|
-
* @description 负责处理谁提及我相关功能,包括查询和定时清理。
|
|
6
|
-
*/
|
|
7
|
-
export declare class WhoAt {
|
|
8
|
-
private ctx;
|
|
9
|
-
private config;
|
|
10
|
-
/**
|
|
11
|
-
* @param ctx - Koishi 的插件上下文。
|
|
12
|
-
* @param config - 插件的配置对象。
|
|
13
|
-
*/
|
|
14
|
-
constructor(ctx: Context, config: Config);
|
|
15
|
-
/**
|
|
16
|
-
* @public @method registerCommand
|
|
17
|
-
* @description 在主命令下注册子命令。
|
|
18
|
-
* @param cmd - 主命令实例。
|
|
19
|
-
*/
|
|
20
|
-
registerCommand(cmd: Command): void;
|
|
21
|
-
}
|