koishi-plugin-ciyi 1.2.8 → 1.2.10

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 ADDED
@@ -0,0 +1,26 @@
1
+ # koishi-plugin-ciyi
2
+
3
+ 词意猜词:每天一个两字词,在群里开猜
4
+
5
+ ## 安装
6
+
7
+ ```sh
8
+ yarn add koishi-plugin-ciyi
9
+ ```
10
+
11
+ 在 Koishi 配置中启用,并提供 database 服务;需要图片输出时再提供 canvas 服务。
12
+
13
+ ## 指令
14
+
15
+ | 指令 | 说明 |
16
+ | --- | --- |
17
+ | `ciyi` | 玩法 |
18
+ | `ciyi.猜 <词>` | 开始今日游戏并提交猜测 |
19
+ | `ciyi.裸词 [开/关]` | 切换本群无前缀续猜 |
20
+ | `ciyi.排行榜` | 猜中次数排行 |
21
+
22
+ 首次用 `ciyi.猜 <词>` 开题,之后可直接发送两字词继续猜。裸词设置只对当前群生效,重启后恢复默认。
23
+
24
+ ## 许可证
25
+
26
+ 可按 [Apache-2.0](LICENSE-APACHE) 或 [MIT](LICENSE-MIT) 使用。
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\u8BBE\u7F6E\u6307\u4EE4\u522B\u540D\u540E\uFF0C\u53D1\u9001 `ciyi` \u67E5\u770B\u73A9\u6CD5\u3002\u6BCF\u65E5\u85CF\u4E00\u4E2A\u4E24\u5B57\u8BCD\uFF0C\u4F7F\u7528 `ciyi.\u731C <\u8BCD>` \u63D0\u4EA4\u7B2C\u4E00\u6B21\u731C\u6D4B\u5E76\u5F00\u9898\uFF1B\u5F00\u9898\u540E\u53EF\u76F4\u63A5\u53D1\u9001\u4E24\u5B57\u8BCD\u7EE7\u7EED\u731C\u6D4B\u3002\n\n## \u6307\u4EE4\n\n| \u6307\u4EE4 | \u8BF4\u660E |\n| --- | --- |\n| `ciyi` | \u73A9\u6CD5\u8BF4\u660E |\n| `ciyi.\u731C <\u8BCD>` | \u5F00\u59CB\u4ECA\u65E5\u6E38\u620F\u5E76\u63D0\u4EA4\u731C\u6D4B |\n| `ciyi.\u6392\u884C\u699C` | \u731C\u4E2D\u6B21\u6570\u699C |";
4
+ export declare const usage = "## \u4F7F\u7528\n\n\u8BBE\u7F6E\u6307\u4EE4\u522B\u540D\u540E\uFF0C\u53D1\u9001 `ciyi` \u67E5\u770B\u73A9\u6CD5\u3002\u6BCF\u65E5\u85CF\u4E00\u4E2A\u4E24\u5B57\u8BCD\uFF0C\u9996\u6B21\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\u7FA4\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\u6E38\u620F\u5E76\u63D0\u4EA4\u731C\u6D4B |\n| `ciyi.\u88F8\u8BCD [\u5F00/\u5173]` | \u5207\u6362\u672C\u7FA4\u65E0\u524D\u7F00\u7EED\u731C |\n| `ciyi.\u6392\u884C\u699C` | \u731C\u4E2D\u6B21\u6570\u6392\u884C |";
5
5
  export declare const inject: {
6
6
  required: string[];
7
7
  optional: string[];
@@ -40,10 +40,27 @@ export interface CiyiRank {
40
40
  }
41
41
  export type { History };
42
42
  /**
43
- * 裸词中间件只接受一条完整、无修饰的两字中文纯文本消息。
44
- * 引用、@、图片、富文本以及机器人消息都可能只是普通聊天的一部分,不能据此猜词。
43
+ * 裸词中间件只看消息里的文本,引用、@、图片等非文本元素一律过滤掉:
44
+ * 回复时带上引用或 @ 是常见操作,不应因此把一条两字词挡在门外。
45
+ * 剩下的正文仍必须是一条完整、无多余空白的两字中文词。
45
46
  */
46
47
  export declare function getMiddlewareGuess(session: Session): string | null;
47
48
  /** 裸词只能加入已开始且未封题的游戏,不能开题,也不重复提交旧猜测。 */
48
49
  export declare function canHandleMiddlewareGuess(game: Ciyi | null | undefined, guess: string): boolean;
50
+ export type MiddlewareSwitch = {
51
+ error: string;
52
+ } | {
53
+ /** 写回本群覆盖表的新值;undefined 表示清除覆盖,回到插件配置 */
54
+ override: boolean | undefined;
55
+ /** 切换后的生效状态 */
56
+ on: boolean;
57
+ /** 本次是否清除了本群已有的临时改动 */
58
+ reverted: boolean;
59
+ };
60
+ /**
61
+ * 裸词开关是「插件配置」与「本群临时相反」之间的往返:本群从不保留与配置相同的冗余覆盖,
62
+ * 状态只有两种来历 —— 本群临时改动,或跟随插件配置,回复也因此总能如实相告。
63
+ * 无参调用是切换,显式 开/关 与配置同值时视作复原,状态 只查不改。
64
+ */
65
+ export declare function resolveMiddlewareSwitch(config: boolean, override: boolean | undefined, action: string | null | undefined): MiddlewareSwitch;
49
66
  export declare function apply(ctx: Context, cfg: Config): void;
package/lib/index.js CHANGED
@@ -18,17 +18,18 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
20
  // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
23
  Config: () => Config,
24
24
  apply: () => apply,
25
25
  canHandleMiddlewareGuess: () => canHandleMiddlewareGuess,
26
26
  getMiddlewareGuess: () => getMiddlewareGuess,
27
27
  inject: () => inject,
28
28
  name: () => name,
29
+ resolveMiddlewareSwitch: () => resolveMiddlewareSwitch,
29
30
  usage: () => usage
30
31
  });
31
- module.exports = __toCommonJS(index_exports);
32
+ module.exports = __toCommonJS(src_exports);
32
33
  var import_koishi = require("koishi");
33
34
 
34
35
  // src/data/allWords.json
@@ -565,7 +566,7 @@ function renderBoardCard(service, opts) {
565
566
  __name(renderBoardCard, "renderBoardCard");
566
567
  function renderIntroCard(service, opts) {
567
568
  const innerW = s(720);
568
- const innerH = s(688);
569
+ const innerH = s(716);
569
570
  return toPng(service, innerW, innerH, (ctx, ix, iy, iw, ih) => {
570
571
  let y = iy + drawHeader(ctx, ix, iy, iw, "按意思远近找词 · 每日一题", {
571
572
  big: opts.words.toLocaleString(),
@@ -574,7 +575,7 @@ function renderIntroCard(service, opts) {
574
575
  y += s(20);
575
576
  y += drawSectionTitle(ctx, ix + s(22), y, iw - s(44), "玩法");
576
577
  y += s(11);
577
- const p1h = s(76);
578
+ const p1h = s(104);
578
579
  drawPanel(ctx, ix + s(22), y, iw - s(44), p1h);
579
580
  textLeft(ctx, "开始", ix + s(38), y + s(24), `${s(14)}px ${FONT_SERIF}`, C.seal);
580
581
  textLeft(ctx, "ciyi.猜 山水", ix + s(96), y + s(24), `${s(13.5)}px ${FONT_NUM}`, C.ink);
@@ -587,9 +588,20 @@ function renderIntroCard(service, opts) {
587
588
  `${s(14)}px ${FONT_SERIF}`,
588
589
  C.ink
589
590
  );
590
- textLeft(ctx, "排行", ix + s(38), y + s(52), `${s(14)}px ${FONT_SERIF}`, C.seal);
591
- textLeft(ctx, "ciyi.排行榜", ix + s(96), y + s(52), `${s(13.5)}px ${FONT_NUM}`, C.ink);
592
- textLeft(ctx, "看谁猜中得最多", ix + s(230), y + s(52), `${s(14)}px ${FONT_SERIF}`, C.ink);
591
+ textLeft(ctx, "切换", ix + s(38), y + s(52), `${s(14)}px ${FONT_SERIF}`, C.seal);
592
+ textLeft(ctx, "ciyi.裸词 开/关", ix + s(96), y + s(52), `${s(13.5)}px ${FONT_NUM}`, C.ink);
593
+ textLeftFit(
594
+ ctx,
595
+ "临时改本群的续猜方式",
596
+ ix + s(230),
597
+ y + s(52),
598
+ iw - s(268),
599
+ `${s(14)}px ${FONT_SERIF}`,
600
+ C.ink
601
+ );
602
+ textLeft(ctx, "排行", ix + s(38), y + s(80), `${s(14)}px ${FONT_SERIF}`, C.seal);
603
+ textLeft(ctx, "ciyi.排行榜", ix + s(96), y + s(80), `${s(13.5)}px ${FONT_NUM}`, C.ink);
604
+ textLeft(ctx, "看谁猜中得最多", ix + s(230), y + s(80), `${s(14)}px ${FONT_SERIF}`, C.ink);
593
605
  y += p1h + s(19);
594
606
  y += drawSectionTitle(ctx, ix + s(22), y, iw - s(44), "读板");
595
607
  y += s(11);
@@ -871,35 +883,34 @@ __name(renderRankCard, "renderRankCard");
871
883
  var name = "ciyi";
872
884
  var usage = `## 使用
873
885
 
874
- 设置指令别名后,发送 \`ciyi\` 查看玩法。每日藏一个两字词,使用 \`ciyi.猜 <词>\` 提交第一次猜测并开题;开题后可直接发送两字词继续猜测。
886
+ 设置指令别名后,发送 \`ciyi\` 查看玩法。每日藏一个两字词,首次用 \`ciyi.猜 <词>\` 开题,之后可以直接发送两字词继续猜。裸词设置只对当前群生效,重启后恢复默认。
875
887
 
876
888
  ## 指令
877
889
 
878
890
  | 指令 | 说明 |
879
891
  | --- | --- |
880
- | \`ciyi\` | 玩法说明 |
892
+ | \`ciyi\` | 玩法 |
881
893
  | \`ciyi.猜 <词>\` | 开始今日游戏并提交猜测 |
882
- | \`ciyi.排行榜\` | 猜中次数榜 |`;
894
+ | \`ciyi.裸词 [开/关]\` | 切换本群无前缀续猜 |
895
+ | \`ciyi.排行榜\` | 猜中次数排行 |`;
883
896
  var inject = { required: ["database"], optional: ["canvas"] };
884
897
  var Config = import_koishi.Schema.object({
885
898
  atReply: import_koishi.Schema.boolean().default(false).description("响应时 @"),
886
899
  quoteReply: import_koishi.Schema.boolean().default(false).description("响应时引用"),
887
- isEnableMiddleware: import_koishi.Schema.boolean().default(true).description("是否启用中间件(若启用,已开题时可以不使用指令直接猜测)"),
900
+ isEnableMiddleware: import_koishi.Schema.boolean().default(true).description(
901
+ "是否启用中间件(若启用,已开题时可以不使用指令直接猜测;ciyi.裸词 指令可在单个群内临时切换)"
902
+ ),
888
903
  renderImage: import_koishi.Schema.boolean().default(true).description("渲染图片(复用 Koishi Canvas 服务;不可用时使用等价文本)"),
889
904
  maxHistory: import_koishi.Schema.number().default(10).min(0).description("猜测板最多列出的历史条数(最新一次猜测始终会列出)"),
890
905
  maxRank: import_koishi.Schema.number().default(10).min(0).description("最大排行榜人数")
891
906
  });
892
907
  function getMiddlewareGuess(session) {
893
908
  const message = session.event.message;
894
- if (!message || message.quote || session.event.user?.isBot || message.user?.isBot) {
895
- return null;
896
- }
897
- const elements = message.elements ?? [];
898
- if (elements.length !== 1 || elements[0].type !== "text") {
909
+ if (!message || session.event.user?.isBot || message.user?.isBot) {
899
910
  return null;
900
911
  }
901
- const text = elements[0].attrs.content;
902
- if (typeof text !== "string" || text !== text.trim() || Array.from(text).length !== 2 || !/^\p{Script=Han}{2}$/u.test(text) || !allWords_default.includes(text)) {
912
+ const text = (message.elements ?? []).filter((element) => element.type === "text").map((element) => element.attrs.content).filter((content) => typeof content === "string").join("");
913
+ if (text !== text.trim() || Array.from(text).length !== 2 || !/^\p{Script=Han}{2}$/u.test(text) || !allWords_default.includes(text)) {
903
914
  return null;
904
915
  }
905
916
  return text;
@@ -911,6 +922,24 @@ function canHandleMiddlewareGuess(game, guess) {
911
922
  return guess === game.answer || game.rankList?.includes(guess) === true;
912
923
  }
913
924
  __name(canHandleMiddlewareGuess, "canHandleMiddlewareGuess");
925
+ function resolveMiddlewareSwitch(config, override, action) {
926
+ const arg = action?.trim();
927
+ if (!arg) {
928
+ return override === void 0 ? { override: !config, on: !config, reverted: false } : { override: void 0, on: config, reverted: true };
929
+ }
930
+ if (arg === "状态") {
931
+ return { override, on: override ?? config, reverted: false };
932
+ }
933
+ const on = arg === "开" || arg === "开启";
934
+ if (!on && arg !== "关" && arg !== "关闭") {
935
+ return { error: "裸词开关只认 开 / 关 / 状态" };
936
+ }
937
+ if (on === config) {
938
+ return { override: void 0, on: config, reverted: override !== void 0 };
939
+ }
940
+ return { override: on, on, reverted: false };
941
+ }
942
+ __name(resolveMiddlewareSwitch, "resolveMiddlewareSwitch");
914
943
  function apply(ctx, cfg) {
915
944
  ctx.model.extend(
916
945
  "ciyi",
@@ -940,25 +969,26 @@ function apply(ctx, cfg) {
940
969
  const logger = ctx.logger("ciyi");
941
970
  const random = new import_koishi.Random(() => Math.random());
942
971
  const RULE = "────────────";
943
- if (cfg.isEnableMiddleware) {
944
- ctx.middleware(async (session, next) => {
945
- const guess = getMiddlewareGuess(session);
946
- if (!guess || !session.channelId) return await next();
947
- try {
948
- const [game] = await ctx.database.get("ciyi", {
949
- channelId: session.channelId
950
- });
951
- if (!canHandleMiddlewareGuess(game, guess)) return await next();
952
- } catch (error) {
953
- logger.warn(
954
- "中间件读取游戏状态失败,本次消息不作猜测:%s",
955
- error?.message ?? error
956
- );
957
- return await next();
958
- }
959
- return await session.execute(`ciyi.猜 ${guess}`);
960
- });
961
- }
972
+ const middlewareOverrides = /* @__PURE__ */ new Map();
973
+ const middlewareOn = /* @__PURE__ */ __name((channelId) => channelId === void 0 ? cfg.isEnableMiddleware : middlewareOverrides.get(channelId) ?? cfg.isEnableMiddleware, "middlewareOn");
974
+ ctx.middleware(async (session, next) => {
975
+ if (!session.channelId || !middlewareOn(session.channelId)) return await next();
976
+ const guess = getMiddlewareGuess(session);
977
+ if (!guess) return await next();
978
+ try {
979
+ const [game] = await ctx.database.get("ciyi", {
980
+ channelId: session.channelId
981
+ });
982
+ if (!canHandleMiddlewareGuess(game, guess)) return await next();
983
+ } catch (error) {
984
+ logger.warn(
985
+ "中间件读取游戏状态失败,本次消息不作猜测:%s",
986
+ error instanceof Error ? error.message : String(error)
987
+ );
988
+ return await next();
989
+ }
990
+ return await session.execute(`ciyi.猜 ${guess}`);
991
+ });
962
992
  ctx.command("ciyi", "词意(猜词游戏)").action(async ({ session }) => {
963
993
  return await wf(session);
964
994
  });
@@ -968,6 +998,26 @@ function apply(ctx, cfg) {
968
998
  ctx.command("ciyi.排行榜", "累计猜中次数榜").action(async ({ session }) => {
969
999
  return await phb(session);
970
1000
  });
1001
+ ctx.command("ciyi.裸词 [state:string]", "临时开关本群的裸词续猜").usage("例:ciyi.裸词(切换)· ciyi.裸词 开 · ciyi.裸词 关 · ciyi.裸词 状态").action(async ({ session }, state) => {
1002
+ const result = resolveMiddlewareSwitch(
1003
+ cfg.isEnableMiddleware,
1004
+ session.channelId === void 0 ? void 0 : middlewareOverrides.get(session.channelId),
1005
+ state
1006
+ );
1007
+ if ("error" in result) {
1008
+ return await sendMsg(session, `⚠️ ${result.error}。例:ciyi.裸词 开`);
1009
+ }
1010
+ if (result.override === void 0) middlewareOverrides.delete(session.channelId);
1011
+ else middlewareOverrides.set(session.channelId, result.override);
1012
+ return await sendMsg(
1013
+ session,
1014
+ middlewareSwitchText({
1015
+ on: result.on,
1016
+ temporary: result.override !== void 0,
1017
+ reverted: result.reverted
1018
+ })
1019
+ );
1020
+ });
971
1021
  function getNewUniqueAnswer(oldGuessedWords) {
972
1022
  const usedWordsSet = new Set(oldGuessedWords);
973
1023
  const availableWords = questionList_default.filter(
@@ -1053,14 +1103,15 @@ function apply(ctx, cfg) {
1053
1103
  );
1054
1104
  }
1055
1105
  __name(boardText, "boardText");
1056
- function introText() {
1106
+ function introText(channelOn) {
1057
1107
  return textCard(
1058
1108
  "词意 · 按意思远近找词",
1059
1109
  `每天藏起一个两字词。你报词,我回它与答案的语义排名 —— 名次越小越近,#1 就是答案本身。`,
1060
1110
  [
1061
1111
  `开始 ciyi.猜 山水  开题并报一个两字词`,
1062
- cfg.isEnableMiddleware ? `续猜 直接发送两字词 仅限已开题且未结束` : null,
1063
- `排行 ciyi.排行榜  看谁猜中得最多`
1112
+ channelOn ? `续猜 直接发送两字词 仅限已开题且未结束` : null,
1113
+ `排行 ciyi.排行榜  看谁猜中得最多`,
1114
+ `切换 ciyi.裸词 开/关  临时改本群续猜方式`
1064
1115
  ].filter(Boolean),
1065
1116
  [
1066
1117
  `读板 ?好)企业(地? #467 · 沾边`,
@@ -1076,6 +1127,20 @@ function apply(ctx, cfg) {
1076
1127
  );
1077
1128
  }
1078
1129
  __name(introText, "introText");
1130
+ function middlewareSwitchText(o) {
1131
+ const mark = o.on ? "✅" : "⛔";
1132
+ const state = o.on ? "开启" : "停用";
1133
+ const config = cfg.isEnableMiddleware ? "开启" : "停用";
1134
+ if (!o.temporary) {
1135
+ return `${mark} 裸词续猜 · ${o.reverted ? "已复原," : ""}跟随插件配置(${state})`;
1136
+ }
1137
+ const hint = o.on ? "开题后直接发送两字词即可续猜" : "续猜请用 ciyi.猜 山水";
1138
+ return [
1139
+ `${mark} 裸词续猜 · 本群临时${state}(插件配置:${config})`,
1140
+ `${hint};再次发送 ciyi.裸词 复原`
1141
+ ].join("\n");
1142
+ }
1143
+ __name(middlewareSwitchText, "middlewareSwitchText");
1079
1144
  function winText(o) {
1080
1145
  return textCard(
1081
1146
  "猜中了 · 今日已封题",
@@ -1130,14 +1195,15 @@ function apply(ctx, cfg) {
1130
1195
  }
1131
1196
  __name(sendCard, "sendCard");
1132
1197
  async function wf(session) {
1198
+ const channelOn = middlewareOn(session.channelId);
1133
1199
  return await sendCard(
1134
1200
  session,
1135
1201
  () => renderIntroCard(ctx.canvas, {
1136
1202
  total: allWords_default.length,
1137
1203
  words: allWords_default.length,
1138
- middleware: cfg.isEnableMiddleware
1204
+ middleware: channelOn
1139
1205
  }),
1140
- introText()
1206
+ introText(channelOn)
1141
1207
  );
1142
1208
  }
1143
1209
  __name(wf, "wf");
@@ -1331,5 +1397,6 @@ __name(apply, "apply");
1331
1397
  getMiddlewareGuess,
1332
1398
  inject,
1333
1399
  name,
1400
+ resolveMiddlewareSwitch,
1334
1401
  usage
1335
1402
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ciyi",
3
3
  "description": "Koishi 的词意猜词插件。",
4
- "version": "1.2.8",
4
+ "version": "1.2.10",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md DELETED
@@ -1,40 +0,0 @@
1
- koishi-plugin-ciyi
2
- ==================
3
-
4
- [<img alt="github" src="https://img.shields.io/badge/github-araea/koishi__plugin__ciyi-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/araea/koishi-plugin-ciyi)
5
- [<img alt="npm" src="https://img.shields.io/npm/v/koishi-plugin-ciyi.svg?style=for-the-badge&color=fc8d62&logo=npm" height="20">](https://www.npmjs.com/package/koishi-plugin-ciyi)
6
-
7
- Koishi 的词意猜词插件。
8
-
9
- ## 使用
10
-
11
- 设置指令别名后,发送 `ciyi` 查看玩法。每日藏一个两字词,使用 `ciyi.猜 <词>` 提交第一次猜测并开题;开题后可直接发送两字词继续猜测。
12
-
13
- ## 指令
14
-
15
- | 指令 | 说明 |
16
- | --- | --- |
17
- | `ciyi` | 玩法说明 |
18
- | `ciyi.猜 <词>` | 开始今日游戏并提交猜测 |
19
- | `ciyi.排行榜` | 猜中次数榜 |
20
-
21
- ## QQ 群
22
-
23
- 956758505
24
-
25
- <br>
26
-
27
- #### License
28
-
29
- <sup>
30
- Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
31
- 2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
32
- </sup>
33
-
34
- <br>
35
-
36
- <sub>
37
- Unless you explicitly state otherwise, any contribution intentionally submitted
38
- for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
39
- be dual licensed as above, without any additional terms or conditions.
40
- </sub>