koishi-plugin-chat-analyse 0.1.1 → 0.1.2
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 +35 -14
- package/lib/index.js +90 -85
- package/package.json +1 -1
package/lib/collector.d.ts
CHANGED
|
@@ -1,42 +1,63 @@
|
|
|
1
1
|
import { Context } from 'koishi';
|
|
2
2
|
/**
|
|
3
3
|
* @file collector.ts
|
|
4
|
-
* @description
|
|
4
|
+
* @description 通过统一的事件监听器,持久化存储所有收到的消息和命令,并高效地维护用户/群组ID与名称的映射。
|
|
5
5
|
*/
|
|
6
6
|
declare module 'koishi' {
|
|
7
7
|
interface Tables {
|
|
8
|
-
|
|
8
|
+
analyse_msg: {
|
|
9
9
|
id: number;
|
|
10
10
|
channelId: string;
|
|
11
11
|
userId: string;
|
|
12
|
+
type: string;
|
|
12
13
|
content: string;
|
|
13
14
|
timestamp: Date;
|
|
14
15
|
};
|
|
15
|
-
|
|
16
|
-
id: number;
|
|
17
|
-
channelId: string;
|
|
18
|
-
userId: string;
|
|
19
|
-
command: string;
|
|
20
|
-
content: string;
|
|
21
|
-
timestamp: Date;
|
|
22
|
-
};
|
|
23
|
-
analyse_name_map: {
|
|
16
|
+
analyse_name: {
|
|
24
17
|
id: number;
|
|
25
18
|
channelId: string;
|
|
26
19
|
channelName: string;
|
|
27
20
|
userId: string;
|
|
28
21
|
userName: string;
|
|
29
|
-
timestamp: Date;
|
|
30
22
|
};
|
|
31
23
|
}
|
|
32
24
|
}
|
|
33
25
|
/**
|
|
34
26
|
* @class Collector
|
|
35
|
-
* @description
|
|
27
|
+
* @description 核心收集器类。负责初始化数据库、监听消息、分类处理并持久化数据。
|
|
36
28
|
*/
|
|
37
29
|
export declare class Collector {
|
|
38
30
|
private ctx;
|
|
31
|
+
/**
|
|
32
|
+
* @property {Map<string, { name: string, timestamp: number }>} nameCache
|
|
33
|
+
* @description 用户名缓存,键为“频道ID:用户ID”,值为名称和时间戳。
|
|
34
|
+
*/
|
|
39
35
|
private nameCache;
|
|
36
|
+
/**
|
|
37
|
+
* @constructor
|
|
38
|
+
* @param {Context} ctx Koishi 的上下文对象
|
|
39
|
+
*/
|
|
40
40
|
constructor(ctx: Context);
|
|
41
|
-
|
|
41
|
+
/**
|
|
42
|
+
* @private
|
|
43
|
+
* @method defineModels
|
|
44
|
+
* @description 初始化插件所需的两个数据表模型。
|
|
45
|
+
*/
|
|
46
|
+
private defineModels;
|
|
47
|
+
/**
|
|
48
|
+
* @private
|
|
49
|
+
* @method sanitizeContent
|
|
50
|
+
* @description 将消息元素数组转换并清理为用于存储的字符串。
|
|
51
|
+
* @param {Element[]} elements 消息元素数组
|
|
52
|
+
* @returns {string} 清理后的消息内容
|
|
53
|
+
*/
|
|
54
|
+
private sanitizeContent;
|
|
55
|
+
/**
|
|
56
|
+
* @private
|
|
57
|
+
* @method updateNameIfNeeded
|
|
58
|
+
* @description 检查并按需更新用户和频道的名称。
|
|
59
|
+
* @param {Session} session 当前会话对象
|
|
60
|
+
* @param {string} effectiveId 当前生效的频道/群组ID
|
|
61
|
+
*/
|
|
62
|
+
private updateNameIfNeeded;
|
|
42
63
|
}
|
package/lib/index.js
CHANGED
|
@@ -31,108 +31,113 @@ var import_koishi = require("koishi");
|
|
|
31
31
|
|
|
32
32
|
// src/collector.ts
|
|
33
33
|
var Collector = class {
|
|
34
|
+
/**
|
|
35
|
+
* @constructor
|
|
36
|
+
* @param {Context} ctx Koishi 的上下文对象
|
|
37
|
+
*/
|
|
34
38
|
constructor(ctx) {
|
|
35
39
|
this.ctx = ctx;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
channelId
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
this.defineModels();
|
|
41
|
+
ctx.on("message", async (session) => {
|
|
42
|
+
const { userId, channelId, guildId, content, timestamp, argv, elements } = session;
|
|
43
|
+
const effectiveId = channelId || guildId;
|
|
44
|
+
if (!effectiveId || !userId) return;
|
|
45
|
+
this.updateNameIfNeeded(session, effectiveId);
|
|
46
|
+
const isCommand = !!argv?.command;
|
|
47
|
+
const type = isCommand ? argv.command.name : [...new Set(elements.map((e) => `[${e.type}]`))].join("");
|
|
48
|
+
const finalContent = isCommand ? content : this.sanitizeContent(elements);
|
|
49
|
+
if (!finalContent?.trim()) return;
|
|
50
|
+
await ctx.database.create("analyse_msg", {
|
|
51
|
+
channelId: effectiveId,
|
|
52
|
+
userId,
|
|
53
|
+
type,
|
|
54
|
+
content: finalContent,
|
|
55
|
+
timestamp: new Date(timestamp)
|
|
56
|
+
});
|
|
45
57
|
});
|
|
46
|
-
|
|
58
|
+
}
|
|
59
|
+
static {
|
|
60
|
+
__name(this, "Collector");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* @property {Map<string, { name: string, timestamp: number }>} nameCache
|
|
64
|
+
* @description 用户名缓存,键为“频道ID:用户ID”,值为名称和时间戳。
|
|
65
|
+
*/
|
|
66
|
+
nameCache = /* @__PURE__ */ new Map();
|
|
67
|
+
/**
|
|
68
|
+
* @private
|
|
69
|
+
* @method defineModels
|
|
70
|
+
* @description 初始化插件所需的两个数据表模型。
|
|
71
|
+
*/
|
|
72
|
+
defineModels() {
|
|
73
|
+
this.ctx.model.extend("analyse_msg", {
|
|
47
74
|
id: "unsigned",
|
|
48
75
|
channelId: "string",
|
|
49
76
|
userId: "string",
|
|
50
|
-
|
|
77
|
+
type: "string",
|
|
51
78
|
content: "text",
|
|
52
79
|
timestamp: "timestamp"
|
|
53
|
-
}, {
|
|
54
|
-
|
|
55
|
-
indexes: ["channelId", "userId", "command", "timestamp"]
|
|
56
|
-
});
|
|
57
|
-
ctx.model.extend("analyse_name_map", {
|
|
80
|
+
}, { autoInc: true, indexes: ["channelId", "userId", "type", "timestamp"] });
|
|
81
|
+
this.ctx.model.extend("analyse_name", {
|
|
58
82
|
id: "unsigned",
|
|
59
83
|
channelId: "string",
|
|
60
84
|
channelName: "string",
|
|
61
85
|
userId: "string",
|
|
62
|
-
userName: "string"
|
|
63
|
-
|
|
64
|
-
}, {
|
|
65
|
-
autoInc: true,
|
|
66
|
-
unique: [["channelId", "userId"]]
|
|
67
|
-
});
|
|
68
|
-
ctx.on("command/before-execute", async (argv) => {
|
|
69
|
-
const effectiveId = argv.session.channelId || argv.session.guildId;
|
|
70
|
-
if (!effectiveId) return;
|
|
71
|
-
await ctx.database.create("analyse_origin_cmd", {
|
|
72
|
-
channelId: effectiveId,
|
|
73
|
-
userId: argv.session.userId,
|
|
74
|
-
command: argv.command.name,
|
|
75
|
-
content: argv.session.content,
|
|
76
|
-
timestamp: new Date(argv.session.timestamp)
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
ctx.on("message", async (session) => {
|
|
80
|
-
const { userId, author } = session;
|
|
81
|
-
const effectiveId = session.channelId || session.guildId;
|
|
82
|
-
const { channelName, guildName } = session;
|
|
83
|
-
const effectiveName = channelName || guildName;
|
|
84
|
-
const currentName = author?.name || author?.nick;
|
|
85
|
-
if (currentName && effectiveName && effectiveId && userId) {
|
|
86
|
-
const cacheKey = `${effectiveId}:${userId}`;
|
|
87
|
-
const cachedName = this.nameCache.get(cacheKey);
|
|
88
|
-
if (cachedName && currentName !== cachedName) await this.updateName(effectiveId, effectiveName, userId, currentName);
|
|
89
|
-
else if (!cachedName) {
|
|
90
|
-
const dbRecord = await ctx.database.get("analyse_name_map", {
|
|
91
|
-
channelId: effectiveId,
|
|
92
|
-
userId
|
|
93
|
-
});
|
|
94
|
-
const dbName = dbRecord[0]?.userName;
|
|
95
|
-
if (!dbName || currentName !== dbName) await this.updateName(effectiveId, effectiveName, userId, currentName);
|
|
96
|
-
else this.nameCache.set(cacheKey, currentName);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (session.argv?.command) return;
|
|
100
|
-
if (!effectiveId) return;
|
|
101
|
-
const content = session.elements.map((element) => {
|
|
102
|
-
switch (element.type) {
|
|
103
|
-
case "text":
|
|
104
|
-
return element.attrs.content;
|
|
105
|
-
case "img":
|
|
106
|
-
return element.attrs.summary === "[动画表情]" ? "[gif]" : `[${element.type}]`;
|
|
107
|
-
case "at":
|
|
108
|
-
return `[at:${element.attrs.id}]`;
|
|
109
|
-
default:
|
|
110
|
-
return `[${element.type}]`;
|
|
111
|
-
}
|
|
112
|
-
}).join("");
|
|
113
|
-
const sanitizedContent = content.trim();
|
|
114
|
-
if (!sanitizedContent) return;
|
|
115
|
-
await ctx.database.create("analyse_origin_msg", {
|
|
116
|
-
channelId: effectiveId,
|
|
117
|
-
userId: session.userId,
|
|
118
|
-
content: sanitizedContent,
|
|
119
|
-
timestamp: new Date(session.timestamp)
|
|
120
|
-
});
|
|
121
|
-
});
|
|
86
|
+
userName: "string"
|
|
87
|
+
}, { autoInc: true, unique: [["channelId", "userId"]] });
|
|
122
88
|
}
|
|
123
|
-
|
|
124
|
-
|
|
89
|
+
/**
|
|
90
|
+
* @private
|
|
91
|
+
* @method sanitizeContent
|
|
92
|
+
* @description 将消息元素数组转换并清理为用于存储的字符串。
|
|
93
|
+
* @param {Element[]} elements 消息元素数组
|
|
94
|
+
* @returns {string} 清理后的消息内容
|
|
95
|
+
*/
|
|
96
|
+
sanitizeContent(elements) {
|
|
97
|
+
return elements.map((element) => {
|
|
98
|
+
switch (element.type) {
|
|
99
|
+
case "text":
|
|
100
|
+
return element.attrs.content;
|
|
101
|
+
case "img":
|
|
102
|
+
return element.attrs.summary === "[动画表情]" ? "[gif]" : `[img]`;
|
|
103
|
+
case "at":
|
|
104
|
+
return `[at:${element.attrs.id}]`;
|
|
105
|
+
default:
|
|
106
|
+
return `[${element.type}]`;
|
|
107
|
+
}
|
|
108
|
+
}).join("");
|
|
125
109
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
110
|
+
/**
|
|
111
|
+
* @private
|
|
112
|
+
* @method updateNameIfNeeded
|
|
113
|
+
* @description 检查并按需更新用户和频道的名称。
|
|
114
|
+
* @param {Session} session 当前会话对象
|
|
115
|
+
* @param {string} effectiveId 当前生效的频道/群组ID
|
|
116
|
+
*/
|
|
117
|
+
async updateNameIfNeeded(session, effectiveId) {
|
|
118
|
+
const { userId, guildId, bot } = session;
|
|
119
|
+
const cacheKey = `${effectiveId}:${userId}`;
|
|
120
|
+
const cached = this.nameCache.get(cacheKey);
|
|
121
|
+
if (cached && Date.now() - cached.timestamp < 864e5) return;
|
|
122
|
+
const [guild, member] = await Promise.all([
|
|
123
|
+
bot.getGuild(guildId),
|
|
124
|
+
bot.getGuildMember(guildId, userId)
|
|
125
|
+
]);
|
|
126
|
+
const channelName = guild?.name;
|
|
127
|
+
const userName = member?.nick || member?.name;
|
|
128
|
+
if (!channelName || !userName) return;
|
|
129
|
+
const [record] = await this.ctx.database.get("analyse_name", { channelId: effectiveId, userId });
|
|
130
|
+
if (record?.userName === userName && record?.channelName === channelName) {
|
|
131
|
+
this.nameCache.set(cacheKey, { name: userName, timestamp: Date.now() });
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
await this.ctx.database.upsert("analyse_name", [{
|
|
135
|
+
channelId: effectiveId,
|
|
130
136
|
channelName,
|
|
131
137
|
userId,
|
|
132
|
-
userName
|
|
133
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
138
|
+
userName
|
|
134
139
|
}]);
|
|
135
|
-
this.nameCache.set(
|
|
140
|
+
this.nameCache.set(cacheKey, { name: userName, timestamp: Date.now() });
|
|
136
141
|
}
|
|
137
142
|
};
|
|
138
143
|
|