koishi-plugin-chat-analyse 0.4.7 → 0.4.8
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 +11 -0
- package/lib/index.d.ts +0 -1
- package/lib/index.js +57 -107
- package/package.json +1 -1
package/lib/Collector.d.ts
CHANGED
|
@@ -91,6 +91,17 @@ export declare class Collector {
|
|
|
91
91
|
* @returns {Promise<UserCache | null>} 返回用户的缓存对象,如果操作失败则返回 `null`。
|
|
92
92
|
*/
|
|
93
93
|
private getOrCreateCachedUser;
|
|
94
|
+
/**
|
|
95
|
+
* @private
|
|
96
|
+
* @async
|
|
97
|
+
* @method fetchApiNames
|
|
98
|
+
* @description 并发调用机器人API以获取用户和频道名称,作为备用数据源。
|
|
99
|
+
* @param bot - The bot instance.
|
|
100
|
+
* @param guildId - The guild ID.
|
|
101
|
+
* @param userId - The user ID.
|
|
102
|
+
* @returns {Promise<{userName: string, channelName: string}>} - 包含名称的对象。
|
|
103
|
+
*/
|
|
104
|
+
private fetchApiNames;
|
|
94
105
|
/**
|
|
95
106
|
* @private
|
|
96
107
|
* @method sanitizeContent
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -202,46 +202,42 @@ var Collector = class _Collector {
|
|
|
202
202
|
* @returns {Promise<UserCache | null>} 返回用户的缓存对象,如果操作失败则返回 `null`。
|
|
203
203
|
*/
|
|
204
204
|
async getOrCreateCachedUser(session, channelId) {
|
|
205
|
-
const { userId, bot
|
|
205
|
+
const { userId, bot } = session;
|
|
206
206
|
const cacheKey = `${channelId}:${userId}`;
|
|
207
207
|
if (this.userCache.has(cacheKey)) return this.userCache.get(cacheKey);
|
|
208
208
|
if (this.pendingUserRequests.has(cacheKey)) return this.pendingUserRequests.get(cacheKey);
|
|
209
209
|
const promise = (async () => {
|
|
210
210
|
try {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const fetchedChannelName = guild?.name || "";
|
|
223
|
-
if (dbUser) {
|
|
224
|
-
const needsUpdate = !dbUser.userName && fetchedUserName || !dbUser.channelName && fetchedChannelName;
|
|
225
|
-
if (needsUpdate) {
|
|
226
|
-
dbUser.userName = dbUser.userName || fetchedUserName;
|
|
227
|
-
dbUser.channelName = dbUser.channelName || fetchedChannelName;
|
|
228
|
-
await this.ctx.database.set("analyse_user", { uid: dbUser.uid }, {
|
|
229
|
-
userName: dbUser.userName,
|
|
230
|
-
channelName: dbUser.channelName
|
|
231
|
-
});
|
|
211
|
+
const sessionUserName = session.username || "";
|
|
212
|
+
const sessionChannelName = session.event._data.raw.peerName || session.guild.name || "";
|
|
213
|
+
const dbUser = await this.ctx.database.get("analyse_user", { channelId, userId });
|
|
214
|
+
if (dbUser[0]) {
|
|
215
|
+
const user = dbUser[0];
|
|
216
|
+
const nameChanged = sessionUserName && user.userName !== sessionUserName;
|
|
217
|
+
const channelNameChanged = sessionChannelName && user.channelName !== sessionChannelName;
|
|
218
|
+
if (nameChanged || channelNameChanged) {
|
|
219
|
+
user.userName = nameChanged ? sessionUserName : user.userName;
|
|
220
|
+
user.channelName = channelNameChanged ? sessionChannelName : user.channelName;
|
|
221
|
+
await this.ctx.database.set("analyse_user", { uid: user.uid }, { userName: user.userName, channelName: user.channelName });
|
|
232
222
|
}
|
|
233
|
-
|
|
234
|
-
return
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (createdUser.userName && createdUser.channelName) this.userCache.set(cacheKey, createdUser);
|
|
243
|
-
return createdUser;
|
|
223
|
+
this.userCache.set(cacheKey, user);
|
|
224
|
+
return user;
|
|
225
|
+
}
|
|
226
|
+
let finalUserName = sessionUserName;
|
|
227
|
+
let finalChannelName = sessionChannelName;
|
|
228
|
+
if (!finalUserName || !finalChannelName) {
|
|
229
|
+
const apiData = await this.fetchApiNames(bot, session.guildId || channelId, userId);
|
|
230
|
+
finalUserName = finalUserName || apiData.userName;
|
|
231
|
+
finalChannelName = finalChannelName || apiData.channelName;
|
|
244
232
|
}
|
|
233
|
+
const createdUser = await this.ctx.database.create("analyse_user", {
|
|
234
|
+
channelId,
|
|
235
|
+
userId,
|
|
236
|
+
userName: finalUserName,
|
|
237
|
+
channelName: finalChannelName
|
|
238
|
+
});
|
|
239
|
+
this.userCache.set(cacheKey, createdUser);
|
|
240
|
+
return createdUser;
|
|
245
241
|
} catch (error) {
|
|
246
242
|
this.ctx.logger.error(`创建或获取用户(${cacheKey})失败:`, error);
|
|
247
243
|
return null;
|
|
@@ -252,6 +248,30 @@ var Collector = class _Collector {
|
|
|
252
248
|
this.pendingUserRequests.set(cacheKey, promise);
|
|
253
249
|
return promise;
|
|
254
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* @private
|
|
253
|
+
* @async
|
|
254
|
+
* @method fetchApiNames
|
|
255
|
+
* @description 并发调用机器人API以获取用户和频道名称,作为备用数据源。
|
|
256
|
+
* @param bot - The bot instance.
|
|
257
|
+
* @param guildId - The guild ID.
|
|
258
|
+
* @param userId - The user ID.
|
|
259
|
+
* @returns {Promise<{userName: string, channelName: string}>} - 包含名称的对象。
|
|
260
|
+
*/
|
|
261
|
+
async fetchApiNames(bot, guildId, userId) {
|
|
262
|
+
const results = await Promise.allSettled([
|
|
263
|
+
guildId && bot.getGuildMember ? bot.getGuildMember(guildId, userId) : Promise.resolve(null),
|
|
264
|
+
bot.getUser ? bot.getUser(userId) : Promise.resolve(null),
|
|
265
|
+
guildId && bot.getGuild ? bot.getGuild(guildId) : Promise.resolve(null)
|
|
266
|
+
]);
|
|
267
|
+
const member = results[0].status === "fulfilled" ? results[0].value : null;
|
|
268
|
+
const user = results[1].status === "fulfilled" ? results[1].value : null;
|
|
269
|
+
const guild = results[2].status === "fulfilled" ? results[2].value : null;
|
|
270
|
+
return {
|
|
271
|
+
userName: member?.username || member?.nick || member?.name || user?.name || "",
|
|
272
|
+
channelName: guild?.name || ""
|
|
273
|
+
};
|
|
274
|
+
}
|
|
255
275
|
/**
|
|
256
276
|
* @private
|
|
257
277
|
* @method sanitizeContent
|
|
@@ -1078,72 +1098,6 @@ var Data = class {
|
|
|
1078
1098
|
return "数据清理失败";
|
|
1079
1099
|
}
|
|
1080
1100
|
});
|
|
1081
|
-
}
|
|
1082
|
-
};
|
|
1083
|
-
|
|
1084
|
-
// src/Debug.ts
|
|
1085
|
-
var Debug = class {
|
|
1086
|
-
/**
|
|
1087
|
-
* @constructor
|
|
1088
|
-
* @param {Context} ctx - Koishi 的插件上下文。
|
|
1089
|
-
*/
|
|
1090
|
-
constructor(ctx) {
|
|
1091
|
-
this.ctx = ctx;
|
|
1092
|
-
}
|
|
1093
|
-
static {
|
|
1094
|
-
__name(this, "Debug");
|
|
1095
|
-
}
|
|
1096
|
-
/**
|
|
1097
|
-
* @public
|
|
1098
|
-
* @method registerCommands
|
|
1099
|
-
* @description 在 'analyse' 命令下注册所有调试相关的子命令。
|
|
1100
|
-
* @param {Command} analyse - 主 'analyse' 命令实例。
|
|
1101
|
-
*/
|
|
1102
|
-
registerCommands(analyse) {
|
|
1103
|
-
analyse.subcommand(".fill", "手动补全用户信息", { authority: 4 }).action(async ({ session }) => {
|
|
1104
|
-
const bots = this.ctx.bots;
|
|
1105
|
-
if (bots.length === 0) return "暂无可用机器人";
|
|
1106
|
-
const usersToUpdate = await this.ctx.database.get("analyse_user", {
|
|
1107
|
-
$or: [{ userName: "" }, { channelName: "" }]
|
|
1108
|
-
});
|
|
1109
|
-
if (usersToUpdate.length === 0) return "暂无用户信息需要补全";
|
|
1110
|
-
const usersByChannel = usersToUpdate.reduce((acc, user) => {
|
|
1111
|
-
(acc[user.channelId] = acc[user.channelId] || []).push(user);
|
|
1112
|
-
return acc;
|
|
1113
|
-
}, {});
|
|
1114
|
-
let updatedCount = 0;
|
|
1115
|
-
const bot = bots.find((b) => b.platform === session.platform) || bots[0];
|
|
1116
|
-
for (const channelId in usersByChannel) {
|
|
1117
|
-
const usersInChannel = usersByChannel[channelId];
|
|
1118
|
-
let channelName = usersInChannel.find((u) => u.channelName)?.channelName || "";
|
|
1119
|
-
if (!channelName && channelId) {
|
|
1120
|
-
try {
|
|
1121
|
-
channelName = (await bot.getGuild(channelId))?.name || "";
|
|
1122
|
-
} catch (e) {
|
|
1123
|
-
this.ctx.logger.warn(`获取频道 ${channelId} 信息失败:`, e);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
for (const user of usersInChannel) {
|
|
1127
|
-
if (user.userName && user.channelName) continue;
|
|
1128
|
-
let userName = user.userName;
|
|
1129
|
-
if (!userName && user.userId && channelId) {
|
|
1130
|
-
try {
|
|
1131
|
-
const member = await bot.getGuildMember(channelId, user.userId);
|
|
1132
|
-
userName = member?.nick || member?.name || "";
|
|
1133
|
-
if (!userName) userName = (await bot.getUser(user.userId))?.name || "";
|
|
1134
|
-
} catch (e) {
|
|
1135
|
-
this.ctx.logger.warn(`获取频道 ${channelId} 的用户 ${user.userId} 信息失败:`, e);
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
await this.ctx.database.set("analyse_user", { uid: user.uid }, {
|
|
1139
|
-
userName: userName || user.userName,
|
|
1140
|
-
channelName: channelName || user.channelName
|
|
1141
|
-
});
|
|
1142
|
-
updatedCount++;
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
return `已补全 ${updatedCount} 条用户信息`;
|
|
1146
|
-
});
|
|
1147
1101
|
analyse.subcommand(".list", "列出频道及命令", { authority: 4 }).action(async () => {
|
|
1148
1102
|
const allChannelInfo = await this.ctx.database.get("analyse_user", {}, ["channelId", "channelName"]);
|
|
1149
1103
|
const uniqueChannels = [...new Map(allChannelInfo.map((item) => [item.channelId, item])).values()];
|
|
@@ -1174,11 +1128,12 @@ var using = ["database", "puppeteer", "cron"];
|
|
|
1174
1128
|
var Config = import_koishi6.Schema.intersect([
|
|
1175
1129
|
import_koishi6.Schema.object({
|
|
1176
1130
|
enableListener: import_koishi6.Schema.boolean().default(true).description("启用消息监听"),
|
|
1177
|
-
|
|
1131
|
+
enableData: import_koishi6.Schema.boolean().default(false).description("启用数据管理")
|
|
1178
1132
|
}).description("监听配置"),
|
|
1179
1133
|
import_koishi6.Schema.object({
|
|
1180
1134
|
enableCmdStat: import_koishi6.Schema.boolean().default(true).description("启用命令统计"),
|
|
1181
|
-
enableMsgStat: import_koishi6.Schema.boolean().default(true).description("启用消息统计")
|
|
1135
|
+
enableMsgStat: import_koishi6.Schema.boolean().default(true).description("启用消息统计"),
|
|
1136
|
+
enableOriRecord: import_koishi6.Schema.boolean().default(true).description("启用原始记录")
|
|
1182
1137
|
}).description("功能配置"),
|
|
1183
1138
|
import_koishi6.Schema.object({
|
|
1184
1139
|
enableRankStat: import_koishi6.Schema.boolean().default(true).description("启用发言排行"),
|
|
@@ -1187,11 +1142,7 @@ var Config = import_koishi6.Schema.intersect([
|
|
|
1187
1142
|
import_koishi6.Schema.object({
|
|
1188
1143
|
enableWhoAt: import_koishi6.Schema.boolean().default(true).description("启用 @ 记录"),
|
|
1189
1144
|
atRetentionDays: import_koishi6.Schema.number().min(0).default(7).description("记录保留天数")
|
|
1190
|
-
}).description("@ 记录配置")
|
|
1191
|
-
import_koishi6.Schema.object({
|
|
1192
|
-
enableData: import_koishi6.Schema.boolean().default(false).description("启用数据管理"),
|
|
1193
|
-
enableDebug: import_koishi6.Schema.boolean().default(false).description("启用调试工具")
|
|
1194
|
-
}).description("高级功能")
|
|
1145
|
+
}).description("@ 记录配置")
|
|
1195
1146
|
]);
|
|
1196
1147
|
function apply(ctx, config) {
|
|
1197
1148
|
if (config.enableListener) new Collector(ctx, config);
|
|
@@ -1199,7 +1150,6 @@ function apply(ctx, config) {
|
|
|
1199
1150
|
new Stat(ctx, config).registerCommands(analyse);
|
|
1200
1151
|
if (config.enableWhoAt) new WhoAt(ctx, config).registerCommand(analyse);
|
|
1201
1152
|
if (config.enableData) new Data(ctx).registerCommands(analyse);
|
|
1202
|
-
if (config.enableDebug) new Debug(ctx).registerCommands(analyse);
|
|
1203
1153
|
}
|
|
1204
1154
|
__name(apply, "apply");
|
|
1205
1155
|
// Annotate the CommonJS export names for ESM import in node:
|