koishi-plugin-ll-mc 3.1.14 → 3.1.16

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.
Binary file
package/lib/index.js CHANGED
@@ -61,8 +61,8 @@ var usage = `## 📝 使用说明
61
61
  详细选项请发送 \`help messageCounter\` 查看。`;
62
62
  var logger = new import_koishi.Logger("messageCounter");
63
63
  var FONT_OPTIONS = {
64
- TITLE: "NotoSans-Regular",
65
- NICKNAME: "NotoSans-Regular"
64
+ TITLE: "Microsoft YaHei",
65
+ NICKNAME: "Microsoft YaHei"
66
66
  };
67
67
  var Config = import_koishi.Schema.intersect([
68
68
  // --- 核心功能 ---
@@ -77,6 +77,9 @@ var Config = import_koishi.Schema.intersect([
77
77
  defaultMaxDisplayCount: import_koishi.Schema.number().min(0).default(20).description("排行榜默认显示的人数。设置为 0 则显示所有。"),
78
78
  isTimeInfoSupplementEnabled: import_koishi.Schema.boolean().default(true).description("是否在排行榜标题中显示生成时间。"),
79
79
  isUserMessagePercentageVisible: import_koishi.Schema.boolean().default(false).description("是否在排行榜中显示用户的消息数占比。"),
80
+ appendRequesterToLeaderboard: import_koishi.Schema.boolean().default(true).description(
81
+ "发指令的人如果没排进前 N 名,是否仍把他追加到榜单末尾(并标 ★)。关闭后只有真正排进前 N 名才会出现。"
82
+ ),
80
83
  hiddenUserIdsInLeaderboard: import_koishi.Schema.array(String).role("table").description("全局隐藏的用户 ID 列表,在所有用户排行榜中生效。"),
81
84
  hiddenChannelIdsInLeaderboard: import_koishi.Schema.array(String).role("table").description("全局隐藏的频道 ID 列表,在群排行榜中生效。")
82
85
  }).description("排行榜设置"),
@@ -283,7 +286,7 @@ async function apply(ctx, config) {
283
286
  } catch (error) {
284
287
  logger.warn("无法加载 fallbackBase64.json,将使用空的回退头像。", error);
285
288
  }
286
- const fontFiles = ["NotoSans-Regular.ttf", "NotoEmoji.ttf"];
289
+ const fontFiles = ["NotoEmoji.woff2"];
287
290
  for (const fontFile of fontFiles) {
288
291
  await copyAssetIfNotExists(
289
292
  import_path.default.join(assetsDir, "fonts"),
@@ -291,29 +294,41 @@ async function apply(ctx, config) {
291
294
  fontFile
292
295
  );
293
296
  }
294
- // 官机补丁:强制覆盖 NotoEmoji 字体(纯 emoji 版),
295
- // 防止旧版本(带数字字形)缓存在 data/messageCounter/fonts/ 里导致数字被加宽
297
+ // 官机补丁:强制覆盖 emoji 字体,并清掉旧版本残留(NotoEmoji.ttf / patched / 坏掉的 HarmonyOS)
296
298
  try {
297
- const emojiSrc = import_path.default.join(assetsDir, "fonts", "NotoEmoji.ttf");
298
- const emojiDest = import_path.default.join(fontsPath, "NotoEmoji.ttf");
299
+ const emojiSrc = import_path.default.join(assetsDir, "fonts", "NotoEmoji.woff2");
300
+ const emojiDest = import_path.default.join(fontsPath, "NotoEmoji.woff2");
299
301
  await fs.copyFile(emojiSrc, emojiDest);
300
- // 删除旧版补丁字体(避免旧版带数字字形的 patched 文件被继续加载、家族名冲突)
301
- const patched = import_path.default.join(fontsPath, "NotoEmoji-patched.ttf");
302
- await fs.rm(patched, { force: true });
303
- logger.info("[ll-mc] 已强制更新 NotoEmoji 字体为纯 emoji 版,并清理旧补丁字体。");
302
+ for (const stale of [
303
+ "NotoEmoji.ttf",
304
+ "NotoEmoji-patched.ttf",
305
+ "NotoSans-Regular.ttf",
306
+ "HarmonyOS_Sans_Medium.ttf"
307
+ ]) {
308
+ await fs.rm(import_path.default.join(fontsPath, stale), { force: true });
309
+ }
310
+ logger.info("[ll-mc] 已更新内置 emoji 字体(woff2)并清理旧字体残留。");
304
311
  } catch (error) {
305
- logger.warn("[ll-mc] 强制更新 NotoEmoji 字体失败:", error?.message);
312
+ logger.warn("[ll-mc] 更新内置 emoji 字体失败:", error?.message);
306
313
  }
307
- // 官机补丁(HTML 模板):系统装了 emoji 字体(如 font-noto-emoji)时,emoji 字体自带 0-9 字形
308
- // (给 keycap 用)且字宽很宽,会导致所有数字被撑成 "2 9 1 7"。
309
- // 这里内嵌一个只含 0-9 的极小正常字体(约 3KB),用 unicode-range 把数字锁死在它上面;
310
- // 用 base64 data URI 内嵌(而非相对路径),因为 file:// 页面加载 @font-face 本地文件不可靠。
311
- let digitsFontFaceCss = "";
314
+ // 官机补丁(HTML 模板):系统装了 emoji 字体(如 font-noto-emoji)后,emoji 字体自带 0-9 与空格字形
315
+ // (给 keycap 用)且字宽极宽,会把数字撑成 "2 9 1 7"、把空格撑近一个字宽。
316
+ // 1) 内嵌一个只含【空格 + 0-9】的极小正常字体(约 3KB),用 unicode-range 把这些字符锁死在它上面;
317
+ // 2) emoji 本身也内嵌(woff2,约 1.9MB),因为系统 emoji 字体两侧各留约 0.19em 空白,
318
+ // 正是"emoji 两边像有空格"的来源,而内置这个墨迹几乎填满字身。
319
+ // 全部用 base64 data URI 内嵌,不依赖 file:// 相对路径,goto/setContent 两条路径都生效。
320
+ let embeddedFontFacesCss = "";
312
321
  try {
313
- const digitsBuf = await fs.readFile(import_path.default.join(assetsDir, "fonts", "digits.ttf"));
314
- digitsFontFaceCss = `@font-face { font-family: 'LLMCDigits'; src: url("data:font/ttf;base64,${digitsBuf.toString("base64")}") format('truetype'); unicode-range: U+0030-0039; }`;
322
+ const lockBuf = await fs.readFile(import_path.default.join(assetsDir, "fonts", "lock.ttf"));
323
+ embeddedFontFacesCss += `@font-face { font-family: 'LLMCDigits'; src: url("data:font/ttf;base64,${lockBuf.toString("base64")}") format('truetype'); unicode-range: U+0020, U+0030-0039; }\n`;
315
324
  } catch (error) {
316
- logger.warn("[ll-mc] 加载 digits.ttf 失败,模板数字可能被 emoji 字体撑宽:", error?.message);
325
+ logger.warn("[ll-mc] 加载 lock.ttf 失败,模板数字/空格可能被 emoji 字体撑宽:", error?.message);
326
+ }
327
+ try {
328
+ const emojiBuf = await fs.readFile(import_path.default.join(assetsDir, "fonts", "NotoEmoji.woff2"));
329
+ embeddedFontFacesCss += `@font-face { font-family: 'LLMCEmoji'; src: url("data:font/woff2;base64,${emojiBuf.toString("base64")}") format('woff2'); }`;
330
+ } catch (error) {
331
+ logger.warn("[ll-mc] 加载 NotoEmoji.woff2 失败,模板 emoji 将回退到系统字体:", error?.message);
317
332
  }
318
333
  const avatarCache = /* @__PURE__ */ new Map();
319
334
  let iconCache = [];
@@ -1793,7 +1808,7 @@ ${targetUserRecord[0].username}
1793
1808
  let context = canvas.getContext('2d');
1794
1809
 
1795
1810
  // 根据最大计数的文本宽度动态调整画布宽度,以防数字溢出
1796
- context.font = \`30px "\${config.chartNicknameFont}", NotoSans-Regular, "Microsoft YaHei", sans-serif, "NotoEmoji"\`;
1811
+ context.font = \`30px "\${config.chartNicknameFont}", "Microsoft YaHei", sans-serif, "NotoEmoji"\`;
1797
1812
  // 找到拥有最大发言数的条目,因为它的文本通常最长
1798
1813
  const maxCountData = rankingData.find(d => d.count === maxCount) || rankingData[0] || { count: 1, percentage: 0 };
1799
1814
  let maxCountText = maxCount.toString();
@@ -1877,7 +1892,7 @@ ${targetUserRecord[0].username}
1877
1892
 
1878
1893
  async function drawTextAndIcons(context, data, index, avgColor, barX, barY, barWidth, barHeight) {
1879
1894
  // 字体栈包含了用户选择的字体、插件内置字体和通用字体,以确保兼容性。
1880
- context.font = \`30px "\${config.chartNicknameFont}", NotoSans-Regular, "Microsoft YaHei", sans-serif, "NotoEmoji"\`;
1895
+ context.font = \`30px "\${config.chartNicknameFont}", "Microsoft YaHei", sans-serif, "NotoEmoji"\`;
1881
1896
  const textY = barY + barHeight / 2 + 10.5;
1882
1897
 
1883
1898
 
@@ -2400,7 +2415,8 @@ ${targetUserRecord[0].username}
2400
2415
  function prepareRankingData(sortedUsers, userInfo, totalCount, limit, requesterId) {
2401
2416
  const topUsers = sortedUsers.slice(0, limit);
2402
2417
  const isRequesterInTop = requesterId && topUsers.some(([userId]) => userId === requesterId);
2403
- if (requesterId && !isRequesterInTop) {
2418
+ // 官机补丁:发指令的人没进前 N 时是否仍追加。由配置 appendRequesterToLeaderboard 控制(默认开,保持原行为)。
2419
+ if (requesterId && !isRequesterInTop && config.appendRequesterToLeaderboard !== false) {
2404
2420
  const requesterData = sortedUsers.find(
2405
2421
  ([userId]) => userId === requesterId
2406
2422
  );
@@ -2482,12 +2498,10 @@ ${targetUserRecord[0].username}
2482
2498
  <meta charset="UTF-8">
2483
2499
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
2484
2500
  <style>
2485
- /* 数字:内嵌极小(约3KB)的纯 0-9 字体并锁死,避免被系统 emoji 字体(自带 0-9 且字宽极宽)撑成 "2 9 1 7"。
2486
- 用 base64 内嵌,保证 setContent 回退路径下也生效。 */
2487
- ${digitsFontFaceCss}
2488
- /* emoji:用内置的紧身 emoji 字体(墨迹几乎填满字身),而系统 emoji 字体两侧各留约 0.19em 空白,
2489
- 那正是"emoji 两边像有空格"的来源。相对路径 fonts/ 由 file:// 页面解析(模板已写入该目录)。 */
2490
- @font-face { font-family: 'LLMCEmoji'; src: url("fonts/NotoEmoji.ttf") format('truetype'); }
2501
+ /* 两个字体都以 base64 内嵌(见 embeddedFontFacesCss):
2502
+ - LLMCDigits:只含【空格 + 0-9】,用 unicode-range 把这两个易被 emoji 字体抢走、且字宽被撑宽的字符锁回正常文本字体;
2503
+ - LLMCEmoji :内置 emoji 字体,墨迹几乎填满字身(系统 emoji 字体两侧各留约 0.19em 空白,即"emoji 两边像有空格")。 */
2504
+ ${embeddedFontFacesCss}
2491
2505
  * { box-sizing: border-box; margin: 0; padding: 0; }
2492
2506
  html, body {
2493
2507
  width: 900px;
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.14",
4
+ "version": "3.1.16",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
Binary file
Binary file