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
|
|
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: "
|
|
65
|
-
NICKNAME: "
|
|
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 = ["
|
|
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
|
|
295
|
-
// 防止旧版本(带数字字形)缓存在 data/messageCounter/fonts/ 里导致数字被加宽
|
|
297
|
+
// 官机补丁:强制覆盖 emoji 字体,并清掉旧版本残留(NotoEmoji.ttf / patched / 坏掉的 HarmonyOS)
|
|
296
298
|
try {
|
|
297
|
-
const emojiSrc = import_path.default.join(assetsDir, "fonts", "NotoEmoji.
|
|
298
|
-
const emojiDest = import_path.default.join(fontsPath, "NotoEmoji.
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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]
|
|
312
|
+
logger.warn("[ll-mc] 更新内置 emoji 字体失败:", error?.message);
|
|
306
313
|
}
|
|
307
|
-
// 官机补丁(HTML 模板):系统装了 emoji 字体(如 font-noto-emoji)
|
|
308
|
-
// (给 keycap 用)
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
|
|
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
|
|
314
|
-
|
|
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] 加载
|
|
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}",
|
|
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}",
|
|
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
|
-
|
|
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
|
-
/*
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
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
|
Binary file
|
|
Binary file
|