koishi-plugin-chat-analyse 1.4.7 → 1.4.9
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/Renderer.d.ts +0 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +32 -56
- package/package.json +1 -1
- package/readme.md +13 -11
package/lib/Renderer.d.ts
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -22,15 +22,17 @@ export interface Config {
|
|
|
22
22
|
cacheRetentionDays: number;
|
|
23
23
|
enableSimilarActivity: boolean;
|
|
24
24
|
enableAutoBackup: boolean;
|
|
25
|
+
color: string;
|
|
26
|
+
shape: string;
|
|
25
27
|
ellipticity: number;
|
|
26
28
|
rotateRatio: number;
|
|
27
29
|
minRotation: number;
|
|
28
30
|
maxRotation: number;
|
|
31
|
+
rotationSteps: number;
|
|
29
32
|
minFontSize: number;
|
|
30
33
|
maxFontSize: number;
|
|
31
34
|
gridSize: number;
|
|
32
35
|
fontFamily: string;
|
|
33
|
-
shape: 'square' | 'circle' | 'cardioid' | 'diamond' | 'triangle-forward' | 'triangle' | 'pentagon' | 'star';
|
|
34
36
|
maskImage: string;
|
|
35
37
|
}
|
|
36
38
|
/** @description 插件的配置项定义 */
|
package/lib/index.js
CHANGED
|
@@ -1493,26 +1493,6 @@ var Renderer = class {
|
|
|
1493
1493
|
static {
|
|
1494
1494
|
__name(this, "Renderer");
|
|
1495
1495
|
}
|
|
1496
|
-
COLOR_PALETTES = [
|
|
1497
|
-
// --- 4组近似色 ---
|
|
1498
|
-
// 1. Oceanic Blues: 更深邃、专业的蓝色系
|
|
1499
|
-
["#A9D6E5", "#89C2D9", "#61A5C2", "#2A6F97", "#012A4A"],
|
|
1500
|
-
// 2. Forest Greens: 丰富、饱和的绿色系
|
|
1501
|
-
["#ADDDBC", "#80C9A7", "#52B69A", "#34A0A4", "#168AAD"],
|
|
1502
|
-
// 3. Royal Purples: 优雅、浓郁的紫色系
|
|
1503
|
-
["#C792DF", "#AB69C6", "#9040AD", "#7B2CBF", "#5A189A"],
|
|
1504
|
-
// 4. Sunset Glow: 温暖、明亮的日落色系
|
|
1505
|
-
["#FFDD77", "#FFC94A", "#FFB703", "#F8961E", "#E85D04"],
|
|
1506
|
-
// --- 4组缤纷色 ---
|
|
1507
|
-
// 5. Vivid Candy: 鲜艳的糖果色
|
|
1508
|
-
["#E63946", "#588157", "#A8DADC", "#457B9D", "#1D3557"],
|
|
1509
|
-
// 6. Retro Groove: 复古风格
|
|
1510
|
-
["#264653", "#2A9D8F", "#F0C151", "#F4A261", "#E76F51"],
|
|
1511
|
-
// 7. Neon Pop: 高对比度的现代色彩组合
|
|
1512
|
-
["#EF476F", "#FFD166", "#06D6A0", "#118AB2", "#073B4C"],
|
|
1513
|
-
// 8. Bold Impact: 大胆且冲击力强的撞色
|
|
1514
|
-
["#D90429", "#F95738", "#F2C57C", "#0C7C59", "#003E1F"]
|
|
1515
|
-
];
|
|
1516
1496
|
COMMON_STYLE = `
|
|
1517
1497
|
:root {
|
|
1518
1498
|
--card-bg: #fff; --text-color: #111827; --header-color: #111827;
|
|
@@ -1582,13 +1562,13 @@ var Renderer = class {
|
|
|
1582
1562
|
async htmlToImage(fullHtmlContent) {
|
|
1583
1563
|
const page = await this.ctx.puppeteer.page();
|
|
1584
1564
|
try {
|
|
1585
|
-
await page.setViewport({ width:
|
|
1565
|
+
await page.setViewport({ width: 1080, height: 720, deviceScaleFactor: 1 });
|
|
1586
1566
|
await page.setContent(fullHtmlContent, { waitUntil: "networkidle0" });
|
|
1587
1567
|
const { width, height } = await page.evaluate(() => ({
|
|
1588
1568
|
width: document.body.scrollWidth,
|
|
1589
1569
|
height: document.body.scrollHeight
|
|
1590
1570
|
}));
|
|
1591
|
-
await page.setViewport({ width, height, deviceScaleFactor:
|
|
1571
|
+
await page.setViewport({ width, height, deviceScaleFactor: 1 });
|
|
1592
1572
|
return await page.screenshot({ type: "png", omitBackground: true });
|
|
1593
1573
|
} catch (error) {
|
|
1594
1574
|
this.ctx.logger.error("图片渲染失败:", error);
|
|
@@ -1702,34 +1682,32 @@ var Renderer = class {
|
|
|
1702
1682
|
*/
|
|
1703
1683
|
async *renderLineChart(data) {
|
|
1704
1684
|
const { title, time, series, labels } = data;
|
|
1705
|
-
const
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
const chartWidth = width - padding.left - padding.right;
|
|
1712
|
-
const chartHeight = height - padding.top - padding.bottom;
|
|
1685
|
+
const seriesColors = series.map(() => {
|
|
1686
|
+
const hue = Math.floor(Math.random() * 360);
|
|
1687
|
+
const saturation = Math.floor(Math.random() * 30 + 70);
|
|
1688
|
+
const lightness = Math.floor(Math.random() * 20 + 50);
|
|
1689
|
+
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
|
|
1690
|
+
});
|
|
1713
1691
|
const maxVal = Math.max(1, ...series.flatMap((s) => s.data));
|
|
1714
1692
|
const yTickCount = 5;
|
|
1715
1693
|
const yTickValue = Math.ceil(maxVal / yTickCount);
|
|
1716
1694
|
const yAxisMax = yTickValue * yTickCount;
|
|
1717
1695
|
const getX = /* @__PURE__ */ __name((index) => {
|
|
1718
|
-
if (labels.length <= 1) return
|
|
1719
|
-
return
|
|
1696
|
+
if (labels.length <= 1) return 300;
|
|
1697
|
+
return 20 + index / (labels.length - 1) * 560;
|
|
1720
1698
|
}, "getX");
|
|
1721
|
-
const getY = /* @__PURE__ */ __name((value) =>
|
|
1699
|
+
const getY = /* @__PURE__ */ __name((value) => 250 - value / yAxisMax * 240, "getY");
|
|
1722
1700
|
let svgElements = "";
|
|
1723
1701
|
for (let i = 0; i <= yTickCount; i++) {
|
|
1724
1702
|
const y = getY(i * yTickValue);
|
|
1725
1703
|
const value = i * yTickValue;
|
|
1726
|
-
svgElements += `<line x1="${
|
|
1727
|
-
svgElements += `<text x="${
|
|
1704
|
+
svgElements += `<line x1="${20}" y1="${y}" x2="580" y2="${y}" stroke="var(--border-color)" stroke-width="1"/>`;
|
|
1705
|
+
svgElements += `<text x="${20 - 8}" y="${y + 4}" font-size="10" fill="var(--sub-text-color)" text-anchor="end">${value}</text>`;
|
|
1728
1706
|
}
|
|
1729
1707
|
labels.forEach((label, index) => {
|
|
1730
1708
|
if (index % Math.ceil(labels.length / 12) === 0) {
|
|
1731
1709
|
const x = getX(index);
|
|
1732
|
-
svgElements += `<text x="${x}" y="
|
|
1710
|
+
svgElements += `<text x="${x}" y="270" font-size="10" fill="var(--sub-text-color)" text-anchor="middle">${label}</text>`;
|
|
1733
1711
|
}
|
|
1734
1712
|
});
|
|
1735
1713
|
series.forEach((s, seriesIndex) => {
|
|
@@ -1738,15 +1716,13 @@ var Renderer = class {
|
|
|
1738
1716
|
svgElements += `<polyline points="${points}" fill="none" stroke="${color}" stroke-width="2"/>`;
|
|
1739
1717
|
});
|
|
1740
1718
|
if (series.length > 1) {
|
|
1741
|
-
const
|
|
1742
|
-
const
|
|
1743
|
-
const LEGEND_START_Y = height - padding.bottom + 45;
|
|
1744
|
-
const columnWidth = chartWidth / ITEMS_PER_ROW;
|
|
1719
|
+
const LEGEND_START_Y = 295;
|
|
1720
|
+
const columnWidth = 560 / 3;
|
|
1745
1721
|
series.forEach((s, seriesIndex) => {
|
|
1746
|
-
const rowIndex = Math.floor(seriesIndex /
|
|
1747
|
-
const colIndex = seriesIndex %
|
|
1748
|
-
const legendX =
|
|
1749
|
-
const legendY = LEGEND_START_Y + rowIndex *
|
|
1722
|
+
const rowIndex = Math.floor(seriesIndex / 3);
|
|
1723
|
+
const colIndex = seriesIndex % 3;
|
|
1724
|
+
const legendX = 20 + colIndex * columnWidth;
|
|
1725
|
+
const legendY = LEGEND_START_Y + rowIndex * 20;
|
|
1750
1726
|
const color = seriesColors[seriesIndex];
|
|
1751
1727
|
svgElements += `<rect x="${legendX}" y="${legendY - 8}" width="12" height="8" fill="${color}" rx="2"/>`;
|
|
1752
1728
|
svgElements += `<text x="${legendX + 18}" y="${legendY}" font-size="12" fill="var(--text-color)">${s.name}</text>`;
|
|
@@ -1761,7 +1737,7 @@ var Renderer = class {
|
|
|
1761
1737
|
<div class="time-label">${time.toLocaleString("zh-CN", { hour12: false })}</div>
|
|
1762
1738
|
</div>
|
|
1763
1739
|
<div class="chart-wrapper">
|
|
1764
|
-
<svg width="
|
|
1740
|
+
<svg width="600" height="320" xmlns="http://www.w3.org/2000/svg">
|
|
1765
1741
|
${svgElements}
|
|
1766
1742
|
</svg>
|
|
1767
1743
|
</div>
|
|
@@ -1783,7 +1759,6 @@ var Renderer = class {
|
|
|
1783
1759
|
const { title, time, words } = data;
|
|
1784
1760
|
if (!words?.length) return;
|
|
1785
1761
|
const wordsJson = JSON.stringify(words);
|
|
1786
|
-
const selectedPalette = this.COLOR_PALETTES[Math.floor(Math.random() * this.COLOR_PALETTES.length)];
|
|
1787
1762
|
const weights = words.map((w) => w[1]);
|
|
1788
1763
|
const maxWeight = Math.max(...weights, 1);
|
|
1789
1764
|
const minWeight = Math.min(...weights);
|
|
@@ -1794,13 +1769,12 @@ var Renderer = class {
|
|
|
1794
1769
|
<h1 class="title-text">${title}</h1>
|
|
1795
1770
|
<div class="time-label">${time.toLocaleString("zh-CN", { hour12: false })}</div>
|
|
1796
1771
|
</div>
|
|
1797
|
-
<div style="width:
|
|
1798
|
-
<canvas id="wordcloud-container" width="
|
|
1772
|
+
<div style="width: 600px; height: 600px; margin: auto;">
|
|
1773
|
+
<canvas id="wordcloud-container" width="600" height="600"></canvas>
|
|
1799
1774
|
</div>
|
|
1800
1775
|
<script>${wordCloudScript}</script>
|
|
1801
1776
|
<script>
|
|
1802
1777
|
const canvas = document.getElementById('wordcloud-container');
|
|
1803
|
-
const palette = ${JSON.stringify(selectedPalette)};
|
|
1804
1778
|
const options = {
|
|
1805
1779
|
list: ${wordsJson},
|
|
1806
1780
|
fontFamily: ${JSON.stringify(config.fontFamily)},
|
|
@@ -1809,17 +1783,17 @@ var Renderer = class {
|
|
|
1809
1783
|
const normalizedWeight = (size - ${minWeight}) / (${maxWeight} - ${minWeight});
|
|
1810
1784
|
return ${config.minFontSize} + normalizedWeight * (${config.maxFontSize} - ${config.minFontSize});
|
|
1811
1785
|
},
|
|
1812
|
-
color: (
|
|
1813
|
-
return palette[Math.floor(Math.random() * palette.length)];
|
|
1814
|
-
},
|
|
1786
|
+
color: ${JSON.stringify(config.color)},
|
|
1815
1787
|
shape: ${JSON.stringify(config.shape)},
|
|
1816
1788
|
gridSize: ${config.gridSize},
|
|
1817
1789
|
ellipticity: ${config.ellipticity},
|
|
1818
1790
|
rotateRatio: ${config.rotateRatio},
|
|
1819
1791
|
minRotation: ${config.minRotation},
|
|
1820
1792
|
maxRotation: ${config.maxRotation},
|
|
1793
|
+
rotationSteps: ${config.rotationSteps},
|
|
1821
1794
|
backgroundColor: 'transparent',
|
|
1822
1795
|
clearCanvas: true,
|
|
1796
|
+
shrinkToFit: true,
|
|
1823
1797
|
shuffle: true,
|
|
1824
1798
|
};
|
|
1825
1799
|
|
|
@@ -2548,14 +2522,16 @@ var Config3 = import_koishi7.Schema.intersect([
|
|
|
2548
2522
|
import_koishi7.Schema.object({
|
|
2549
2523
|
ellipticity: import_koishi7.Schema.number().min(0).max(1).default(1).description("长宽比"),
|
|
2550
2524
|
rotateRatio: import_koishi7.Schema.number().min(0).max(1).default(0.5).description("旋转比"),
|
|
2551
|
-
|
|
2525
|
+
rotationSteps: import_koishi7.Schema.number().min(0).default(3).description("旋转步数"),
|
|
2526
|
+
minRotation: import_koishi7.Schema.number().default(0).description("最小旋转角"),
|
|
2552
2527
|
maxRotation: import_koishi7.Schema.number().default(Math.PI / 2).description("最大旋转角"),
|
|
2553
2528
|
minFontSize: import_koishi7.Schema.number().min(1).default(4).description("最小字号"),
|
|
2554
2529
|
maxFontSize: import_koishi7.Schema.number().min(1).default(64).description("最大字号"),
|
|
2555
|
-
gridSize: import_koishi7.Schema.number().min(0).default(1).description("
|
|
2530
|
+
gridSize: import_koishi7.Schema.number().min(0).default(1).description("词语间距"),
|
|
2531
|
+
color: import_koishi7.Schema.string().default("random-light").description("词云颜色"),
|
|
2532
|
+
shape: import_koishi7.Schema.string().default("square").description("词云形状"),
|
|
2556
2533
|
fontFamily: import_koishi7.Schema.string().default('"Noto Sans CJK SC", "Arial", sans-serif').description("词云字体"),
|
|
2557
|
-
|
|
2558
|
-
maskImage: import_koishi7.Schema.string().role("link").description("词云蒙版 (一个图片的URL,会覆盖形状设置)")
|
|
2534
|
+
maskImage: import_koishi7.Schema.string().role("link").description("蒙版图片")
|
|
2559
2535
|
}).description("词云生成配置")
|
|
2560
2536
|
]);
|
|
2561
2537
|
async function parseQueryScope(ctx, session, options) {
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -142,14 +142,14 @@
|
|
|
142
142
|
`enableMsgStat`: **启用消息统计**。 (默认: `true`)
|
|
143
143
|
`enableActivity`: **启用活跃统计**。 (默认: `true`)
|
|
144
144
|
`enableRankStat`: **启用发言排行**。 (默认: `true`)
|
|
145
|
-
`rankRetentionDays`: **排行保留天数**。发言排行数据的保留时长(天),`0` 为永久保留。 (默认: `
|
|
145
|
+
`rankRetentionDays`: **排行保留天数**。发言排行数据的保留时长(天),`0` 为永久保留。 (默认: `365`)
|
|
146
146
|
`enableWhoAt`: **启用提及记录**。 (默认: `true`)
|
|
147
147
|
`atRetentionDays`: **提及保留天数**。`whoatme` 数据的保留时长(天),`0` 为永久保留。 (默认: `3`)
|
|
148
148
|
|
|
149
149
|
### 高级分析配置
|
|
150
150
|
|
|
151
151
|
`enableOriRecord`: **启用原始记录**。是否记录原始消息内容。这是 `.view` 和 `wordcloud` 功能的基础。 (默认: `true`)
|
|
152
|
-
`cacheRetentionDays`: **原始记录保留天数**。原始消息记录的保留时长(天),`0` 为永久保留。 (默认: `
|
|
152
|
+
`cacheRetentionDays`: **原始记录保留天数**。原始消息记录的保留时长(天),`0` 为永久保留。 (默认: `31`)
|
|
153
153
|
`enableAutoBackup`: **启用自动备份记录**。是否开启每月自动备份原始消息记录。 (默认: `false`)
|
|
154
154
|
`enableWordCloud`: **启用词云生成**。
|
|
155
155
|
> **!** 此功能依赖 **`启用原始记录`**。 (默认: `true`)
|
|
@@ -160,16 +160,18 @@
|
|
|
160
160
|
|
|
161
161
|
| 配置项 | 描述 | 默认值 |
|
|
162
162
|
| :--- | :--- | :--- |
|
|
163
|
-
| `
|
|
163
|
+
| `color` | **词云颜色**:可设为 `random-light`、`random-dark`、CSS颜色值(如`#ff0000`)或一个返回颜色的JS函数。 | `random-light` |
|
|
164
|
+
| `shape` | **词云形状**:预设值包括 `circle`, `cardioid`, `diamond`, `square`, `triangle`, `pentagon`, `star`。 | `square` |
|
|
165
|
+
| `maskImage` | **蒙版图片**:提供一个图片的URL作为词云的形状蒙版。**注意:这会覆盖“词云形状”选项。** | (空) |
|
|
164
166
|
| `fontFamily` | **词云字体**:用于渲染词云的字体列表。 | `"Noto Sans CJK SC", "Arial", sans-serif` |
|
|
165
|
-
| `minFontSize` |
|
|
166
|
-
| `maxFontSize` |
|
|
167
|
-
| `gridSize` |
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
167
|
+
| `minFontSize` | **最小字号**:权重最小的单词所使用的字号(px)。 | `4` |
|
|
168
|
+
| `maxFontSize` | **最大字号**:权重最大的单词所使用的字号(px)。 | `64` |
|
|
169
|
+
| `gridSize` | **词语间距**:用于分隔单词的网格大小(px)。值越大,单词间距越大。 | `1` |
|
|
170
|
+
| `ellipticity` | **长宽比**:形状的扁平程度(0-1),仅对非方形形状有效。 | `1` |
|
|
171
|
+
| `rotateRatio` | **旋转比**:随机旋转的单词所占的比例(0-1)。 | `0.5` |
|
|
172
|
+
| `minRotation` | **最小旋转角**:单词随机旋转的最小角度(弧度)。 | `0` |
|
|
173
|
+
| `maxRotation` | **最大旋转角**:单词随机旋转的最大角度(弧度)。 | `1.570796` (π/2) |
|
|
174
|
+
| `rotationSteps`| **旋转步数**:旋转角度的选择方式。0表示随机,2表示只在最小/最大角度中二选一。 | `3` |
|
|
173
175
|
|
|
174
176
|
## 📌 注意事项
|
|
175
177
|
|