koishi-plugin-ciyi 1.4.1 → 2.1.0
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/README.md +4 -4
- package/lib/index.d.ts +3 -2
- package/lib/index.js +302 -119
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,11 +15,11 @@ yarn add koishi-plugin-ciyi
|
|
|
15
15
|
| 指令 | 说明 |
|
|
16
16
|
| --- | --- |
|
|
17
17
|
| `ciyi` | 玩法 |
|
|
18
|
-
| `ciyi.猜 <词>` |
|
|
19
|
-
| `ciyi.裸词 [开/关]` |
|
|
20
|
-
| `ciyi.排行榜` |
|
|
18
|
+
| `ciyi.猜 <词>` | 开始今日对局并报一个两字词 |
|
|
19
|
+
| `ciyi.裸词 [开/关]` | 切换本频道无前缀续猜 |
|
|
20
|
+
| `ciyi.排行榜` | 累计猜中排行榜 |
|
|
21
21
|
|
|
22
|
-
首次用 `ciyi.猜 <词>`
|
|
22
|
+
首次用 `ciyi.猜 <词>` 开题,之后可直接发送两字词继续猜。裸词设置只对当前频道生效,重启后恢复默认。
|
|
23
23
|
|
|
24
24
|
## 许可证
|
|
25
25
|
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Context, Schema, Session } from "koishi";
|
|
2
2
|
import { History } from "./view";
|
|
3
3
|
export declare const name = "ciyi";
|
|
4
|
-
export declare const usage = "## \u4F7F\u7528\n\n\u53D1\u9001 `ciyi` \u67E5\u770B\u73A9\u6CD5\u3002\u6BCF\u65E5\u85CF\u4E00\u4E2A\u4E24\u5B57\u8BCD\uFF0C\u7528 `ciyi.\u731C <\u8BCD>` \u5F00\u9898\uFF0C\u4E4B\u540E\u53EF\u4EE5\u76F4\u63A5\u53D1\u9001\u4E24\u5B57\u8BCD\u7EE7\u7EED\u731C\u3002\u88F8\u8BCD\u8BBE\u7F6E\u53EA\u5BF9\u5F53\u524D\u9891\u9053\u751F\u6548\uFF0C\u91CD\u542F\u540E\u6062\u590D\u9ED8\u8BA4\u3002\n\n## \u6307\u4EE4\n\n| \u6307\u4EE4 | \u8BF4\u660E |\n| --- | --- |\n| `ciyi` | \u73A9\u6CD5 |\n| `ciyi.\u731C <\u8BCD>` | \u5F00\u59CB\u4ECA\u65E5\
|
|
4
|
+
export declare const usage = "## \u4F7F\u7528\n\n\u53D1\u9001 `ciyi` \u67E5\u770B\u73A9\u6CD5\u3002\u6BCF\u65E5\u85CF\u4E00\u4E2A\u4E24\u5B57\u8BCD\uFF0C\u7528 `ciyi.\u731C <\u8BCD>` \u5F00\u9898\uFF0C\u4E4B\u540E\u53EF\u4EE5\u76F4\u63A5\u53D1\u9001\u4E24\u5B57\u8BCD\u7EE7\u7EED\u731C\u3002\u88F8\u8BCD\u8BBE\u7F6E\u53EA\u5BF9\u5F53\u524D\u9891\u9053\u751F\u6548\uFF0C\u91CD\u542F\u540E\u6062\u590D\u9ED8\u8BA4\u3002\n\n## \u6307\u4EE4\n\n| \u6307\u4EE4 | \u8BF4\u660E |\n| --- | --- |\n| `ciyi` | \u73A9\u6CD5 |\n| `ciyi.\u731C <\u8BCD>` | \u5F00\u59CB\u4ECA\u65E5\u5BF9\u5C40\u5E76\u62A5\u4E00\u4E2A\u4E24\u5B57\u8BCD |\n| `ciyi.\u88F8\u8BCD [\u5F00/\u5173]` | \u5F00\u5173\u672C\u9891\u9053\u7684\u65E0\u524D\u7F00\u7EED\u731C |\n| `ciyi.\u6392\u884C\u699C` | \u7D2F\u8BA1\u731C\u4E2D\u6392\u884C\u699C |";
|
|
5
5
|
export declare const inject: {
|
|
6
6
|
required: string[];
|
|
7
7
|
optional: string[];
|
|
@@ -9,10 +9,11 @@ export declare const inject: {
|
|
|
9
9
|
export interface Config {
|
|
10
10
|
atReply: boolean;
|
|
11
11
|
quoteReply: boolean;
|
|
12
|
-
|
|
12
|
+
enableDirectInput: boolean;
|
|
13
13
|
renderImage: boolean;
|
|
14
14
|
maxHistory: number;
|
|
15
15
|
maxRank: number;
|
|
16
|
+
retractDelay: number;
|
|
16
17
|
}
|
|
17
18
|
export declare const Config: Schema<Config>;
|
|
18
19
|
declare module "koishi" {
|
package/lib/index.js
CHANGED
|
@@ -203,6 +203,25 @@ var SHAPE = {
|
|
|
203
203
|
full: 9999
|
|
204
204
|
};
|
|
205
205
|
var FONT_STACK = '"Roboto Flex", "Roboto", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "Source Han Sans SC", system-ui, sans-serif';
|
|
206
|
+
var MONO_STACK = '"Roboto Mono", "JetBrains Mono", "SF Mono", "Cascadia Mono", Consolas, "Noto Sans Mono", monospace';
|
|
207
|
+
var TYPE = {
|
|
208
|
+
displayLarge: { size: 57, line: 64, weight: 400, tracking: -0.25 },
|
|
209
|
+
displayMedium: { size: 45, line: 52, weight: 400, tracking: 0 },
|
|
210
|
+
displaySmall: { size: 36, line: 44, weight: 400, tracking: 0 },
|
|
211
|
+
headlineLarge: { size: 32, line: 40, weight: 400, tracking: 0 },
|
|
212
|
+
headlineMedium: { size: 28, line: 36, weight: 400, tracking: 0 },
|
|
213
|
+
headlineSmall: { size: 24, line: 32, weight: 400, tracking: 0 },
|
|
214
|
+
titleLarge: { size: 22, line: 28, weight: 400, tracking: 0 },
|
|
215
|
+
titleMedium: { size: 16, line: 24, weight: 500, tracking: 0.15 },
|
|
216
|
+
titleSmall: { size: 14, line: 20, weight: 500, tracking: 0.1 },
|
|
217
|
+
bodyLarge: { size: 16, line: 24, weight: 400, tracking: 0.5 },
|
|
218
|
+
bodyMedium: { size: 14, line: 20, weight: 400, tracking: 0.25 },
|
|
219
|
+
bodySmall: { size: 12, line: 16, weight: 400, tracking: 0.4 },
|
|
220
|
+
labelLarge: { size: 14, line: 20, weight: 500, tracking: 0.1 },
|
|
221
|
+
labelMedium: { size: 12, line: 16, weight: 500, tracking: 0.5 },
|
|
222
|
+
labelSmall: { size: 11, line: 16, weight: 500, tracking: 0.5 }
|
|
223
|
+
};
|
|
224
|
+
var EMPHASIZED_WEIGHT = { display: 600, headline: 600, title: 600, label: 600 };
|
|
206
225
|
var MEDAL = {
|
|
207
226
|
gold: lch(58, 52, 85),
|
|
208
227
|
silver: lch(66, 5, 260),
|
|
@@ -247,11 +266,13 @@ var C = {
|
|
|
247
266
|
primary: SCHEME.primary,
|
|
248
267
|
onPrimary: SCHEME.onPrimary,
|
|
249
268
|
primaryContainer: SCHEME.primaryContainer,
|
|
250
|
-
onPrimaryContainer: SCHEME.onPrimaryContainer
|
|
269
|
+
onPrimaryContainer: SCHEME.onPrimaryContainer,
|
|
270
|
+
secondaryContainer: SCHEME.secondaryContainer,
|
|
271
|
+
onSecondaryContainer: SCHEME.onSecondaryContainer
|
|
251
272
|
};
|
|
252
273
|
var FRESH = lch(94, 14, HUE);
|
|
253
274
|
var FONT_SANS = FONT_STACK;
|
|
254
|
-
var FONT_NUM = FONT_STACK
|
|
275
|
+
var FONT_NUM = `${MONO_STACK},${FONT_STACK}`;
|
|
255
276
|
function s(n) {
|
|
256
277
|
return Math.round(n * SCALE);
|
|
257
278
|
}
|
|
@@ -317,7 +338,7 @@ function textRight(ctx, text, x, y, font, color, offsetY = 0) {
|
|
|
317
338
|
}
|
|
318
339
|
__name(textRight, "textRight");
|
|
319
340
|
function fontSizeOf(font) {
|
|
320
|
-
return Number(font.match(/([\d.]+)px/)?.[1] ?? s(
|
|
341
|
+
return Number(font.match(/([\d.]+)px/)?.[1] ?? s(TYPE.bodySmall.size));
|
|
321
342
|
}
|
|
322
343
|
__name(fontSizeOf, "fontSizeOf");
|
|
323
344
|
function textWidth(text, fontSize) {
|
|
@@ -378,9 +399,30 @@ async function toPng(service, innerW, innerH, draw) {
|
|
|
378
399
|
}
|
|
379
400
|
__name(toPng, "toPng");
|
|
380
401
|
var GRID = {
|
|
381
|
-
sm: {
|
|
382
|
-
|
|
383
|
-
|
|
402
|
+
sm: {
|
|
403
|
+
cell: 26,
|
|
404
|
+
gap: 4,
|
|
405
|
+
border: 1,
|
|
406
|
+
font: TYPE.labelLarge.size,
|
|
407
|
+
weight: EMPHASIZED_WEIGHT.label,
|
|
408
|
+
radius: SHAPE.extraSmall
|
|
409
|
+
},
|
|
410
|
+
md: {
|
|
411
|
+
cell: 34,
|
|
412
|
+
gap: 4,
|
|
413
|
+
border: 1,
|
|
414
|
+
font: TYPE.titleLarge.size,
|
|
415
|
+
weight: EMPHASIZED_WEIGHT.title,
|
|
416
|
+
radius: SHAPE.small
|
|
417
|
+
},
|
|
418
|
+
lg: {
|
|
419
|
+
cell: 78,
|
|
420
|
+
gap: 9,
|
|
421
|
+
border: 1.5,
|
|
422
|
+
font: TYPE.displayMedium.size,
|
|
423
|
+
weight: EMPHASIZED_WEIGHT.display,
|
|
424
|
+
radius: SHAPE.large
|
|
425
|
+
}
|
|
384
426
|
};
|
|
385
427
|
function gridWidth(word, size) {
|
|
386
428
|
const g = GRID[size];
|
|
@@ -389,9 +431,9 @@ function gridWidth(word, size) {
|
|
|
389
431
|
return s(g.cell * n + g.gap * (n - 1));
|
|
390
432
|
}
|
|
391
433
|
__name(gridWidth, "gridWidth");
|
|
392
|
-
function drawGridCell(ctx, x, y, size, ch, mode,
|
|
434
|
+
function drawGridCell(ctx, x, y, size, ch, mode, g) {
|
|
393
435
|
const half = size / 2;
|
|
394
|
-
const radius =
|
|
436
|
+
const radius = s(g.radius);
|
|
395
437
|
if (mode === "mk") {
|
|
396
438
|
fillRound(ctx, x, y, size, size, radius, C.surfaceContainerHighest);
|
|
397
439
|
ctx.save();
|
|
@@ -404,20 +446,20 @@ function drawGridCell(ctx, x, y, size, ch, mode, fontSize, borderW) {
|
|
|
404
446
|
size - s(1),
|
|
405
447
|
radius,
|
|
406
448
|
C.outline,
|
|
407
|
-
s(
|
|
449
|
+
s(g.border)
|
|
408
450
|
);
|
|
409
451
|
ctx.restore();
|
|
410
452
|
return;
|
|
411
453
|
}
|
|
412
454
|
fillRound(ctx, x, y, size, size, radius, C.surfaceContainerHigh);
|
|
413
455
|
if (ch) {
|
|
414
|
-
const fs = s(
|
|
456
|
+
const fs = s(g.font);
|
|
415
457
|
textCenter(
|
|
416
458
|
ctx,
|
|
417
459
|
ch,
|
|
418
460
|
x + half,
|
|
419
461
|
y + half,
|
|
420
|
-
|
|
462
|
+
`${g.weight} ${fs}px ${FONT_SANS}`,
|
|
421
463
|
C.onSurface,
|
|
422
464
|
cjkOffset(fs)
|
|
423
465
|
);
|
|
@@ -430,15 +472,15 @@ function drawWordGrid(ctx, x, y, word, size, mask) {
|
|
|
430
472
|
const gap = s(g.gap);
|
|
431
473
|
const chars = Array.from(word || "");
|
|
432
474
|
if (chars.length === 0) {
|
|
433
|
-
textCenter(ctx, "—", x + s(14), y + cell / 2, `${s(
|
|
475
|
+
textCenter(ctx, "—", x + s(14), y + cell / 2, `${s(TYPE.bodySmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
434
476
|
return s(28);
|
|
435
477
|
}
|
|
436
478
|
let cx = x;
|
|
437
479
|
for (let i = 0; i < chars.length; i++) {
|
|
438
480
|
if (i === mask) {
|
|
439
|
-
drawGridCell(ctx, cx, y, cell, null, "mk", g
|
|
481
|
+
drawGridCell(ctx, cx, y, cell, null, "mk", g);
|
|
440
482
|
} else {
|
|
441
|
-
drawGridCell(ctx, cx, y, cell, chars[i], "on", g
|
|
483
|
+
drawGridCell(ctx, cx, y, cell, chars[i], "on", g);
|
|
442
484
|
}
|
|
443
485
|
cx += cell + gap;
|
|
444
486
|
}
|
|
@@ -447,12 +489,12 @@ function drawWordGrid(ctx, x, y, word, size, mask) {
|
|
|
447
489
|
__name(drawWordGrid, "drawWordGrid");
|
|
448
490
|
function drawSeal(ctx, x, y, size, lines) {
|
|
449
491
|
fillRound(ctx, x, y, size, size, s(SHAPE.extraLarge), C.primary);
|
|
450
|
-
const fs = s(
|
|
492
|
+
const fs = s(TYPE.titleLarge.size);
|
|
451
493
|
const lh = fs * 1.06;
|
|
452
494
|
const total = lh * lines.length;
|
|
453
495
|
let cy = y + size / 2 - total / 2 + lh / 2;
|
|
454
496
|
for (const line of lines) {
|
|
455
|
-
textCenter(ctx, line, x + size / 2, cy,
|
|
497
|
+
textCenter(ctx, line, x + size / 2, cy, `${EMPHASIZED_WEIGHT.title} ${fs}px ${FONT_SANS}`, C.onPrimary);
|
|
456
498
|
cy += lh;
|
|
457
499
|
}
|
|
458
500
|
}
|
|
@@ -465,13 +507,20 @@ function drawHeader(ctx, innerX, innerY, innerW, sub, right) {
|
|
|
465
507
|
const h2 = padTop + sealSize + padBottom;
|
|
466
508
|
drawSeal(ctx, innerX + padX, innerY + padTop, sealSize, ["词", "意"]);
|
|
467
509
|
const brandX = innerX + padX + sealSize + s(15);
|
|
468
|
-
textLeft(
|
|
510
|
+
textLeft(
|
|
511
|
+
ctx,
|
|
512
|
+
"词意",
|
|
513
|
+
brandX,
|
|
514
|
+
innerY + padTop + s(14),
|
|
515
|
+
`${EMPHASIZED_WEIGHT.headline} ${s(TYPE.headlineMedium.size)}px ${FONT_SANS}`,
|
|
516
|
+
C.onSurface
|
|
517
|
+
);
|
|
469
518
|
textLeft(
|
|
470
519
|
ctx,
|
|
471
520
|
"ci yi",
|
|
472
521
|
brandX,
|
|
473
522
|
innerY + padTop + s(36),
|
|
474
|
-
`italic ${s(
|
|
523
|
+
`italic ${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
|
|
475
524
|
C.onSurfaceVariant
|
|
476
525
|
);
|
|
477
526
|
const subRight = right ? innerX + innerW - padX - s(150) : innerX + innerW - padX;
|
|
@@ -481,13 +530,27 @@ function drawHeader(ctx, innerX, innerY, innerW, sub, right) {
|
|
|
481
530
|
brandX,
|
|
482
531
|
innerY + padTop + s(58),
|
|
483
532
|
subRight - brandX,
|
|
484
|
-
`${s(
|
|
533
|
+
`${s(TYPE.bodySmall.size)}px ${FONT_SANS}`,
|
|
485
534
|
C.onSurfaceVariant
|
|
486
535
|
);
|
|
487
536
|
if (right) {
|
|
488
537
|
const rx = innerX + innerW - padX;
|
|
489
|
-
textRight(
|
|
490
|
-
|
|
538
|
+
textRight(
|
|
539
|
+
ctx,
|
|
540
|
+
right.big,
|
|
541
|
+
rx,
|
|
542
|
+
innerY + padTop + s(16),
|
|
543
|
+
`${EMPHASIZED_WEIGHT.headline} ${s(TYPE.headlineLarge.size)}px ${FONT_NUM}`,
|
|
544
|
+
C.primary
|
|
545
|
+
);
|
|
546
|
+
textRight(
|
|
547
|
+
ctx,
|
|
548
|
+
right.cap,
|
|
549
|
+
rx,
|
|
550
|
+
innerY + padTop + s(44),
|
|
551
|
+
`${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
|
|
552
|
+
C.onSurfaceVariant
|
|
553
|
+
);
|
|
491
554
|
}
|
|
492
555
|
const lineY = innerY + h2;
|
|
493
556
|
ctx.strokeStyle = C.outlineVariant;
|
|
@@ -525,16 +588,16 @@ function drawFooter(ctx, innerX, y, innerW, legendItems, tip) {
|
|
|
525
588
|
for (const { tier, range } of legendItems) {
|
|
526
589
|
fillRound(ctx, lx, cy - s(4.5), s(9), s(9), s(SHAPE.full), tier.color);
|
|
527
590
|
lx += s(9) + s(6);
|
|
528
|
-
textLeft(ctx, tier.name, lx, cy, `${s(
|
|
529
|
-
lx += textWidth(tier.name, s(
|
|
530
|
-
textLeft(ctx, range, lx, cy, `${s(
|
|
531
|
-
lx += textWidth(range, s(
|
|
591
|
+
textLeft(ctx, tier.name, lx, cy, `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
592
|
+
lx += textWidth(tier.name, s(TYPE.labelSmall.size)) + s(4);
|
|
593
|
+
textLeft(ctx, range, lx, cy, `${s(TYPE.labelSmall.size)}px ${FONT_NUM}`, C.onSurfaceVariant);
|
|
594
|
+
lx += textWidth(range, s(TYPE.labelSmall.size)) + s(13);
|
|
532
595
|
}
|
|
533
|
-
const tipFont = `${s(
|
|
596
|
+
const tipFont = `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`;
|
|
534
597
|
const maxTipWidth = legendItems.length ? s(145) : innerW - padX * 2;
|
|
535
598
|
textRight(
|
|
536
599
|
ctx,
|
|
537
|
-
ellipsize(tip, maxTipWidth, s(
|
|
600
|
+
ellipsize(tip, maxTipWidth, s(TYPE.labelSmall.size)),
|
|
538
601
|
innerX + innerW - padX,
|
|
539
602
|
cy,
|
|
540
603
|
tipFont,
|
|
@@ -551,17 +614,24 @@ function drawLegendFull(ctx, x, y, w) {
|
|
|
551
614
|
const cx = x + i % 2 * colW;
|
|
552
615
|
const cy = y + Math.floor(i / 2) * rowH;
|
|
553
616
|
fillRound(ctx, cx, cy - s(4.5), s(9), s(9), s(SHAPE.full), t.color);
|
|
554
|
-
textLeft(ctx, t.name, cx + s(15), cy, `${s(
|
|
555
|
-
textLeft(ctx, tierRange(i), cx + s(52), cy, `${s(
|
|
556
|
-
textLeft(ctx, t.note, cx + s(100), cy, `${s(
|
|
617
|
+
textLeft(ctx, t.name, cx + s(15), cy, `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
618
|
+
textLeft(ctx, tierRange(i), cx + s(52), cy, `${s(TYPE.labelSmall.size)}px ${FONT_NUM}`, C.onSurfaceVariant);
|
|
619
|
+
textLeft(ctx, t.note, cx + s(100), cy, `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
557
620
|
}
|
|
558
621
|
return rowH * Math.ceil(TIERS.length / 2) + s(8);
|
|
559
622
|
}
|
|
560
623
|
__name(drawLegendFull, "drawLegendFull");
|
|
561
624
|
function drawSectionTitle(ctx, x, y, w, title) {
|
|
562
625
|
fillRound(ctx, x, y - s(1), s(3), s(12), s(SHAPE.full), C.primary);
|
|
563
|
-
textLeft(
|
|
564
|
-
|
|
626
|
+
textLeft(
|
|
627
|
+
ctx,
|
|
628
|
+
title,
|
|
629
|
+
x + s(15),
|
|
630
|
+
y + s(5),
|
|
631
|
+
`${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelMedium.size)}px ${FONT_SANS}`,
|
|
632
|
+
C.onSurfaceVariant
|
|
633
|
+
);
|
|
634
|
+
const tw = textWidth(title, s(TYPE.labelMedium.size));
|
|
565
635
|
ctx.strokeStyle = C.outlineVariant;
|
|
566
636
|
ctx.lineWidth = s(1);
|
|
567
637
|
ctx.beginPath();
|
|
@@ -581,7 +651,7 @@ function drawNearBar(ctx, x, y, pct, tier) {
|
|
|
581
651
|
const gap = s(3);
|
|
582
652
|
const fillW = Math.max(barH, barW * pct / 100);
|
|
583
653
|
const top = y - barH / 2;
|
|
584
|
-
fillRound(ctx, x, top, fillW, barH,
|
|
654
|
+
fillRound(ctx, x, top, fillW, barH, s(SHAPE.full), tier.color);
|
|
585
655
|
if (fillW + gap < barW) {
|
|
586
656
|
fillRound(
|
|
587
657
|
ctx,
|
|
@@ -589,11 +659,18 @@ function drawNearBar(ctx, x, y, pct, tier) {
|
|
|
589
659
|
top,
|
|
590
660
|
barW - fillW - gap,
|
|
591
661
|
barH,
|
|
592
|
-
|
|
662
|
+
s(SHAPE.full),
|
|
593
663
|
C.surfaceContainerHighest
|
|
594
664
|
);
|
|
595
665
|
}
|
|
596
|
-
textLeft(
|
|
666
|
+
textLeft(
|
|
667
|
+
ctx,
|
|
668
|
+
tier.name,
|
|
669
|
+
x + barW + s(9),
|
|
670
|
+
y,
|
|
671
|
+
`${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelMedium.size)}px ${FONT_SANS}`,
|
|
672
|
+
tier.color
|
|
673
|
+
);
|
|
597
674
|
}
|
|
598
675
|
__name(drawNearBar, "drawNearBar");
|
|
599
676
|
function measureBoard(opts) {
|
|
@@ -633,10 +710,10 @@ function drawBoardTable(ctx, x, y, w, opts, rowH) {
|
|
|
633
710
|
const rk0 = nb1 + cols.nb;
|
|
634
711
|
const mt0 = rk0 + cols.rk;
|
|
635
712
|
const noX = bx + s(2);
|
|
636
|
-
const meterBlockW = s(88) + s(9) + textWidth("咫尺", s(
|
|
713
|
+
const meterBlockW = s(88) + s(9) + textWidth("咫尺", s(TYPE.labelMedium.size));
|
|
637
714
|
const mtX = mt0 + Math.max(0, (cols.mt - meterBlockW) / 2);
|
|
638
715
|
const headY = y;
|
|
639
|
-
const fs = s(
|
|
716
|
+
const fs = s(TYPE.labelSmall.size);
|
|
640
717
|
textLeft(ctx, "序", noX, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
641
718
|
textCenter(ctx, "更近 ◀", nb0 + cols.nb / 2, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
642
719
|
textCenter(ctx, "猜测", gw0 + cols.gw / 2, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
@@ -650,10 +727,10 @@ function drawBoardTable(ctx, x, y, w, opts, rowH) {
|
|
|
650
727
|
ry += s(8);
|
|
651
728
|
textCenter(
|
|
652
729
|
ctx,
|
|
653
|
-
|
|
730
|
+
`…… 另有 ${row.gapBefore} 词未列`,
|
|
654
731
|
cx + tableW / 2,
|
|
655
732
|
ry + s(10),
|
|
656
|
-
`${s(
|
|
733
|
+
`${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
|
|
657
734
|
C.onSurfaceVariant
|
|
658
735
|
);
|
|
659
736
|
ctx.strokeStyle = C.outlineVariant;
|
|
@@ -683,7 +760,7 @@ function drawBoardTable(ctx, x, y, w, opts, rowH) {
|
|
|
683
760
|
String(ordinal).padStart(2, "0"),
|
|
684
761
|
noX,
|
|
685
762
|
midY,
|
|
686
|
-
`${s(
|
|
763
|
+
`${s(TYPE.labelMedium.size)}px ${FONT_NUM}`,
|
|
687
764
|
C.onSurfaceVariant
|
|
688
765
|
);
|
|
689
766
|
const nbX = nb0 + (cols.nb - gridWidth(history.leftHint, "sm")) / 2;
|
|
@@ -693,12 +770,12 @@ function drawBoardTable(ctx, x, y, w, opts, rowH) {
|
|
|
693
770
|
const rbX = nb1 + (cols.nb - gridWidth(history.rightHint, "sm")) / 2;
|
|
694
771
|
drawWordGrid(ctx, rbX, midY - s(13), history.rightHint, "sm", 1);
|
|
695
772
|
const rankText = String(history.rank);
|
|
696
|
-
const rankFont =
|
|
697
|
-
const rankWidth = textWidth(rankText, s(
|
|
698
|
-
const hashW = textWidth("#", s(
|
|
773
|
+
const rankFont = `${EMPHASIZED_WEIGHT.title} ${s(TYPE.titleLarge.size)}px ${FONT_NUM}`;
|
|
774
|
+
const rankWidth = textWidth(rankText, s(TYPE.titleLarge.size));
|
|
775
|
+
const hashW = textWidth("#", s(TYPE.labelMedium.size));
|
|
699
776
|
const rankBlock = hashW + s(3) + rankWidth;
|
|
700
777
|
const rankLeft = rk0 + (cols.rk - rankBlock) / 2;
|
|
701
|
-
textLeft(ctx, "#", rankLeft, midY, `${s(
|
|
778
|
+
textLeft(ctx, "#", rankLeft, midY, `${s(TYPE.labelMedium.size)}px ${FONT_NUM}`, C.onSurfaceVariant);
|
|
702
779
|
textLeft(ctx, rankText, rankLeft + hashW + s(3), midY, rankFont, tier.color);
|
|
703
780
|
drawNearBar(ctx, mtX, midY, pct, tier);
|
|
704
781
|
ry += rowH;
|
|
@@ -740,31 +817,59 @@ function renderIntroCard(service, opts) {
|
|
|
740
817
|
y += s(11);
|
|
741
818
|
const p1h = s(104);
|
|
742
819
|
drawPanel(ctx, ix + s(22), y, iw - s(44), p1h);
|
|
743
|
-
textLeft(ctx, "开始", ix + s(38), y + s(24), `${s(
|
|
744
|
-
textLeft(
|
|
820
|
+
textLeft(ctx, "开始", ix + s(38), y + s(24), `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, C.primary);
|
|
821
|
+
textLeft(
|
|
822
|
+
ctx,
|
|
823
|
+
"ciyi.猜 山水",
|
|
824
|
+
ix + s(96),
|
|
825
|
+
y + s(24),
|
|
826
|
+
`${s(TYPE.labelLarge.size)}px ${FONT_SANS}`,
|
|
827
|
+
C.onSurface
|
|
828
|
+
);
|
|
745
829
|
textLeftFit(
|
|
746
830
|
ctx,
|
|
747
831
|
`开题并报一个两字词${opts.middleware ? ";开题后可直接发词" : ""}`,
|
|
748
832
|
ix + s(230),
|
|
749
833
|
y + s(24),
|
|
750
834
|
iw - s(268),
|
|
751
|
-
`${s(
|
|
835
|
+
`${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
|
|
836
|
+
C.onSurface
|
|
837
|
+
);
|
|
838
|
+
textLeft(ctx, "切换", ix + s(38), y + s(52), `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, C.primary);
|
|
839
|
+
textLeft(
|
|
840
|
+
ctx,
|
|
841
|
+
"ciyi.裸词 开/关",
|
|
842
|
+
ix + s(96),
|
|
843
|
+
y + s(52),
|
|
844
|
+
`${s(TYPE.labelLarge.size)}px ${FONT_SANS}`,
|
|
752
845
|
C.onSurface
|
|
753
846
|
);
|
|
754
|
-
textLeft(ctx, "切换", ix + s(38), y + s(52), `${s(14)}px ${FONT_SANS}`, C.primary);
|
|
755
|
-
textLeft(ctx, "ciyi.裸词 开/关", ix + s(96), y + s(52), `${s(13.5)}px ${FONT_NUM}`, C.onSurface);
|
|
756
847
|
textLeftFit(
|
|
757
848
|
ctx,
|
|
758
|
-
"
|
|
849
|
+
"临时改本频道的续猜方式",
|
|
759
850
|
ix + s(230),
|
|
760
851
|
y + s(52),
|
|
761
852
|
iw - s(268),
|
|
762
|
-
`${s(
|
|
853
|
+
`${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
|
|
854
|
+
C.onSurface
|
|
855
|
+
);
|
|
856
|
+
textLeft(ctx, "排行", ix + s(38), y + s(80), `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, C.primary);
|
|
857
|
+
textLeft(
|
|
858
|
+
ctx,
|
|
859
|
+
"ciyi.排行榜",
|
|
860
|
+
ix + s(96),
|
|
861
|
+
y + s(80),
|
|
862
|
+
`${s(TYPE.labelLarge.size)}px ${FONT_SANS}`,
|
|
863
|
+
C.onSurface
|
|
864
|
+
);
|
|
865
|
+
textLeft(
|
|
866
|
+
ctx,
|
|
867
|
+
"看谁猜中得最多",
|
|
868
|
+
ix + s(230),
|
|
869
|
+
y + s(80),
|
|
870
|
+
`${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
|
|
763
871
|
C.onSurface
|
|
764
872
|
);
|
|
765
|
-
textLeft(ctx, "排行", ix + s(38), y + s(80), `${s(14)}px ${FONT_SANS}`, C.primary);
|
|
766
|
-
textLeft(ctx, "ciyi.排行榜", ix + s(96), y + s(80), `${s(13.5)}px ${FONT_NUM}`, C.onSurface);
|
|
767
|
-
textLeft(ctx, "看谁猜中得最多", ix + s(230), y + s(80), `${s(14)}px ${FONT_SANS}`, C.onSurface);
|
|
768
873
|
y += p1h + s(19);
|
|
769
874
|
y += drawSectionTitle(ctx, ix + s(22), y, iw - s(44), "读板");
|
|
770
875
|
y += s(11);
|
|
@@ -788,7 +893,7 @@ function renderIntroCard(service, opts) {
|
|
|
788
893
|
"这一行是说:「企业」与今日答案的意思相近程度,排在第 467 位。",
|
|
789
894
|
ix + s(38),
|
|
790
895
|
y + s(104),
|
|
791
|
-
`${s(
|
|
896
|
+
`${s(TYPE.bodySmall.size)}px ${FONT_SANS}`,
|
|
792
897
|
C.onSurfaceVariant
|
|
793
898
|
);
|
|
794
899
|
textLeft(
|
|
@@ -796,7 +901,7 @@ function renderIntroCard(service, opts) {
|
|
|
796
901
|
"两侧空格各藏一字:左邻更近,右邻更远。",
|
|
797
902
|
ix + s(38),
|
|
798
903
|
y + s(126),
|
|
799
|
-
`${s(
|
|
904
|
+
`${s(TYPE.bodySmall.size)}px ${FONT_SANS}`,
|
|
800
905
|
C.onSurfaceVariant
|
|
801
906
|
);
|
|
802
907
|
textLeft(
|
|
@@ -804,7 +909,7 @@ function renderIntroCard(service, opts) {
|
|
|
804
909
|
"名次越小,离答案越近;#1 就是答案本身。",
|
|
805
910
|
ix + s(38),
|
|
806
911
|
y + s(148),
|
|
807
|
-
`${s(
|
|
912
|
+
`${s(TYPE.bodySmall.size)}px ${FONT_SANS}`,
|
|
808
913
|
C.onSurfaceVariant
|
|
809
914
|
);
|
|
810
915
|
y += sampleH + s(19);
|
|
@@ -827,7 +932,7 @@ function renderIntroCard(service, opts) {
|
|
|
827
932
|
"一日一词,猜中即止,次日零点换题",
|
|
828
933
|
ix + s(22),
|
|
829
934
|
iy + ih - s(30),
|
|
830
|
-
`${s(
|
|
935
|
+
`${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
|
|
831
936
|
C.onSurfaceVariant
|
|
832
937
|
);
|
|
833
938
|
});
|
|
@@ -842,37 +947,44 @@ function renderWinCard(service, opts) {
|
|
|
842
947
|
const revealH = s(130);
|
|
843
948
|
drawPanel(ctx, ix + s(22), y, iw - s(44), revealH);
|
|
844
949
|
drawSeal(ctx, ix + iw - s(80), y - s(13), s(42), ["中"]);
|
|
845
|
-
textLeft(ctx, "今日答案", ix + s(38), y + s(24), `${s(
|
|
950
|
+
textLeft(ctx, "今日答案", ix + s(38), y + s(24), `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
846
951
|
drawWordGrid(ctx, ix + s(38), y + s(40), opts.answer, "lg");
|
|
847
952
|
const ax = ix + iw - s(310);
|
|
848
953
|
if (opts.neighbors.length) {
|
|
849
|
-
textLeft(ctx, "意思最近的几个词", ax, y + s(24), `${s(
|
|
954
|
+
textLeft(ctx, "意思最近的几个词", ax, y + s(24), `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
850
955
|
let nx = ax;
|
|
851
956
|
let ny = y + s(48);
|
|
852
|
-
const chipFont = `${s(
|
|
957
|
+
const chipFont = `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`;
|
|
853
958
|
const chipRight = ix + iw - s(38);
|
|
854
959
|
for (const word of opts.neighbors) {
|
|
855
|
-
const fittedWord = ellipsize(word, s(116), s(
|
|
856
|
-
const pw = textWidth(fittedWord, s(
|
|
960
|
+
const fittedWord = ellipsize(word, s(116), s(TYPE.labelLarge.size));
|
|
961
|
+
const pw = textWidth(fittedWord, s(TYPE.labelLarge.size)) + s(20);
|
|
857
962
|
if (nx !== ax && nx + pw > chipRight) {
|
|
858
963
|
nx = ax;
|
|
859
964
|
ny += s(36);
|
|
860
965
|
}
|
|
861
966
|
if (ny + s(14) > y + revealH - s(12)) break;
|
|
862
|
-
fillRound(ctx, nx, ny - s(14), pw, s(28), s(SHAPE.full),
|
|
967
|
+
fillRound(ctx, nx, ny - s(14), pw, s(28), s(SHAPE.full), C.secondaryContainer);
|
|
863
968
|
textCenter(
|
|
864
969
|
ctx,
|
|
865
970
|
fittedWord,
|
|
866
971
|
nx + pw / 2,
|
|
867
972
|
ny,
|
|
868
973
|
chipFont,
|
|
869
|
-
|
|
870
|
-
cjkOffset(s(
|
|
974
|
+
C.onSecondaryContainer,
|
|
975
|
+
cjkOffset(s(TYPE.labelLarge.size))
|
|
871
976
|
);
|
|
872
977
|
nx += pw + s(7);
|
|
873
978
|
}
|
|
874
979
|
} else {
|
|
875
|
-
textLeft(
|
|
980
|
+
textLeft(
|
|
981
|
+
ctx,
|
|
982
|
+
"今日一词,就此收笔。",
|
|
983
|
+
ax,
|
|
984
|
+
y + s(50),
|
|
985
|
+
`${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
|
|
986
|
+
C.onSurfaceVariant
|
|
987
|
+
);
|
|
876
988
|
}
|
|
877
989
|
y += revealH + s(14);
|
|
878
990
|
const statW = (iw - s(44) - s(20)) / 3;
|
|
@@ -884,11 +996,32 @@ function renderWinCard(service, opts) {
|
|
|
884
996
|
for (let i = 0; i < 3; i++) {
|
|
885
997
|
const sx = ix + s(22) + i * (statW + s(10));
|
|
886
998
|
drawPanel(ctx, sx, y, statW, s(56));
|
|
887
|
-
textLeft(
|
|
999
|
+
textLeft(
|
|
1000
|
+
ctx,
|
|
1001
|
+
stats[i].n,
|
|
1002
|
+
sx + s(14),
|
|
1003
|
+
y + s(22),
|
|
1004
|
+
`${EMPHASIZED_WEIGHT.headline} ${s(TYPE.headlineSmall.size)}px ${FONT_NUM}`,
|
|
1005
|
+
C.primary
|
|
1006
|
+
);
|
|
888
1007
|
if (stats[i].u) {
|
|
889
|
-
textLeft(
|
|
1008
|
+
textLeft(
|
|
1009
|
+
ctx,
|
|
1010
|
+
stats[i].u,
|
|
1011
|
+
sx + s(14) + textWidth(stats[i].n, s(TYPE.headlineSmall.size)) + s(3),
|
|
1012
|
+
y + s(26),
|
|
1013
|
+
`${s(TYPE.labelMedium.size)}px ${FONT_SANS}`,
|
|
1014
|
+
C.onSurfaceVariant
|
|
1015
|
+
);
|
|
890
1016
|
}
|
|
891
|
-
textLeft(
|
|
1017
|
+
textLeft(
|
|
1018
|
+
ctx,
|
|
1019
|
+
stats[i].c,
|
|
1020
|
+
sx + s(14),
|
|
1021
|
+
y + s(44),
|
|
1022
|
+
`${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
|
|
1023
|
+
C.onSurfaceVariant
|
|
1024
|
+
);
|
|
892
1025
|
}
|
|
893
1026
|
const quip = opts.closest === null ? "一击即中,今日无需第二次落笔" : `从 #${opts.closest} 一步跨到 #1`;
|
|
894
1027
|
const footerTip = opts.canStartToday ? "ciyi.猜 山水 · 开启今日新题" : "明日零点换新题 · ciyi.排行榜";
|
|
@@ -900,8 +1033,8 @@ function renderWinCard(service, opts) {
|
|
|
900
1033
|
[],
|
|
901
1034
|
footerTip
|
|
902
1035
|
);
|
|
903
|
-
const footerFont = `${s(
|
|
904
|
-
const footerTipWidth = textWidth(footerTip, s(
|
|
1036
|
+
const footerFont = `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`;
|
|
1037
|
+
const footerTipWidth = textWidth(footerTip, s(TYPE.labelSmall.size));
|
|
905
1038
|
textLeftFit(
|
|
906
1039
|
ctx,
|
|
907
1040
|
`${opts.username} 拿下今日一词 · ${quip}`,
|
|
@@ -941,10 +1074,10 @@ function renderRankCard(service, opts) {
|
|
|
941
1074
|
if (!opts.entries.length) {
|
|
942
1075
|
textCenter(
|
|
943
1076
|
ctx,
|
|
944
|
-
"
|
|
1077
|
+
"排行榜还空着",
|
|
945
1078
|
panelX + panelW / 2,
|
|
946
1079
|
y + s(31),
|
|
947
|
-
`${s(
|
|
1080
|
+
`${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
|
|
948
1081
|
C.onSurfaceVariant
|
|
949
1082
|
);
|
|
950
1083
|
textCenter(
|
|
@@ -952,7 +1085,7 @@ function renderRankCard(service, opts) {
|
|
|
952
1085
|
"今日第一个猜中的人,会写在这里。",
|
|
953
1086
|
panelX + panelW / 2,
|
|
954
1087
|
y + s(61),
|
|
955
|
-
`${s(
|
|
1088
|
+
`${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
|
|
956
1089
|
C.onSurfaceVariant
|
|
957
1090
|
);
|
|
958
1091
|
} else {
|
|
@@ -974,35 +1107,49 @@ function renderRankCard(service, opts) {
|
|
|
974
1107
|
const px = contentLeft;
|
|
975
1108
|
const py = midY - posSize / 2;
|
|
976
1109
|
const podium = [MEDAL.gold, MEDAL.silver, MEDAL.bronze];
|
|
977
|
-
const pc = i < 3 ? { bg: podium[i], fg:
|
|
1110
|
+
const pc = i < 3 ? { bg: podium[i], fg: C.onPrimary } : { bg: C.surfaceContainerHighest, fg: C.onSurfaceVariant };
|
|
978
1111
|
fillRound(ctx, px, py, posSize, posSize, s(SHAPE.full), pc.bg);
|
|
979
|
-
textCenter(
|
|
1112
|
+
textCenter(
|
|
1113
|
+
ctx,
|
|
1114
|
+
String(i + 1),
|
|
1115
|
+
px + posSize / 2,
|
|
1116
|
+
midY,
|
|
1117
|
+
`${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelLarge.size)}px ${FONT_NUM}`,
|
|
1118
|
+
pc.fg
|
|
1119
|
+
);
|
|
980
1120
|
const name2 = e.username || "无名氏";
|
|
981
1121
|
const nameX = px + posSize + s(14);
|
|
982
|
-
const nameFont = `${s(
|
|
1122
|
+
const nameFont = `${s(TYPE.titleMedium.size)}px ${FONT_SANS}`;
|
|
983
1123
|
const score = String(e.score);
|
|
984
|
-
const unitFont = s(
|
|
1124
|
+
const unitFont = s(TYPE.labelSmall.size);
|
|
985
1125
|
const unitW = textWidth("次", unitFont);
|
|
986
|
-
const scoreFont = s(
|
|
1126
|
+
const scoreFont = s(TYPE.titleLarge.size);
|
|
987
1127
|
const scoreRight = contentRight - unitW - s(8);
|
|
988
1128
|
const scoreLeft = scoreRight - textWidth(score, scoreFont);
|
|
989
|
-
const meWidth = e.me ? textWidth("我", s(
|
|
1129
|
+
const meWidth = e.me ? textWidth("我", s(TYPE.labelSmall.size)) + s(18) : 0;
|
|
990
1130
|
const fittedName = ellipsize(
|
|
991
1131
|
name2,
|
|
992
1132
|
Math.max(0, scoreLeft - s(16) - nameX - meWidth),
|
|
993
|
-
s(
|
|
1133
|
+
s(TYPE.titleMedium.size)
|
|
994
1134
|
);
|
|
995
1135
|
textLeft(ctx, fittedName, nameX, midY, nameFont, C.onSurface);
|
|
996
1136
|
if (e.me) {
|
|
997
|
-
const nw = textWidth(fittedName, s(
|
|
998
|
-
textLeft(
|
|
1137
|
+
const nw = textWidth(fittedName, s(TYPE.titleMedium.size));
|
|
1138
|
+
textLeft(
|
|
1139
|
+
ctx,
|
|
1140
|
+
"我",
|
|
1141
|
+
nameX + nw + s(8),
|
|
1142
|
+
midY,
|
|
1143
|
+
`${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
|
|
1144
|
+
C.primary
|
|
1145
|
+
);
|
|
999
1146
|
}
|
|
1000
1147
|
textRight(
|
|
1001
1148
|
ctx,
|
|
1002
1149
|
score,
|
|
1003
1150
|
scoreRight,
|
|
1004
1151
|
midY,
|
|
1005
|
-
|
|
1152
|
+
`${EMPHASIZED_WEIGHT.title} ${scoreFont}px ${FONT_NUM}`,
|
|
1006
1153
|
C.onSurface
|
|
1007
1154
|
);
|
|
1008
1155
|
textRight(ctx, "次", contentRight, midY, `${unitFont}px ${FONT_SANS}`, C.onSurfaceVariant);
|
|
@@ -1011,10 +1158,10 @@ function renderRankCard(service, opts) {
|
|
|
1011
1158
|
if (opts.hidden > 0) {
|
|
1012
1159
|
textLeft(
|
|
1013
1160
|
ctx,
|
|
1014
|
-
|
|
1161
|
+
`…… 另有 ${opts.hidden} 人在榜`,
|
|
1015
1162
|
contentLeft,
|
|
1016
1163
|
ry + s(16),
|
|
1017
|
-
`${s(
|
|
1164
|
+
`${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
|
|
1018
1165
|
C.onSurfaceVariant
|
|
1019
1166
|
);
|
|
1020
1167
|
}
|
|
@@ -1032,7 +1179,7 @@ function renderRankCard(service, opts) {
|
|
|
1032
1179
|
"每猜中一日之词,记一次",
|
|
1033
1180
|
panelX,
|
|
1034
1181
|
iy + ih - s(30),
|
|
1035
|
-
`${s(
|
|
1182
|
+
`${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
|
|
1036
1183
|
C.onSurfaceVariant
|
|
1037
1184
|
);
|
|
1038
1185
|
});
|
|
@@ -1050,19 +1197,20 @@ var usage = `## 使用
|
|
|
1050
1197
|
| 指令 | 说明 |
|
|
1051
1198
|
| --- | --- |
|
|
1052
1199
|
| \`ciyi\` | 玩法 |
|
|
1053
|
-
| \`ciyi.猜 <词>\` |
|
|
1200
|
+
| \`ciyi.猜 <词>\` | 开始今日对局并报一个两字词 |
|
|
1054
1201
|
| \`ciyi.裸词 [开/关]\` | 开关本频道的无前缀续猜 |
|
|
1055
1202
|
| \`ciyi.排行榜\` | 累计猜中排行榜 |`;
|
|
1056
1203
|
var inject = { required: ["database"], optional: ["canvas"] };
|
|
1057
1204
|
var Config = import_koishi.Schema.object({
|
|
1058
1205
|
atReply: import_koishi.Schema.boolean().default(false).description("回复时 @ 用户。"),
|
|
1059
1206
|
quoteReply: import_koishi.Schema.boolean().default(false).description("回复时引用消息。"),
|
|
1060
|
-
|
|
1061
|
-
"
|
|
1207
|
+
enableDirectInput: import_koishi.Schema.boolean().default(true).description(
|
|
1208
|
+
"对局中直接发送两字词即可续猜,无需指令前缀。各频道可用「ciyi.裸词」临时切换。"
|
|
1062
1209
|
),
|
|
1063
1210
|
renderImage: import_koishi.Schema.boolean().default(true).description("把猜测板渲染成图片,复用 Koishi Canvas 服务。服务不可用时自动回退为等价文本。"),
|
|
1064
1211
|
maxHistory: import_koishi.Schema.number().default(10).min(0).description("猜测板最多列出的历史条数。最新一次猜测始终会列出。"),
|
|
1065
|
-
maxRank: import_koishi.Schema.number().default(10).min(0).description("排行榜最多显示的人数。")
|
|
1212
|
+
maxRank: import_koishi.Schema.number().default(10).min(0).description("排行榜最多显示的人数。"),
|
|
1213
|
+
retractDelay: import_koishi.Schema.number().min(0).default(0).description("自动撤回延迟(秒),0 表示不撤回。")
|
|
1066
1214
|
});
|
|
1067
1215
|
function getMiddlewareGuess(session) {
|
|
1068
1216
|
const message = session.event.message;
|
|
@@ -1128,9 +1276,9 @@ function apply(ctx, cfg) {
|
|
|
1128
1276
|
);
|
|
1129
1277
|
const logger = ctx.logger("ciyi");
|
|
1130
1278
|
const random = new import_koishi.Random(() => Math.random());
|
|
1131
|
-
const
|
|
1279
|
+
const busy = /* @__PURE__ */ new Set();
|
|
1132
1280
|
const middlewareOverrides = /* @__PURE__ */ new Map();
|
|
1133
|
-
const middlewareOn = /* @__PURE__ */ __name((channelId) => channelId === void 0 ? cfg.
|
|
1281
|
+
const middlewareOn = /* @__PURE__ */ __name((channelId) => channelId === void 0 ? cfg.enableDirectInput : middlewareOverrides.get(channelId) ?? cfg.enableDirectInput, "middlewareOn");
|
|
1134
1282
|
ctx.middleware(async (session, next) => {
|
|
1135
1283
|
if (!session.channelId || !middlewareOn(session.channelId)) return await next();
|
|
1136
1284
|
const guess = getMiddlewareGuess(session);
|
|
@@ -1152,7 +1300,7 @@ function apply(ctx, cfg) {
|
|
|
1152
1300
|
ctx.command("ciyi", "词意 · 按意思远近找词").action(async ({ session }) => {
|
|
1153
1301
|
return await wf(session);
|
|
1154
1302
|
});
|
|
1155
|
-
ctx.command("ciyi.猜 <guess:string>", "
|
|
1303
|
+
ctx.command("ciyi.猜 <guess:string>", "开题并报一个两字词").usage("例:ciyi.猜 山水").action(async ({ session }, guess) => {
|
|
1156
1304
|
return await c(session, guess?.trim());
|
|
1157
1305
|
});
|
|
1158
1306
|
ctx.command("ciyi.排行榜", "查看累计猜中排行榜").action(async ({ session }) => {
|
|
@@ -1160,7 +1308,7 @@ function apply(ctx, cfg) {
|
|
|
1160
1308
|
});
|
|
1161
1309
|
ctx.command("ciyi.裸词 [state:string]", "开关本频道的无前缀续猜").usage("例:ciyi.裸词(切换)· ciyi.裸词 开 · ciyi.裸词 关 · ciyi.裸词 状态").action(async ({ session }, state) => {
|
|
1162
1310
|
const result = resolveMiddlewareSwitch(
|
|
1163
|
-
cfg.
|
|
1311
|
+
cfg.enableDirectInput,
|
|
1164
1312
|
session.channelId === void 0 ? void 0 : middlewareOverrides.get(session.channelId),
|
|
1165
1313
|
state
|
|
1166
1314
|
);
|
|
@@ -1235,7 +1383,7 @@ function apply(ctx, cfg) {
|
|
|
1235
1383
|
__name(bestRank, "bestRank");
|
|
1236
1384
|
function textCard(title, ...sections) {
|
|
1237
1385
|
const body = sections.map((s2) => Array.isArray(s2) ? s2.filter(Boolean).join("\n") : s2).filter((s2) => !!s2 && !!s2.trim()).join("\n\n");
|
|
1238
|
-
return [`▍${title}`,
|
|
1386
|
+
return [`▍${title}`, body].filter(Boolean).join("\n");
|
|
1239
1387
|
}
|
|
1240
1388
|
__name(textCard, "textCard");
|
|
1241
1389
|
function neighborText(hint, side) {
|
|
@@ -1246,7 +1394,7 @@ function apply(ctx, cfg) {
|
|
|
1246
1394
|
function boardText(rows, hidden, attempts) {
|
|
1247
1395
|
const lines = [];
|
|
1248
1396
|
rows.forEach((row, i) => {
|
|
1249
|
-
if (row.gapBefore) lines.push(`
|
|
1397
|
+
if (row.gapBefore) lines.push(` …… 另有 ${row.gapBefore} 词未列`);
|
|
1250
1398
|
const { guess, rank, leftHint, rightHint } = row.history;
|
|
1251
1399
|
const mark = row.fresh ? "▸" : " ";
|
|
1252
1400
|
lines.push(
|
|
@@ -1259,7 +1407,7 @@ function apply(ctx, cfg) {
|
|
|
1259
1407
|
`词意 · 第 ${attempts} 次猜测`,
|
|
1260
1408
|
best === null ? null : `当前最佳 #${best} · ${tierOf(best).name}(${tierOf(best).note})`,
|
|
1261
1409
|
lines,
|
|
1262
|
-
hidden > 0 && !tail ? `
|
|
1410
|
+
hidden > 0 && !tail ? ` …… 另有 ${hidden} 词未列` : null,
|
|
1263
1411
|
"左邻更近答案,右邻更远;? 为隐去的字"
|
|
1264
1412
|
);
|
|
1265
1413
|
}
|
|
@@ -1267,12 +1415,16 @@ function apply(ctx, cfg) {
|
|
|
1267
1415
|
function introText(channelOn) {
|
|
1268
1416
|
return textCard(
|
|
1269
1417
|
"词意 · 按意思远近找词",
|
|
1270
|
-
|
|
1418
|
+
[
|
|
1419
|
+
`每天藏起一个两字词。`,
|
|
1420
|
+
`报一个两字词,回它与答案的语义排名。`,
|
|
1421
|
+
`名次越小越近,#1 就是答案本身。`
|
|
1422
|
+
],
|
|
1271
1423
|
[
|
|
1272
1424
|
`开始 ciyi.猜 山水 开题并报一个两字词`,
|
|
1273
1425
|
channelOn ? `续猜 直接发送两字词 仅限已开题且未结束` : null,
|
|
1274
|
-
|
|
1275
|
-
`切换 ciyi.裸词 开/关
|
|
1426
|
+
`排行榜 ciyi.排行榜 看谁猜中得最多`,
|
|
1427
|
+
`切换 ciyi.裸词 开/关 临时改本频道续猜方式`
|
|
1276
1428
|
].filter(Boolean),
|
|
1277
1429
|
[
|
|
1278
1430
|
`读板 ?好)企业(地? #467 · 沾边`,
|
|
@@ -1284,27 +1436,28 @@ function apply(ctx, cfg) {
|
|
|
1284
1436
|
`亲疏 咫尺 ≤10 毗邻 ≤50 相近 ≤200`,
|
|
1285
1437
|
` 沾边 ≤1000 疏远 ≤5000 天涯 5000+`
|
|
1286
1438
|
],
|
|
1287
|
-
`词库 ${allWords_default.length.toLocaleString()}
|
|
1439
|
+
`词库 ${allWords_default.length.toLocaleString()} 词,一日一词。`
|
|
1288
1440
|
);
|
|
1289
1441
|
}
|
|
1290
1442
|
__name(introText, "introText");
|
|
1291
1443
|
function middlewareSwitchText(o) {
|
|
1292
1444
|
const mark = "✅";
|
|
1293
1445
|
const state = o.on ? "开启" : "停用";
|
|
1294
|
-
const config = cfg.
|
|
1446
|
+
const config = cfg.enableDirectInput ? "开启" : "停用";
|
|
1295
1447
|
if (!o.temporary) {
|
|
1296
|
-
return `${mark} 裸词续猜 ·
|
|
1448
|
+
return `${mark} 裸词续猜 · 跟随插件配置
|
|
1449
|
+
当前${state}。${o.reverted ? "本次已复原。" : ""}`;
|
|
1297
1450
|
}
|
|
1298
1451
|
const hint = o.on ? "开题后直接发送两字词即可续猜" : "续猜改用「ciyi.猜 山水」";
|
|
1299
1452
|
return [
|
|
1300
|
-
`${mark} 裸词续猜 · 本频道临时${state}
|
|
1301
|
-
`${hint};再次发送「ciyi.裸词」复原`
|
|
1453
|
+
`${mark} 裸词续猜 · 本频道临时${state}`,
|
|
1454
|
+
`${hint};插件配置为${config};再次发送「ciyi.裸词」复原`
|
|
1302
1455
|
].join("\n");
|
|
1303
1456
|
}
|
|
1304
1457
|
__name(middlewareSwitchText, "middlewareSwitchText");
|
|
1305
1458
|
function winText(o) {
|
|
1306
1459
|
return textCard(
|
|
1307
|
-
"猜中了 · 今日已封题",
|
|
1460
|
+
"🏆 猜中了 · 今日已封题",
|
|
1308
1461
|
`答案 ${o.answer}`,
|
|
1309
1462
|
[
|
|
1310
1463
|
`猜测 ${o.attempts} 次`,
|
|
@@ -1312,23 +1465,24 @@ function apply(ctx, cfg) {
|
|
|
1312
1465
|
`累计 ${o.username} 已猜中 ${o.score} 次`,
|
|
1313
1466
|
o.neighbors.length ? `近旁 ${o.neighbors.join(" · ")}` : null
|
|
1314
1467
|
].filter(Boolean),
|
|
1315
|
-
o.canStartToday ? "
|
|
1468
|
+
o.canStartToday ? "继续发送「ciyi.猜 山水」开启今日新题" : "明日零点换新题 · 发送「ciyi.排行榜」看战绩"
|
|
1316
1469
|
);
|
|
1317
1470
|
}
|
|
1318
1471
|
__name(winText, "winText");
|
|
1319
1472
|
function rankText(entries, hidden) {
|
|
1320
1473
|
if (!entries.length) {
|
|
1321
1474
|
return textCard(
|
|
1322
|
-
"
|
|
1323
|
-
"
|
|
1475
|
+
"📋 排行榜还空着",
|
|
1476
|
+
"第一个猜中的人,名字会写在这里。",
|
|
1477
|
+
"发送「ciyi.猜 山水」开一局。"
|
|
1324
1478
|
);
|
|
1325
1479
|
}
|
|
1326
1480
|
return textCard(
|
|
1327
|
-
"词意每日挑战排行榜",
|
|
1481
|
+
"📋 词意每日挑战排行榜",
|
|
1328
1482
|
entries.map(
|
|
1329
1483
|
(e, i) => `${String(i + 1).padStart(2, "0")}. ${e.username || "无名氏"} ${e.score} 次`
|
|
1330
1484
|
),
|
|
1331
|
-
hidden > 0 ?
|
|
1485
|
+
hidden > 0 ? `…… 另有 ${hidden} 人在榜` : null
|
|
1332
1486
|
);
|
|
1333
1487
|
}
|
|
1334
1488
|
__name(rankText, "rankText");
|
|
@@ -1343,16 +1497,33 @@ function apply(ctx, cfg) {
|
|
|
1343
1497
|
}
|
|
1344
1498
|
}
|
|
1345
1499
|
__name(renderCard, "renderCard");
|
|
1500
|
+
const lastMessage = /* @__PURE__ */ new Map();
|
|
1501
|
+
async function send(session, content) {
|
|
1502
|
+
const ids = await session.send(content);
|
|
1503
|
+
const messageId = ids?.[0];
|
|
1504
|
+
if (!cfg.retractDelay || !messageId) return;
|
|
1505
|
+
const previous = lastMessage.get(session.channelId);
|
|
1506
|
+
if (previous) {
|
|
1507
|
+
const passed = Date.now() - previous.timestamp;
|
|
1508
|
+
if (passed < 118e3) {
|
|
1509
|
+
ctx.setTimeout(() => {
|
|
1510
|
+
session.bot.deleteMessage(session.channelId, previous.id).catch((error) => logger.debug("撤回消息 %s 失败:%s", previous.id, error.message));
|
|
1511
|
+
}, Math.max(0, cfg.retractDelay * 1e3 - passed));
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
lastMessage.set(session.channelId, { id: messageId, timestamp: Date.now() });
|
|
1515
|
+
}
|
|
1516
|
+
__name(send, "send");
|
|
1346
1517
|
async function sendCard(session, render, fallback) {
|
|
1347
1518
|
const image = await renderCard(render);
|
|
1348
1519
|
const prefix = [];
|
|
1349
1520
|
if (cfg.quoteReply && session.messageId) prefix.push(import_koishi.h.quote(session.messageId));
|
|
1350
1521
|
if (cfg.atReply) prefix.push(import_koishi.h.at(session.userId), (0, import_koishi.h)("p"));
|
|
1351
1522
|
if (image) {
|
|
1352
|
-
await
|
|
1523
|
+
await send(session, [...prefix, image]);
|
|
1353
1524
|
return;
|
|
1354
1525
|
}
|
|
1355
|
-
await
|
|
1526
|
+
await send(session, [...prefix, ...import_koishi.h.normalize(fallback)]);
|
|
1356
1527
|
}
|
|
1357
1528
|
__name(sendCard, "sendCard");
|
|
1358
1529
|
async function wf(session) {
|
|
@@ -1429,6 +1600,18 @@ function apply(ctx, cfg) {
|
|
|
1429
1600
|
return await sendMsg(session, `⚠️ 「${guess}」不在词库里
|
|
1430
1601
|
换个常见些的词试试。`);
|
|
1431
1602
|
}
|
|
1603
|
+
if (session.channelId && busy.has(session.channelId)) {
|
|
1604
|
+
return await sendMsg(session, "⏳ 上一手还在处理\n稍等再发一次。");
|
|
1605
|
+
}
|
|
1606
|
+
if (session.channelId) busy.add(session.channelId);
|
|
1607
|
+
try {
|
|
1608
|
+
return await guessOnce(session, guess);
|
|
1609
|
+
} finally {
|
|
1610
|
+
if (session.channelId) busy.delete(session.channelId);
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
__name(c, "c");
|
|
1614
|
+
async function guessOnce(session, guess) {
|
|
1432
1615
|
const gameInfo = await getTodayGame(session);
|
|
1433
1616
|
if (!gameInfo) {
|
|
1434
1617
|
return await sendMsg(
|
|
@@ -1439,7 +1622,7 @@ function apply(ctx, cfg) {
|
|
|
1439
1622
|
if (gameInfo.isOver) {
|
|
1440
1623
|
return await sendMsg(
|
|
1441
1624
|
session,
|
|
1442
|
-
`💡 今日挑战已结束,答案是「${gameInfo.answer}
|
|
1625
|
+
`💡 今日挑战已结束,答案是「${gameInfo.answer}」
|
|
1443
1626
|
明日零点换新题,发送「ciyi.排行榜」看战绩。`
|
|
1444
1627
|
);
|
|
1445
1628
|
}
|
|
@@ -1499,7 +1682,7 @@ function apply(ctx, cfg) {
|
|
|
1499
1682
|
const entry = getHistory(guess, rankList);
|
|
1500
1683
|
if (!entry) {
|
|
1501
1684
|
logger.warn(`「${guess}」不在 ${gameInfo.answer} 的榜单中`);
|
|
1502
|
-
return await sendMsg(session, `⚠️ 「${guess}
|
|
1685
|
+
return await sendMsg(session, `⚠️ 「${guess}」不在今日的榜里
|
|
1503
1686
|
换个词试试。`);
|
|
1504
1687
|
}
|
|
1505
1688
|
const history = [...gameInfo.history, entry];
|
|
@@ -1524,7 +1707,7 @@ function apply(ctx, cfg) {
|
|
|
1524
1707
|
boardText(rows, hidden, history.length)
|
|
1525
1708
|
);
|
|
1526
1709
|
}
|
|
1527
|
-
__name(
|
|
1710
|
+
__name(guessOnce, "guessOnce");
|
|
1528
1711
|
async function fetchCiYi(word) {
|
|
1529
1712
|
const url = `https://ci-ying.oss-cn-zhangjiakou.aliyuncs.com/v1/ci-yi-list/${word}.txt`;
|
|
1530
1713
|
try {
|
|
@@ -1547,7 +1730,7 @@ function apply(ctx, cfg) {
|
|
|
1547
1730
|
const prefix = [];
|
|
1548
1731
|
if (cfg.quoteReply && session.messageId) prefix.push(import_koishi.h.quote(session.messageId));
|
|
1549
1732
|
if (cfg.atReply) prefix.push(import_koishi.h.at(session.userId), (0, import_koishi.h)("p"));
|
|
1550
|
-
await
|
|
1733
|
+
await send(session, [...prefix, ...import_koishi.h.normalize(msg)]);
|
|
1551
1734
|
}
|
|
1552
1735
|
__name(sendMsg, "sendMsg");
|
|
1553
1736
|
}
|