koishi-plugin-cchess 2.0.0 → 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 CHANGED
@@ -19,7 +19,7 @@ yarn add koishi-plugin-cchess
19
19
  | `cchess.悔棋 [同意/拒绝]` | 请求悔棋,也可直接回复同意或拒绝表决 |
20
20
  | `cchess.认输` | 认输 |
21
21
  | `cchess.结束` | 强制结束棋局 |
22
- | `cchess.棋绩 [@某人/榜] [胜场/输场] [人数]` | 查询战绩与排行榜 |
22
+ | `cchess.战绩 [@某人/榜] [胜场/输场] [人数]` | 查询战绩与排行榜 |
23
23
  | `cchess.查看云库残局 [DTM/DTC]` | 云库残局统计 |
24
24
 
25
25
  发送 `cchess.开始` 即入座,双方就位后自动开局,无需单独的加入与退出指令。
package/lib/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export interface Config {
7
7
  pieceSkin: string;
8
8
  defaultEngineThinkingDepth: number;
9
9
  allowFreePieceMovementInHumanMachineMode: boolean;
10
+ enableDirectInput: boolean;
10
11
  defaultMaxLeaderboardEntries: number;
11
12
  retractDelay: number;
12
13
  isChessImageWithOutlineEnabled: boolean;
package/lib/index.js CHANGED
@@ -268,6 +268,7 @@ var Config = import_koishi.Schema.object({
268
268
  boardSkin: import_koishi.Schema.union(boardSkins).default("象甲2023棋盘").description(`棋盘皮肤。`),
269
269
  pieceSkin: import_koishi.Schema.union(pieceSkins).default("象甲棋子").description(`棋子皮肤。`),
270
270
  allowFreePieceMovementInHumanMachineMode: import_koishi.Schema.boolean().default(false).description(`人机模式下,允许所有人自由移动棋子,开启后可以不入座直接开始人机对局。`),
271
+ enableDirectInput: import_koishi.Schema.boolean().default(true).description(`对局中直接发送棋谱或「同意」「拒绝」即可落子与应答,无需指令前缀。`),
271
272
  defaultEngineThinkingDepth: import_koishi.Schema.number().min(0).max(100).default(10).description(`默认引擎思考深度,越高 AI 棋力越强,耗时也越长(小于 1 时按 1 计算)。由于 Nodejs 不支持 SIMD,所以不建议设置过高。`),
272
273
  defaultMaxLeaderboardEntries: import_koishi.Schema.number().min(0).default(10).description(`排行榜默认显示的人数。`),
273
274
  retractDelay: import_koishi.Schema.number().min(0).default(0).description(`自动撤回延迟(秒),0 表示不撤回。`),
@@ -275,7 +276,6 @@ var Config = import_koishi.Schema.object({
275
276
  imageType: import_koishi.Schema.union(["png", "jpeg", "webp"]).default("png").description(`发送的图片格式。`),
276
277
  isChessImageWithOutlineEnabled: import_koishi.Schema.boolean().default(true).description(`给棋盘图片加上坐标外框。关闭后出图明显更快,但没有外框,玩起来可能会比较累。`)
277
278
  });
278
- var RULE = "━━━━━━━━━━━━━━";
279
279
  var SIDE_ICONS = { 红方: "🔴", 黑方: "⚫" };
280
280
  var MEDALS = ["🥇", "🥈", "🥉"];
281
281
  function withSideIcon(side) {
@@ -292,11 +292,11 @@ function isLine(line) {
292
292
  __name(isLine, "isLine");
293
293
  function panel(options) {
294
294
  const { icon = "♟️", title, at, body = [], tips = [], image } = options;
295
- const lines = [`${icon} ${title}`, RULE];
295
+ const lines = [`${icon} ${title}`];
296
296
  if (at) lines.push(`@${at}`);
297
297
  lines.push(...body.filter(isLine));
298
298
  const validTips = tips.filter(isLine).filter(Boolean);
299
- if (validTips.length) lines.push(RULE, ...validTips);
299
+ if (validTips.length) lines.push(...validTips);
300
300
  if (image) lines.push(image);
301
301
  return lines.join("\n");
302
302
  }
@@ -430,6 +430,7 @@ function apply(ctx, config) {
430
430
  }
431
431
  __name(matchRegretDecision, "matchRegretDecision");
432
432
  ctx.middleware(async (session, next) => {
433
+ if (!config.enableDirectInput) return next();
433
434
  const { channelId, content, userId } = session;
434
435
  if (!channelId || !content) return next();
435
436
  const decision = matchRegretDecision(content);
@@ -441,6 +442,7 @@ function apply(ctx, config) {
441
442
  await resolveRegret(session, decision === "agree");
442
443
  });
443
444
  ctx.middleware(async (session, next) => {
445
+ if (!config.enableDirectInput) return next();
444
446
  const { channelId, content, userId, username } = session;
445
447
  if (!channelId || !content) return next();
446
448
  const moveOperation = content.trim();
@@ -551,7 +553,7 @@ function apply(ctx, config) {
551
553
  const gameRecord = await getGameRecord(channelId);
552
554
  if (!gameRecord.isStarted) {
553
555
  return await sendMessage(session, panel({
554
- icon: "⚠️",
556
+ icon: "💡",
555
557
  title: "棋盘尚且空空",
556
558
  at: username,
557
559
  body: ["当前没有进行中的对局,无需收拾残局。"],
@@ -577,7 +579,7 @@ function apply(ctx, config) {
577
579
  if (busyChannels.has(channelId)) {
578
580
  return await sendMessage(session, panel({
579
581
  icon: "⏳",
580
- title: "棋局正在推演",
582
+ title: "对局正在推演",
581
583
  at: username,
582
584
  body: ["上一着棋尚未结算,稍候片刻。"]
583
585
  }));
@@ -593,7 +595,7 @@ function apply(ctx, config) {
593
595
  icon: "⏳",
594
596
  title: "悔棋请求待决",
595
597
  at: username,
596
- body: ["对方的悔棋请求尚未答复,棋局暂歇。"],
598
+ body: ["对方的悔棋请求尚未答复,对局暂歇。"],
597
599
  tips: ["对方回复「同意」或「拒绝」即可表决"]
598
600
  }));
599
601
  }
@@ -727,8 +729,8 @@ function apply(ctx, config) {
727
729
  }
728
730
  if (gameRecord.moveList.length < 1) {
729
731
  return await sendMessage(session, panel({
730
- icon: "⚠️",
731
- title: "棋局初开",
732
+ icon: "💡",
733
+ title: "对局初开",
732
734
  at: username,
733
735
  body: ["一子未落,无悔可言。"]
734
736
  }));
@@ -755,12 +757,23 @@ function apply(ctx, config) {
755
757
  }));
756
758
  }
757
759
  await ctx.database.set("cchess_game_records", { channelId }, { isRegretRequest: true });
760
+ ctx.setTimeout(async () => {
761
+ const current = await getGameRecord(channelId);
762
+ if (!current?.isRegretRequest) return;
763
+ await ctx.database.set("cchess_game_records", { channelId }, { isRegretRequest: false });
764
+ await sendMessage(session, panel({
765
+ icon: "⏳",
766
+ title: "悔棋请求已作罢",
767
+ body: ["等了 30 秒没有等到答复,请求自动撤回。"],
768
+ tips: ["发送「cchess.悔棋」可以再提一次。"]
769
+ }));
770
+ }, 30 * 1e3);
758
771
  return await sendMessage(session, panel({
759
772
  icon: "⏳",
760
773
  title: "悔棋请求已送出",
761
774
  at: username,
762
775
  body: [field("等待答复", withSideIcon(sideString))],
763
- tips: ["对方回复「同意」或「拒绝」即可表决"]
776
+ tips: ["对方回复「同意」或「拒绝」即可表决;30 秒等不到答复会自动作废"]
764
777
  }));
765
778
  });
766
779
  ctx.command("cchess.认输", "认输并结束对局").action(async ({ session }) => {
@@ -823,7 +836,7 @@ function apply(ctx, config) {
823
836
  session,
824
837
  byLose ? "lose" : "win",
825
838
  byLose ? "总输场排行榜" : "总胜场排行榜",
826
- byLose ? "🍂" : "🏆",
839
+ "📋",
827
840
  size
828
841
  );
829
842
  }
@@ -842,7 +855,7 @@ function apply(ctx, config) {
842
855
  win: 0
843
856
  });
844
857
  return await sendMessage(session, panel({
845
- icon: "🔍",
858
+ icon: "📋",
846
859
  title: "棋士档案",
847
860
  at: session.username,
848
861
  body: [field("查询对象", username), "尚无对局记录,静待首战。"],
@@ -853,7 +866,7 @@ function apply(ctx, config) {
853
866
  const total = win + lose;
854
867
  const winRate = total === 0 ? "—" : `${(win / total * 100).toFixed(1)}%`;
855
868
  return await sendMessage(session, panel({
856
- icon: "🔍",
869
+ icon: "📋",
857
870
  title: "棋士档案",
858
871
  at: session.username,
859
872
  body: [
@@ -867,8 +880,8 @@ function apply(ctx, config) {
867
880
  });
868
881
  function notStartedPanel(username) {
869
882
  return panel({
870
- icon: "⚠️",
871
- title: "棋局尚未开始",
883
+ icon: "💡",
884
+ title: "对局尚未开始",
872
885
  at: username,
873
886
  body: ["当前频道还没有进行中的对局。"],
874
887
  tips: ["「cchess.开始」入座并对弈", "「cchess.开始 人机」挑战皮卡鱼"]
@@ -877,8 +890,8 @@ function apply(ctx, config) {
877
890
  __name(notStartedPanel, "notStartedPanel");
878
891
  function alreadyStartedPanel(username) {
879
892
  return panel({
880
- icon: "⚠️",
881
- title: "棋局已经开战",
893
+ icon: "💡",
894
+ title: "对局已经开战",
882
895
  at: username,
883
896
  body: ["本局尚未结束,不必重开。"],
884
897
  tips: ["「cchess.认输」结束本局", "「cchess.结束」强制清盘"]
@@ -887,7 +900,7 @@ function apply(ctx, config) {
887
900
  __name(alreadyStartedPanel, "alreadyStartedPanel");
888
901
  function notJoinedPanel(username) {
889
902
  return panel({
890
- icon: "⚠️",
903
+ icon: "💡",
891
904
  title: "你尚未入座",
892
905
  at: username,
893
906
  body: ["先入座,才好落子。"],
@@ -906,7 +919,7 @@ function apply(ctx, config) {
906
919
  __name(analyzingPanel, "analyzingPanel");
907
920
  function notYourTurnPanel(username, yourSide, currentSide) {
908
921
  return panel({
909
- icon: "⚠️",
922
+ icon: "",
910
923
  title: "尚未轮到你",
911
924
  at: username,
912
925
  body: [field("你的阵营", withSideIcon(yourSide)), field("当前轮走", withSideIcon(currentSide))],
@@ -916,7 +929,7 @@ function apply(ctx, config) {
916
929
  __name(notYourTurnPanel, "notYourTurnPanel");
917
930
  function invalidMovePanel(username, reason) {
918
931
  return panel({
919
- icon: "",
932
+ icon: "⚠️",
920
933
  title: reason,
921
934
  at: username,
922
935
  body: ["请确认着法是否合乎棋规。"],
@@ -937,7 +950,7 @@ function apply(ctx, config) {
937
950
  function invalidLeaderboardSizePanel(username) {
938
951
  return panel({
939
952
  icon: "⚠️",
940
- title: "榜单人数有误",
953
+ title: "排行榜人数有误",
941
954
  at: username,
942
955
  body: ["请输入不小于 0 的整数。"],
943
956
  tips: ["例如:cchess.战绩 榜 10"]
@@ -945,7 +958,12 @@ function apply(ctx, config) {
945
958
  }
946
959
  __name(invalidLeaderboardSizePanel, "invalidLeaderboardSizePanel");
947
960
  async function renderBoard(channelId) {
948
- return import_koishi.h.image(await drawChessBoard(channelId), imageMimeType).toString();
961
+ try {
962
+ return import_koishi.h.image(await drawChessBoard(channelId), imageMimeType).toString();
963
+ } catch (error) {
964
+ logger.warn("棋盘渲染失败,本次只发文本:%s", error.message);
965
+ return void 0;
966
+ }
949
967
  }
950
968
  __name(renderBoard, "renderBoard");
951
969
  async function settleVictory(channelId, winSide, loseSide, title, options = {}) {
@@ -1044,7 +1062,7 @@ function apply(ctx, config) {
1044
1062
  icon: "⚠️",
1045
1063
  title: "悔棋被拒",
1046
1064
  at: username,
1047
- body: ["落子无悔,棋局继续。"],
1065
+ body: ["落子无悔,对局继续。"],
1048
1066
  image: await renderBoard(channelId)
1049
1067
  }));
1050
1068
  }
@@ -1640,15 +1658,15 @@ function apply(ctx, config) {
1640
1658
  for (let col = 0; col < 9; col++) {
1641
1659
  const x = columnAt(isFlipBoard ? 8 - col : col);
1642
1660
  label(FILES[col], x, 30, 22, FRAME.onSurfaceVariant);
1643
- label(String(col + 1), x, 64, 22, FRAME.outline);
1661
+ label(String(col + 1), x, 64, 22, FRAME.onSurfaceVariant);
1644
1662
  label(RED_FILES[col], x, FRAME_HEIGHT - 62, 24, FRAME.onSurfaceVariant);
1645
- label(FILES[col], x, FRAME_HEIGHT - 26, 22, FRAME.outline);
1663
+ label(FILES[col], x, FRAME_HEIGHT - 26, 22, FRAME.onSurfaceVariant);
1646
1664
  }
1647
1665
  for (let row = 0; row < 10; row++) {
1648
1666
  const y = rowAt(isFlipBoard ? 9 - row : row);
1649
1667
  const text = String(9 - row);
1650
- label(text, 19, y, 22, FRAME.outline);
1651
- label(text, FRAME_WIDTH - 19, y, 22, FRAME.outline);
1668
+ label(text, 19, y, 22, FRAME.onSurfaceVariant);
1669
+ label(text, FRAME_WIDTH - 19, y, 22, FRAME.onSurfaceVariant);
1652
1670
  }
1653
1671
  return canvas.toBuffer(imageMimeType);
1654
1672
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-cchess",
3
3
  "description": "Koishi 的中国象棋插件。",
4
- "version": "2.0.0",
4
+ "version": "2.1.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [