koishi-plugin-wordle-game 4.2.1 → 4.4.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/CHANGELOG.md +8 -0
- package/DESIGN_SYSTEM.md +8 -8
- package/README.md +37 -10
- package/lib/config.d.ts +1 -1
- package/lib/help.d.ts +14 -0
- package/lib/index.js +164 -232
- package/lib/ux.d.ts +4 -22
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# 4.4.0 · 2026-10-02
|
|
2
|
+
|
|
3
|
+
修复未启用 help 插件时发送 `wordle` 会让指令无限调用自己、Koishi 收不到任何消息的问题。主指令现在自带帮助:按常用顺序列出各子指令并提示如何开局,不再依赖 help 插件;带 `-h` 时仍由 help 插件接手。发送 `wordle` 不再顺带登记玩家名(首次游玩时照常登记)。
|
|
4
|
+
|
|
5
|
+
# 4.3.0 · 2026-09-26
|
|
6
|
+
|
|
7
|
+
无前缀猜测只接手确定有效的猜测(在词库中、未猜过),其余静默放行;汉兜提示色改为清晰的绿 / 琥珀,下划线对齐拼音;模式清单上下留白对称。 主指令在 help 列表里补回描述;去掉「.显示」显示模式指令,有图只发图、出图失败才退回文字;多轮输入不再追加计时说明。
|
|
8
|
+
|
|
1
9
|
# 4.2.0 · 2026-09-26
|
|
2
10
|
|
|
3
11
|
M3 明暗棋盘与非颜色状态符号;棋盘文字替代;多轮输入可延长;修复模式编号越界及并发锁被错误释放,渲染失败不阻断对局。
|
package/DESIGN_SYSTEM.md
CHANGED
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
## 输出、交互与内容
|
|
15
15
|
|
|
16
|
-
1.
|
|
17
|
-
2.
|
|
16
|
+
1. 有渲染图时只发图片,不附同内容的文字,避免在群里重复刷屏;图片生成失败时才退回文字。插件不提供个人显示模式开关。
|
|
17
|
+
2. 每个插件的主指令都带描述,`help` 列表右侧不留空;主指令先于任何子指令注册(Koishi 会忽略已存在指令的描述)。文本回复不能把后续条目无提示地压掉;较长管理列表分页。
|
|
18
18
|
3. 裸输入必须符合词法、对局状态及参与条件。多个插件匹配时不执行,要求明确完整指令;完整指令继续可用。
|
|
19
19
|
4. 回复说明当前结果、未完成原因和可执行的下一步,不用管理员日志替代用户可用的反馈。退款、赔付与渲染失败不能伪装成功。
|
|
20
20
|
5. 破坏性清空需展示范围、不可撤销性和限时确认码。确认码限制到同一机器人、平台、用户与频道,使用后立即失效。
|
|
21
|
-
6.
|
|
21
|
+
6. 多轮输入等待 5 分钟,提问里写明可回复的内容与「取消」,不再追加计时说明。对局内的短窗口(如 21 点保险、投降)提供显式跳过,全员表态后立即进入下一阶段,而不是提供「延长」。
|
|
22
22
|
7. 货币发放先写入核对记录,入账结果不明确时保留待核对状态,不自动重发。管理员核对实际流水并按需人工补发后标记确认;确认操作自身不转账。
|
|
23
23
|
|
|
24
24
|
## 内容边界
|
|
@@ -44,7 +44,7 @@ node markdown-to-image-service/scripts/sync-design-system.mjs --check
|
|
|
44
44
|
|
|
45
45
|
官方色彩算法可在源仓库安装开发依赖后通过 `node scripts/build-material-colors.mjs` 重建,再同步消费者。
|
|
46
46
|
|
|
47
|
-
回归检查覆盖:24 个源色相的明暗角色对比度、奖牌前景、图表填充/轨道差异、浏览器/服务端 HCT
|
|
47
|
+
回归检查覆盖:24 个源色相的明暗角色对比度、奖牌前景、图表填充/轨道差异、浏览器/服务端 HCT 一致性、主指令描述、输入冲突、入账异常、清空确认和 Wordle 并发/渲染失败。工作区截图脚本为 `scripts/verify-rendering.ts`,需 Puppeteer Core 和可用 Chromium。
|
|
48
48
|
|
|
49
49
|
## 本轮改动对应关系
|
|
50
50
|
|
|
@@ -53,9 +53,9 @@ node markdown-to-image-service/scripts/sync-design-system.mjs --check
|
|
|
53
53
|
| 共享设计规范 | 官方 HCT、2025 动态颜色角色、共享字阶与间距、同步与哈希校验 |
|
|
54
54
|
| Markdown | M3 明暗主题;旧主题名按明暗迁移;代码高亮与 Mermaid 使用 M3 角色;长代码可换行 |
|
|
55
55
|
| message-counter / monetary-rank | M3 角色与共享图表字阶、HCT 头像配色、完整文字榜单;保留昵称截断 |
|
|
56
|
-
| Wordle | 替换复制的 NYT
|
|
57
|
-
| MCDLE |
|
|
56
|
+
| Wordle | 替换复制的 NYT 外观;状态符号与图例;明暗角色;异常释放并发锁;安全解析模式编号;裸词只在确定是猜测时回应 |
|
|
57
|
+
| MCDLE | 猜测按行排成一张表,方便逐项对照;单句页脚居中 |
|
|
58
58
|
| 2048 / ciyi | 数字与奖牌前景按实际对比度选色 |
|
|
59
|
-
| 斗牛 / 21 点 |
|
|
59
|
+
| 斗牛 / 21 点 | 付款核对记录、如实反馈退款失败、明确下注金额、可显式跳过的短窗口 |
|
|
60
60
|
| sentinel | 完整封印列表按每页 10 位展示 |
|
|
61
|
-
| 其余插件 |
|
|
61
|
+
| 其余插件 | 渲染失败退回文字、可取消输入与上下文反馈 |
|
package/README.md
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# 猜单词
|
|
2
2
|
|
|
3
|
-
Koishi
|
|
3
|
+
Koishi 插件:猜词游戏,支持单词、成语、数字与方程式等多种模式
|
|
4
|
+
|
|
5
|
+
[](https://github.com/araea/koishi-plugin-wordle-game)
|
|
6
|
+
[](https://www.npmjs.com/package/koishi-plugin-wordle-game)
|
|
4
7
|
|
|
5
8
|
## 安装
|
|
6
9
|
|
|
7
10
|
```sh
|
|
8
|
-
|
|
11
|
+
npm i koishi-plugin-wordle-game
|
|
9
12
|
```
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
启用插件,并安装 `database` 与 `puppeteer` 服务。
|
|
15
|
+
|
|
16
|
+
## 快速使用
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
发送 `wordle.开始` 选择模式开局,之后直接发送猜测词即可续猜(默认开启 `enableDirectInput`)。
|
|
14
19
|
|
|
15
20
|
| 指令 | 说明 |
|
|
16
21
|
| --- | --- |
|
|
22
|
+
| `wordle` | 查看指令列表 |
|
|
17
23
|
| `wordle.开始 [长度]` | 开始模式选择 |
|
|
18
24
|
| `wordle.开始.<模式> [长度]` | 指定模式开局 |
|
|
19
25
|
| `wordle.猜 <内容>` | 提交猜测 |
|
|
@@ -26,14 +32,35 @@ yarn add koishi-plugin-wordle-game
|
|
|
26
32
|
| `wordle.拼音速查表` | 查看拼音速查表 |
|
|
27
33
|
| `wordle.玩法介绍` | 查看玩法 |
|
|
28
34
|
|
|
29
|
-
模式包括经典、汉兜、词影、Numberle、Math
|
|
35
|
+
模式包括经典、汉兜、词影、Numberle、Math 与 Lewdle 等。部分模式支持 `--hard`、`--uhard`、`--absurd`、`--challenge`、`--wordles`、`--free` 和 `--all` 选项。
|
|
36
|
+
|
|
37
|
+
## 配置
|
|
38
|
+
|
|
39
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `isDarkThemeEnabled` | boolean | `false` | 使用黑暗主题 |
|
|
42
|
+
| `isHighContrastThemeEnabled` | boolean | `false` | 使用高对比度主题,为色觉障碍准备 |
|
|
43
|
+
| `compositeImagePageWidth` | number | `800` | 多词合成图的页面宽度(像素) |
|
|
44
|
+
| `compositeImagePageHeight` | number | `100` | 多词合成图的页面高度(像素) |
|
|
45
|
+
| `maxSimultaneousGuesses` | number | `4` | 同时猜测的单词数量上限 |
|
|
46
|
+
| `defaultMaxLeaderboardEntries` | number | `10` | 排行榜默认显示的人数 |
|
|
47
|
+
| `defaultWordLengthForGuessing` | number | `5` | 非经典模式下默认的猜测长度 |
|
|
48
|
+
| `enableDirectInput` | boolean | `true` | 对局中直接发送猜测词即可续猜 |
|
|
49
|
+
| `isPreventUserDuplicateGuessInput` | boolean | `true` | 拦截重复提交的猜测词 |
|
|
50
|
+
| `shouldPromptWordLengthInput` | boolean | `true` | 引导式开局时追问一次单词长度 |
|
|
51
|
+
| `shouldPromptForWordLengthOnNonClassicStart` | boolean | `true` | 非经典模式开局时追问一次单词长度 |
|
|
52
|
+
| `enableWordGuessTimeLimit` | boolean | `false` | 给每次作答加上时间限制 |
|
|
53
|
+
| `wordGuessTimeLimitInSeconds` | number | `120` | 每次作答的时间限制(秒) |
|
|
54
|
+
| `retractDelay` | number | `0` | 自动撤回延迟(秒),0 表示不撤回 |
|
|
55
|
+
| `imageType` | `png` / `jpeg` / `webp` | `png` | 发送的图片格式 |
|
|
30
56
|
|
|
31
|
-
##
|
|
57
|
+
## 限制 / 风险
|
|
32
58
|
|
|
33
|
-
|
|
59
|
+
需要 `database` 服务保存战绩,以及 `puppeteer` 服务渲染图片。
|
|
34
60
|
|
|
35
|
-
|
|
61
|
+
`wordle.查成语` 依赖外部汉典数据,拼音查询依赖配置的拼音 API。
|
|
36
62
|
|
|
37
|
-
|
|
63
|
+
## 必要链接
|
|
38
64
|
|
|
39
|
-
|
|
65
|
+
- [设计系统](DESIGN_SYSTEM.md)
|
|
66
|
+
- [MIT](LICENSE-MIT) / [Apache-2.0](LICENSE-APACHE)
|
package/lib/config.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema } from "koishi";
|
|
2
|
-
export declare const usage = "## \u4F7F\u7528\n\n\u53D1\u9001 `wordle.\u5F00\u59CB` \u5F00\u5C40\uFF0C\u4E4B\u540E\u76F4\u63A5\u53D1\u9001\u731C\u6D4B\u8BCD\u5373\u53EF\u3002\n\n## \u6307\u4EE4\n\n| \u6307\u4EE4 | \u8BF4\u660E |\n| --- | --- |\n| `wordle.\u5F00\u59CB [\u957F\u5EA6]` | \
|
|
2
|
+
export declare const usage = "## \u4F7F\u7528\n\n\u53D1\u9001 `wordle.\u5F00\u59CB` \u5F00\u5C40\uFF0C\u4E4B\u540E\u76F4\u63A5\u53D1\u9001\u731C\u6D4B\u8BCD\u5373\u53EF\u3002\n\n## \u6307\u4EE4\n\n| \u6307\u4EE4 | \u8BF4\u660E |\n| --- | --- |\n| `wordle` | \u67E5\u770B\u6307\u4EE4\u5217\u8868 |\n| `wordle.\u5F00\u59CB [\u957F\u5EA6]` | \u5F00\u59CB\u6A21\u5F0F\u9009\u62E9 |\n| `wordle.\u5F00\u59CB.<\u6A21\u5F0F> [\u957F\u5EA6]` | \u6307\u5B9A\u6A21\u5F0F\u5F00\u5C40 |\n| `wordle.\u731C <\u5185\u5BB9>` | \u63D0\u4EA4\u731C\u6D4B |\n| `wordle.\u67E5\u8BE2\u8FDB\u5EA6` | \u67E5\u770B\u5F53\u524D\u8FDB\u5EA6 |\n| `wordle.\u7ED3\u675F` | \u7ED3\u675F\u6E38\u620F |\n| `wordle.\u6392\u884C\u699C [\u4EBA\u6570]` | \u67E5\u770B\u6392\u884C\u699C |\n| `wordle.\u6218\u7EE9 [@\u67D0\u4EBA]` | \u67E5\u8BE2\u73A9\u5BB6\u8BB0\u5F55 |\n| `wordle.\u67E5\u5355\u8BCD <\u8BCD>` | \u67E5\u8BE2\u5355\u8BCD\u5E76\u5F00\u59CB\u5F15\u5BFC |\n| `wordle.\u67E5\u6210\u8BED <\u6210\u8BED>` | \u67E5\u8BE2\u62FC\u97F3\u548C\u91CA\u4E49\uFF08\u6C49\u5178\uFF09 |\n| `wordle.\u62FC\u97F3\u901F\u67E5\u8868` | \u67E5\u770B\u62FC\u97F3\u901F\u67E5\u8868 |\n| `wordle.\u73A9\u6CD5\u4ECB\u7ECD` | \u67E5\u770B\u73A9\u6CD5 |\n\n\u6A21\u5F0F\u5305\u62EC\u7ECF\u5178\u3001\u6C49\u515C\u3001\u8BCD\u5F71\u3001Numberle\u3001Math \u548C Lewdle \u7B49\u3002\u90E8\u5206\u6A21\u5F0F\u652F\u6301 `--hard`\u3001`--uhard`\u3001`--absurd`\u3001`--challenge`\u3001`--wordles`\u3001`--free` \u548C `--all` \u9009\u9879\u3002";
|
|
3
3
|
export interface Config {
|
|
4
4
|
isDarkThemeEnabled: boolean;
|
|
5
5
|
isHighContrastThemeEnabled: boolean;
|
package/lib/help.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Session } from 'koishi';
|
|
2
|
+
export interface HelpEntry {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 主指令帮助的内容:从已注册的指令树现取标题与直接子指令,不依赖 help 插件,也不会和指令脱节。
|
|
8
|
+
* `order` 列出希望靠前的子指令(完整指令名),没列到的按注册顺序排在后面;排版交给各插件。
|
|
9
|
+
* 不要在动作里 `session.execute('… -h')` 转发:`-h` 由 help 插件提供,缺它指令会无限调用自己。
|
|
10
|
+
*/
|
|
11
|
+
export declare function helpOf(session: Session, name: string, order?: string[]): {
|
|
12
|
+
title: string;
|
|
13
|
+
entries: HelpEntry[];
|
|
14
|
+
};
|
package/lib/index.js
CHANGED
|
@@ -37,113 +37,11 @@ __export(src_exports, {
|
|
|
37
37
|
usage: () => usage
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(src_exports);
|
|
40
|
-
|
|
41
|
-
// src/ux.ts
|
|
42
|
-
var import_koishi = require("koishi");
|
|
43
|
-
var KEY = Symbol.for("araea.koishi.ux.v1");
|
|
44
|
-
function state(ctx) {
|
|
45
|
-
const root2 = ctx.root;
|
|
46
|
-
return root2[KEY] ??= { modes: /* @__PURE__ */ new Map(), owners: /* @__PURE__ */ new Set() };
|
|
47
|
-
}
|
|
48
|
-
__name(state, "state");
|
|
49
|
-
var userKey = /* @__PURE__ */ __name((session) => `${session.platform}:${session.selfId}:${session.userId}`, "userKey");
|
|
50
|
-
function usePresentation(ctx, command) {
|
|
51
|
-
const shared = state(ctx);
|
|
52
|
-
ctx.command(`${command}.显示 [mode:string]`, "选择图文或文字输出").action(({ session }, mode) => {
|
|
53
|
-
if (!mode) return `当前:${shared.modes.get(userKey(session)) ? "文字" : "图文"}。发送「${command}.显示 文字」或「${command}.显示 图文」。设置对本机器人各插件生效,重启后恢复图文。`;
|
|
54
|
-
if (!["文字", "图文"].includes(mode)) return "请选择「文字」或「图文」。";
|
|
55
|
-
shared.modes.set(userKey(session), mode === "文字");
|
|
56
|
-
return `已切换为${mode}输出。`;
|
|
57
|
-
});
|
|
58
|
-
return {
|
|
59
|
-
textOnly: /* @__PURE__ */ __name((session) => shared.modes.get(userKey(session)) === true, "textOnly"),
|
|
60
|
-
/** 图文模式只给图片;图片缺失或用户选了文字模式时才退回文字。 */
|
|
61
|
-
present(session, image, text) {
|
|
62
|
-
if (!image || shared.modes.get(userKey(session))) return text;
|
|
63
|
-
return import_koishi.h.normalize(image);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
__name(usePresentation, "usePresentation");
|
|
68
|
-
function registerDirectInput(ctx, name2, matches) {
|
|
69
|
-
const owner = { name: name2, matches };
|
|
70
|
-
state(ctx).owners.add(owner);
|
|
71
|
-
ctx.on("dispose", () => {
|
|
72
|
-
state(ctx).owners.delete(owner);
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
__name(registerDirectInput, "registerDirectInput");
|
|
76
|
-
async function directInputConflict(ctx, session) {
|
|
77
|
-
if (session.event.user?.isBot) return true;
|
|
78
|
-
const owners = [];
|
|
79
|
-
for (const owner of state(ctx).owners) {
|
|
80
|
-
try {
|
|
81
|
-
if (await owner.matches(session)) owners.push(owner.name);
|
|
82
|
-
} catch {
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (owners.length < 2) return false;
|
|
86
|
-
await session.send(`这条消息同时适用于 ${owners.join("、")},尚未执行。请使用对应游戏的完整指令。`);
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
__name(directInputConflict, "directInputConflict");
|
|
90
|
-
async function promptInput(session, instruction) {
|
|
91
|
-
for (; ; ) {
|
|
92
|
-
await session.send(`${instruction}
|
|
93
|
-
请在 5 分钟内回复;发送「延长」重新计时,发送「取消」退出。`);
|
|
94
|
-
const input = await session.prompt(3e5);
|
|
95
|
-
if (input?.trim() === "延长") continue;
|
|
96
|
-
return input;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
__name(promptInput, "promptInput");
|
|
100
|
-
var IMAGE_TEXT_TYPE = "ux-image-text";
|
|
101
|
-
function imageText(text) {
|
|
102
|
-
return (0, import_koishi.h)(IMAGE_TEXT_TYPE, {}, import_koishi.h.normalize(text));
|
|
103
|
-
}
|
|
104
|
-
__name(imageText, "imageText");
|
|
105
|
-
function isImage(element) {
|
|
106
|
-
return element.type === "img" || element.type === "image";
|
|
107
|
-
}
|
|
108
|
-
__name(isImage, "isImage");
|
|
109
|
-
function isImageText(element) {
|
|
110
|
-
return element.type === IMAGE_TEXT_TYPE;
|
|
111
|
-
}
|
|
112
|
-
__name(isImageText, "isImageText");
|
|
113
|
-
function imagesOnly(content) {
|
|
114
|
-
const walk = /* @__PURE__ */ __name((elements) => elements.flatMap((element) => {
|
|
115
|
-
if (isImageText(element)) return [];
|
|
116
|
-
if (isImage(element) || !element.children.length) return [element];
|
|
117
|
-
return [(0, import_koishi.h)(element.type, element.attrs, walk(element.children))];
|
|
118
|
-
}), "walk");
|
|
119
|
-
return walk(import_koishi.h.normalize(content));
|
|
120
|
-
}
|
|
121
|
-
__name(imagesOnly, "imagesOnly");
|
|
122
|
-
function withoutImages(content) {
|
|
123
|
-
const walk = /* @__PURE__ */ __name((elements) => elements.flatMap((element) => {
|
|
124
|
-
if (isImage(element)) return [];
|
|
125
|
-
if (isImageText(element)) return walk(element.children);
|
|
126
|
-
if (element.children.length) return [(0, import_koishi.h)(element.type, element.attrs, walk(element.children))];
|
|
127
|
-
return [element];
|
|
128
|
-
}), "walk");
|
|
129
|
-
return walk(import_koishi.h.normalize(content));
|
|
130
|
-
}
|
|
131
|
-
__name(withoutImages, "withoutImages");
|
|
132
|
-
function choosePresentation(content, textOnly) {
|
|
133
|
-
return textOnly ? withoutImages(content) : imagesOnly(content);
|
|
134
|
-
}
|
|
135
|
-
__name(choosePresentation, "choosePresentation");
|
|
136
|
-
function isTextOnly(ctx, session) {
|
|
137
|
-
return state(ctx).modes.get(userKey(session)) === true;
|
|
138
|
-
}
|
|
139
|
-
__name(isTextOnly, "isTextOnly");
|
|
140
|
-
|
|
141
|
-
// src/index.ts
|
|
142
40
|
var fs6 = __toESM(require("fs"));
|
|
143
41
|
var path3 = __toESM(require("path"));
|
|
144
42
|
|
|
145
43
|
// src/config.ts
|
|
146
|
-
var
|
|
44
|
+
var import_koishi = require("koishi");
|
|
147
45
|
var usage = `## 使用
|
|
148
46
|
|
|
149
47
|
发送 \`wordle.开始\` 开局,之后直接发送猜测词即可。
|
|
@@ -152,59 +50,58 @@ var usage = `## 使用
|
|
|
152
50
|
|
|
153
51
|
| 指令 | 说明 |
|
|
154
52
|
| --- | --- |
|
|
155
|
-
| \`wordle
|
|
53
|
+
| \`wordle\` | 查看指令列表 |
|
|
54
|
+
| \`wordle.开始 [长度]\` | 开始模式选择 |
|
|
156
55
|
| \`wordle.开始.<模式> [长度]\` | 指定模式开局 |
|
|
157
56
|
| \`wordle.猜 <内容>\` | 提交猜测 |
|
|
158
|
-
| \`wordle.查询进度\` |
|
|
159
|
-
| \`wordle.结束\` |
|
|
57
|
+
| \`wordle.查询进度\` | 查看当前进度 |
|
|
58
|
+
| \`wordle.结束\` | 结束游戏 |
|
|
160
59
|
| \`wordle.排行榜 [人数]\` | 查看排行榜 |
|
|
161
|
-
| \`wordle.战绩 [@某人]\` |
|
|
162
|
-
| \`wordle.查单词 <词>\` |
|
|
163
|
-
| \`wordle.查成语 <成语>\` |
|
|
60
|
+
| \`wordle.战绩 [@某人]\` | 查询玩家记录 |
|
|
61
|
+
| \`wordle.查单词 <词>\` | 查询单词并开始引导 |
|
|
62
|
+
| \`wordle.查成语 <成语>\` | 查询拼音和释义(汉典) |
|
|
164
63
|
| \`wordle.拼音速查表\` | 查看拼音速查表 |
|
|
165
|
-
| \`wordle.玩法介绍\` |
|
|
64
|
+
| \`wordle.玩法介绍\` | 查看玩法 |
|
|
166
65
|
|
|
167
|
-
模式包括经典、汉兜、词影、Numberle、Math
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
isDarkThemeEnabled: import_koishi2.Schema.boolean().default(false).description(`使用黑暗主题。`),
|
|
173
|
-
isHighContrastThemeEnabled: import_koishi2.Schema.boolean().default(false).description(`使用高对比度主题,为色觉障碍准备。`)
|
|
66
|
+
模式包括经典、汉兜、词影、Numberle、Math 和 Lewdle 等。部分模式支持 \`--hard\`、\`--uhard\`、\`--absurd\`、\`--challenge\`、\`--wordles\`、\`--free\` 和 \`--all\` 选项。`;
|
|
67
|
+
var Config = import_koishi.Schema.intersect([
|
|
68
|
+
import_koishi.Schema.object({
|
|
69
|
+
isDarkThemeEnabled: import_koishi.Schema.boolean().default(false).description(`使用黑暗主题。`),
|
|
70
|
+
isHighContrastThemeEnabled: import_koishi.Schema.boolean().default(false).description(`使用高对比度主题,为色觉障碍准备。`)
|
|
174
71
|
}).description("主题设置"),
|
|
175
|
-
|
|
176
|
-
compositeImagePageWidth:
|
|
177
|
-
compositeImagePageHeight:
|
|
178
|
-
maxSimultaneousGuesses:
|
|
179
|
-
defaultMaxLeaderboardEntries:
|
|
180
|
-
defaultWordLengthForGuessing:
|
|
72
|
+
import_koishi.Schema.object({
|
|
73
|
+
compositeImagePageWidth: import_koishi.Schema.number().min(1).default(800).description(`多词合成图的页面宽度(像素)。`),
|
|
74
|
+
compositeImagePageHeight: import_koishi.Schema.number().min(1).default(100).description(`多词合成图的页面高度(像素)。`),
|
|
75
|
+
maxSimultaneousGuesses: import_koishi.Schema.number().min(1).default(4).description(`同时猜测的单词数量上限。`),
|
|
76
|
+
defaultMaxLeaderboardEntries: import_koishi.Schema.number().min(0).default(10).description(`排行榜默认显示的人数。`),
|
|
77
|
+
defaultWordLengthForGuessing: import_koishi.Schema.number().min(1).default(5).description(`非经典模式下默认的猜测长度。`)
|
|
181
78
|
}).description("游戏设置"),
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
enableDirectInput:
|
|
185
|
-
isPreventUserDuplicateGuessInput:
|
|
186
|
-
shouldPromptWordLengthInput:
|
|
79
|
+
import_koishi.Schema.intersect([
|
|
80
|
+
import_koishi.Schema.object({
|
|
81
|
+
enableDirectInput: import_koishi.Schema.boolean().default(true).description(`对局中直接发送猜测词即可续猜,无需指令前缀。`),
|
|
82
|
+
isPreventUserDuplicateGuessInput: import_koishi.Schema.boolean().default(true).description(`拦截重复提交的猜测词。`),
|
|
83
|
+
shouldPromptWordLengthInput: import_koishi.Schema.boolean().default(true).description(
|
|
187
84
|
`引导式开局时,追问一次单词长度。关闭则直接用默认长度。`
|
|
188
85
|
),
|
|
189
|
-
shouldPromptForWordLengthOnNonClassicStart:
|
|
86
|
+
shouldPromptForWordLengthOnNonClassicStart: import_koishi.Schema.boolean().default(true).description(
|
|
190
87
|
`以非经典模式开局时,追问一次单词长度。关闭则直接用默认长度。`
|
|
191
88
|
)
|
|
192
89
|
}).description("游戏行为设置"),
|
|
193
|
-
|
|
194
|
-
enableWordGuessTimeLimit:
|
|
90
|
+
import_koishi.Schema.object({
|
|
91
|
+
enableWordGuessTimeLimit: import_koishi.Schema.boolean().default(false).description(`给每次作答加上时间限制。`)
|
|
195
92
|
}),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
enableWordGuessTimeLimit:
|
|
199
|
-
wordGuessTimeLimitInSeconds:
|
|
93
|
+
import_koishi.Schema.union([
|
|
94
|
+
import_koishi.Schema.object({
|
|
95
|
+
enableWordGuessTimeLimit: import_koishi.Schema.const(true).required(),
|
|
96
|
+
wordGuessTimeLimitInSeconds: import_koishi.Schema.number().min(0).default(120).description(`每次作答的时间限制(秒)。`)
|
|
200
97
|
}),
|
|
201
|
-
|
|
98
|
+
import_koishi.Schema.object({})
|
|
202
99
|
]),
|
|
203
|
-
|
|
204
|
-
retractDelay:
|
|
100
|
+
import_koishi.Schema.object({
|
|
101
|
+
retractDelay: import_koishi.Schema.number().min(0).default(0).description(
|
|
205
102
|
`自动撤回延迟(秒),0 表示不撤回。`
|
|
206
103
|
),
|
|
207
|
-
imageType:
|
|
104
|
+
imageType: import_koishi.Schema.union(["png", "jpeg", "webp"]).default("png").description(`发送的图片格式。`)
|
|
208
105
|
})
|
|
209
106
|
])
|
|
210
107
|
]);
|
|
@@ -441,6 +338,24 @@ async function updateDataInTargetFile(logger, newFilePath, targetFilePath, missi
|
|
|
441
338
|
}
|
|
442
339
|
__name(updateDataInTargetFile, "updateDataInTargetFile");
|
|
443
340
|
|
|
341
|
+
// src/help.ts
|
|
342
|
+
function helpOf(session, name2, order = []) {
|
|
343
|
+
const describe = /* @__PURE__ */ __name((command) => session.text([`commands.${command}.description`, ""]), "describe");
|
|
344
|
+
const rank = /* @__PURE__ */ __name((command) => {
|
|
345
|
+
const i = order.indexOf(command);
|
|
346
|
+
return i < 0 ? order.length : i;
|
|
347
|
+
}, "rank");
|
|
348
|
+
const entries = [];
|
|
349
|
+
for (const child of session.app.$commander.get(name2)?.children ?? []) {
|
|
350
|
+
const { hidden } = child.config;
|
|
351
|
+
if (!child.match(session) || session.resolve(hidden)) continue;
|
|
352
|
+
entries.push({ name: child.name, description: describe(child.name) });
|
|
353
|
+
}
|
|
354
|
+
entries.sort((a, b) => rank(a.name) - rank(b.name));
|
|
355
|
+
return { title: describe(name2), entries };
|
|
356
|
+
}
|
|
357
|
+
__name(helpOf, "helpOf");
|
|
358
|
+
|
|
444
359
|
// src/utils/string.ts
|
|
445
360
|
function removeDuplicates(inputString) {
|
|
446
361
|
let result = "";
|
|
@@ -737,8 +652,8 @@ __name(processExtraGameRecords, "processExtraGameRecords");
|
|
|
737
652
|
async function sendMessage(g, session, message) {
|
|
738
653
|
const config = g.config;
|
|
739
654
|
const { bot, channelId } = session;
|
|
740
|
-
const [messageId] = await session.send(
|
|
741
|
-
if (
|
|
655
|
+
const [messageId] = await session.send(message);
|
|
656
|
+
if (config.retractDelay > 0 && messageId) {
|
|
742
657
|
const prevMessage = g.lastMessageInfo.get(channelId);
|
|
743
658
|
if (prevMessage) {
|
|
744
659
|
const timePassed = Date.now() - prevMessage.timestamp;
|
|
@@ -795,10 +710,22 @@ __name(getSessionUserName, "getSessionUserName");
|
|
|
795
710
|
function register(g) {
|
|
796
711
|
const { ctx, config } = g;
|
|
797
712
|
ctx.command("wordle", "猜单词 · 多词库多模式").alias("wordleGame").action(async ({ session }) => {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
713
|
+
const { title, entries } = helpOf(
|
|
714
|
+
session,
|
|
715
|
+
"wordle",
|
|
716
|
+
["开始", "猜", "查询进度", "结束", "排行榜", "战绩", "查单词", "查成语", "拼音速查表", "玩法介绍"].map(
|
|
717
|
+
(name2) => `wordle.${name2}`
|
|
718
|
+
)
|
|
719
|
+
);
|
|
720
|
+
return await sendMessage(
|
|
721
|
+
g,
|
|
722
|
+
session,
|
|
723
|
+
[
|
|
724
|
+
title,
|
|
725
|
+
...entries.map(({ name: name2, description }) => ` ${name2} ${description}`),
|
|
726
|
+
"发送「wordle.开始」开局,之后直接发送猜测词即可。"
|
|
727
|
+
].join("\n")
|
|
728
|
+
);
|
|
802
729
|
});
|
|
803
730
|
ctx.command("wordle.玩法介绍", "查看玩法介绍").action(async ({ session }) => {
|
|
804
731
|
let { userId, username } = session;
|
|
@@ -807,12 +734,47 @@ function register(g) {
|
|
|
807
734
|
return sendMessage(
|
|
808
735
|
g,
|
|
809
736
|
session,
|
|
810
|
-
"玩法:发送「wordle.开始」选择词库,再发送「wordle.猜 单词」。\n每次猜测会标记:✓ 字符与位置均正确;↔ 答案含该字符但位置不符;× 不包含(重复字符按数量判断)。\n「wordle.查询进度」查看已知线索;「wordle.拼音速查表」查看汉兜拼音;「wordle
|
|
737
|
+
"玩法:发送「wordle.开始」选择词库,再发送「wordle.猜 单词」。\n每次猜测会标记:✓ 字符与位置均正确;↔ 答案含该字符但位置不符;× 不包含(重复字符按数量判断)。\n「wordle.查询进度」查看已知线索;「wordle.拼音速查表」查看汉兜拼音;「wordle.结束」结束本局。"
|
|
811
738
|
);
|
|
812
739
|
});
|
|
813
740
|
}
|
|
814
741
|
__name(register, "register");
|
|
815
742
|
|
|
743
|
+
// src/ux.ts
|
|
744
|
+
var import_koishi2 = require("koishi");
|
|
745
|
+
var KEY = Symbol.for("araea.koishi.ux.v1");
|
|
746
|
+
function state(ctx) {
|
|
747
|
+
const root2 = ctx.root;
|
|
748
|
+
return root2[KEY] ??= { owners: /* @__PURE__ */ new Set() };
|
|
749
|
+
}
|
|
750
|
+
__name(state, "state");
|
|
751
|
+
function registerDirectInput(ctx, name2, matches) {
|
|
752
|
+
const owner = { name: name2, matches };
|
|
753
|
+
state(ctx).owners.add(owner);
|
|
754
|
+
ctx.on("dispose", () => {
|
|
755
|
+
state(ctx).owners.delete(owner);
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
__name(registerDirectInput, "registerDirectInput");
|
|
759
|
+
async function directInputConflict(ctx, session) {
|
|
760
|
+
if (session.event.user?.isBot) return true;
|
|
761
|
+
const owners = [];
|
|
762
|
+
for (const owner of state(ctx).owners) {
|
|
763
|
+
try {
|
|
764
|
+
if (await owner.matches(session)) owners.push(owner.name);
|
|
765
|
+
} catch {
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (owners.length < 2) return false;
|
|
769
|
+
await session.send(`这条消息同时适用于 ${owners.join("、")},尚未执行。请使用对应游戏的完整指令。`);
|
|
770
|
+
return true;
|
|
771
|
+
}
|
|
772
|
+
__name(directInputConflict, "directInputConflict");
|
|
773
|
+
function promptInput(session) {
|
|
774
|
+
return session.prompt(3e5);
|
|
775
|
+
}
|
|
776
|
+
__name(promptInput, "promptInput");
|
|
777
|
+
|
|
816
778
|
// src/services/renderer.ts
|
|
817
779
|
var import_cheerio = require("cheerio");
|
|
818
780
|
var import_url = require("url");
|
|
@@ -6168,16 +6130,20 @@ __name(resource, "resource");
|
|
|
6168
6130
|
// src/services/renderer.ts
|
|
6169
6131
|
var HUE = 142;
|
|
6170
6132
|
var imageDescriptions = /* @__PURE__ */ new WeakMap();
|
|
6171
|
-
function
|
|
6133
|
+
function imageText(buffer) {
|
|
6172
6134
|
return imageDescriptions.get(buffer) ?? "棋盘说明暂不可用,可发送「wordle.查询进度」。";
|
|
6173
6135
|
}
|
|
6174
|
-
__name(
|
|
6136
|
+
__name(imageText, "imageText");
|
|
6175
6137
|
function imageMessage(buffer, type) {
|
|
6176
|
-
const text =
|
|
6138
|
+
const text = imageText(buffer) ?? "图片内容请使用「wordle.查询进度」查询。";
|
|
6177
6139
|
if (!buffer.length) return (0, import_koishi3.h)("p", {}, import_koishi3.h.text(text));
|
|
6178
|
-
return
|
|
6140
|
+
return import_koishi3.h.image(buffer, type);
|
|
6179
6141
|
}
|
|
6180
6142
|
__name(imageMessage, "imageMessage");
|
|
6143
|
+
var HINT = {
|
|
6144
|
+
light: { ok: lch(40, 48, 142), mis: lch(46, 60, 62), okAlt: lch(46, 70, 50), misAlt: lch(42, 52, 260) },
|
|
6145
|
+
dark: { ok: lch(80, 56, 142), mis: lch(82, 60, 85), okAlt: lch(74, 62, 55), misAlt: lch(76, 40, 255) }
|
|
6146
|
+
};
|
|
6181
6147
|
var PANEL_WIDTH = 611;
|
|
6182
6148
|
async function capture(g, html, viewport, fileOrigin = false) {
|
|
6183
6149
|
const $ = (0, import_cheerio.load)(html);
|
|
@@ -6203,9 +6169,14 @@ async function capture(g, html, viewport, fileOrigin = false) {
|
|
|
6203
6169
|
await page.setViewport({ ...viewport, deviceScaleFactor: 1 });
|
|
6204
6170
|
if (fileOrigin) await page.goto((0, import_url.pathToFileURL)(resource("emptyHtml.html")).href);
|
|
6205
6171
|
await page.setContent(html, { waitUntil: "load" });
|
|
6206
|
-
const
|
|
6172
|
+
const dark = g.config.isDarkThemeEnabled;
|
|
6173
|
+
const c = scheme(HUE, dark);
|
|
6174
|
+
const hint = HINT[dark ? "dark" : "light"];
|
|
6207
6175
|
await page.addStyleTag({ content: `${baseline(c)} body{background:${c.surface}!important;color:${c.onSurface}!important} input{color:${c.onSurface};border-radius:12px}
|
|
6208
|
-
.text-ok{
|
|
6176
|
+
.text-ok,.text-mis{text-decoration-line:underline;text-decoration-thickness:1.5px;text-underline-offset:4px;text-decoration-skip-ink:none}
|
|
6177
|
+
.text-ok{color:${hint.ok}!important;text-decoration-style:double}.text-mis{color:${hint.mis}!important;text-decoration-style:dashed}
|
|
6178
|
+
.colorblind .text-ok{color:${hint.okAlt}!important}.colorblind .text-mis{color:${hint.misAlt}!important}
|
|
6179
|
+
.op35,.op80{opacity:1!important;color:${c.onSurfaceVariant}!important}
|
|
6209
6180
|
.bg-correct{background:${c.primary}!important;border-radius:12px}.fill-correct{fill:${c.primary}!important}.fill-white{fill:${c.onPrimary}!important}
|
|
6210
6181
|
.bg-base{background:${c.surface}!important} *{animation:none!important;transition:none!important}` });
|
|
6211
6182
|
buffer = await page.screenshot({ fullPage: true, type: g.config.imageType });
|
|
@@ -6378,11 +6349,12 @@ async function generatePanelImage(g, rows, isRanked = false) {
|
|
|
6378
6349
|
<style>
|
|
6379
6350
|
${baseline(s)}
|
|
6380
6351
|
${components()}
|
|
6381
|
-
body
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
}
|
|
6352
|
+
/* 留白放在外层容器上:截图时注入的基线样式会把 body 的内边距清零,上下就不对称了 */
|
|
6353
|
+
body { width: ${PANEL_WIDTH}px; background: var(--md-sys-color-surface); }
|
|
6354
|
+
.panel { padding: 24px; }
|
|
6355
|
+
/* 序号一位数、两位数时徽标同宽,名字才对得齐 */
|
|
6356
|
+
.m3-list-item .m3-badge { width: 36px; padding: 0; }
|
|
6357
|
+
.m3-list { margin: 0; }
|
|
6386
6358
|
/* 一屏里要放下十几行,行高比默认的列表项紧一档 */
|
|
6387
6359
|
.m3-list-item {
|
|
6388
6360
|
min-height: 48px;
|
|
@@ -6408,12 +6380,12 @@ async function generatePanelImage(g, rows, isRanked = false) {
|
|
|
6408
6380
|
</style>
|
|
6409
6381
|
</head>
|
|
6410
6382
|
<body>
|
|
6411
|
-
<ul class="m3-list">
|
|
6383
|
+
<main class="panel"><ul class="m3-list">
|
|
6412
6384
|
${items}
|
|
6413
|
-
</ul>
|
|
6385
|
+
</ul></main>
|
|
6414
6386
|
</body>
|
|
6415
6387
|
</html>`;
|
|
6416
|
-
return capture(g, html, { width: PANEL_WIDTH, height:
|
|
6388
|
+
return capture(g, html, { width: PANEL_WIDTH, height: 1 });
|
|
6417
6389
|
}
|
|
6418
6390
|
__name(generatePanelImage, "generatePanelImage");
|
|
6419
6391
|
async function renderPanel(g, rows, isRanked = false) {
|
|
@@ -17589,7 +17561,7 @@ function generateEmptyGridHtmlForHandle(rowNum, tileNum) {
|
|
|
17589
17561
|
__name(generateEmptyGridHtmlForHandle, "generateEmptyGridHtmlForHandle");
|
|
17590
17562
|
function generateImageTags(buffers) {
|
|
17591
17563
|
return buffers.map((buffer, index) => {
|
|
17592
|
-
const description = `棋盘 ${index + 1}:${
|
|
17564
|
+
const description = `棋盘 ${index + 1}:${imageText(buffer)}`;
|
|
17593
17565
|
if (!buffer.length) return `<p>${import_koishi4.h.escape(description)}</p>`;
|
|
17594
17566
|
return `<img src="data:image/png;base64,${buffer.toString("base64")}" alt="${import_koishi4.h.escape(description)}">`;
|
|
17595
17567
|
}).join("\n");
|
|
@@ -18429,75 +18401,36 @@ function register2(g) {
|
|
|
18429
18401
|
const idiomsList = g.data.idiomsList;
|
|
18430
18402
|
const pinyinData = g.data.pinyinData;
|
|
18431
18403
|
const equations = g.data.equations;
|
|
18404
|
+
async function directGuess(session) {
|
|
18405
|
+
if (!config.enableDirectInput || !session.channelId) return;
|
|
18406
|
+
const content = `${import_koishi5.h.select(session.content ?? "", "text")}`.trim();
|
|
18407
|
+
if (!content || !/^[\p{L}\d+*/=().-]+$/u.test(content)) return;
|
|
18408
|
+
const [gameInfo] = await ctx.database.get("wordle_game_records", { channelId: session.channelId });
|
|
18409
|
+
if (!gameInfo?.isStarted) return;
|
|
18410
|
+
const { gameMode } = gameInfo;
|
|
18411
|
+
const word = gameMode === "汉兜" || gameMode === "词影" ? content : content.toLowerCase();
|
|
18412
|
+
if (gameInfo.guessHistory?.includes(word)) return;
|
|
18413
|
+
if (gameMode === "汉兜" || gameMode === "词影") {
|
|
18414
|
+
if (!isFourCharacterIdiom(content)) return;
|
|
18415
|
+
const known = isIdiomInList(content, idiomsList) || gameMode === "汉兜" && pinyinData.some((item) => item.term === content);
|
|
18416
|
+
if (!known) return;
|
|
18417
|
+
if (gameMode === "词影" && !checkStrokesData(content, g.data.strokesData)) return;
|
|
18418
|
+
return content;
|
|
18419
|
+
}
|
|
18420
|
+
if (content.length !== gameInfo.guessWordLength) return;
|
|
18421
|
+
if (gameMode === "Numberle") return isNumericString(content) ? content : void 0;
|
|
18422
|
+
if (gameMode === "Math") return isMathEquationValid(content) ? content : void 0;
|
|
18423
|
+
if (!/^[a-zA-Z]+$/.test(content) || !findWord(word)) return;
|
|
18424
|
+
return content;
|
|
18425
|
+
}
|
|
18426
|
+
__name(directGuess, "directGuess");
|
|
18432
18427
|
registerDirectInput(ctx, "wordle-game", async (session) => {
|
|
18433
18428
|
if (!ctx.filter(session)) return false;
|
|
18434
|
-
|
|
18435
|
-
if (!config.enableDirectInput) {
|
|
18436
|
-
return false;
|
|
18437
|
-
}
|
|
18438
|
-
if (content) {
|
|
18439
|
-
content = `${import_koishi5.h.select(content, "text")}`.trim();
|
|
18440
|
-
}
|
|
18441
|
-
if (!channelId || !content || !/^[\p{L}\d+*/=().-]+$/u.test(content)) return false;
|
|
18442
|
-
const [gameInfo] = await ctx.database.get("wordle_game_records", { channelId });
|
|
18443
|
-
if (!gameInfo?.isStarted) {
|
|
18444
|
-
return false;
|
|
18445
|
-
}
|
|
18446
|
-
if (gameInfo.gameMode === "汉兜" || gameInfo.gameMode === "词影") {
|
|
18447
|
-
if (!isFourCharacterIdiom(content)) {
|
|
18448
|
-
return false;
|
|
18449
|
-
}
|
|
18450
|
-
} else if (gameInfo.gameMode === "Numberle") {
|
|
18451
|
-
if (!isNumericString(content)) {
|
|
18452
|
-
return false;
|
|
18453
|
-
}
|
|
18454
|
-
} else if (gameInfo.gameMode === "Math") {
|
|
18455
|
-
if (!isMathEquationValid(content)) {
|
|
18456
|
-
return false;
|
|
18457
|
-
}
|
|
18458
|
-
} else {
|
|
18459
|
-
if (!/^[a-zA-Z]+$/.test(content)) {
|
|
18460
|
-
return false;
|
|
18461
|
-
}
|
|
18462
|
-
}
|
|
18463
|
-
if (content.length !== gameInfo.guessWordLength) {
|
|
18464
|
-
return false;
|
|
18465
|
-
}
|
|
18466
|
-
return true;
|
|
18429
|
+
return Boolean(await directGuess(session));
|
|
18467
18430
|
});
|
|
18468
18431
|
ctx.middleware(async (session, next) => {
|
|
18469
|
-
|
|
18470
|
-
if (!
|
|
18471
|
-
return await next();
|
|
18472
|
-
}
|
|
18473
|
-
if (content) {
|
|
18474
|
-
content = `${import_koishi5.h.select(content, "text")}`.trim();
|
|
18475
|
-
}
|
|
18476
|
-
if (!channelId || !content || !/^[\p{L}\d+*/=().-]+$/u.test(content)) return next();
|
|
18477
|
-
const [gameInfo] = await ctx.database.get("wordle_game_records", { channelId });
|
|
18478
|
-
if (!gameInfo?.isStarted) {
|
|
18479
|
-
return await next();
|
|
18480
|
-
}
|
|
18481
|
-
if (gameInfo.gameMode === "汉兜" || gameInfo.gameMode === "词影") {
|
|
18482
|
-
if (!isFourCharacterIdiom(content)) {
|
|
18483
|
-
return await next();
|
|
18484
|
-
}
|
|
18485
|
-
} else if (gameInfo.gameMode === "Numberle") {
|
|
18486
|
-
if (!isNumericString(content)) {
|
|
18487
|
-
return await next();
|
|
18488
|
-
}
|
|
18489
|
-
} else if (gameInfo.gameMode === "Math") {
|
|
18490
|
-
if (!isMathEquationValid(content)) {
|
|
18491
|
-
return await next();
|
|
18492
|
-
}
|
|
18493
|
-
} else {
|
|
18494
|
-
if (!/^[a-zA-Z]+$/.test(content)) {
|
|
18495
|
-
return await next();
|
|
18496
|
-
}
|
|
18497
|
-
}
|
|
18498
|
-
if (content.length !== gameInfo.guessWordLength) {
|
|
18499
|
-
return await next();
|
|
18500
|
-
}
|
|
18432
|
+
const content = await directGuess(session);
|
|
18433
|
+
if (!content) return next();
|
|
18501
18434
|
if (await directInputConflict(ctx, session)) return;
|
|
18502
18435
|
await session.execute(`wordle.猜 ${content}`);
|
|
18503
18436
|
return;
|
|
@@ -18576,7 +18509,7 @@ ${generateGameEndMessage(gameInfo)}`}${processedResult}
|
|
|
18576
18509
|
${examPanel ?? examList}
|
|
18577
18510
|
发送序号或模式名即可开局,或发送「取消」。`
|
|
18578
18511
|
);
|
|
18579
|
-
const userInput = await promptInput(session
|
|
18512
|
+
const userInput = await promptInput(session);
|
|
18580
18513
|
if (!userInput)
|
|
18581
18514
|
return await sendMessage(
|
|
18582
18515
|
g,
|
|
@@ -18599,7 +18532,7 @@ ${examPanel ?? examList}
|
|
|
18599
18532
|
)}
|
|
18600
18533
|
发送一个长度即可开局,或发送「取消」。`
|
|
18601
18534
|
);
|
|
18602
|
-
const userInput2 = await promptInput(session
|
|
18535
|
+
const userInput2 = await promptInput(session);
|
|
18603
18536
|
if (!userInput2)
|
|
18604
18537
|
return await sendMessage(
|
|
18605
18538
|
g,
|
|
@@ -18779,7 +18712,7 @@ ${image}
|
|
|
18779
18712
|
)}
|
|
18780
18713
|
发送一个长度即可开局,或发送「取消」。`
|
|
18781
18714
|
);
|
|
18782
|
-
const userInput = await promptInput(session
|
|
18715
|
+
const userInput = await promptInput(session);
|
|
18783
18716
|
if (!userInput)
|
|
18784
18717
|
return await sendMessage(
|
|
18785
18718
|
g,
|
|
@@ -19059,7 +18992,7 @@ ${image}${tail}`
|
|
|
19059
18992
|
session,
|
|
19060
18993
|
`💡 发送一个猜测词,或发送「取消」。`
|
|
19061
18994
|
);
|
|
19062
|
-
const userInput = await promptInput(session
|
|
18995
|
+
const userInput = await promptInput(session);
|
|
19063
18996
|
if (!userInput) {
|
|
19064
18997
|
await setGuessRunningStatus(g, channelId, false);
|
|
19065
18998
|
return await sendMessage(
|
|
@@ -19400,7 +19333,7 @@ ${imageMessage(
|
|
|
19400
19333
|
发送「撤销」回到上一步,或发送「结束」收掉这一局。
|
|
19401
19334
|
没有等到有效输入时,按「撤销」处理。`
|
|
19402
19335
|
);
|
|
19403
|
-
let userInput = await promptInput(session
|
|
19336
|
+
let userInput = await promptInput(session);
|
|
19404
19337
|
const imageBuffer22 = await generateImage(
|
|
19405
19338
|
g,
|
|
19406
19339
|
styledHtml,
|
|
@@ -20460,7 +20393,6 @@ var inject = {
|
|
|
20460
20393
|
};
|
|
20461
20394
|
var name = "wordle-game";
|
|
20462
20395
|
async function apply(ctx, config) {
|
|
20463
|
-
const presentation = usePresentation(ctx, "wordle");
|
|
20464
20396
|
const logger = ctx.logger(`wordleGame`);
|
|
20465
20397
|
const wordleGameDirPath = path3.join(ctx.baseDir, "data", "wordleGame");
|
|
20466
20398
|
const idiomsFilePath = path3.join(__dirname, "assets", "汉兜", "idioms.json");
|
package/lib/ux.d.ts
CHANGED
|
@@ -3,29 +3,11 @@ type Owner = {
|
|
|
3
3
|
name: string;
|
|
4
4
|
matches: (session: Session) => boolean | Promise<boolean>;
|
|
5
5
|
};
|
|
6
|
-
/**
|
|
7
|
-
export declare function
|
|
8
|
-
textOnly: (session: Session) => boolean;
|
|
9
|
-
/** 图文模式只给图片;图片缺失或用户选了文字模式时才退回文字。 */
|
|
10
|
-
present(session: Session, image: h.Fragment | null | undefined, text: h.Fragment): h.Fragment;
|
|
11
|
-
};
|
|
6
|
+
/** 渲染图优先,只发图片;图片缺失时才退回等价文字。 */
|
|
7
|
+
export declare function present(image: h.Fragment | null | undefined, text: h.Fragment): h.Fragment;
|
|
12
8
|
/** Plugins register their active input grammar; overlapping messages are never executed. */
|
|
13
9
|
export declare function registerDirectInput(ctx: Context, name: string, matches: Owner['matches']): void;
|
|
14
10
|
export declare function directInputConflict(ctx: Context, session: Session): Promise<boolean>;
|
|
15
|
-
/**
|
|
16
|
-
export declare function promptInput(session: Session
|
|
17
|
-
export declare const IMAGE_FAILURE = "\u56FE\u7247\u6682\u65F6\u65E0\u6CD5\u751F\u6210\uFF0C\u5DF2\u4FDD\u7559\u6587\u5B57\u5185\u5BB9\u3002\u53EF\u4EE5\u7A0D\u540E\u91CD\u8BD5\uFF1B\u82E5\u6301\u7EED\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002";
|
|
18
|
-
/**
|
|
19
|
-
* 渲染图的等价文字。图文模式随图片一起丢掉、文字模式展开成正文,
|
|
20
|
-
* 因此它永远不会原样发到聊天平台。
|
|
21
|
-
*/
|
|
22
|
-
export declare const IMAGE_TEXT_TYPE = "ux-image-text";
|
|
23
|
-
export declare function imageText(text: h.Fragment): h;
|
|
24
|
-
/** 图文模式:保留图片与普通文字,去掉图片的等价文字。 */
|
|
25
|
-
export declare function imagesOnly(content: h.Fragment): h[];
|
|
26
|
-
/** 文字模式:去掉图片,把等价文字展开成普通文本。 */
|
|
27
|
-
export declare function withoutImages(content: h.Fragment): h[];
|
|
28
|
-
/** 发送前的统一选择:文字模式去掉图片,图文模式去掉等价文字。 */
|
|
29
|
-
export declare function choosePresentation(content: h.Fragment, textOnly: boolean): h[];
|
|
30
|
-
export declare function isTextOnly(ctx: Context, session: Session): boolean;
|
|
11
|
+
/** 等待下一句回复。提问本身已经写明了可回复的内容与「取消」,这里不再追加说明。 */
|
|
12
|
+
export declare function promptInput(session: Session): Promise<string | undefined>;
|
|
31
13
|
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-wordle-game",
|
|
3
|
-
"description": "Koishi
|
|
4
|
-
"version": "4.
|
|
3
|
+
"description": "Koishi 插件:猜词游戏,支持单词、成语、数字与方程式等多种模式。",
|
|
4
|
+
"version": "4.4.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|