koishi-plugin-chat-analyse 1.4.0 → 1.4.1

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/Data.d.ts CHANGED
@@ -8,6 +8,12 @@ export declare class Data {
8
8
  private config;
9
9
  private dataDir;
10
10
  constructor(ctx: Context, config: any);
11
+ /**
12
+ * @private
13
+ * @method backupCache
14
+ * @description 备份 analyse_cache 表到 JSON 文件。
15
+ */
16
+ private backupCache;
11
17
  /**
12
18
  * @public
13
19
  * @method registerCommands
package/lib/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export interface Config {
21
21
  enableWordCloud: boolean;
22
22
  cacheRetentionDays: number;
23
23
  enableSimilarActivity: boolean;
24
+ enableAutoBackup: boolean;
24
25
  }
25
26
  /** @description 插件的配置项定义 */
26
27
  export declare const Config: Schema<Config>;
package/lib/index.js CHANGED
@@ -2104,17 +2104,51 @@ var import_koishi5 = require("koishi");
2104
2104
  var fs = __toESM(require("fs/promises"));
2105
2105
  var path = __toESM(require("path"));
2106
2106
  var ALL_TABLES = ["analyse_user", "analyse_cmd", "analyse_msg", "analyse_rank", "analyse_at", "analyse_cache"];
2107
- var BATCH_SIZE = 100;
2107
+ var DEFAULT_BACKUP_TABLES = ["analyse_user", "analyse_cmd", "analyse_msg", "analyse_rank"];
2108
+ var BATCH_SIZE = 1e3;
2108
2109
  var Data = class {
2109
2110
  constructor(ctx, config) {
2110
2111
  this.ctx = ctx;
2111
2112
  this.config = config;
2112
2113
  this.dataDir = path.join(this.ctx.baseDir, "data", "chat-analyse");
2114
+ if (this.config.enableAutoBackup) this.ctx.cron("0 0 1 * *", () => {
2115
+ this.backupCache();
2116
+ });
2113
2117
  }
2114
2118
  static {
2115
2119
  __name(this, "Data");
2116
2120
  }
2117
2121
  dataDir;
2122
+ /**
2123
+ * @private
2124
+ * @method backupCache
2125
+ * @description 备份 analyse_cache 表到 JSON 文件。
2126
+ */
2127
+ async backupCache() {
2128
+ try {
2129
+ await fs.mkdir(this.dataDir, { recursive: true });
2130
+ const now = /* @__PURE__ */ new Date();
2131
+ const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
2132
+ const year = lastMonth.getFullYear();
2133
+ const month = (lastMonth.getMonth() + 1).toString().padStart(2, "0");
2134
+ const filename = `analyse_cache_${year}-${month}.json`;
2135
+ const filepath = path.join(this.dataDir, filename);
2136
+ const allUsers = await this.ctx.database.get("analyse_user", {});
2137
+ if (allUsers.length === 0) return;
2138
+ const uidToUserInfoMap = new Map(allUsers.map((u) => [u.uid, u]));
2139
+ const records = await this.ctx.database.get("analyse_cache", {});
2140
+ if (records.length === 0) return;
2141
+ const dataToExport = records.map((record) => {
2142
+ const userInfo = uidToUserInfoMap.get(record.uid);
2143
+ if (!userInfo) return null;
2144
+ const { id, uid, ...restOfRecord } = record;
2145
+ return { userId: userInfo.userId, channelId: userInfo.channelId, ...restOfRecord };
2146
+ }).filter(Boolean);
2147
+ await fs.writeFile(filepath, JSON.stringify(dataToExport, null, 2));
2148
+ } catch (error) {
2149
+ this.ctx.logger.error("原始记录备份失败:", error);
2150
+ }
2151
+ }
2118
2152
  /**
2119
2153
  * @public
2120
2154
  * @method registerCommands
@@ -2122,12 +2156,13 @@ var Data = class {
2122
2156
  * @param cmd - 主命令实例。
2123
2157
  */
2124
2158
  registerCommands(cmd) {
2125
- cmd.subcommand(".backup", "备份数据", { authority: 4 }).usage("将所有统计数据导出为 JSON 文件并保存到本地。").action(async () => {
2159
+ cmd.subcommand(".backup", "备份数据", { authority: 4 }).usage("将统计数据导出为 JSON 文件,并保存到本地。").option("all", "-a 全量备份").action(async ({ options }) => {
2160
+ const tablesToProcess = options.all ? ALL_TABLES : DEFAULT_BACKUP_TABLES;
2126
2161
  try {
2127
2162
  await fs.mkdir(this.dataDir, { recursive: true });
2128
2163
  const allUsers = await this.ctx.database.get("analyse_user", {});
2129
2164
  const uidToUserInfoMap = new Map(allUsers.map((u) => [u.uid, u]));
2130
- for (const tableName of ALL_TABLES) {
2165
+ for (const tableName of tablesToProcess) {
2131
2166
  const filepath = path.join(this.dataDir, `${tableName}.json`);
2132
2167
  let dataToExport;
2133
2168
  if (tableName === "analyse_user") {
@@ -2149,14 +2184,15 @@ var Data = class {
2149
2184
  return "数据备份失败";
2150
2185
  }
2151
2186
  });
2152
- cmd.subcommand(".restore", "恢复数据", { authority: 4 }).usage(`从本地的 JSON 文件中恢复统计数据。`).action(async () => {
2187
+ cmd.subcommand(".restore", "恢复数据", { authority: 4 }).usage("从本地的 JSON 文件中恢复统计数据。").option("all", "-a 全量恢复").action(async ({ options }) => {
2153
2188
  try {
2154
2189
  const userTablePath = path.join(this.dataDir, "analyse_user.json");
2155
2190
  const usersToImport = JSON.parse(await fs.readFile(userTablePath, "utf-8").catch(() => "[]"));
2156
2191
  if (usersToImport.length) for (let i = 0; i < usersToImport.length; i += BATCH_SIZE) await this.ctx.database.upsert("analyse_user", usersToImport.slice(i, i + BATCH_SIZE));
2157
2192
  const allUsers = await this.ctx.database.get("analyse_user", {});
2158
2193
  const userToUidMap = new Map(allUsers.map((u) => [`${u.channelId}:${u.userId}`, u.uid]));
2159
- for (const tableName of ALL_TABLES.filter((t) => t !== "analyse_user")) {
2194
+ const tablesToProcess = options.all ? ALL_TABLES.filter((t) => t !== "analyse_user") : DEFAULT_BACKUP_TABLES.filter((t) => t !== "analyse_user");
2195
+ for (const tableName of tablesToProcess) {
2160
2196
  const filepath = path.join(this.dataDir, `${tableName}.json`);
2161
2197
  const recordsToImport = JSON.parse(await fs.readFile(filepath, "utf-8").catch(() => "[]"));
2162
2198
  if (!recordsToImport.length) continue;
@@ -2472,13 +2508,14 @@ var Config3 = import_koishi7.Schema.intersect([
2472
2508
  enableMsgStat: import_koishi7.Schema.boolean().default(true).description("启用消息统计"),
2473
2509
  enableActivity: import_koishi7.Schema.boolean().default(true).description("启用活跃统计"),
2474
2510
  enableRankStat: import_koishi7.Schema.boolean().default(true).description("启用发言排行"),
2475
- rankRetentionDays: import_koishi7.Schema.number().min(0).default(180).description("排行保留天数"),
2511
+ rankRetentionDays: import_koishi7.Schema.number().min(0).default(365).description("排行保留天数"),
2476
2512
  enableWhoAt: import_koishi7.Schema.boolean().default(true).description("启用提及记录"),
2477
2513
  atRetentionDays: import_koishi7.Schema.number().min(0).default(3).description("提及保留天数")
2478
2514
  }).description("基础分析配置"),
2479
2515
  import_koishi7.Schema.object({
2480
2516
  enableOriRecord: import_koishi7.Schema.boolean().default(true).description("启用原始记录"),
2481
- cacheRetentionDays: import_koishi7.Schema.number().min(0).default(30).description("记录保留天数"),
2517
+ cacheRetentionDays: import_koishi7.Schema.number().min(0).default(31).description("记录保留天数"),
2518
+ enableAutoBackup: import_koishi7.Schema.boolean().default(false).description("启用自动备份"),
2482
2519
  enableWordCloud: import_koishi7.Schema.boolean().default(true).description("启用词云生成"),
2483
2520
  enableSimilarActivity: import_koishi7.Schema.boolean().default(true).description("启用相似活跃分析")
2484
2521
  }).description("高级分析配置")
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chat-analyse",
3
3
  "description": "强大而全面的聊天数据分析插件。支持多维度统计(命令、发言、消息类型、活跃度),可生成发言排行、词云图,并提供完善的数据管理。",
4
- "version": "1.4.0",
4
+ "version": "1.4.1",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],
package/readme.md CHANGED
@@ -150,6 +150,7 @@
150
150
 
151
151
  `enableOriRecord`: **启用原始记录**。是否记录原始消息内容。这是 `.view` 和 `wordcloud` 功能的基础。 (默认: `true`)
152
152
  `cacheRetentionDays`: **原始记录保留天数**。原始消息记录的保留时长(天),`0` 为永久保留。 (默认: `30`)
153
+ `enableAutoBackup`: **启用自动备份记录**。是否开启每月自动备份原始消息记录。 (默认: `false`)
153
154
  `enableWordCloud`: **启用词云生成**。
154
155
  > **!** 此功能依赖 **`启用原始记录`**。 (默认: `true`)
155
156
  `enableSimilarActivity`: **启用相似活跃分析**。