koishi-plugin-chat-analyse 1.3.6 → 1.3.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/index.js +70 -66
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1487,22 +1487,24 @@ var Renderer = class {
|
|
|
1487
1487
|
__name(this, "Renderer");
|
|
1488
1488
|
}
|
|
1489
1489
|
COLOR_PALETTES = [
|
|
1490
|
-
//
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
//
|
|
1501
|
-
["#
|
|
1502
|
-
//
|
|
1503
|
-
["#
|
|
1504
|
-
//
|
|
1505
|
-
["#
|
|
1490
|
+
// --- 4组近似色 ---
|
|
1491
|
+
// 1. Oceanic Blues: 更深邃、专业的蓝色系
|
|
1492
|
+
["#A9D6E5", "#89C2D9", "#61A5C2", "#2A6F97", "#012A4A"],
|
|
1493
|
+
// 2. Forest Greens: 丰富、饱和的绿色系
|
|
1494
|
+
["#ADDDBC", "#80C9A7", "#52B69A", "#34A0A4", "#168AAD"],
|
|
1495
|
+
// 3. Royal Purples: 优雅、浓郁的紫色系
|
|
1496
|
+
["#C792DF", "#AB69C6", "#9040AD", "#7B2CBF", "#5A189A"],
|
|
1497
|
+
// 4. Sunset Glow: 温暖、明亮的日落色系
|
|
1498
|
+
["#FFDD77", "#FFC94A", "#FFB703", "#F8961E", "#E85D04"],
|
|
1499
|
+
// --- 4组缤纷色 ---
|
|
1500
|
+
// 5. Vivid Candy: 鲜艳的糖果色
|
|
1501
|
+
["#E63946", "#588157", "#A8DADC", "#457B9D", "#1D3557"],
|
|
1502
|
+
// 6. Retro Groove: 复古风格
|
|
1503
|
+
["#264653", "#2A9D8F", "#F0C151", "#F4A261", "#E76F51"],
|
|
1504
|
+
// 7. Neon Pop: 高对比度的现代色彩组合
|
|
1505
|
+
["#EF476F", "#FFD166", "#06D6A0", "#118AB2", "#073B4C"],
|
|
1506
|
+
// 8. Bold Impact: 大胆且冲击力强的撞色
|
|
1507
|
+
["#D90429", "#F95738", "#F2C57C", "#0C7C59", "#003E1F"]
|
|
1506
1508
|
];
|
|
1507
1509
|
COMMON_STYLE = `
|
|
1508
1510
|
:root {
|
|
@@ -1697,8 +1699,8 @@ var Renderer = class {
|
|
|
1697
1699
|
const selectedPalette = colorfulPalettes[Math.floor(Math.random() * colorfulPalettes.length)];
|
|
1698
1700
|
const shuffledColors = [...selectedPalette].sort(() => 0.5 - Math.random());
|
|
1699
1701
|
const seriesColors = series.map((_, index) => shuffledColors[index % shuffledColors.length]);
|
|
1700
|
-
const width = 600, height =
|
|
1701
|
-
const padding = { top:
|
|
1702
|
+
const width = 600, height = 320;
|
|
1703
|
+
const padding = { top: 10, right: 15, bottom: 60, left: 25 };
|
|
1702
1704
|
const chartWidth = width - padding.left - padding.right;
|
|
1703
1705
|
const chartHeight = height - padding.top - padding.bottom;
|
|
1704
1706
|
const maxVal = Math.max(1, ...series.flatMap((s) => s.data));
|
|
@@ -2318,64 +2320,66 @@ var Analyse = class {
|
|
|
2318
2320
|
});
|
|
2319
2321
|
}
|
|
2320
2322
|
if (this.config.enableSimilarActivity) {
|
|
2321
|
-
cmd.subcommand("simiactive", "相似活跃分析").usage("
|
|
2323
|
+
cmd.subcommand("simiactive", "相似活跃分析").usage("分析你和群友的活跃规律,找出谁和你的作息最相似。").option("hours", "-n <hours:number> 指定时长", { fallback: 24 }).option("separate", "-p 分时分析").action(async ({ session, options }) => {
|
|
2322
2324
|
if (!session.guildId) return "请在群组中使用此命令";
|
|
2323
2325
|
try {
|
|
2324
|
-
const until = /* @__PURE__ */ new Date();
|
|
2325
|
-
let since;
|
|
2326
|
-
let points;
|
|
2327
|
-
let title;
|
|
2328
|
-
let labels;
|
|
2329
|
-
let daysToAnalyze = 0;
|
|
2330
|
-
if (options.separate) {
|
|
2331
|
-
points = options.hours;
|
|
2332
|
-
since = new Date(until.getTime() - options.hours * import_koishi6.Time.hour);
|
|
2333
|
-
title = `${options.hours}小时相似活跃分析`;
|
|
2334
|
-
labels = Array.from({ length: points }, (_, i) => String(new Date(until.getTime() - (points - 1 - i) * import_koishi6.Time.hour).getHours()));
|
|
2335
|
-
} else {
|
|
2336
|
-
daysToAnalyze = Math.floor(options.hours / 24);
|
|
2337
|
-
if (daysToAnalyze < 1) return "请指定至少 24 小时时长";
|
|
2338
|
-
const analysisDurationHours = daysToAnalyze * 24;
|
|
2339
|
-
since = new Date(until.getTime() - analysisDurationHours * import_koishi6.Time.hour);
|
|
2340
|
-
points = 24;
|
|
2341
|
-
title = `${daysToAnalyze}天相似活跃分析`;
|
|
2342
|
-
labels = Array.from({ length: 24 }, (_, i) => String(i));
|
|
2343
|
-
}
|
|
2344
2326
|
const guildUsers = await this.ctx.database.get("analyse_user", { channelId: session.guildId });
|
|
2345
2327
|
if (guildUsers.length < 2) return "暂无用户数据";
|
|
2346
2328
|
const selfUser = guildUsers.find((u) => u.userId === session.userId);
|
|
2347
2329
|
const guildUserUids = guildUsers.map((u) => u.uid);
|
|
2348
2330
|
const uidToNameMap = new Map(guildUsers.map((u) => [u.uid, u.userName]));
|
|
2349
|
-
const
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2331
|
+
const until = /* @__PURE__ */ new Date();
|
|
2332
|
+
let analysisConfig;
|
|
2333
|
+
if (options.separate) {
|
|
2334
|
+
const { hours } = options;
|
|
2335
|
+
analysisConfig = {
|
|
2336
|
+
points: hours,
|
|
2337
|
+
since: new Date(until.getTime() - hours * import_koishi6.Time.hour),
|
|
2338
|
+
title: `${hours}小时相似活跃分析`,
|
|
2339
|
+
labels: Array.from({ length: hours }, (_, i) => String(new Date(until.getTime() - (hours - 1 - i) * import_koishi6.Time.hour).getHours())),
|
|
2340
|
+
getIndex: /* @__PURE__ */ __name((timestamp) => {
|
|
2341
|
+
const diff = until.getTime() - timestamp.getTime();
|
|
2342
|
+
const index = hours - 1 - Math.floor(diff / import_koishi6.Time.hour);
|
|
2343
|
+
return index >= 0 && index < hours ? index : -1;
|
|
2344
|
+
}, "getIndex"),
|
|
2345
|
+
reorderVector: /* @__PURE__ */ __name((vec) => vec, "reorderVector")
|
|
2346
|
+
};
|
|
2347
|
+
} else {
|
|
2348
|
+
const daysToAnalyse = Math.floor(options.hours / 24);
|
|
2349
|
+
if (daysToAnalyse < 1) return "分析时长请指定至少 1 天";
|
|
2350
|
+
const hoursToAnalyse = daysToAnalyse * 24;
|
|
2351
|
+
const currentHour = until.getHours();
|
|
2352
|
+
const labels = Array.from({ length: 24 }, (_, i) => String((currentHour - (23 - i) + 24) % 24));
|
|
2353
|
+
analysisConfig = {
|
|
2354
|
+
points: 24,
|
|
2355
|
+
since: new Date(until.getTime() - hoursToAnalyse * import_koishi6.Time.hour),
|
|
2356
|
+
title: `${daysToAnalyse}天相似活跃分析`,
|
|
2357
|
+
labels,
|
|
2358
|
+
getIndex: /* @__PURE__ */ __name((timestamp) => timestamp.getHours(), "getIndex"),
|
|
2359
|
+
reorderVector: /* @__PURE__ */ __name((vector) => labels.map((label) => vector[parseInt(label)]), "reorderVector")
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
const records = await this.ctx.database.get("analyse_rank", { uid: { $in: guildUserUids }, timestamp: { $gte: analysisConfig.since } });
|
|
2363
|
+
if (!records.length) return "暂无统计数据";
|
|
2364
|
+
const activityVectors = new Map(guildUserUids.map((uid) => [uid, Array(analysisConfig.points).fill(0)]));
|
|
2365
|
+
for (const record of records) {
|
|
2366
|
+
const index = analysisConfig.getIndex(record.timestamp);
|
|
2367
|
+
if (index !== -1) activityVectors.get(record.uid)[index] += record.count;
|
|
2368
|
+
}
|
|
2368
2369
|
const selfVector = activityVectors.get(selfUser.uid);
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2370
|
+
const similarities = guildUserUids.filter((uid) => uid !== selfUser.uid && activityVectors.get(uid).some((v) => v !== 0)).map((uid) => ({
|
|
2371
|
+
uid,
|
|
2372
|
+
score: cosineSimilarity(selfVector, activityVectors.get(uid))
|
|
2373
|
+
})).sort((a, b) => b.score - a.score);
|
|
2374
|
+
if (!similarities.length) return "暂无相似用户";
|
|
2372
2375
|
const top5 = similarities.slice(0, 5);
|
|
2373
|
-
const series = [{ name: uidToNameMap.get(selfUser.uid) || "您", data: selfVector }];
|
|
2374
|
-
|
|
2376
|
+
const series = [{ name: uidToNameMap.get(selfUser.uid) || "您", data: analysisConfig.reorderVector(selfVector) }];
|
|
2377
|
+
for (const sim of top5) {
|
|
2375
2378
|
const name2 = uidToNameMap.get(sim.uid) || `UID ${sim.uid}`;
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
+
const data = analysisConfig.reorderVector(activityVectors.get(sim.uid));
|
|
2380
|
+
series.push({ name: `${name2} (${(sim.score * 100).toFixed(1)}%)`, data });
|
|
2381
|
+
}
|
|
2382
|
+
const imageGenerator = this.renderer.renderLineChart({ title: analysisConfig.title, time: /* @__PURE__ */ new Date(), series, labels: analysisConfig.labels });
|
|
2379
2383
|
for await (const buffer of imageGenerator) await session.send(import_koishi6.h.image(buffer, "image/png"));
|
|
2380
2384
|
} catch (error) {
|
|
2381
2385
|
this.ctx.logger.error("生成作息分析图片失败:", error);
|