koishi-plugin-group-analysis-mx 1.2.7 → 1.2.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/service/renderer.js +51 -6
- package/package.json +1 -1
- package/resources/fonts/00.woff2 +0 -0
- package/resources/fonts/01.woff2 +0 -0
- package/resources/fonts/02.woff2 +0 -0
- package/resources/fonts/03.woff2 +0 -0
- package/resources/fonts/04.woff2 +0 -0
- package/resources/fonts/05.woff2 +0 -0
- package/resources/fonts/06.woff2 +0 -0
- package/resources/fonts/07.woff2 +0 -0
- package/resources/fonts/08.woff2 +0 -0
- package/resources/fonts/09.woff2 +0 -0
package/lib/service/renderer.js
CHANGED
|
@@ -12,6 +12,24 @@ const utils_1 = require("../utils");
|
|
|
12
12
|
const skins_1 = require("../skins");
|
|
13
13
|
/** 来自原项目 astrbot_plugin_qq_group_daily_analysis 的皮肤(Jinja2 模板,用 nunjucks 渲染) */
|
|
14
14
|
const ORIGINAL_SKINS = ['atri', 'bluearchive', 'format', 'hack', 'hatsunemiku', 'retrofuturism', 'simple', 'springfestival'];
|
|
15
|
+
/**
|
|
16
|
+
* Noto Color Emoji 字体分片的 unicode-range(与 resources/fonts/00.woff2..09.woff2 顺序对应)。
|
|
17
|
+
* 让 Puppeteer 在 Docker/Alpine 等缺 emoji 字体的环境也能渲染彩色 emoji。
|
|
18
|
+
*/
|
|
19
|
+
const EMOJI_RANGES = [
|
|
20
|
+
'U+1f1e6-1f1ff',
|
|
21
|
+
'U+200d, U+2620, U+26a7, U+fe0f, U+1f308, U+1f38c, U+1f3c1, U+1f3f3-1f3f4, U+1f6a9, U+e0062-e0063, U+e0065, U+e0067, U+e006c, U+e006e, U+e0073-e0074, U+e0077, U+e007f',
|
|
22
|
+
'U+23, U+2a, U+30-39, U+a9, U+ae, U+200d, U+203c, U+2049, U+20e3, U+2122, U+2139, U+2194-2199, U+21a9-21aa, U+23cf, U+23e9-23ef, U+23f8-23fa, U+24c2, U+25aa-25ab, U+25b6, U+25c0, U+25fb-25fe, U+2611, U+2622-2623, U+2626, U+262a, U+262e-262f, U+2638, U+2640, U+2642, U+2648-2653, U+2660, U+2663, U+2665-2666, U+2668, U+267b, U+267e-267f, U+2695, U+269b-269c, U+26a0, U+26a7, U+26aa-26ab, U+26ce, U+26d4, U+2705, U+2714, U+2716, U+271d, U+2721, U+2733-2734, U+2747, U+274c, U+274e, U+2753-2755, U+2757, U+2764, U+2795-2797, U+27a1, U+27b0, U+27bf, U+2934-2935, U+2b05-2b07, U+2b1b-2b1c, U+2b55, U+3030, U+303d, U+3297, U+3299, U+fe0f, U+1f170-1f171, U+1f17e-1f17f, U+1f18e, U+1f191-1f19a, U+1f201-1f202, U+1f21a, U+1f22f, U+1f232-1f23a, U+1f250-1f251, U+1f310, U+1f3a6, U+1f3b5-1f3b6, U+1f3bc, U+1f3e7, U+1f441, U+1f499-1f49c, U+1f49f-1f4a0, U+1f4ac-1f4ad, U+1f4b1-1f4b2, U+1f4b9, U+1f4db, U+1f4f2-1f4f6, U+1f500-1f507, U+1f515, U+1f518-1f524, U+1f52f-1f53d, U+1f549, U+1f54e, U+1f5a4, U+1f5e8, U+1f5ef, U+1f6ab, U+1f6ad-1f6b1, U+1f6b3, U+1f6b7-1f6bc, U+1f6be, U+1f6c2-1f6c5, U+1f6d0-1f6d1, U+1f6d7, U+1f6dc, U+1f7e0-1f7eb, U+1f7f0, U+1f90d-1f90e, U+1f9d1-1f9d2, U+1f9e1, U+1fa75-1fa77, U+1faaf',
|
|
23
|
+
'U+200d, U+231a-231b, U+2328, U+23f0-23f3, U+2602, U+260e, U+2692, U+2694, U+2696-2697, U+2699, U+26b0-26b1, U+26cf, U+26d1, U+26d3, U+2702, U+2709, U+270f, U+2712, U+fe0f, U+1f302, U+1f321, U+1f392-1f393, U+1f3a9, U+1f3bd, U+1f3ee, U+1f3f7, U+1f3fa, U+1f451-1f462, U+1f484, U+1f489-1f48a, U+1f48c-1f48e, U+1f4a1, U+1f4a3, U+1f4a5, U+1f4b0, U+1f4b3-1f4b8, U+1f4bb-1f4da, U+1f4dc-1f4f1, U+1f4ff, U+1f508-1f514, U+1f516-1f517, U+1f526-1f529, U+1f52c-1f52e, U+1f550-1f567, U+1f56f-1f570, U+1f576, U+1f587, U+1f58a-1f58d, U+1f5a5, U+1f5a8, U+1f5b1-1f5b2, U+1f5c2-1f5c4, U+1f5d1-1f5d3, U+1f5dc-1f5de, U+1f5e1, U+1f5f3, U+1f6aa, U+1f6ac, U+1f6bd, U+1f6bf, U+1f6c1, U+1f6cb, U+1f6cd-1f6cf, U+1f6d2, U+1f6e0-1f6e1, U+1f6f0, U+1f97b-1f97f, U+1f9af, U+1f9ba, U+1f9e2-1f9e6, U+1f9ea-1f9ec, U+1f9ee-1f9f4, U+1f9f7-1f9ff, U+1fa71-1fa74, U+1fa79-1fa7b, U+1fa86, U+1fa8e-1fa8f, U+1fa91-1fa93, U+1fa96, U+1fa99-1faa0, U+1faa2-1faa7, U+1faaa-1faae',
|
|
24
|
+
'U+265f, U+26bd-26be, U+26f3, U+26f8, U+fe0f, U+1f004, U+1f0cf, U+1f380-1f384, U+1f386-1f38b, U+1f38d-1f391, U+1f396-1f397, U+1f399-1f39b, U+1f39e-1f39f, U+1f3a3-1f3a5, U+1f3a7-1f3a9, U+1f3ab-1f3b4, U+1f3b7-1f3bb, U+1f3bd-1f3c0, U+1f3c5-1f3c6, U+1f3c8-1f3c9, U+1f3cf-1f3d3, U+1f3f8-1f3f9, U+1f47e, U+1f4e2, U+1f4f7-1f4fd, U+1f52b, U+1f579, U+1f58c-1f58d, U+1f5bc, U+1f6f7, U+1f6f9, U+1f6fc, U+1f93f, U+1f941, U+1f945, U+1f947-1f94f, U+1f9e7-1f9e9, U+1f9f5-1f9f6, U+1fa70-1fa71, U+1fa80-1fa81, U+1fa83-1fa85, U+1fa87-1fa8a, U+1fa94-1fa95, U+1fa97-1fa98, U+1faa1, U+1faa9, U+1fadf',
|
|
25
|
+
'U+2693, U+26e9-26ea, U+26f1-26f2, U+26f4-26f5, U+26fa, U+26fd, U+2708, U+fe0f, U+1f301, U+1f303, U+1f306-1f307, U+1f309, U+1f310, U+1f3a0-1f3a2, U+1f3aa, U+1f3cd-1f3ce, U+1f3d5, U+1f3d7-1f3db, U+1f3df-1f3e6, U+1f3e8-1f3ed, U+1f3ef-1f3f0, U+1f488, U+1f492, U+1f4ba, U+1f54b-1f54d, U+1f5fa-1f5ff, U+1f680-1f6a2, U+1f6a4-1f6a8, U+1f6b2, U+1f6d1, U+1f6d5-1f6d6, U+1f6dd-1f6df, U+1f6e2-1f6e5, U+1f6e9, U+1f6eb-1f6ec, U+1f6f3-1f6f6, U+1f6f8, U+1f6fa-1f6fb, U+1f9bc-1f9bd, U+1f9ed, U+1f9f3, U+1fa7c',
|
|
26
|
+
'U+200d, U+2615, U+fe0f, U+1f32d-1f330, U+1f336, U+1f33d, U+1f344-1f37f, U+1f382, U+1f52a, U+1f7e9, U+1f7eb, U+1f942-1f944, U+1f950-1f96f, U+1f99e, U+1f9aa, U+1f9c0-1f9cb, U+1fad0-1fadc',
|
|
27
|
+
'U+200d, U+2600-2601, U+2603-2604, U+2614, U+2618, U+26a1, U+26c4-26c5, U+26c8, U+26f0, U+2728, U+2744, U+2b1b, U+2b50, U+fe0f, U+1f300, U+1f304-1f305, U+1f308, U+1f30a-1f30f, U+1f311-1f321, U+1f324-1f32c, U+1f331-1f335, U+1f337-1f33c, U+1f33e-1f344, U+1f3d4, U+1f3d6, U+1f3dc-1f3de, U+1f3f5, U+1f400-1f43f, U+1f490, U+1f4a7, U+1f4ae, U+1f525, U+1f54a, U+1f573, U+1f577-1f578, U+1f648-1f64a, U+1f6d8, U+1f940, U+1f980-1f9ae, U+1f9ba, U+1fa90, U+1faa8, U+1fab0-1fabf, U+1facd-1facf, U+1fae7',
|
|
28
|
+
'U+200d, U+2640, U+2642, U+2695-2696, U+26f7, U+26f9, U+2708, U+2764, U+27a1, U+fe0f, U+1f33e, U+1f373, U+1f37c, U+1f384-1f385, U+1f393, U+1f3a4, U+1f3a8, U+1f3c2-1f3c4, U+1f3c7, U+1f3ca-1f3cc, U+1f3eb, U+1f3ed, U+1f3fb-1f3ff, U+1f430, U+1f466-1f469, U+1f46b-1f478, U+1f47c, U+1f481-1f483, U+1f486-1f487, U+1f48b, U+1f48f, U+1f491, U+1f4bb-1f4bc, U+1f527, U+1f52c, U+1f574-1f575, U+1f57a, U+1f645-1f647, U+1f64b, U+1f64d-1f64e, U+1f680, U+1f692, U+1f6a3, U+1f6b4-1f6b6, U+1f6c0, U+1f6cc, U+1f91d, U+1f926, U+1f930-1f931, U+1f934-1f93a, U+1f93c-1f93e, U+1f977, U+1f9af-1f9b3, U+1f9b8-1f9b9, U+1f9bc-1f9bd, U+1f9cc-1f9cf, U+1f9d1-1f9df, U+1fa70, U+1fa82, U+1fac3-1fac5, U+1fac8, U+1faef',
|
|
29
|
+
'U+200d, U+2194-2195, U+2603, U+261d, U+2620, U+2639-263a, U+2665, U+26a1, U+26c4, U+270a-270d, U+2728, U+2763-2764, U+2b50, U+fe0f, U+1f31a-1f31f, U+1f32b, U+1f389-1f38a, U+1f3fb-1f3ff, U+1f440-1f450, U+1f463-1f465, U+1f479-1f47b, U+1f47d-1f480, U+1f485, U+1f48b-1f48c, U+1f493-1f49f, U+1f4a2, U+1f4a4-1f4a6, U+1f4a8-1f4ab, U+1f4af, U+1f525, U+1f573, U+1f590, U+1f595-1f596, U+1f5a4, U+1f5e3, U+1f600-1f644, U+1f648-1f64a, U+1f64c, U+1f64f, U+1f90c-1f925, U+1f927-1f92f, U+1f932-1f933, U+1f970-1f976, U+1f978-1f97a, U+1f9a0, U+1f9b4-1f9b7, U+1f9bb, U+1f9be-1f9bf, U+1f9d0, U+1f9e0-1f9e1, U+1fa75-1fa79, U+1fac0-1fac2, U+1fac6, U+1fae0-1fae6, U+1fae8-1faea, U+1faef-1faf8'
|
|
30
|
+
];
|
|
31
|
+
/** Noto Color Emoji 字体的 base64 @font-face 缓存(首次读取后复用) */
|
|
32
|
+
let _emojiFontCssCache = null;
|
|
15
33
|
class RendererService extends koishi_1.Service {
|
|
16
34
|
config;
|
|
17
35
|
static inject = ['puppeteer'];
|
|
@@ -54,15 +72,42 @@ class RendererService extends koishi_1.Service {
|
|
|
54
72
|
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
|
|
55
73
|
}
|
|
56
74
|
}
|
|
57
|
-
/** 往渲染 HTML 注入 emoji
|
|
58
|
-
injectEmojiFont(html) {
|
|
75
|
+
/** 往渲染 HTML 注入 emoji 字体 @font-face 与字体栈,避免 puppeteer 截图时 emoji 显示为方框/问号。 */
|
|
76
|
+
async injectEmojiFont(html) {
|
|
59
77
|
if (!html || typeof html !== 'string')
|
|
60
78
|
return html;
|
|
61
|
-
|
|
79
|
+
let emojiCss = '';
|
|
80
|
+
try {
|
|
81
|
+
emojiCss = await this.buildEmojiFontCss();
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
this.ctx.logger.warn('构建 emoji 字体 CSS 失败:', e);
|
|
85
|
+
}
|
|
86
|
+
const style = `<style>${emojiCss}html,body{font-family:-apple-system,'Segoe UI','Segoe UI Emoji','NotoColorEmoji','Noto Sans SC','Noto Color Emoji','Apple Color Emoji','Microsoft YaHei','Hiragino Sans GB',sans-serif !important;}</style>`;
|
|
62
87
|
if (html.includes('</head>'))
|
|
63
88
|
return html.replace('</head>', style + '</head>');
|
|
64
89
|
return html;
|
|
65
90
|
}
|
|
91
|
+
/** 读取内嵌的 Noto Color Emoji 字体分片并生成 @font-face(base64 内联,跨环境可靠)。 */
|
|
92
|
+
async buildEmojiFontCss() {
|
|
93
|
+
if (_emojiFontCssCache)
|
|
94
|
+
return _emojiFontCssCache;
|
|
95
|
+
const dir = __dirname + '/../../resources/fonts';
|
|
96
|
+
let css = '';
|
|
97
|
+
for (let i = 0; i < EMOJI_RANGES.length; i++) {
|
|
98
|
+
const file = path_1.default.join(dir, `${String(i).padStart(2, '0')}.woff2`);
|
|
99
|
+
try {
|
|
100
|
+
const buf = await fs_1.promises.readFile(file);
|
|
101
|
+
const b64 = buf.toString('base64');
|
|
102
|
+
css += `@font-face{font-family:'NotoColorEmoji';font-style:normal;font-weight:400;src:url(data:font/woff2;base64,${b64}) format('woff2');unicode-range:${EMOJI_RANGES[i]};}`;
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
this.ctx.logger.warn(`加载 emoji 字体分片 ${i} 失败: ${e}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
_emojiFontCssCache = css;
|
|
109
|
+
return css;
|
|
110
|
+
}
|
|
66
111
|
async init() {
|
|
67
112
|
const resourcesDir = __dirname + '/../../resources';
|
|
68
113
|
const templateDir = this.templateDir;
|
|
@@ -193,7 +238,7 @@ class RendererService extends koishi_1.Service {
|
|
|
193
238
|
dynamicAvatarUrl: dynamicAvatarBase64
|
|
194
239
|
});
|
|
195
240
|
// 写入临时 HTML 文件
|
|
196
|
-
await fs_1.promises.writeFile(outTemplateHtmlPath, this.injectEmojiFont(filledHtml));
|
|
241
|
+
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(filledHtml));
|
|
197
242
|
this.ctx.logger.info('HTML 模板填充完成,正在调用 Puppeteer 进行渲染...');
|
|
198
243
|
const page = await this.ctx.puppeteer.page();
|
|
199
244
|
// 重新加载页面并使用 goto 访问本地文件
|
|
@@ -264,7 +309,7 @@ class RendererService extends koishi_1.Service {
|
|
|
264
309
|
const html = env.render('html_template.html', context);
|
|
265
310
|
const randomId = Math.random().toString(36).substring(2, 15);
|
|
266
311
|
const outTemplateHtmlPath = path_1.default.resolve(skinDir, `${randomId}.html`);
|
|
267
|
-
await fs_1.promises.writeFile(outTemplateHtmlPath, this.injectEmojiFont(html));
|
|
312
|
+
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(html));
|
|
268
313
|
this.ctx.logger.info(`原项目皮肤 ${skin} HTML 生成完成,正在调用 Puppeteer 渲染...`);
|
|
269
314
|
const page = await this.ctx.puppeteer.page();
|
|
270
315
|
await page.setViewport({ width: 1080, height: 1920, deviceScaleFactor: 2 });
|
|
@@ -480,7 +525,7 @@ class RendererService extends koishi_1.Service {
|
|
|
480
525
|
theme,
|
|
481
526
|
dynamicAvatarUrl: dynamicAvatarBase64
|
|
482
527
|
});
|
|
483
|
-
await fs_1.promises.writeFile(outTemplateHtmlPath, this.injectEmojiFont(filledHtml));
|
|
528
|
+
await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(filledHtml));
|
|
484
529
|
this.ctx.logger.info('用户画像 HTML 模板填充完成,正在调用 Puppeteer 进行渲染...');
|
|
485
530
|
const page = await this.ctx.puppeteer.page();
|
|
486
531
|
// 重新加载页面并使用 goto 访问本地文件
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|