koishi-plugin-chat-analyse 1.4.6 → 1.4.7
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 +3 -1
- package/lib/index.d.ts +10 -0
- package/lib/index.js +47 -18
- package/package.json +1 -1
- package/readme.md +15 -0
package/lib/Renderer.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Context } from 'koishi';
|
|
2
2
|
import { WordCloudData } from './Analyse';
|
|
3
|
+
import { Config } from './index';
|
|
3
4
|
/**
|
|
4
5
|
* @interface ListRenderData
|
|
5
6
|
* @description 定义了调用 `renderList` 方法所需的数据结构。
|
|
@@ -84,7 +85,8 @@ export declare class Renderer {
|
|
|
84
85
|
* @method renderWordCloud
|
|
85
86
|
* @description 将词频数据渲染成一张词云图片,使用 Puppeteer 和 wordcloud2.js。
|
|
86
87
|
* @param {WordCloudData} data - 包含标题、时间和词汇列表的对象。
|
|
88
|
+
* @param {Config} config - 插件的配置对象。
|
|
87
89
|
* @returns {AsyncGenerator<Buffer>} - 一个异步生成器,产出渲染后的图片 Buffer。
|
|
88
90
|
*/
|
|
89
|
-
renderWordCloud(data: WordCloudData): AsyncGenerator<Buffer>;
|
|
91
|
+
renderWordCloud(data: WordCloudData, config: Config): AsyncGenerator<Buffer>;
|
|
90
92
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -22,6 +22,16 @@ export interface Config {
|
|
|
22
22
|
cacheRetentionDays: number;
|
|
23
23
|
enableSimilarActivity: boolean;
|
|
24
24
|
enableAutoBackup: boolean;
|
|
25
|
+
ellipticity: number;
|
|
26
|
+
rotateRatio: number;
|
|
27
|
+
minRotation: number;
|
|
28
|
+
maxRotation: number;
|
|
29
|
+
minFontSize: number;
|
|
30
|
+
maxFontSize: number;
|
|
31
|
+
gridSize: number;
|
|
32
|
+
fontFamily: string;
|
|
33
|
+
shape: 'square' | 'circle' | 'cardioid' | 'diamond' | 'triangle-forward' | 'triangle' | 'pentagon' | 'star';
|
|
34
|
+
maskImage: string;
|
|
25
35
|
}
|
|
26
36
|
/** @description 插件的配置项定义 */
|
|
27
37
|
export declare const Config: Schema<Config>;
|
package/lib/index.js
CHANGED
|
@@ -1776,9 +1776,10 @@ var Renderer = class {
|
|
|
1776
1776
|
* @method renderWordCloud
|
|
1777
1777
|
* @description 将词频数据渲染成一张词云图片,使用 Puppeteer 和 wordcloud2.js。
|
|
1778
1778
|
* @param {WordCloudData} data - 包含标题、时间和词汇列表的对象。
|
|
1779
|
+
* @param {Config} config - 插件的配置对象。
|
|
1779
1780
|
* @returns {AsyncGenerator<Buffer>} - 一个异步生成器,产出渲染后的图片 Buffer。
|
|
1780
1781
|
*/
|
|
1781
|
-
async *renderWordCloud(data) {
|
|
1782
|
+
async *renderWordCloud(data, config) {
|
|
1782
1783
|
const { title, time, words } = data;
|
|
1783
1784
|
if (!words?.length) return;
|
|
1784
1785
|
const wordsJson = JSON.stringify(words);
|
|
@@ -1786,8 +1787,6 @@ var Renderer = class {
|
|
|
1786
1787
|
const weights = words.map((w) => w[1]);
|
|
1787
1788
|
const maxWeight = Math.max(...weights, 1);
|
|
1788
1789
|
const minWeight = Math.min(...weights);
|
|
1789
|
-
const MAX_FONT_SIZE = 64;
|
|
1790
|
-
const MIN_FONT_SIZE = 4;
|
|
1791
1790
|
const cardHtml = `
|
|
1792
1791
|
<div class="container">
|
|
1793
1792
|
<div class="header">
|
|
@@ -1795,31 +1794,49 @@ var Renderer = class {
|
|
|
1795
1794
|
<h1 class="title-text">${title}</h1>
|
|
1796
1795
|
<div class="time-label">${time.toLocaleString("zh-CN", { hour12: false })}</div>
|
|
1797
1796
|
</div>
|
|
1798
|
-
<div
|
|
1797
|
+
<div style="width: 512px; height: 512px; margin: auto;">
|
|
1798
|
+
<canvas id="wordcloud-container" width="512" height="512"></canvas>
|
|
1799
|
+
</div>
|
|
1799
1800
|
<script>${wordCloudScript}</script>
|
|
1800
1801
|
<script>
|
|
1802
|
+
const canvas = document.getElementById('wordcloud-container');
|
|
1801
1803
|
const palette = ${JSON.stringify(selectedPalette)};
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
+
const options = {
|
|
1805
|
+
list: ${wordsJson},
|
|
1806
|
+
fontFamily: ${JSON.stringify(config.fontFamily)},
|
|
1804
1807
|
weightFactor: (size) => {
|
|
1805
|
-
if (${maxWeight} === ${minWeight}) return (${
|
|
1808
|
+
if (${maxWeight} === ${minWeight}) return (${config.minFontSize} + ${config.maxFontSize}) / 2;
|
|
1806
1809
|
const normalizedWeight = (size - ${minWeight}) / (${maxWeight} - ${minWeight});
|
|
1807
|
-
return ${
|
|
1810
|
+
return ${config.minFontSize} + normalizedWeight * (${config.maxFontSize} - ${config.minFontSize});
|
|
1808
1811
|
},
|
|
1809
1812
|
color: (word, weight, fontSize, distance, theta) => {
|
|
1810
1813
|
return palette[Math.floor(Math.random() * palette.length)];
|
|
1811
1814
|
},
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
maxRotation: Math.PI / 2,
|
|
1815
|
+
shape: ${JSON.stringify(config.shape)},
|
|
1816
|
+
gridSize: ${config.gridSize},
|
|
1817
|
+
ellipticity: ${config.ellipticity},
|
|
1818
|
+
rotateRatio: ${config.rotateRatio},
|
|
1819
|
+
minRotation: ${config.minRotation},
|
|
1820
|
+
maxRotation: ${config.maxRotation},
|
|
1819
1821
|
backgroundColor: 'transparent',
|
|
1820
1822
|
clearCanvas: true,
|
|
1821
1823
|
shuffle: true,
|
|
1822
|
-
}
|
|
1824
|
+
};
|
|
1825
|
+
|
|
1826
|
+
const maskImageUrl = ${JSON.stringify(config.maskImage)};
|
|
1827
|
+
if (maskImageUrl) {
|
|
1828
|
+
const maskImage = new Image();
|
|
1829
|
+
maskImage.crossOrigin = "anonymous";
|
|
1830
|
+
maskImage.onload = () => {
|
|
1831
|
+
const ctx = canvas.getContext('2d');
|
|
1832
|
+
ctx.drawImage(maskImage, 0, 0, canvas.width, canvas.height);
|
|
1833
|
+
options.clearCanvas = false; // Don't clear the mask image
|
|
1834
|
+
WordCloud(canvas, options);
|
|
1835
|
+
};
|
|
1836
|
+
maskImage.src = maskImageUrl;
|
|
1837
|
+
} else {
|
|
1838
|
+
WordCloud(canvas, options);
|
|
1839
|
+
}
|
|
1823
1840
|
</script>
|
|
1824
1841
|
</div>`;
|
|
1825
1842
|
const fullHtml = `<!DOCTYPE html>
|
|
@@ -2412,7 +2429,7 @@ var Analyse = class {
|
|
|
2412
2429
|
const topWordsPreview = wordList.slice(0, 10).map((item) => item[0]).join(", ");
|
|
2413
2430
|
session.send(`正在生成词云,热门词汇:${topWordsPreview}...`);
|
|
2414
2431
|
const title = await generateTitle(this.ctx, scope.scopeDesc, { main: "词云", timeRange: options.hours });
|
|
2415
|
-
const imageGenerator = this.renderer.renderWordCloud({ title, time: /* @__PURE__ */ new Date(), words: wordList });
|
|
2432
|
+
const imageGenerator = this.renderer.renderWordCloud({ title, time: /* @__PURE__ */ new Date(), words: wordList }, this.config);
|
|
2416
2433
|
for await (const buffer of imageGenerator) await session.send(import_koishi6.h.image(buffer, "image/png"));
|
|
2417
2434
|
} catch (error) {
|
|
2418
2435
|
this.ctx.logger.error("生成词云图片失败:", error);
|
|
@@ -2527,7 +2544,19 @@ var Config3 = import_koishi7.Schema.intersect([
|
|
|
2527
2544
|
enableAutoBackup: import_koishi7.Schema.boolean().default(false).description("启用自动备份"),
|
|
2528
2545
|
enableWordCloud: import_koishi7.Schema.boolean().default(true).description("启用词云生成"),
|
|
2529
2546
|
enableSimilarActivity: import_koishi7.Schema.boolean().default(true).description("启用相似活跃分析")
|
|
2530
|
-
}).description("高级分析配置")
|
|
2547
|
+
}).description("高级分析配置"),
|
|
2548
|
+
import_koishi7.Schema.object({
|
|
2549
|
+
ellipticity: import_koishi7.Schema.number().min(0).max(1).default(1).description("长宽比"),
|
|
2550
|
+
rotateRatio: import_koishi7.Schema.number().min(0).max(1).default(0.5).description("旋转比"),
|
|
2551
|
+
minRotation: import_koishi7.Schema.number().default(Math.PI / 2).description("最小旋转角"),
|
|
2552
|
+
maxRotation: import_koishi7.Schema.number().default(Math.PI / 2).description("最大旋转角"),
|
|
2553
|
+
minFontSize: import_koishi7.Schema.number().min(1).default(4).description("最小字号"),
|
|
2554
|
+
maxFontSize: import_koishi7.Schema.number().min(1).default(64).description("最大字号"),
|
|
2555
|
+
gridSize: import_koishi7.Schema.number().min(0).default(1).description("词云间距"),
|
|
2556
|
+
fontFamily: import_koishi7.Schema.string().default('"Noto Sans CJK SC", "Arial", sans-serif').description("词云字体"),
|
|
2557
|
+
shape: import_koishi7.Schema.union(["square", "circle", "cardioid", "diamond", "triangle-forward", "triangle", "pentagon", "star"]).default("square").description("词云形状"),
|
|
2558
|
+
maskImage: import_koishi7.Schema.string().role("link").description("词云蒙版 (一个图片的URL,会覆盖形状设置)")
|
|
2559
|
+
}).description("词云生成配置")
|
|
2531
2560
|
]);
|
|
2532
2561
|
async function parseQueryScope(ctx, session, options) {
|
|
2533
2562
|
const scopeDesc = { guildId: options.guild, userId: void 0 };
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -156,6 +156,21 @@
|
|
|
156
156
|
`enableSimilarActivity`: **启用相似活跃分析**。
|
|
157
157
|
> **!** 此功能依赖 **`启用发言排行`** 或 **`启用活跃统计`**。 (默认: `true`)
|
|
158
158
|
|
|
159
|
+
### 词云生成配置
|
|
160
|
+
|
|
161
|
+
| 配置项 | 描述 | 默认值 |
|
|
162
|
+
| :--- | :--- | :--- |
|
|
163
|
+
| `maskImage` | **词云蒙版**:提供一个图片的URL作为词云的形状蒙版。**注意:这会覆盖“基础形状”选项。** | (空) |
|
|
164
|
+
| `fontFamily` | **词云字体**:用于渲染词云的字体列表。 | `"Noto Sans CJK SC", "Arial", sans-serif` |
|
|
165
|
+
| `minFontSize` | **最小字号**:权重最小的单词所使用的字号。 | `4` |
|
|
166
|
+
| `maxFontSize` | **最大字号**:权重最大的单词所使用的字号。 | `64` |
|
|
167
|
+
| `gridSize` | **词云间距**:用于分隔单词的网格大小(像素)。值越大,单词间距越大。 | `1` |
|
|
168
|
+
| `shape` | **基础形状**:选择词云的整体轮廓(无蒙版时生效)。 | `square` |
|
|
169
|
+
| `ellipticity` | **长宽比**:当形状为“椭圆”时,定义其扁平程度。值越小越扁。 | `1` |
|
|
170
|
+
| `rotateRatio` | **旋转比**:随机旋转的单词所占的比例(0 到 1)。 | `0.5` |
|
|
171
|
+
| `minRotation` | **最小旋转角**:单词随机旋转的最小角度(弧度)。 | `1.570796` (π/2) |
|
|
172
|
+
| `maxRotation` | **最大旋转角**:单词随机旋转的最大角度(弧度)。 | `1.570796` (π/2) |
|
|
173
|
+
|
|
159
174
|
## 📌 注意事项
|
|
160
175
|
|
|
161
176
|
1. **Puppeteer 配置**:本插件的图片渲染强依赖 `puppeteer` 服务。请确保您已正确安装并配置了该服务,包括正确设置了可执行文件路径(如有需要)。渲染失败通常与此有关。
|