koishi-plugin-group-analysis-mx 1.2.7 → 1.2.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.
@@ -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,19 +72,43 @@ class RendererService extends koishi_1.Service {
54
72
  return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
55
73
  }
56
74
  }
57
- /** 往渲染 HTML 注入 emoji 字体栈,避免 puppeteer 截图时 emoji 显示为方框/问号。 */
58
- injectEmojiFont(html) {
75
+ /** 往渲染 HTML 注入 emoji 字体 @font-face 与字体栈,避免 puppeteer 截图时 emoji 显示为方框/问号。 */
76
+ async injectEmojiFont(html, htmlDir) {
59
77
  if (!html || typeof html !== 'string')
60
78
  return html;
61
- const style = `<style>html,body{font-family:-apple-system,'Segoe UI','Segoe UI Emoji','Noto Sans SC','Noto Color Emoji','Apple Color Emoji','Microsoft YaHei','Hiragino Sans GB',sans-serif !important;}</style>`;
79
+ // 字体目录相对于当前临时 HTML 所在目录,供 @font-face 用相对路径加载(Chromium 最稳定)
80
+ const fontsAbs = path_1.default.resolve(this.templateDir, 'fonts');
81
+ let rel = 'fonts';
82
+ try {
83
+ rel = path_1.default.relative(path_1.default.resolve(htmlDir || this.templateDir), fontsAbs).replace(/\\/g, '/');
84
+ if (rel && !rel.startsWith('.'))
85
+ rel = './' + rel;
86
+ }
87
+ catch (e) { /* use default */ }
88
+ const emojiCss = this.buildEmojiFontCss(rel);
89
+ 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
90
  if (html.includes('</head>'))
63
91
  return html.replace('</head>', style + '</head>');
64
92
  return html;
65
93
  }
94
+ /** 生成引用磁盘上 emoji 字体分片的 @font-face(相对路径,Chromium 稳定加载)。 */
95
+ buildEmojiFontCss(relPath) {
96
+ const rel = relPath || 'fonts';
97
+ let css = '';
98
+ for (let i = 0; i < EMOJI_RANGES.length; i++) {
99
+ const file = `${String(i).padStart(2, '0')}.woff2`;
100
+ css += `@font-face{font-family:'NotoColorEmoji';font-style:normal;font-weight:400;src:url('${rel}/${file}') format('woff2');unicode-range:${EMOJI_RANGES[i]};}`;
101
+ }
102
+ return css;
103
+ }
66
104
  async init() {
67
105
  const resourcesDir = __dirname + '/../../resources';
68
106
  const templateDir = this.templateDir;
69
107
  const skin = this.config.skin || 'md3';
108
+ // 拷贝内嵌的 emoji 字体到 data 目录,供 @font-face 以相对路径引用(Chromium 最稳定的加载方式)
109
+ const fontsDestDir = path_1.default.resolve(templateDir, 'fonts');
110
+ await fs_1.promises.mkdir(fontsDestDir, { recursive: true });
111
+ await fs_1.promises.cp(path_1.default.resolve(resourcesDir, 'fonts'), fontsDestDir, { recursive: true });
70
112
  // 拷贝原项目皮肤模板(nunjucks 渲染用)
71
113
  const originalSourceDir = path_1.default.resolve(resourcesDir, 'original');
72
114
  const originalDestDir = path_1.default.resolve(templateDir, 'original');
@@ -193,7 +235,7 @@ class RendererService extends koishi_1.Service {
193
235
  dynamicAvatarUrl: dynamicAvatarBase64
194
236
  });
195
237
  // 写入临时 HTML 文件
196
- await fs_1.promises.writeFile(outTemplateHtmlPath, this.injectEmojiFont(filledHtml));
238
+ await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(filledHtml, path_1.default.dirname(outTemplateHtmlPath)));
197
239
  this.ctx.logger.info('HTML 模板填充完成,正在调用 Puppeteer 进行渲染...');
198
240
  const page = await this.ctx.puppeteer.page();
199
241
  // 重新加载页面并使用 goto 访问本地文件
@@ -264,7 +306,7 @@ class RendererService extends koishi_1.Service {
264
306
  const html = env.render('html_template.html', context);
265
307
  const randomId = Math.random().toString(36).substring(2, 15);
266
308
  const outTemplateHtmlPath = path_1.default.resolve(skinDir, `${randomId}.html`);
267
- await fs_1.promises.writeFile(outTemplateHtmlPath, this.injectEmojiFont(html));
309
+ await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(html, path_1.default.dirname(outTemplateHtmlPath)));
268
310
  this.ctx.logger.info(`原项目皮肤 ${skin} HTML 生成完成,正在调用 Puppeteer 渲染...`);
269
311
  const page = await this.ctx.puppeteer.page();
270
312
  await page.setViewport({ width: 1080, height: 1920, deviceScaleFactor: 2 });
@@ -480,7 +522,7 @@ class RendererService extends koishi_1.Service {
480
522
  theme,
481
523
  dynamicAvatarUrl: dynamicAvatarBase64
482
524
  });
483
- await fs_1.promises.writeFile(outTemplateHtmlPath, this.injectEmojiFont(filledHtml));
525
+ await fs_1.promises.writeFile(outTemplateHtmlPath, await this.injectEmojiFont(filledHtml, path_1.default.dirname(outTemplateHtmlPath)));
484
526
  this.ctx.logger.info('用户画像 HTML 模板填充完成,正在调用 Puppeteer 进行渲染...');
485
527
  const page = await this.ctx.puppeteer.page();
486
528
  // 重新加载页面并使用 goto 访问本地文件
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-group-analysis-mx",
3
3
  "description": "群聊分析插件 - 生成精美群聊统计报告,支持 AI 话题总结(独立版,不依赖 ChatLuna)",
4
- "version": "1.2.7",
4
+ "version": "1.2.9",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/index.mjs",
7
7
  "typings": "lib/index.d.ts",
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file