koishi-plugin-dew-bot 1.0.257 → 1.0.259
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/index.js +42 -21
- package/lib/plugin/characterCardStyles.d.ts +1 -1
- package/lib/plugin/server.d.ts +2 -1
- package/package.json +1 -1
- package/src/plugin/characterCardStyles.ts +1 -1
- package/src/plugin/characterCardTemplate.ts +2 -2
- package/src/plugin/server.ts +16 -1
- package/src/plugin/serverHandel/qqPageMessage.ts +4 -2
- package/src/plugin/skillCard.ts +23 -14
package/lib/index.js
CHANGED
|
@@ -319,7 +319,7 @@ var characterCardStyles = `
|
|
|
319
319
|
.character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}
|
|
320
320
|
.character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 "Microsoft YaHei",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}
|
|
321
321
|
.character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.combat-row,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}
|
|
322
|
-
.character-identity{text-align:center;min-width:0}.title-image-slot{display:flex;align-items:center;justify-content:center;min-width:0;
|
|
322
|
+
.character-identity{text-align:center;min-width:0}.title-image-slot{position:absolute;inset:12px;display:flex;align-items:center;justify-content:center;min-width:0;z-index:4;pointer-events:none}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:min(110px,100%);object-fit:contain;filter:drop-shadow(0 2px 3px #17213a70)}.title-artwork[alt="称号图片占位"]{max-width:min(244px,100%);max-height:min(44px,100%)}
|
|
323
323
|
.character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,"Microsoft YaHei",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}
|
|
324
324
|
.gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}
|
|
325
325
|
.gene-panel:before,.gene-panel:after{content:"";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}
|
|
@@ -440,8 +440,8 @@ function characterCardHtml(card, artwork, _frame, defaultArtwork, titleArtwork,
|
|
|
440
440
|
const experience = Math.max(0, Math.min(100, finite(card.experiencePercent)));
|
|
441
441
|
const geneRank = card.geneLockRankName || "基因锁未开启";
|
|
442
442
|
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>${characterCardStyles}</style></head><body><main id="capture" class="companion-sheet character-adventure character-sheet gene-tier-${tier}" data-gene-lock-tier="${tier}" data-decoration-level="${tier}"><span class="character-ambient" aria-hidden="true">${characterFloralWatermark}</span><div class="character-card companion-main">
|
|
443
|
-
<div class="portrait-column"><div class="companion-panel companion-portrait character-portrait">${corners(tier)}<div class="portrait-window portrait">${portrait ? `<img id="character-image" class="character-artwork" src="${cardEscape(portrait)}" data-fallback="${cardEscape(defaultArtwork ?? "")}" alt="${cardEscape(card.name)}的形象图">` : '<div class="placeholder"><span>形象尚未生成</span><small>角色形象</small></div>'}</div
|
|
444
|
-
<div class="title-image-slot character-title-
|
|
443
|
+
<div class="portrait-column"><div class="companion-panel companion-portrait character-portrait">${corners(tier)}<div class="portrait-window portrait">${portrait ? `<img id="character-image" class="character-artwork" src="${cardEscape(portrait)}" data-fallback="${cardEscape(defaultArtwork ?? "")}" alt="${cardEscape(card.name)}的形象图">` : '<div class="placeholder"><span>形象尚未生成</span><small>角色形象</small></div>'}</div>
|
|
444
|
+
<div class="title-image-slot character-title-centered"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? "称号图片" : "称号图片占位"}"></div></div></div>
|
|
445
445
|
<section class="companion-panel companion-stats character-stats" aria-label="角色属性"><span class="character-paper-pattern" aria-hidden="true">${characterFloralWatermark}</span>${corners(tier, ["tr", "br"])}<div class="identity character-identity"><h1 id="character-name" class="character-name" title="${cardEscape(card.name)}">${cardEscape(card.name)}</h1><p class="level character-level"><span>Lv.</span><strong id="character-level">${number2(card.level)}</strong></p><div class="gene-panel"><span class="gene-crest" aria-hidden="true">${characterGeneCrest(tier)}</span><div id="gene-rank" class="gene-rank" title="${cardEscape(geneRank)}">${cardEscape(geneRank)}</div></div></div>
|
|
446
446
|
<div class="combat-row companion-power-row"><span class="power-emblem power-icon" aria-hidden="true"><svg viewBox="0 0 42 42"><path d="m9 5 5 1 16 21-4 4L9 11Zm24 0-5 1L12 27l4 4L33 11ZM14 27l14 10m0-10L14 37M9 35l4-4m20 4-4-4"/></svg></span><span class="power-label companion-power-label">战力</span><strong id="combat-power" class="power-value companion-power">${number2(card.power)}</strong></div>
|
|
447
447
|
<div class="resource companion-resource hp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="M16 28C6 21 2 17 2 10a7 7 0 0 1 14-2 7 7 0 0 1 14 2c0 7-4 11-14 18Z"/></svg>生命值</span><span id="hp-numbers" class="resource-number">${number2(card.hp)} / ${number2(card.maxHp)}</span></div><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${hpPercent}"><span class="resource-fill fill" style="width:${hpPercent}%"></span><span class="bar-percent resource-percent">${number2(hpPercent)}%</span></div></div>
|
|
@@ -1422,6 +1422,7 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
|
|
|
1422
1422
|
let timeout;
|
|
1423
1423
|
let workPromise;
|
|
1424
1424
|
let expired = false;
|
|
1425
|
+
let finished = false;
|
|
1425
1426
|
const release = /* @__PURE__ */ __name(() => {
|
|
1426
1427
|
const next = waiting.shift();
|
|
1427
1428
|
if (next) next();
|
|
@@ -1429,12 +1430,26 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
|
|
|
1429
1430
|
}, "release");
|
|
1430
1431
|
try {
|
|
1431
1432
|
const work = /* @__PURE__ */ __name(async () => {
|
|
1432
|
-
const entryArtworkPromise = loadCardEntryArtwork(!titleCard && !bloodlineCard && !equipmentCard ? companionCard : void 0);
|
|
1433
1433
|
const remoteAddress = titleCard ? titleCard.artworkUrl : bloodlineCard ? bloodlineCard.artworkUrl : equipmentCard ? equipmentCard.artworkUrl : companionCard ? companionCard.artworkUrl : characterCard ? characterCard.artworkUrl : itemCard ? itemCard.artworkUrl : leaderboard ? leaderboard.backgroundUrl : card?.artworkUrl;
|
|
1434
|
-
const
|
|
1435
|
-
const
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1434
|
+
const isEquipment = !titleCard && !bloodlineCard && Boolean(equipmentCard);
|
|
1435
|
+
const isCharacter = !titleCard && !bloodlineCard && !equipmentCard && !companionCard && Boolean(characterCard);
|
|
1436
|
+
const [remoteArt, fallbackArt, entryArtwork, characterAssets, header] = await Promise.all([
|
|
1437
|
+
loadSkillArtwork(remoteAddress),
|
|
1438
|
+
isEquipment ? equipmentFallback() : void 0,
|
|
1439
|
+
loadCardEntryArtwork(!titleCard && !bloodlineCard && !equipmentCard ? companionCard : void 0),
|
|
1440
|
+
isCharacter ? Promise.all([characterAsset(), loadSkillArtwork(characterCard.titleImageUrl)]) : void 0,
|
|
1441
|
+
!titleCard && !bloodlineCard && !equipmentCard && !companionCard && !characterCard && !itemCard && leaderboard ? leaderboardHeader() : void 0,
|
|
1442
|
+
ctx.puppeteer.page().then(async (created) => {
|
|
1443
|
+
if (expired || finished) {
|
|
1444
|
+
await created.close();
|
|
1445
|
+
throw new Error(expired ? "绘图超时" : "绘图已结束");
|
|
1446
|
+
}
|
|
1447
|
+
page = created;
|
|
1448
|
+
})
|
|
1449
|
+
]);
|
|
1450
|
+
const dedicatedArt = isEquipment ? remoteArt : void 0;
|
|
1451
|
+
const unknownArt = isEquipment && !dedicatedArt ? fallbackArt : void 0;
|
|
1452
|
+
const art = isEquipment ? dedicatedArt ?? unknownArt : remoteArt;
|
|
1438
1453
|
let html;
|
|
1439
1454
|
let loadedCharacterTitle;
|
|
1440
1455
|
let localAssetsReady = true;
|
|
@@ -1448,27 +1463,19 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
|
|
|
1448
1463
|
} else if (companionCard) {
|
|
1449
1464
|
html = companionCardHtml(companionCard, art, entryArtwork.icons);
|
|
1450
1465
|
} else if (characterCard) {
|
|
1451
|
-
const [defaultArtwork, titleArtwork] =
|
|
1452
|
-
characterAsset(),
|
|
1453
|
-
loadSkillArtwork(characterCard.titleImageUrl)
|
|
1454
|
-
]);
|
|
1466
|
+
const [defaultArtwork, titleArtwork] = characterAssets;
|
|
1455
1467
|
loadedCharacterTitle = titleArtwork;
|
|
1456
1468
|
localAssetsReady = Boolean(art || defaultArtwork) && (!characterCard.titleImageUrl || Boolean(titleArtwork));
|
|
1457
1469
|
html = characterCardHtml(characterCard, art, void 0, defaultArtwork, titleArtwork, entryArtwork.icons);
|
|
1458
1470
|
} else if (itemCard) {
|
|
1459
1471
|
html = itemCard.type === "形象卡" ? appearanceCardHtml(itemCard, art) : itemCardHtml(itemCard, art);
|
|
1460
1472
|
} else if (leaderboard) {
|
|
1461
|
-
html = leaderboardHtml(leaderboard,
|
|
1473
|
+
html = leaderboardHtml(leaderboard, header, art);
|
|
1462
1474
|
} else {
|
|
1463
1475
|
html = skillCardHtml(card, "", art);
|
|
1464
1476
|
}
|
|
1465
1477
|
if (expired) throw new Error("绘图超时");
|
|
1466
|
-
page
|
|
1467
|
-
if (expired) {
|
|
1468
|
-
await page.close();
|
|
1469
|
-
page = void 0;
|
|
1470
|
-
throw new Error("绘图超时");
|
|
1471
|
-
}
|
|
1478
|
+
if (!page) throw new Error("卡片页面缺失");
|
|
1472
1479
|
await page.setViewport({ width: 760, height: 1107, deviceScaleFactor: titleCard || (companionCard || characterCard) && !bloodlineCard && !equipmentCard ? 2 : 1 });
|
|
1473
1480
|
await page.setJavaScriptEnabled(false);
|
|
1474
1481
|
await page.setRequestInterception(true);
|
|
@@ -1518,6 +1525,7 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
|
|
|
1518
1525
|
}, 15e3);
|
|
1519
1526
|
})]);
|
|
1520
1527
|
} finally {
|
|
1528
|
+
finished = true;
|
|
1521
1529
|
if (timeout) clearTimeout(timeout);
|
|
1522
1530
|
try {
|
|
1523
1531
|
await page?.close();
|
|
@@ -1851,7 +1859,7 @@ function renderQQInventoryMarkdown(data, showGreeting) {
|
|
|
1851
1859
|
const sections = [];
|
|
1852
1860
|
const items = [];
|
|
1853
1861
|
const flushItems = /* @__PURE__ */ __name(() => {
|
|
1854
|
-
if (items.length) sections.push(items.splice(0)
|
|
1862
|
+
if (items.length) sections.push(...items.splice(0));
|
|
1855
1863
|
}, "flushItems");
|
|
1856
1864
|
if (showGreeting) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
|
|
1857
1865
|
for (const [lineIndex, item] of data.line.entries()) {
|
|
@@ -4455,6 +4463,7 @@ var Server = class {
|
|
|
4455
4463
|
ctx;
|
|
4456
4464
|
onConnectedCallback;
|
|
4457
4465
|
messageListenerSetup = false;
|
|
4466
|
+
lastUserMessageAt = /* @__PURE__ */ new Map();
|
|
4458
4467
|
_init = false;
|
|
4459
4468
|
async init(ctx, config) {
|
|
4460
4469
|
this.ctx = ctx;
|
|
@@ -4464,6 +4473,12 @@ var Server = class {
|
|
|
4464
4473
|
if (this.wsClient) {
|
|
4465
4474
|
await this.dispose();
|
|
4466
4475
|
}
|
|
4476
|
+
ctx.setInterval(() => {
|
|
4477
|
+
const now = performance.now();
|
|
4478
|
+
for (const [key, lastMessageAt] of this.lastUserMessageAt) {
|
|
4479
|
+
if (now - lastMessageAt > 500) this.lastUserMessageAt.delete(key);
|
|
4480
|
+
}
|
|
4481
|
+
}, 6e4);
|
|
4467
4482
|
const serverUrl = config.服务器地址;
|
|
4468
4483
|
if (!serverUrl || serverUrl.trim() === "") {
|
|
4469
4484
|
throw new Error("服务器地址不能为空");
|
|
@@ -4481,6 +4496,7 @@ var Server = class {
|
|
|
4481
4496
|
}
|
|
4482
4497
|
async dispose() {
|
|
4483
4498
|
this._init = false;
|
|
4499
|
+
this.lastUserMessageAt.clear();
|
|
4484
4500
|
if (this.wsClient) {
|
|
4485
4501
|
this.wsClient.unlistenMsgAll(/.*/);
|
|
4486
4502
|
this.wsClient.disconnect();
|
|
@@ -4556,7 +4572,7 @@ var Server = class {
|
|
|
4556
4572
|
}
|
|
4557
4573
|
this.ctx.logger.warn("请求出错", apiName, req.err.message);
|
|
4558
4574
|
}
|
|
4559
|
-
/**
|
|
4575
|
+
/** 过滤机器人发送者和用户连续消息后,缓存会话并把原始内容交给游戏服。 **/
|
|
4560
4576
|
uploadMessage(session) {
|
|
4561
4577
|
if (isBotMessage(session, this.ctx.bots)) return;
|
|
4562
4578
|
const userId = session.userId;
|
|
@@ -4565,6 +4581,11 @@ var Server = class {
|
|
|
4565
4581
|
this.ctx.logger.warn("消息缺少 userId 或 channelId,已忽略");
|
|
4566
4582
|
return;
|
|
4567
4583
|
}
|
|
4584
|
+
const userKey = JSON.stringify([session.platform ?? session.bot.platform, userId]);
|
|
4585
|
+
const now = performance.now();
|
|
4586
|
+
const lastMessageAt = this.lastUserMessageAt.get(userKey);
|
|
4587
|
+
this.lastUserMessageAt.set(userKey, now);
|
|
4588
|
+
if (lastMessageAt !== void 0 && now - lastMessageAt <= 500) return;
|
|
4568
4589
|
sessions_default.set(sessionKey(session.bot.userId, userId, channelId), session);
|
|
4569
4590
|
this.api("Message", {
|
|
4570
4591
|
bot: {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Character-only moon-white paper, metallic frames and six botanical gene-lock themes. */
|
|
2
|
-
export declare const characterCardStyles = "\n*{box-sizing:border-box}body{margin:0;width:760px}#capture{width:760px;position:relative;overflow:hidden;font-family:\"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif}\n.character-sheet{--ink:#374556;--subtle:#748192;--accent:#9b927d;--edge:#aab4bd;--light-edge:#fffefa;--metal-shadow:#8f9ba8;--panel:#fffefa;--panel-deep:#f0f3f5;--paper:#e8edf0;--flower:#fffefa;--leaf:#9aaabb;--leaf-light:#e2e9ed;--gem:#bbc9d4;--track:#e2e7dd;--track-edge:#afbea9;--health:#759569;--health-light:#b4cea2;--radar:#8995ae;--radar-fill:#aebbdc66;--radar-grid:#c4cbd7;--background:#dce3e9;--background-deep:#bac7d3;--ornament-size:86px;--portrait-ornament-size:74px;--frame-radius:27px;--panel-border:2px;padding:22px;color:var(--ink);background:radial-gradient(ellipse at 12% 6%,#fffefaef,transparent 51%),radial-gradient(ellipse at 85% 90%,#f6f8ffc4,transparent 51%),radial-gradient(ellipse at 78% 12%,#ffffff80,transparent 38%),linear-gradient(145deg,var(--background-deep),var(--background) 48%,var(--background-deep))}\n.gene-tier-0{--ink:#435162;--subtle:#7c8795;--accent:#989081;--edge:#b3bac1;--leaf:#9caab8;--leaf-light:#e4e9ef;--gem:#cbd3de;--background:#e3e8ed;--background-deep:#c6cfd9}\n.gene-tier-1{--ink:#365762;--subtle:#718b90;--accent:#b29d77;--edge:#bbc1b2;--metal-shadow:#949f9f;--panel-deep:#f0f6f3;--paper:#e7f0ec;--leaf:#81a6ad;--leaf-light:#d5e9e6;--gem:#a8d4cf;--radar:#7e9baf;--radar-fill:#b6d2dd69;--radar-grid:#bfced5;--background:#e5f0ed;--background-deep:#bfd6d5;--ornament-size:98px;--portrait-ornament-size:84px}\n.gene-tier-2{--ink:#354c69;--subtle:#72869f;--accent:#afa187;--edge:#b4bdcc;--metal-shadow:#8d9bb0;--panel-deep:#eef3f9;--paper:#e7edf6;--leaf:#829bbd;--leaf-light:#d5e1f1;--gem:#aac6e5;--radar:#8596c1;--radar-fill:#aebcde70;--radar-grid:#c1c9db;--background:#e2e9f5;--background-deep:#bccbdf;--ornament-size:110px;--portrait-ornament-size:94px}\n.gene-tier-3{--ink:#494867;--subtle:#827e9c;--accent:#b3a18b;--edge:#bdb7ca;--metal-shadow:#9992ad;--panel-deep:#f2f0f9;--paper:#ede8f6;--leaf:#9995c1;--leaf-light:#e1def2;--gem:#c0b5e6;--radar:#9b91c5;--radar-fill:#c1b7e16e;--radar-grid:#cdc5dd;--background:#eae6f4;--background-deep:#c9c1df;--ornament-size:123px;--portrait-ornament-size:104px}\n.gene-tier-4{--ink:#644b66;--subtle:#9a7f96;--accent:#b89880;--edge:#c8bac5;--metal-shadow:#ad97a6;--panel-deep:#f7eff4;--paper:#f2e8ee;--leaf:#b797b4;--leaf-light:#f0dce8;--gem:#d5b0ca;--radar:#b498bd;--radar-fill:#d2bddf6b;--radar-grid:#d9c9dc;--background:#f0e6ee;--background-deep:#dbc3d4;--ornament-size:137px;--portrait-ornament-size:115px}\n.gene-tier-5{--ink:#3c416e;--subtle:#797fa1;--accent:#b6995f;--edge:#c2b48f;--metal-shadow:#9c976f;--panel-deep:#f0effa;--paper:#e8e9f5;--leaf:#858dbf;--leaf-light:#d7ddf4;--gem:#b4b6e4;--radar:#9087c8;--radar-fill:#b8b0e476;--radar-grid:#c8c6dd;--background:#e4e5f5;--background-deep:#bdc5e1;--ornament-size:149px;--portrait-ornament-size:126px}\n.character-ambient,.character-paper-pattern{position:absolute;inset:0;pointer-events:none;overflow:hidden;color:var(--leaf)}.character-ambient{opacity:.23}.character-ambient .floral-watermark{width:100%;height:100%;display:block}.character-paper-pattern{opacity:.075;border-radius:25px}.character-paper-pattern .floral-watermark{width:100%;height:100%;display:block}\n.character-card{display:grid;grid-template-columns:344px minmax(0,1fr);gap:16px;align-items:stretch;position:relative;z-index:1}.portrait-column{min-width:0;display:flex;flex-direction:column;justify-content:center}\n.character-sheet .companion-panel{position:relative;min-width:0;border:2px solid var(--edge);border-radius:var(--frame-radius);background:radial-gradient(ellipse at 15% 6%,#ffffffbd,transparent 49%),linear-gradient(155deg,var(--panel),var(--panel-deep));box-shadow:inset 0 0 0 2px #ffffffb5,inset 0 0 0 3px color-mix(in srgb,var(--metal-shadow) 35%,transparent),0 2px 8px #52608026}\n.character-sheet .companion-panel:before{content:\"\";position:absolute;inset:7px;border:1px solid color-mix(in srgb,var(--edge) 65%,#ffffff);border-radius:calc(var(--frame-radius) - 7px);pointer-events:none}\n.character-sheet .companion-panel:after{content:\"\";position:absolute;inset:3px;border:1px solid #fffdf3b3;border-radius:calc(var(--frame-radius) - 2px);pointer-events:none}\n.character-sheet .panel-corner{position:absolute;width:var(--ornament-size);height:var(--ornament-size);pointer-events:none;z-index:3;color:var(--accent)}.panel-corner svg{width:100%;height:100%;display:block;overflow:visible;filter:drop-shadow(0 1px 1px #52608035)}.corner-tl{top:-13px;left:-13px}.corner-tr{top:-13px;right:-13px;transform:rotate(90deg)}.corner-bl{bottom:-13px;left:-13px;transform:rotate(-90deg)}.corner-br{bottom:-13px;right:-13px;transform:rotate(180deg)}\n.character-sheet .companion-portrait{padding:10px;border-radius:18px;flex:0 0 auto;background:linear-gradient(135deg,#fffefa 2%,var(--edge) 18%,#ffffff 30%,var(--accent) 41%,#fffdf2 51%,var(--edge) 71%,#ffffff 82%,var(--metal-shadow) 100%);box-shadow:inset 0 0 0 1px #ffffffd9,0 3px 9px #51628038}\n.character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}\n.character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 \"Microsoft YaHei\",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}\n.character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.combat-row,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}\n.character-identity{text-align:center;min-width:0}.title-image-slot{display:flex;align-items:center;justify-content:center;min-width:0;margin:9px 10px 0}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:110px;object-fit:contain}.title-artwork[alt=\"\u79F0\u53F7\u56FE\u7247\u5360\u4F4D\"]{max-width:244px;max-height:44px}\n.character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,\"Microsoft YaHei\",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}\n.gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}\n.gene-panel:before,.gene-panel:after{content:\"\";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}\n.combat-row{display:flex;align-items:center;gap:8px;margin:14px 0 12px;min-width:0}.power-icon{flex:0 0 42px;height:42px;display:grid;place-items:center;border-radius:50%;border:1px solid var(--accent);box-shadow:inset 0 0 0 3px #fffef3b5;color:var(--accent);background:radial-gradient(ellipse at 38% 25%,#fffdf0,color-mix(in srgb,var(--accent) 14%,var(--panel)))}.power-icon svg{width:27px;height:27px;stroke:currentColor;fill:currentColor;stroke-width:2;stroke-linejoin:round}.power-label{font-size:21px;white-space:nowrap}.power-value{min-width:0;flex:1;text-align:right;overflow-wrap:anywhere;font:35px/1.2 Georgia,\"Microsoft YaHei\",serif;color:var(--accent);letter-spacing:-1px}\n.companion-resource{font-family:\"Microsoft YaHei\",sans-serif;margin-bottom:12px}.resource-heading{display:flex;justify-content:space-between;align-items:baseline;gap:8px;font-size:17px;color:var(--subtle);line-height:1.35}.resource-label{display:flex;align-items:center;gap:6px;font-weight:600;flex-shrink:0}.resource-label svg{width:18px;height:18px;fill:var(--health)}.resource-number{text-align:right;overflow-wrap:anywhere;min-width:0;font-size:15px}\n.resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 \"Microsoft YaHei\",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource .resource-heading{color:var(--subtle)}.exp-resource .resource-label svg{fill:var(--radar)}.exp-resource .resource-track{height:16px;border-color:color-mix(in srgb,var(--radar) 60%,#fff);background:var(--paper)}.exp-resource .resource-fill{background:linear-gradient(var(--leaf-light),var(--radar) 56%,var(--leaf))}\n.companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.ornament-heading{display:flex;align-items:center;justify-content:center;gap:7px;margin:0 0 3px;font-size:22px;font-weight:700;letter-spacing:2px;line-height:1.3;color:var(--ink)}.ornament-heading:before,.ornament-heading:after{content:\"\";height:1px;flex:1;background:linear-gradient(90deg,transparent,var(--edge))}.ornament-heading:after{transform:rotate(180deg)}.heading-star{font-size:12px;color:var(--accent);letter-spacing:0}.companion-radar{width:100%;height:260px;display:block;overflow:visible}\n.radar-grid{fill:none;stroke:var(--radar-grid);stroke-width:1.1}.radar-axis{stroke:var(--radar-grid);stroke-width:1;opacity:.8}.radar-area{fill:var(--radar-fill);stroke:var(--radar);stroke-width:2.3;stroke-linejoin:round}.radar-dot{fill:#fffefa;stroke:var(--radar);stroke-width:2}.radar-label{font:19px \"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif;fill:var(--ink)}.radar-value{font:20px Georgia,\"Microsoft YaHei\",serif;fill:var(--ink)}.radar-scale{font:11px Georgia,serif;fill:var(--subtle)}.empty-attributes{padding:45px 10px;color:var(--subtle);font:18px/1.6 \"Microsoft YaHei\",sans-serif;text-align:center}.custom-attributes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px 10px;margin:6px 0 0;font:15px/1.5 \"Microsoft YaHei\",sans-serif;color:var(--subtle)}.custom-attributes span{overflow-wrap:anywhere}.custom-attributes b{font-weight:500;color:var(--ink)}\n";
|
|
2
|
+
export declare const characterCardStyles = "\n*{box-sizing:border-box}body{margin:0;width:760px}#capture{width:760px;position:relative;overflow:hidden;font-family:\"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif}\n.character-sheet{--ink:#374556;--subtle:#748192;--accent:#9b927d;--edge:#aab4bd;--light-edge:#fffefa;--metal-shadow:#8f9ba8;--panel:#fffefa;--panel-deep:#f0f3f5;--paper:#e8edf0;--flower:#fffefa;--leaf:#9aaabb;--leaf-light:#e2e9ed;--gem:#bbc9d4;--track:#e2e7dd;--track-edge:#afbea9;--health:#759569;--health-light:#b4cea2;--radar:#8995ae;--radar-fill:#aebbdc66;--radar-grid:#c4cbd7;--background:#dce3e9;--background-deep:#bac7d3;--ornament-size:86px;--portrait-ornament-size:74px;--frame-radius:27px;--panel-border:2px;padding:22px;color:var(--ink);background:radial-gradient(ellipse at 12% 6%,#fffefaef,transparent 51%),radial-gradient(ellipse at 85% 90%,#f6f8ffc4,transparent 51%),radial-gradient(ellipse at 78% 12%,#ffffff80,transparent 38%),linear-gradient(145deg,var(--background-deep),var(--background) 48%,var(--background-deep))}\n.gene-tier-0{--ink:#435162;--subtle:#7c8795;--accent:#989081;--edge:#b3bac1;--leaf:#9caab8;--leaf-light:#e4e9ef;--gem:#cbd3de;--background:#e3e8ed;--background-deep:#c6cfd9}\n.gene-tier-1{--ink:#365762;--subtle:#718b90;--accent:#b29d77;--edge:#bbc1b2;--metal-shadow:#949f9f;--panel-deep:#f0f6f3;--paper:#e7f0ec;--leaf:#81a6ad;--leaf-light:#d5e9e6;--gem:#a8d4cf;--radar:#7e9baf;--radar-fill:#b6d2dd69;--radar-grid:#bfced5;--background:#e5f0ed;--background-deep:#bfd6d5;--ornament-size:98px;--portrait-ornament-size:84px}\n.gene-tier-2{--ink:#354c69;--subtle:#72869f;--accent:#afa187;--edge:#b4bdcc;--metal-shadow:#8d9bb0;--panel-deep:#eef3f9;--paper:#e7edf6;--leaf:#829bbd;--leaf-light:#d5e1f1;--gem:#aac6e5;--radar:#8596c1;--radar-fill:#aebcde70;--radar-grid:#c1c9db;--background:#e2e9f5;--background-deep:#bccbdf;--ornament-size:110px;--portrait-ornament-size:94px}\n.gene-tier-3{--ink:#494867;--subtle:#827e9c;--accent:#b3a18b;--edge:#bdb7ca;--metal-shadow:#9992ad;--panel-deep:#f2f0f9;--paper:#ede8f6;--leaf:#9995c1;--leaf-light:#e1def2;--gem:#c0b5e6;--radar:#9b91c5;--radar-fill:#c1b7e16e;--radar-grid:#cdc5dd;--background:#eae6f4;--background-deep:#c9c1df;--ornament-size:123px;--portrait-ornament-size:104px}\n.gene-tier-4{--ink:#644b66;--subtle:#9a7f96;--accent:#b89880;--edge:#c8bac5;--metal-shadow:#ad97a6;--panel-deep:#f7eff4;--paper:#f2e8ee;--leaf:#b797b4;--leaf-light:#f0dce8;--gem:#d5b0ca;--radar:#b498bd;--radar-fill:#d2bddf6b;--radar-grid:#d9c9dc;--background:#f0e6ee;--background-deep:#dbc3d4;--ornament-size:137px;--portrait-ornament-size:115px}\n.gene-tier-5{--ink:#3c416e;--subtle:#797fa1;--accent:#b6995f;--edge:#c2b48f;--metal-shadow:#9c976f;--panel-deep:#f0effa;--paper:#e8e9f5;--leaf:#858dbf;--leaf-light:#d7ddf4;--gem:#b4b6e4;--radar:#9087c8;--radar-fill:#b8b0e476;--radar-grid:#c8c6dd;--background:#e4e5f5;--background-deep:#bdc5e1;--ornament-size:149px;--portrait-ornament-size:126px}\n.character-ambient,.character-paper-pattern{position:absolute;inset:0;pointer-events:none;overflow:hidden;color:var(--leaf)}.character-ambient{opacity:.23}.character-ambient .floral-watermark{width:100%;height:100%;display:block}.character-paper-pattern{opacity:.075;border-radius:25px}.character-paper-pattern .floral-watermark{width:100%;height:100%;display:block}\n.character-card{display:grid;grid-template-columns:344px minmax(0,1fr);gap:16px;align-items:stretch;position:relative;z-index:1}.portrait-column{min-width:0;display:flex;flex-direction:column;justify-content:center}\n.character-sheet .companion-panel{position:relative;min-width:0;border:2px solid var(--edge);border-radius:var(--frame-radius);background:radial-gradient(ellipse at 15% 6%,#ffffffbd,transparent 49%),linear-gradient(155deg,var(--panel),var(--panel-deep));box-shadow:inset 0 0 0 2px #ffffffb5,inset 0 0 0 3px color-mix(in srgb,var(--metal-shadow) 35%,transparent),0 2px 8px #52608026}\n.character-sheet .companion-panel:before{content:\"\";position:absolute;inset:7px;border:1px solid color-mix(in srgb,var(--edge) 65%,#ffffff);border-radius:calc(var(--frame-radius) - 7px);pointer-events:none}\n.character-sheet .companion-panel:after{content:\"\";position:absolute;inset:3px;border:1px solid #fffdf3b3;border-radius:calc(var(--frame-radius) - 2px);pointer-events:none}\n.character-sheet .panel-corner{position:absolute;width:var(--ornament-size);height:var(--ornament-size);pointer-events:none;z-index:3;color:var(--accent)}.panel-corner svg{width:100%;height:100%;display:block;overflow:visible;filter:drop-shadow(0 1px 1px #52608035)}.corner-tl{top:-13px;left:-13px}.corner-tr{top:-13px;right:-13px;transform:rotate(90deg)}.corner-bl{bottom:-13px;left:-13px;transform:rotate(-90deg)}.corner-br{bottom:-13px;right:-13px;transform:rotate(180deg)}\n.character-sheet .companion-portrait{padding:10px;border-radius:18px;flex:0 0 auto;background:linear-gradient(135deg,#fffefa 2%,var(--edge) 18%,#ffffff 30%,var(--accent) 41%,#fffdf2 51%,var(--edge) 71%,#ffffff 82%,var(--metal-shadow) 100%);box-shadow:inset 0 0 0 1px #ffffffd9,0 3px 9px #51628038}\n.character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}\n.character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 \"Microsoft YaHei\",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}\n.character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.combat-row,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}\n.character-identity{text-align:center;min-width:0}.title-image-slot{position:absolute;inset:12px;display:flex;align-items:center;justify-content:center;min-width:0;z-index:4;pointer-events:none}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:min(110px,100%);object-fit:contain;filter:drop-shadow(0 2px 3px #17213a70)}.title-artwork[alt=\"\u79F0\u53F7\u56FE\u7247\u5360\u4F4D\"]{max-width:min(244px,100%);max-height:min(44px,100%)}\n.character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,\"Microsoft YaHei\",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}\n.gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}\n.gene-panel:before,.gene-panel:after{content:\"\";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}\n.combat-row{display:flex;align-items:center;gap:8px;margin:14px 0 12px;min-width:0}.power-icon{flex:0 0 42px;height:42px;display:grid;place-items:center;border-radius:50%;border:1px solid var(--accent);box-shadow:inset 0 0 0 3px #fffef3b5;color:var(--accent);background:radial-gradient(ellipse at 38% 25%,#fffdf0,color-mix(in srgb,var(--accent) 14%,var(--panel)))}.power-icon svg{width:27px;height:27px;stroke:currentColor;fill:currentColor;stroke-width:2;stroke-linejoin:round}.power-label{font-size:21px;white-space:nowrap}.power-value{min-width:0;flex:1;text-align:right;overflow-wrap:anywhere;font:35px/1.2 Georgia,\"Microsoft YaHei\",serif;color:var(--accent);letter-spacing:-1px}\n.companion-resource{font-family:\"Microsoft YaHei\",sans-serif;margin-bottom:12px}.resource-heading{display:flex;justify-content:space-between;align-items:baseline;gap:8px;font-size:17px;color:var(--subtle);line-height:1.35}.resource-label{display:flex;align-items:center;gap:6px;font-weight:600;flex-shrink:0}.resource-label svg{width:18px;height:18px;fill:var(--health)}.resource-number{text-align:right;overflow-wrap:anywhere;min-width:0;font-size:15px}\n.resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 \"Microsoft YaHei\",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource .resource-heading{color:var(--subtle)}.exp-resource .resource-label svg{fill:var(--radar)}.exp-resource .resource-track{height:16px;border-color:color-mix(in srgb,var(--radar) 60%,#fff);background:var(--paper)}.exp-resource .resource-fill{background:linear-gradient(var(--leaf-light),var(--radar) 56%,var(--leaf))}\n.companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.ornament-heading{display:flex;align-items:center;justify-content:center;gap:7px;margin:0 0 3px;font-size:22px;font-weight:700;letter-spacing:2px;line-height:1.3;color:var(--ink)}.ornament-heading:before,.ornament-heading:after{content:\"\";height:1px;flex:1;background:linear-gradient(90deg,transparent,var(--edge))}.ornament-heading:after{transform:rotate(180deg)}.heading-star{font-size:12px;color:var(--accent);letter-spacing:0}.companion-radar{width:100%;height:260px;display:block;overflow:visible}\n.radar-grid{fill:none;stroke:var(--radar-grid);stroke-width:1.1}.radar-axis{stroke:var(--radar-grid);stroke-width:1;opacity:.8}.radar-area{fill:var(--radar-fill);stroke:var(--radar);stroke-width:2.3;stroke-linejoin:round}.radar-dot{fill:#fffefa;stroke:var(--radar);stroke-width:2}.radar-label{font:19px \"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif;fill:var(--ink)}.radar-value{font:20px Georgia,\"Microsoft YaHei\",serif;fill:var(--ink)}.radar-scale{font:11px Georgia,serif;fill:var(--subtle)}.empty-attributes{padding:45px 10px;color:var(--subtle);font:18px/1.6 \"Microsoft YaHei\",sans-serif;text-align:center}.custom-attributes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px 10px;margin:6px 0 0;font:15px/1.5 \"Microsoft YaHei\",sans-serif;color:var(--subtle)}.custom-attributes span{overflow-wrap:anywhere}.custom-attributes b{font-weight:500;color:var(--ink)}\n";
|
package/lib/plugin/server.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ declare class Server {
|
|
|
6
6
|
private ctx;
|
|
7
7
|
private onConnectedCallback?;
|
|
8
8
|
private messageListenerSetup;
|
|
9
|
+
private readonly lastUserMessageAt;
|
|
9
10
|
_init: boolean;
|
|
10
11
|
init(ctx: Context, config: Config): Promise<void>;
|
|
11
12
|
onConnected(callback: () => void): void;
|
|
@@ -14,7 +15,7 @@ declare class Server {
|
|
|
14
15
|
setWsUrl(link: string, maxRetries?: number): Promise<void>;
|
|
15
16
|
private reconnect;
|
|
16
17
|
api<T extends keyof ServiceType['api']>(apiName: T, posData: ServiceType['api'][T]['req']): Promise<ServiceType['api'][T]['res'] | undefined>;
|
|
17
|
-
/**
|
|
18
|
+
/** 过滤机器人发送者和用户连续消息后,缓存会话并把原始内容交给游戏服。 **/
|
|
18
19
|
uploadMessage(session: Session): void;
|
|
19
20
|
}
|
|
20
21
|
declare const _default: Server;
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ export const characterCardStyles = `
|
|
|
18
18
|
.character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}
|
|
19
19
|
.character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 "Microsoft YaHei",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}
|
|
20
20
|
.character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.combat-row,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}
|
|
21
|
-
.character-identity{text-align:center;min-width:0}.title-image-slot{display:flex;align-items:center;justify-content:center;min-width:0;
|
|
21
|
+
.character-identity{text-align:center;min-width:0}.title-image-slot{position:absolute;inset:12px;display:flex;align-items:center;justify-content:center;min-width:0;z-index:4;pointer-events:none}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:min(110px,100%);object-fit:contain;filter:drop-shadow(0 2px 3px #17213a70)}.title-artwork[alt="称号图片占位"]{max-width:min(244px,100%);max-height:min(44px,100%)}
|
|
22
22
|
.character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,"Microsoft YaHei",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}
|
|
23
23
|
.gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}
|
|
24
24
|
.gene-panel:before,.gene-panel:after{content:"";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}
|
|
@@ -58,8 +58,8 @@ export function characterCardHtml(card: CharacterCardData, artwork?: string, _fr
|
|
|
58
58
|
const experience = Math.max(0, Math.min(100, finite(card.experiencePercent)))
|
|
59
59
|
const geneRank = card.geneLockRankName || '基因锁未开启'
|
|
60
60
|
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>${characterCardStyles}</style></head><body><main id="capture" class="companion-sheet character-adventure character-sheet gene-tier-${tier}" data-gene-lock-tier="${tier}" data-decoration-level="${tier}"><span class="character-ambient" aria-hidden="true">${characterFloralWatermark}</span><div class="character-card companion-main">
|
|
61
|
-
<div class="portrait-column"><div class="companion-panel companion-portrait character-portrait">${corners(tier)}<div class="portrait-window portrait">${portrait ? `<img id="character-image" class="character-artwork" src="${cardEscape(portrait)}" data-fallback="${cardEscape(defaultArtwork ?? '')}" alt="${cardEscape(card.name)}的形象图">` : '<div class="placeholder"><span>形象尚未生成</span><small>角色形象</small></div>'}</div
|
|
62
|
-
<div class="title-image-slot character-title-
|
|
61
|
+
<div class="portrait-column"><div class="companion-panel companion-portrait character-portrait">${corners(tier)}<div class="portrait-window portrait">${portrait ? `<img id="character-image" class="character-artwork" src="${cardEscape(portrait)}" data-fallback="${cardEscape(defaultArtwork ?? '')}" alt="${cardEscape(card.name)}的形象图">` : '<div class="placeholder"><span>形象尚未生成</span><small>角色形象</small></div>'}</div>
|
|
62
|
+
<div class="title-image-slot character-title-centered"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? '称号图片' : '称号图片占位'}"></div></div></div>
|
|
63
63
|
<section class="companion-panel companion-stats character-stats" aria-label="角色属性"><span class="character-paper-pattern" aria-hidden="true">${characterFloralWatermark}</span>${corners(tier, ['tr', 'br'])}<div class="identity character-identity"><h1 id="character-name" class="character-name" title="${cardEscape(card.name)}">${cardEscape(card.name)}</h1><p class="level character-level"><span>Lv.</span><strong id="character-level">${number(card.level)}</strong></p><div class="gene-panel"><span class="gene-crest" aria-hidden="true">${characterGeneCrest(tier)}</span><div id="gene-rank" class="gene-rank" title="${cardEscape(geneRank)}">${cardEscape(geneRank)}</div></div></div>
|
|
64
64
|
<div class="combat-row companion-power-row"><span class="power-emblem power-icon" aria-hidden="true"><svg viewBox="0 0 42 42"><path d="m9 5 5 1 16 21-4 4L9 11Zm24 0-5 1L12 27l4 4L33 11ZM14 27l14 10m0-10L14 37M9 35l4-4m20 4-4-4"/></svg></span><span class="power-label companion-power-label">战力</span><strong id="combat-power" class="power-value companion-power">${number(card.power)}</strong></div>
|
|
65
65
|
<div class="resource companion-resource hp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="M16 28C6 21 2 17 2 10a7 7 0 0 1 14-2 7 7 0 0 1 14 2c0 7-4 11-14 18Z"/></svg>生命值</span><span id="hp-numbers" class="resource-number">${number(card.hp)} / ${number(card.maxHp)}</span></div><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${hpPercent}"><span class="resource-fill fill" style="width:${hpPercent}%"></span><span class="bar-percent resource-percent">${number(hpPercent)}%</span></div></div>
|
package/src/plugin/server.ts
CHANGED
|
@@ -10,6 +10,7 @@ class Server {
|
|
|
10
10
|
private ctx!: Context
|
|
11
11
|
private onConnectedCallback?: () => void
|
|
12
12
|
private messageListenerSetup = false
|
|
13
|
+
private readonly lastUserMessageAt = new Map<string, number>()
|
|
13
14
|
_init = false
|
|
14
15
|
|
|
15
16
|
async init(ctx: Context, config: Config) {
|
|
@@ -20,6 +21,12 @@ class Server {
|
|
|
20
21
|
if (this.wsClient) {
|
|
21
22
|
await this.dispose()
|
|
22
23
|
}
|
|
24
|
+
ctx.setInterval(() => {
|
|
25
|
+
const now = performance.now()
|
|
26
|
+
for (const [key, lastMessageAt] of this.lastUserMessageAt) {
|
|
27
|
+
if (now - lastMessageAt > 500) this.lastUserMessageAt.delete(key)
|
|
28
|
+
}
|
|
29
|
+
}, 60_000)
|
|
23
30
|
const serverUrl = config.服务器地址
|
|
24
31
|
if (!serverUrl || serverUrl.trim() === '') {
|
|
25
32
|
throw new Error('服务器地址不能为空')
|
|
@@ -39,6 +46,7 @@ class Server {
|
|
|
39
46
|
|
|
40
47
|
async dispose() {
|
|
41
48
|
this._init = false
|
|
49
|
+
this.lastUserMessageAt.clear()
|
|
42
50
|
if (this.wsClient) {
|
|
43
51
|
this.wsClient.unlistenMsgAll(/.*/)
|
|
44
52
|
this.wsClient.disconnect()
|
|
@@ -125,7 +133,7 @@ class Server {
|
|
|
125
133
|
this.ctx.logger.warn('请求出错', apiName, req.err.message)
|
|
126
134
|
}
|
|
127
135
|
|
|
128
|
-
/**
|
|
136
|
+
/** 过滤机器人发送者和用户连续消息后,缓存会话并把原始内容交给游戏服。 **/
|
|
129
137
|
uploadMessage(session: Session) {
|
|
130
138
|
if (isBotMessage(session, this.ctx.bots)) return
|
|
131
139
|
const userId = session.userId
|
|
@@ -134,6 +142,13 @@ class Server {
|
|
|
134
142
|
this.ctx.logger.warn('消息缺少 userId 或 channelId,已忽略')
|
|
135
143
|
return
|
|
136
144
|
}
|
|
145
|
+
const userKey = JSON.stringify([session.platform ?? session.bot.platform, userId])
|
|
146
|
+
const now = performance.now()
|
|
147
|
+
const lastMessageAt = this.lastUserMessageAt.get(userKey)
|
|
148
|
+
// 被忽略的消息也计入上一条,连续快速发送时只有第一条上传。
|
|
149
|
+
this.lastUserMessageAt.set(userKey, now)
|
|
150
|
+
if (lastMessageAt !== undefined && now - lastMessageAt <= 500) return
|
|
151
|
+
|
|
137
152
|
sessions.set(sessionKey(session.bot.userId, userId, channelId), session)
|
|
138
153
|
this.api('Message', {
|
|
139
154
|
bot: {
|
|
@@ -155,7 +155,7 @@ function renderQQTaskMarkdown(data: IMessage): string {
|
|
|
155
155
|
return sections.join('\n\n') || ' '
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
/** Backpack cards use a compact filter row and
|
|
158
|
+
/** Backpack cards use a compact filter row and separate clickable item paragraphs. */
|
|
159
159
|
const inventoryFilterCategories = [
|
|
160
160
|
{ name: '装备', emoji: '⚔️' },
|
|
161
161
|
{ name: '装备图纸', emoji: '📐' },
|
|
@@ -193,7 +193,9 @@ function renderQQInventoryMarkdown(data: IMessage, showGreeting: boolean): strin
|
|
|
193
193
|
const sections: string[] = []
|
|
194
194
|
const items: string[] = []
|
|
195
195
|
const flushItems = () => {
|
|
196
|
-
|
|
196
|
+
// Avoid a shared blockquote/paragraph around mobile QQ command links.
|
|
197
|
+
// Each item keeps its image and single command in an independent paragraph.
|
|
198
|
+
if (items.length) sections.push(...items.splice(0))
|
|
197
199
|
}
|
|
198
200
|
if (showGreeting) sections.push(escapeMarkdown('Ciallo~(∠・ω< )'))
|
|
199
201
|
for (const [lineIndex, item] of data.line.entries()) {
|
package/src/plugin/skillCard.ts
CHANGED
|
@@ -126,17 +126,29 @@ async function renderUncached(ctx: Context, card?: SkillCardData, leaderboard?:
|
|
|
126
126
|
let timeout: ReturnType<typeof setTimeout> | undefined
|
|
127
127
|
let workPromise: Promise<{ image: Buffer; cacheable: boolean }> | undefined
|
|
128
128
|
let expired = false
|
|
129
|
+
let finished = false
|
|
129
130
|
const release = () => { const next = waiting.shift(); if (next) next(); else running-- }
|
|
130
131
|
try {
|
|
131
132
|
const work = async () => {
|
|
132
|
-
const entryArtworkPromise = loadCardEntryArtwork(!titleCard && !bloodlineCard && !equipmentCard ? companionCard : undefined)
|
|
133
133
|
const remoteAddress = titleCard ? titleCard.artworkUrl : bloodlineCard ? bloodlineCard.artworkUrl : equipmentCard ? equipmentCard.artworkUrl : companionCard ? companionCard.artworkUrl : characterCard ? characterCard.artworkUrl : itemCard ? itemCard.artworkUrl : leaderboard ? leaderboard.backgroundUrl : card?.artworkUrl
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
const isEquipment = !titleCard && !bloodlineCard && Boolean(equipmentCard)
|
|
135
|
+
const isCharacter = !titleCard && !bloodlineCard && !equipmentCard && !companionCard && Boolean(characterCard)
|
|
136
|
+
// Download independent artwork and open the browser together. A cold portrait
|
|
137
|
+
// must not postpone its title download or browser startup.
|
|
138
|
+
const [remoteArt, fallbackArt, entryArtwork, characterAssets, header] = await Promise.all([
|
|
139
|
+
loadSkillArtwork(remoteAddress),
|
|
140
|
+
isEquipment ? equipmentFallback() : undefined,
|
|
141
|
+
loadCardEntryArtwork(!titleCard && !bloodlineCard && !equipmentCard ? companionCard : undefined),
|
|
142
|
+
isCharacter ? Promise.all([characterAsset(), loadSkillArtwork(characterCard!.titleImageUrl)]) : undefined,
|
|
143
|
+
!titleCard && !bloodlineCard && !equipmentCard && !companionCard && !characterCard && !itemCard && leaderboard ? leaderboardHeader() : undefined,
|
|
144
|
+
ctx.puppeteer.page().then(async created => {
|
|
145
|
+
if (expired || finished) { await created.close(); throw new Error(expired ? '绘图超时' : '绘图已结束') }
|
|
146
|
+
page = created
|
|
147
|
+
}),
|
|
148
|
+
])
|
|
149
|
+
const dedicatedArt = isEquipment ? remoteArt : undefined
|
|
150
|
+
const unknownArt = isEquipment && !dedicatedArt ? fallbackArt : undefined
|
|
151
|
+
const art = isEquipment ? dedicatedArt ?? unknownArt : remoteArt
|
|
140
152
|
let html: string
|
|
141
153
|
let loadedCharacterTitle: string | undefined
|
|
142
154
|
let localAssetsReady = true
|
|
@@ -150,23 +162,19 @@ async function renderUncached(ctx: Context, card?: SkillCardData, leaderboard?:
|
|
|
150
162
|
} else if (companionCard) {
|
|
151
163
|
html = companionCardHtml(companionCard, art, entryArtwork.icons)
|
|
152
164
|
} else if (characterCard) {
|
|
153
|
-
const [defaultArtwork, titleArtwork] =
|
|
154
|
-
characterAsset(),
|
|
155
|
-
loadSkillArtwork(characterCard.titleImageUrl),
|
|
156
|
-
])
|
|
165
|
+
const [defaultArtwork, titleArtwork] = characterAssets!
|
|
157
166
|
loadedCharacterTitle = titleArtwork
|
|
158
167
|
localAssetsReady = Boolean(art || defaultArtwork) && (!characterCard.titleImageUrl || Boolean(titleArtwork))
|
|
159
168
|
html = characterCardHtml(characterCard, art, undefined, defaultArtwork, titleArtwork, entryArtwork.icons)
|
|
160
169
|
} else if (itemCard) {
|
|
161
170
|
html = itemCard.type === '形象卡' ? appearanceCardHtml(itemCard, art) : itemCardHtml(itemCard, art)
|
|
162
171
|
} else if (leaderboard) {
|
|
163
|
-
html = leaderboardHtml(leaderboard,
|
|
172
|
+
html = leaderboardHtml(leaderboard, header, art)
|
|
164
173
|
} else {
|
|
165
174
|
html = skillCardHtml(card!, '', art)
|
|
166
175
|
}
|
|
167
176
|
if (expired) throw new Error('绘图超时')
|
|
168
|
-
page
|
|
169
|
-
if (expired) { await page.close(); page = undefined; throw new Error('绘图超时') }
|
|
177
|
+
if (!page) throw new Error('卡片页面缺失')
|
|
170
178
|
await page.setViewport({ width:760,height:1107,deviceScaleFactor:titleCard || (companionCard || characterCard) && !bloodlineCard && !equipmentCard ? 2 : 1 })
|
|
171
179
|
await page.setJavaScriptEnabled(false)
|
|
172
180
|
await page.setRequestInterception(true)
|
|
@@ -206,6 +214,7 @@ async function renderUncached(ctx: Context, card?: SkillCardData, leaderboard?:
|
|
|
206
214
|
workPromise = work()
|
|
207
215
|
return await Promise.race([workPromise, new Promise<never>((_,reject) => {timeout=setTimeout(()=>{ expired=true; reject(new Error('绘图超时')) },15000)})])
|
|
208
216
|
} finally {
|
|
217
|
+
finished = true
|
|
209
218
|
if(timeout) clearTimeout(timeout)
|
|
210
219
|
try { await page?.close() } catch {}
|
|
211
220
|
if (expired && workPromise) {
|