koishi-plugin-ll-mc 3.1.9 → 3.1.11

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.
Files changed (2) hide show
  1. package/lib/index.js +18 -159
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -49,114 +49,16 @@ var inject = {
49
49
  required: ["database", "cron"],
50
50
  optional: ["markdownToImage", "puppeteer", "canvas"]
51
51
  };
52
- var usage = `## 📝 注意事项
52
+ var usage = `## 📝 使用说明
53
53
 
54
- - 只统计群聊消息
55
- - 初始化需权限等级 3
56
- - 依赖 database 与 cron 服务
57
- - 生成图片时,需 puppeteer 提供 canvas 支持
54
+ - \`messageCounter.查询 [用户]\` — 查询指定用户的发言次数[排名]
55
+ - \`messageCounter.排行榜 [人数]\` — 发言排行榜(默认今日)
56
+ - \`messageCounter.群排行榜\` 各群发言排行榜(默认今日)
57
+ - \`messageCounter.上传柱状条背景\` 上传自定义柱状条背景
58
+ - \`messageCounter.重载资源\` — 重载资源
59
+ - \`messageCounter.清理缓存\` — 清理头像缓存
58
60
 
59
- ## 🔍 关键指令
60
-
61
- ### \`messageCounter.查询 [指定用户]\`
62
-
63
- 查询指定用户的发言次数信息(次数[排名])。若不带任何选项,则显示所有时段的数据。
64
-
65
- **选项:**
66
-
67
- | 参数 | 说明 |
68
- |------|------|
69
- | \`-d, --day\` | 今日发言次数[排名] |
70
- | \`--yd, --yesterday\` | 昨日发言次数[排名] |
71
- | \`-w, --week\` | 本周发言次数[排名] |
72
- | \`-m, --month\` | 本月发言次数[排名] |
73
- | \`-y, --year\` | 今年发言次数[排名] |
74
- | \`-t, --total\` | 总发言次数[排名] |
75
- | \`--dag\` | 跨群今日发言次数[排名] |
76
- | \`--ydag\` | 跨群昨日发言次数[排名] |
77
- | \`--wag\` | 跨群本周发言次数[排名] |
78
- | \`--mag\` | 跨群本月发言次数[排名] |
79
- | \`--yag\` | 跨群本年发言次数[排名] |
80
- | \`-a, --across\` | 跨群总发言次数[排名] |
81
-
82
- ### \`messageCounter.排行榜 [显示的人数]\`
83
-
84
- 发言排行榜。默认为今日发言榜。
85
-
86
- **选项:**
87
-
88
- | 参数 | 说明 |
89
- |------|------|
90
- | \`--yd, --yesterday\` | 昨日发言排行榜 |
91
- | \`-w\` | 本周发言排行榜 |
92
- | \`-m\` | 本月发言排行榜 |
93
- | \`-y\` | 今年发言排行榜 |
94
- | \`-t\` | 总发言排行榜 |
95
- | \`--dag\` | 跨群今日发言排行榜 |
96
- | \`--ydag\` | 跨群昨日发言排行榜 |
97
- | \`--wag\` | 跨群本周发言排行榜 |
98
- | \`--mag\` | 跨群本月发言排行榜 |
99
- | \`--yag\` | 跨群今年发言排行榜 |
100
- | \`--dragon\` | 跨群总发言排行榜(圣龙王榜) |
101
- | \`--whites\` | 白名单,只显示白名单用户 |
102
- | \`--blacks\` | 黑名单,不显示黑名单用户 |
103
-
104
- ### \`messageCounter.群排行榜 [number:number]\`
105
-
106
- 各群聊的发言排行榜。默认为今日发言榜。
107
-
108
- **选项:**
109
-
110
- | 参数 | 说明 |
111
- |------|------|
112
- | \`--yd, --yesterday\` | 昨日发言排行榜 |
113
- | \`-w, -m, -y, -t\` | 本周/本月/今年/总发言排行榜 |
114
- | \`-s\` | 指定用户的群发言排行榜 |
115
- | \`--whites\` | 白名单,只显示白名单群 |
116
- | \`--blacks\` | 黑名单,不显示黑名单群 |
117
-
118
- ### \`messageCounter.上传柱状条背景\`
119
-
120
- - 为自己上传一张自定义的水平柱状条背景图片
121
- - 新图片会覆盖旧的图片。若上传失败,旧图片也会被删除
122
- - 使用此指令时需附带图片
123
-
124
- ### \`messageCounter.重载资源\`
125
-
126
- - 实时重载用户图标、柱状条背景和字体文件,使其更改即时生效(需要权限等级 2)
127
-
128
- ### \`messageCounter.清理缓存\`
129
-
130
- - 清理过期的头像缓存文件,以释放磁盘空间(需要权限等级 3)
131
- - 用户更换头像后,旧的头像缓存会变成“孤儿缓存”。此指令可以安全地移除它们。
132
-
133
- ## 🎨 自定义水平柱状图样式
134
-
135
- - 重载插件或使用 \`messageCounter.重载资源\` 指令可使新增的文件立即生效。
136
-
137
- ### 1. 用户图标
138
-
139
- - 在 \`data/messageCounter/icons\` 文件夹下添加用户图标
140
- - 文件名格式为 \`用户ID.png\`(例:\`1234567890.png\`)
141
- - 支持多图标,文件名格式为 \`用户ID-1.png\`, \`用户ID-2.png\`
142
-
143
- ### 2. 柱状条背景
144
-
145
- - **推荐方式**:使用 \`messageCounter.上传柱状条背景\` 指令
146
- - **手动方式**:在 \`data/messageCounter/barBgImgs\` 文件夹下添加背景图片
147
- - 支持多背景(随机选用),文件名格式为 \`用户ID-1.png\` 等
148
- - 建议尺寸 850x50 像素,文件名 \`用户ID.png\`
149
-
150
- ### 3. 自定义字体
151
-
152
- - 插件启动时,会自动将内置字体 \`HarmonyOS_Sans_Medium.ttf\` 拷贝到 \`data/messageCounter/fonts/\` 目录下。
153
- - 您可以将自己喜爱的字体文件放入此文件夹,并在配置项的“字体设置”中填入该字体的文件名称(不带后缀)。
154
-
155
- ---
156
-
157
- ## 💬 QQ 群
158
-
159
- - 956758505`;
61
+ 详细选项请发送 \`help messageCounter\` 查看。`;
160
62
  var logger = new import_koishi.Logger("messageCounter");
161
63
  var FONT_OPTIONS = {
162
64
  TITLE: "HarmonyOS_Sans_Medium",
@@ -2560,7 +2462,7 @@ ${targetUserRecord[0].username}
2560
2462
  </div>`;
2561
2463
  }
2562
2464
  __name(buildLeaderboardRow, "buildLeaderboardRow");
2563
- function buildLeaderboardHtml(title, date, items, backgroundCss, fontFaces) {
2465
+ function buildLeaderboardHtml(title, date, items, backgroundCss) {
2564
2466
  const maxCount = Math.max(1, ...items.map((item) => item.count || 0));
2565
2467
  const rows = items.map((item, index) => buildLeaderboardRow(item, index, maxCount)).join("\n");
2566
2468
  return `<!DOCTYPE html>
@@ -2569,7 +2471,10 @@ ${targetUserRecord[0].username}
2569
2471
  <meta charset="UTF-8">
2570
2472
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
2571
2473
  <style>
2572
- ${fontFaces || ""}
2474
+ /* 只把"数字"锁定到内置文本字体:系统装了 emoji 字体(如 font-noto-emoji)时,
2475
+ emoji 字体里也带 0-9 字形(给 keycap 用)、字宽很宽,会导致数字被撑成 "2 9 1 7"。
2476
+ 用 unicode-range 只接管 0-9,其余字符照常走系统字体,emoji 也交给系统 emoji 字体。 */
2477
+ @font-face { font-family: 'LLMCDigits'; src: url('fonts/HarmonyOS_Sans_Medium.ttf') format('truetype'); unicode-range: U+0030-0039; }
2573
2478
  * { box-sizing: border-box; margin: 0; padding: 0; }
2574
2479
  html, body {
2575
2480
  width: 900px;
@@ -2578,7 +2483,7 @@ ${targetUserRecord[0].username}
2578
2483
  background-color: #F5F7FA;
2579
2484
  }
2580
2485
  body {
2581
- font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "NotoEmoji", "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
2486
+ font-family: "LLMCDigits", "PingFang SC", "Microsoft YaHei", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
2582
2487
  -webkit-font-smoothing: antialiased;
2583
2488
  }
2584
2489
  .card {
@@ -2622,10 +2527,9 @@ ${rows}
2622
2527
  if (typeof ctx.puppeteer?.page !== "function") return null;
2623
2528
  let page;
2624
2529
  try {
2625
- // 复用图表的内置字体(含纯 emoji 的 NotoEmoji),以相对路径 fonts/<file> 加载,
2626
- // 所以必须把模板写到 data/messageCounter/ 目录里再用 file:// 访问,相对路径才能解析。
2627
- const fontFaces = generateFontFacesCSS(["HarmonyOS_Sans_Medium.ttf", "NotoEmoji.ttf"]);
2628
- const html = buildLeaderboardHtml(title, date, items, backgroundCss, fontFaces);
2530
+ // 官机补丁:模板里的数字用内置文本字体通过 @font-face(unicode-range) 锁定,
2531
+ // 所以必须把模板写到 data/messageCounter/ 目录里再用 file:// 访问,相对路径 fonts/ 才能解析。
2532
+ const html = buildLeaderboardHtml(title, date, items, backgroundCss);
2629
2533
  page = await ctx.puppeteer.page();
2630
2534
  await page.setViewport({ width: 900, height: 400, deviceScaleFactor: 2 });
2631
2535
  const pageUrl = templateHtmlPath.includes(":") ? `file:///${templateHtmlPath}` : `file://${templateHtmlPath}`;
@@ -2644,54 +2548,9 @@ ${rows}
2644
2548
  } else {
2645
2549
  await page.setContent(html, { waitUntil: "networkidle0", timeout: 20000 }).catch(() => {});
2646
2550
  }
2647
- // 确保 emoji 字体就绪再截图
2551
+ // 确保字体就绪再截图
2648
2552
  await page.evaluate(() => document.fonts.ready).catch(() => {});
2649
2553
  await page.waitForSelector(".card").catch(() => {});
2650
- // 官机补丁:背景图右侧可能是深色人物,导致排在右下角的发言数字看不清。
2651
- // 这里对每个数字的位置采样一次背景亮度,凡是背景偏暗的数字改成白色(去掉全局描边)。
2652
- try {
2653
- const dpr = 2;
2654
- // 先隐藏所有文字以采样"纯背景"像素
2655
- await page.evaluate(() => {
2656
- document.querySelectorAll(".bar-content").forEach((el) => { el.style.visibility = "hidden"; });
2657
- });
2658
- const bgShot = await page.screenshot({ type: "png", fullPage: true });
2659
- await page.evaluate(() => {
2660
- document.querySelectorAll(".bar-content").forEach((el) => { el.style.visibility = ""; });
2661
- });
2662
- const bgDataUrl = `data:image/png;base64,${bgShot.toString("base64")}`;
2663
- const luminances = await page.evaluate(async (dataUrl) => {
2664
- const img = new Image();
2665
- img.src = dataUrl;
2666
- await img.decode();
2667
- const canvas = document.createElement("canvas");
2668
- canvas.width = img.naturalWidth;
2669
- canvas.height = img.naturalHeight;
2670
- const ctx = canvas.getContext("2d");
2671
- ctx.drawImage(img, 0, 0);
2672
- const scale = window.devicePixelRatio || 1;
2673
- const out = [];
2674
- document.querySelectorAll(".row-count").forEach((el) => {
2675
- const r = el.getBoundingClientRect();
2676
- const x = Math.max(0, Math.min(Math.round((r.left + r.width / 2) * scale), canvas.width - 1));
2677
- const y = Math.max(0, Math.min(Math.round((r.top + r.height / 2) * scale), canvas.height - 1));
2678
- const d = ctx.getImageData(x, y, 1, 1).data;
2679
- out.push(0.2126 * d[0] + 0.7152 * d[1] + 0.0722 * d[2]);
2680
- });
2681
- return out;
2682
- }, bgDataUrl);
2683
- await page.evaluate((lums) => {
2684
- const nodes = document.querySelectorAll(".row-count");
2685
- nodes.forEach((el, i) => {
2686
- const lum = lums[i];
2687
- if (lum !== undefined && lum < 120) {
2688
- el.style.color = "#FFFFFF";
2689
- }
2690
- });
2691
- }, luminances);
2692
- } catch (sampleError) {
2693
- logger.warn("背景亮度采样失败,使用默认数字颜色:", sampleError?.message);
2694
- }
2695
2554
  // 背景(自定义 CSS/API 背景)是加在 html 上的,必须截整页才能把卡片外的背景一起截进来;
2696
2555
  // body 高度由内容撑开(无 min-height),所以 fullPage 不会产生多余空白。
2697
2556
  const buf = await page.screenshot({ type: "png", fullPage: true });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ll-mc",
3
3
  "description": "消息数量统计插件(基于 koishi-plugin-message-counter 重做,官方QQ机器人兼容)",
4
- "version": "3.1.9",
4
+ "version": "3.1.11",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [