koishi-plugin-chat-analyse 0.2.2 → 0.2.3
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 +6 -4
- package/lib/index.js +46 -30
- package/package.json +1 -1
package/lib/Collector.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export declare class Collector {
|
|
|
29
29
|
private msgBuffer;
|
|
30
30
|
private flushInterval;
|
|
31
31
|
private nameCache;
|
|
32
|
+
private pendingNameRequests;
|
|
32
33
|
/**
|
|
33
34
|
* @constructor
|
|
34
35
|
* @param ctx {Context} Koishi 的上下文对象
|
|
@@ -41,9 +42,6 @@ export declare class Collector {
|
|
|
41
42
|
private handleMessage;
|
|
42
43
|
/**
|
|
43
44
|
* 从消息元素中提取并汇总类型。
|
|
44
|
-
* @example
|
|
45
|
-
* // returns "[text][img]"
|
|
46
|
-
* summarizeElementTypes([{type: 'text'}, {type: 'img'}])
|
|
47
45
|
*/
|
|
48
46
|
private summarizeElementTypes;
|
|
49
47
|
/**
|
|
@@ -56,7 +54,11 @@ export declare class Collector {
|
|
|
56
54
|
private flushBuffer;
|
|
57
55
|
/**
|
|
58
56
|
* 检查并更新用户和群组的名称信息。
|
|
59
|
-
* 如果名称不在缓存或缓存已过期 (超过24小时),则从 API 获取并更新到数据库。
|
|
60
57
|
*/
|
|
61
58
|
private updateNameIfNeeded;
|
|
59
|
+
/**
|
|
60
|
+
* @private
|
|
61
|
+
* 封装了实际获取和更新名称的异步逻辑。
|
|
62
|
+
*/
|
|
63
|
+
private fetchAndUpdateNames;
|
|
62
64
|
}
|
package/lib/index.js
CHANGED
|
@@ -74,6 +74,7 @@ var Collector = class _Collector {
|
|
|
74
74
|
msgBuffer = [];
|
|
75
75
|
flushInterval;
|
|
76
76
|
nameCache = /* @__PURE__ */ new Map();
|
|
77
|
+
pendingNameRequests = /* @__PURE__ */ new Map();
|
|
77
78
|
/**
|
|
78
79
|
* 核心消息处理函数。
|
|
79
80
|
* @param session {Session} 消息会话对象
|
|
@@ -82,7 +83,7 @@ var Collector = class _Collector {
|
|
|
82
83
|
const { userId, channelId, guildId, content, timestamp, argv, elements } = session;
|
|
83
84
|
const effectiveId = channelId || guildId;
|
|
84
85
|
if (!effectiveId || !userId || !timestamp) return;
|
|
85
|
-
this.updateNameIfNeeded(session, effectiveId);
|
|
86
|
+
await this.updateNameIfNeeded(session, effectiveId);
|
|
86
87
|
const isCommand = !!argv?.command;
|
|
87
88
|
const type = isCommand ? argv.command.name : this.summarizeElementTypes(elements);
|
|
88
89
|
const finalContent = isCommand ? content : this.sanitizeContent(elements);
|
|
@@ -100,9 +101,6 @@ var Collector = class _Collector {
|
|
|
100
101
|
}
|
|
101
102
|
/**
|
|
102
103
|
* 从消息元素中提取并汇总类型。
|
|
103
|
-
* @example
|
|
104
|
-
* // returns "[text][img]"
|
|
105
|
-
* summarizeElementTypes([{type: 'text'}, {type: 'img'}])
|
|
106
104
|
*/
|
|
107
105
|
summarizeElementTypes(elements) {
|
|
108
106
|
return [...new Set(elements.map((e) => `[${e.type}]`))].join("");
|
|
@@ -140,31 +138,49 @@ var Collector = class _Collector {
|
|
|
140
138
|
}
|
|
141
139
|
/**
|
|
142
140
|
* 检查并更新用户和群组的名称信息。
|
|
143
|
-
* 如果名称不在缓存或缓存已过期 (超过24小时),则从 API 获取并更新到数据库。
|
|
144
141
|
*/
|
|
145
142
|
async updateNameIfNeeded(session, effectiveId) {
|
|
146
|
-
const { userId
|
|
143
|
+
const { userId } = session;
|
|
144
|
+
if (!userId) return;
|
|
147
145
|
const cacheKey = `${effectiveId}:${userId}`;
|
|
146
|
+
if (this.pendingNameRequests.has(cacheKey)) return this.pendingNameRequests.get(cacheKey);
|
|
148
147
|
const cached = this.nameCache.get(cacheKey);
|
|
149
148
|
const CACHE_EXPIRATION = 24 * 60 * 60 * 1e3;
|
|
150
149
|
if (cached && Date.now() - cached.timestamp < CACHE_EXPIRATION) return;
|
|
150
|
+
const promise = this.fetchAndUpdateNames(session, effectiveId, cacheKey);
|
|
151
|
+
this.pendingNameRequests.set(cacheKey, promise);
|
|
151
152
|
try {
|
|
153
|
+
await promise;
|
|
154
|
+
} finally {
|
|
155
|
+
this.pendingNameRequests.delete(cacheKey);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* @private
|
|
160
|
+
* 封装了实际获取和更新名称的异步逻辑。
|
|
161
|
+
*/
|
|
162
|
+
async fetchAndUpdateNames(session, effectiveId, cacheKey) {
|
|
163
|
+
try {
|
|
164
|
+
const { userId, guildId, bot } = session;
|
|
152
165
|
const [guild, member] = await Promise.all([
|
|
153
|
-
bot.getGuild(guildId),
|
|
154
|
-
bot.getGuildMember(guildId, userId)
|
|
166
|
+
guildId ? bot.getGuild(guildId).catch(() => null) : Promise.resolve(null),
|
|
167
|
+
guildId && userId ? bot.getGuildMember(guildId, userId).catch(() => null) : Promise.resolve(null)
|
|
155
168
|
]);
|
|
156
169
|
const channelName = guild?.name;
|
|
157
170
|
const userName = member?.nick || member?.name;
|
|
158
|
-
if (!channelName || !userName)
|
|
159
|
-
|
|
171
|
+
if (!channelName || !userName) {
|
|
172
|
+
this.nameCache.set(cacheKey, { name: null, timestamp: Date.now() });
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
await this.ctx.database.upsert("analyse_name", [{
|
|
160
176
|
channelId: effectiveId,
|
|
161
|
-
channelName,
|
|
162
177
|
userId,
|
|
178
|
+
channelName,
|
|
163
179
|
userName
|
|
164
180
|
}]);
|
|
165
181
|
this.nameCache.set(cacheKey, { name: userName, timestamp: Date.now() });
|
|
166
182
|
} catch (error) {
|
|
167
|
-
this.
|
|
183
|
+
this.nameCache.set(cacheKey, { name: null, timestamp: Date.now() });
|
|
168
184
|
}
|
|
169
185
|
}
|
|
170
186
|
};
|
|
@@ -328,7 +344,7 @@ var CmdStat = class {
|
|
|
328
344
|
}, { primary: ["channelId", "userId", "command"] });
|
|
329
345
|
this.ctx.on("command/before-execute", async ({ command, session }) => {
|
|
330
346
|
const { userId, guildId } = session;
|
|
331
|
-
if (!guildId || !userId
|
|
347
|
+
if (!guildId || !userId) return;
|
|
332
348
|
const commandName = command.name;
|
|
333
349
|
const query = { channelId: guildId, userId, command: commandName };
|
|
334
350
|
await this.ctx.database.upsert("analyse_cmd", (row) => [{
|
|
@@ -395,25 +411,25 @@ var CmdStat = class {
|
|
|
395
411
|
const query = {};
|
|
396
412
|
if (guildId) query.channelId = guildId;
|
|
397
413
|
if (userId) query.userId = userId;
|
|
398
|
-
const records = await this.ctx.database.get("analyse_cmd", query);
|
|
399
|
-
if (records.length === 0) return "暂无统计数据";
|
|
400
|
-
const totalCount = records.reduce((sum, record) => sum + record.count, 0);
|
|
401
|
-
let sortedList;
|
|
402
414
|
if (userId) {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (record.timestamp > existing.lastUsed) {
|
|
410
|
-
existing.lastUsed = record.timestamp;
|
|
411
|
-
}
|
|
412
|
-
commandMap.set(record.command, existing);
|
|
413
|
-
}
|
|
414
|
-
sortedList = Array.from(commandMap.entries()).map(([name2, data]) => ({ name: name2, ...data })).sort((a, b) => b.count - a.count);
|
|
415
|
+
const records = await this.ctx.database.get("analyse_cmd", query);
|
|
416
|
+
if (records.length === 0) return "暂无统计数据";
|
|
417
|
+
const totalCount2 = records.reduce((sum, record) => sum + record.count, 0);
|
|
418
|
+
const sortedList2 = records.map((r) => ({ name: r.command, count: r.count, lastUsed: r.timestamp })).sort((a, b) => b.count - a.count);
|
|
419
|
+
const list2 = sortedList2.map((item) => [item.name, item.count, item.lastUsed]);
|
|
420
|
+
return { list: list2, total: totalCount2 };
|
|
415
421
|
}
|
|
416
|
-
const
|
|
422
|
+
const aggregatedStats = await this.ctx.database.select("analyse_cmd", query).groupBy(
|
|
423
|
+
["command"],
|
|
424
|
+
{
|
|
425
|
+
count: /* @__PURE__ */ __name((row) => import_koishi2.$.sum(row.count), "count"),
|
|
426
|
+
lastUsed: /* @__PURE__ */ __name((row) => import_koishi2.$.max(row.timestamp), "lastUsed")
|
|
427
|
+
}
|
|
428
|
+
).execute();
|
|
429
|
+
if (aggregatedStats.length === 0) return "暂无统计数据";
|
|
430
|
+
const totalCount = aggregatedStats.reduce((sum, record) => sum + record.count, 0);
|
|
431
|
+
const sortedList = aggregatedStats.sort((a, b) => b.count - a.count);
|
|
432
|
+
const list = sortedList.map((item) => [item.command, item.count, item.lastUsed]);
|
|
417
433
|
return { list, total: totalCount };
|
|
418
434
|
}
|
|
419
435
|
};
|