@wingsbutterfly/pi-zh 0.3.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/LICENSE +21 -0
- package/README.md +107 -0
- package/extensions/index.ts +195 -0
- package/lib/coexist.ts +86 -0
- package/lib/config.ts +71 -0
- package/lib/dict.ts +303 -0
- package/lib/patch.ts +126 -0
- package/lib/sdk.ts +44 -0
- package/lib/targets.ts +96 -0
- package/lib/telegram-commands.ts +185 -0
- package/package.json +45 -0
- package/scripts/zh-dict.mjs +154 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-zh Telegram 命令同步模块
|
|
3
|
+
*
|
|
4
|
+
* 职责:用 curl 调用 Telegram Bot API,把 bot 命令菜单(setMyCommands 默认层)
|
|
5
|
+
* 注册为中文描述。插件每次 /start 会重写默认层为英文,本模块检测到英文描述
|
|
6
|
+
* 时立即重写(每隔 30 秒轮询)。
|
|
7
|
+
*
|
|
8
|
+
* 背景:不能使用 language_code 分层 —— 中文客户端上报 zh-hans,不匹配 zh 层,
|
|
9
|
+
* 会回退默认层英文(Telegram 官方 bug)。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { execFile } from "node:child_process";
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
/** 内置命令的中文描述(与 telegram.json 服务端同步用的) */
|
|
18
|
+
const BUILTIN_ZH: Record<string, string> = {
|
|
19
|
+
start: "🟢 打开菜单 / 配对桥接",
|
|
20
|
+
compact: "🗜 压缩当前会话",
|
|
21
|
+
next: "⏩ 强制下一轮",
|
|
22
|
+
continue: "▶️ 排入继续提示",
|
|
23
|
+
abort: "⏹️ 中止 Pi",
|
|
24
|
+
stop: "🟥 中止 Pi 并清空队列",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** 常见扩展命令的中文描述(未命中时保留原英文描述) */
|
|
28
|
+
const EXTENSION_ZH: Record<string, string> = {
|
|
29
|
+
status: "📊 查看状态菜单",
|
|
30
|
+
model: "🤖 选择模型",
|
|
31
|
+
thinking: "🧠 设置思考级别",
|
|
32
|
+
queue: "⏳ 查看任务队列",
|
|
33
|
+
settings: "⚙️ 打开设置",
|
|
34
|
+
review: "🧩 审查排队任务",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface TelegramCommand {
|
|
38
|
+
command: string;
|
|
39
|
+
description: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface TelegramProfile {
|
|
43
|
+
botToken?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface TelegramConfigFile {
|
|
47
|
+
profiles?: Record<string, TelegramProfile>;
|
|
48
|
+
botToken?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 读取 ~/.pi/agent/telegram.json 中的全部 botToken */
|
|
52
|
+
export function readBotTokens(): string[] {
|
|
53
|
+
const candidates = [
|
|
54
|
+
join(homedir(), ".pi/agent/telegram.json"),
|
|
55
|
+
join(homedir(), ".pi/telegram.json"),
|
|
56
|
+
];
|
|
57
|
+
for (const path of candidates) {
|
|
58
|
+
try {
|
|
59
|
+
const raw = readFileSync(path, "utf8");
|
|
60
|
+
const cfg = JSON.parse(raw) as TelegramConfigFile;
|
|
61
|
+
const tokens = new Set<string>();
|
|
62
|
+
if (cfg.profiles) {
|
|
63
|
+
for (const profile of Object.values(cfg.profiles)) {
|
|
64
|
+
if (profile.botToken) tokens.add(profile.botToken);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (cfg.botToken) tokens.add(cfg.botToken);
|
|
68
|
+
if (tokens.size > 0) return [...tokens];
|
|
69
|
+
} catch {
|
|
70
|
+
// 尝试下一个路径
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function runCurl(
|
|
77
|
+
url: string,
|
|
78
|
+
body?: unknown,
|
|
79
|
+
): Promise<{ ok: boolean; result?: unknown; description?: string }> {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const args = ["-s", "--max-time", "20"];
|
|
82
|
+
if (body === undefined) {
|
|
83
|
+
args.push(url);
|
|
84
|
+
} else {
|
|
85
|
+
args.push(
|
|
86
|
+
"-X",
|
|
87
|
+
"POST",
|
|
88
|
+
"-H",
|
|
89
|
+
"Content-Type: application/json",
|
|
90
|
+
"-d",
|
|
91
|
+
JSON.stringify(body),
|
|
92
|
+
url,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
execFile("curl", args, { timeout: 25_000 }, (error, stdout, stderr) => {
|
|
96
|
+
if (error) {
|
|
97
|
+
reject(new Error(`curl failed: ${error.message} ${stderr}`));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
resolve(
|
|
102
|
+
JSON.parse(stdout) as {
|
|
103
|
+
ok: boolean;
|
|
104
|
+
result?: unknown;
|
|
105
|
+
description?: string;
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
} catch {
|
|
109
|
+
reject(new Error(`curl output not JSON: ${stdout.slice(0, 200)}`));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function callTelegramApi<T>(
|
|
116
|
+
token: string,
|
|
117
|
+
method: string,
|
|
118
|
+
body?: unknown,
|
|
119
|
+
): Promise<T> {
|
|
120
|
+
const url = `https://api.telegram.org/bot${token}/${method}`;
|
|
121
|
+
const payload = await runCurl(url, body);
|
|
122
|
+
if (!payload.ok) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Telegram API ${method} failed: ${payload.description ?? "unknown"}`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return payload.result as T;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function translateDescription(cmd: TelegramCommand): string {
|
|
131
|
+
return BUILTIN_ZH[cmd.command] ?? EXTENSION_ZH[cmd.command] ?? cmd.description;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function isEnglishDescription(description: string): boolean {
|
|
135
|
+
return /Open menu \/ Pair bridge|Compact current session|Force next turn/.test(
|
|
136
|
+
description,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** 同步一次:把默认层命令注册为中文 */
|
|
141
|
+
export async function syncZhCommands(token: string): Promise<void> {
|
|
142
|
+
const current = await callTelegramApi<TelegramCommand[]>(
|
|
143
|
+
token,
|
|
144
|
+
"getMyCommands",
|
|
145
|
+
);
|
|
146
|
+
const commands = current.map((cmd) => ({
|
|
147
|
+
command: cmd.command,
|
|
148
|
+
description: translateDescription(cmd),
|
|
149
|
+
}));
|
|
150
|
+
if (commands.length === 0) {
|
|
151
|
+
for (const [command, description] of Object.entries(BUILTIN_ZH)) {
|
|
152
|
+
commands.push({ command, description });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
await callTelegramApi(token, "setMyCommands", { commands });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 检测默认层是否被插件重写为英文,是则修复,返回是否修复 */
|
|
159
|
+
export async function ensureZhCommands(token: string): Promise<boolean> {
|
|
160
|
+
const current = await callTelegramApi<TelegramCommand[]>(
|
|
161
|
+
token,
|
|
162
|
+
"getMyCommands",
|
|
163
|
+
);
|
|
164
|
+
if (current.length === 0) return false;
|
|
165
|
+
if (current.some((cmd) => isEnglishDescription(cmd.description))) {
|
|
166
|
+
await syncZhCommands(token);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** 启动时同步所有 token(失败不抛错,返回成功数) */
|
|
173
|
+
export async function syncAllTokens(): Promise<number> {
|
|
174
|
+
const tokens = readBotTokens();
|
|
175
|
+
let ok = 0;
|
|
176
|
+
for (const token of tokens) {
|
|
177
|
+
try {
|
|
178
|
+
await syncZhCommands(token);
|
|
179
|
+
ok++;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.warn("[pi-zh] telegram command sync failed:", error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return ok;
|
|
185
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wingsbutterfly/pi-zh",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Pi 汉化插件:slash 命令补全、TUI 文案、pi-telegram 界面统一汉化",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"pi-package",
|
|
9
|
+
"pi",
|
|
10
|
+
"i18n",
|
|
11
|
+
"chinese",
|
|
12
|
+
"localization",
|
|
13
|
+
"汉化"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/wings1848/pi-zh.git"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/wings1848/pi-zh/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/wings1848/pi-zh#readme",
|
|
23
|
+
"files": [
|
|
24
|
+
"extensions",
|
|
25
|
+
"lib",
|
|
26
|
+
"scripts"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "bun test",
|
|
33
|
+
"typecheck": "tsc --noEmit"
|
|
34
|
+
},
|
|
35
|
+
"pi": {
|
|
36
|
+
"extensions": [
|
|
37
|
+
"./extensions"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@earendil-works/pi-coding-agent": "^0.84.4",
|
|
42
|
+
"@types/node": "^26.4.0",
|
|
43
|
+
"typescript": "^7.0.2"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* telegram-zh outbound 词典脚本
|
|
4
|
+
*
|
|
5
|
+
* 供 telegram.json 配置式 outboundHandlers(type: "text")调用:
|
|
6
|
+
* 从 stdin 读取插件发送的文本,对固定 UI 英文短语做精确整句替换,写回 stdout。
|
|
7
|
+
* 只替换完整的、带标记的插件固定文案,避免误伤 agent 回复内容。
|
|
8
|
+
* 未命中任何短语时原样输出(stdout 非空是配置式 handler 生效的前提)。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
|
|
13
|
+
/** 精确短语替换表:EN → ZH。按完整字符串匹配(split/join 全量替换,包含 HTML 标签片段) */
|
|
14
|
+
const DICTIONARY = [
|
|
15
|
+
// --- busy / 状态提示 ---
|
|
16
|
+
["<b>⏳ Cannot open status while Pi is busy. Send /abort, /next, or /stop.</b>",
|
|
17
|
+
"<b>⏳ Pi 忙,无法打开状态菜单。请发送 /abort、/next 或 /stop。</b>"],
|
|
18
|
+
["<b>⏳ Cannot switch model while Pi is busy. Send /abort, /next, or /stop.</b>",
|
|
19
|
+
"<b>⏳ Pi 忙,无法切换模型。请发送 /abort、/next 或 /stop。</b>"],
|
|
20
|
+
["<b>🚫 Cannot compact while Pi or the Telegram queue is busy.</b>",
|
|
21
|
+
"<b>🚫 Pi 或 Telegram 队列忙,无法压缩会话。</b>"],
|
|
22
|
+
["Pi is busy. Send /abort or /stop first.",
|
|
23
|
+
"Pi 忙,请先发送 /abort 或 /stop。"],
|
|
24
|
+
["Pi is busy. Send /abort, /next, or /stop.",
|
|
25
|
+
"Pi 忙,请发送 /abort、/next 或 /stop。"],
|
|
26
|
+
["Cannot compact while Pi or the Telegram queue is busy. Wait for queued turns to finish or send /abort first.",
|
|
27
|
+
"Pi 或 Telegram 队列忙,无法压缩。请等待排队任务结束,或先发送 /abort。"],
|
|
28
|
+
|
|
29
|
+
// --- 压缩状态 ---
|
|
30
|
+
["**✅ Compaction completed.**", "**✅ 压缩完成。**"],
|
|
31
|
+
["Compaction completed.", "压缩完成。"],
|
|
32
|
+
["Compaction started.", "压缩开始。"],
|
|
33
|
+
["Compaction cancelled.", "压缩已取消。"],
|
|
34
|
+
["<b>🚫 Compaction cancelled.</b>", "<b>🚫 压缩已取消。</b>"],
|
|
35
|
+
["Compaction failed: ", "压缩失败:"],
|
|
36
|
+
|
|
37
|
+
// --- 模型 ---
|
|
38
|
+
["<b>🚫 No available models with configured auth.</b>",
|
|
39
|
+
"<b>🚫 没有配置认证的可用模型。</b>"],
|
|
40
|
+
["<b>🤖 Choose a model:</b>", "<b>🤖 选择模型:</b>"],
|
|
41
|
+
["<b>🤖 Model:</b>", "<b>🤖 模型:</b>"],
|
|
42
|
+
["Model is not available.", "模型不可用。"],
|
|
43
|
+
["Invalid model selection.", "无效的模型选择。"],
|
|
44
|
+
["Added to scoped models", "已添加到作用域模型"],
|
|
45
|
+
["Model scope is controlled by CLI --models.",
|
|
46
|
+
"模型作用域由 CLI --models 控制。"],
|
|
47
|
+
["No CLI scoped models matched the current auth configuration. Showing all available models.",
|
|
48
|
+
"没有匹配当前认证配置的 CLI 作用域模型。显示全部可用模型。"],
|
|
49
|
+
["No scoped models matched the current auth configuration. Showing all available models.",
|
|
50
|
+
"没有匹配当前认证配置的作用域模型。显示全部可用模型。"],
|
|
51
|
+
["<b>Choose a page:</b>", "<b>选择页码:</b>"],
|
|
52
|
+
["Invalid page.", "无效的页码。"],
|
|
53
|
+
["All models", "全部模型"],
|
|
54
|
+
["☑️ Activate", "☑️ 激活"],
|
|
55
|
+
["🟢 Active", "🟢 已激活"],
|
|
56
|
+
["🟣 All", "🟣 全部"],
|
|
57
|
+
["⚫️ All", "⚫️ 全部"],
|
|
58
|
+
|
|
59
|
+
// --- 思考级别 ---
|
|
60
|
+
["<b>🧠 Choose a thinking level:</b>", "<b>🧠 选择思考级别:</b>"],
|
|
61
|
+
["Invalid thinking level.", "无效的思考级别。"],
|
|
62
|
+
["Thinking controls are disabled during voice replies.",
|
|
63
|
+
"语音回复期间思考控制已禁用。"],
|
|
64
|
+
["This model has no reasoning controls.", "此模型没有推理控制。"],
|
|
65
|
+
["Choose how much technical model activity Telegram shows.",
|
|
66
|
+
"选择 Telegram 显示多少技术性模型活动。"],
|
|
67
|
+
|
|
68
|
+
// --- 队列 ---
|
|
69
|
+
["<b>⏳ Queue:</b>", "<b>⏳ 队列:</b>"],
|
|
70
|
+
["<b>🕳 Nothing queued yet.</b>", "<b>🕳 队列中还没有任务。</b>"],
|
|
71
|
+
["<b>⌛ Queue is empty.</b>", "<b>⌛ 队列为空。</b>"],
|
|
72
|
+
["<b>⌛ Queue is still empty.</b>", "<b>⌛ 队列仍然为空。</b>"],
|
|
73
|
+
["<b>🧺 Basket is empty.</b>", "<b>🧺 篮子为空。</b>"],
|
|
74
|
+
["<b>🫧 Queue bubbles: none.</b>", "<b>🫧 队列气泡:无。</b>"],
|
|
75
|
+
["<b>🦗 Queue crickets continue.</b>", "<b>🦗 队列中蟋蟀还在叫。</b>"],
|
|
76
|
+
["<b>🌙 Queue is peacefully idle.</b>", "<b>🌙 队列安静闲置中。</b>"],
|
|
77
|
+
["<b>🪐 Queue orbit is clear.</b>", "<b>🪐 队列轨道畅通。</b>"],
|
|
78
|
+
["<b>🍃 Queue remains empty.</b>", "<b>🍃 队列依然为空。</b>"],
|
|
79
|
+
["<b>🫙 Still nothing in queue.</b>", "<b>🫙 队列里还是什么都没有。</b>"],
|
|
80
|
+
["<b>🔭 No prompts on the horizon.</b>", "<b>🔭 地平线上没有提示。</b>"],
|
|
81
|
+
["<b>🛸 No queued signals detected.</b>", "<b>🛸 未检测到排队信号。</b>"],
|
|
82
|
+
["<b>🧘 Nothing waiting. Very zen.</b>", "<b>🧘 没有等待,很禅。</b>"],
|
|
83
|
+
["<b>🕳 Nothing queued yet.</b>", "<b>🕳 还没有排队任务。</b>"],
|
|
84
|
+
["Item no longer in queue.", "项目已不在队列中。"],
|
|
85
|
+
|
|
86
|
+
// --- 设置 ---
|
|
87
|
+
["<b>⚙️ Settings:</b>", "<b>⚙️ 设置:</b>"],
|
|
88
|
+
["<b>🔬 Activity:</b>", "<b>🔬 活动显示:</b>"],
|
|
89
|
+
["<b>🧾 Assistant rendering:</b>", "<b>🧾 助手渲染:</b>"],
|
|
90
|
+
["<b>📝 Draft previews:</b>", "<b>📝 草稿预览:</b>"],
|
|
91
|
+
["<b>🧹 Thread cleanup:</b>", "<b>🧹 会话清理:</b>"],
|
|
92
|
+
["<b>🕒 Time injection mode:</b>", "<b>🕒 时间注入模式:</b>"],
|
|
93
|
+
["<b>👄 Voice reply mode:</b>", "<b>👄 语音回复模式:</b>"],
|
|
94
|
+
["Choose how final assistant Markdown answers are delivered.",
|
|
95
|
+
"选择最终助手 Markdown 回复的投递方式。"],
|
|
96
|
+
["Controls when pi-telegram converts assistant text replies into Telegram voice messages.",
|
|
97
|
+
"控制 pi-telegram 何时将助手文本回复转换为 Telegram 语音消息。"],
|
|
98
|
+
["Controls whether Telegram-originated prompts include a compact wall-clock [time] line.",
|
|
99
|
+
"控制 Telegram 来源的提示是否包含一行紧凑的墙钟 [time]。"],
|
|
100
|
+
["Delete this Pi instance's Telegram tab when Pi quits normally.",
|
|
101
|
+
"Pi 正常退出时删除此 Pi 实例的 Telegram 标签页。"],
|
|
102
|
+
["<code>-</code> <code>rich</code> (default): use Telegram Native Rich Markdown.",
|
|
103
|
+
"<code>-</code> <code>rich</code>(默认):使用 Telegram 原生富文本 Markdown。"],
|
|
104
|
+
["<code>-</code> <code>html</code>: use the legacy Markdown-to-HTML renderer.",
|
|
105
|
+
"<code>-</code> <code>html</code>:使用旧版 Markdown 转 HTML 渲染器。"],
|
|
106
|
+
["<code>-</code> <code>on</code> (default): stream safe Telegram Rich Draft frames before the final answer.",
|
|
107
|
+
"<code>-</code> <code>on</code>(默认):在最终回复前流式发送安全的 Telegram 富文本草稿。"],
|
|
108
|
+
["<code>-</code> <code>off</code> (default): show native active status, then send one final answer.",
|
|
109
|
+
"<code>-</code> <code>off</code>(默认):显示原生活跃状态,然后发送一条最终回复。"],
|
|
110
|
+
["<code>-</code> <code>quiet</code>: show no thinking or tool traffic.",
|
|
111
|
+
"<code>-</code> <code>quiet</code>:不显示思考或工具动态。"],
|
|
112
|
+
["<code>-</code> <code>thinking</code>: show persistent collapsed thinking.",
|
|
113
|
+
"<code>-</code> <code>thinking</code>:显示持久折叠的思考。"],
|
|
114
|
+
["<code>-</code> <code>tools</code>: show persistent Rich tool details.",
|
|
115
|
+
"<code>-</code> <code>tools</code>:显示持久富文本工具详情。"],
|
|
116
|
+
["<code>-</code> <code>verbose</code> (default): show both thinking and tools.",
|
|
117
|
+
"<code>-</code> <code>verbose</code>(默认):同时显示思考和工具。"],
|
|
118
|
+
["<code>-</code> <code>off</code>: preserve the tab as a restart hint; manual <code>/telegram-disconnect</code> still confirms and deletes it.",
|
|
119
|
+
"<code>-</code> <code>off</code>:保留标签页作为重启提示;手动 <code>/telegram-disconnect</code> 仍会确认并删除它。"],
|
|
120
|
+
["<code>-</code> <code>on</code> (default): delete the bound thread and release Telegram authority on graceful quit.",
|
|
121
|
+
"<code>-</code> <code>on</code>(默认):优雅退出时删除绑定的会话并释放 Telegram 权限。"],
|
|
122
|
+
["<code>-</code> <code>manual</code> (default): add no automatic voice context; explicit 'telegram_voice' actions still work.",
|
|
123
|
+
"<code>-</code> <code>manual</code>(默认):不自动添加语音上下文;显式 'telegram_voice' 操作仍然有效。"],
|
|
124
|
+
["<code>-</code> <code>mirror</code>: voice input activates automatic voice delivery; text input follows 'manual' behavior.",
|
|
125
|
+
"<code>-</code> <code>mirror</code>:语音输入激活自动语音回复;文本输入遵循 'manual' 行为。"],
|
|
126
|
+
["<code>-</code> <code>always</code>: activate automatic voice delivery for every reply.",
|
|
127
|
+
"<code>-</code> <code>always</code>:每次回复都激活自动语音投递。"],
|
|
128
|
+
["<code>-</code> <code>off</code> (default): add no time line to prompt context.",
|
|
129
|
+
"<code>-</code> <code>off</code>(默认):不向提示上下文添加时间行。"],
|
|
130
|
+
["<code>-</code> <code>hidden</code>: no time line is added to prompt context.",
|
|
131
|
+
"<code>-</code> <code>hidden</code>:不向提示上下文添加时间行。"],
|
|
132
|
+
["<code>-</code> <code>interval</code> (default): add time at most once per chat interval (1 hour unless configured).",
|
|
133
|
+
"<code>-</code> <code>interval</code>(默认):每个聊天间隔最多添加一次时间(默认 1 小时,可配置)。"],
|
|
134
|
+
["<code>-</code> <code>always</code>: add time to every Telegram turn.",
|
|
135
|
+
"<code>-</code> <code>always</code>:每个 Telegram 轮次都添加时间。"],
|
|
136
|
+
|
|
137
|
+
// --- 导航 ---
|
|
138
|
+
["⬆️ Back", "⬆️ 返回"],
|
|
139
|
+
["⬆️ Main menu", "⬆️ 主菜单"],
|
|
140
|
+
["⚙️ Settings", "⚙️ 设置"],
|
|
141
|
+
["Interactive message expired.", "交互消息已过期。"],
|
|
142
|
+
|
|
143
|
+
// --- 命令帮助文案 ---
|
|
144
|
+
["<b>🟢 Open menu / Pair bridge</b>", "<b>🟢 打开菜单 / 配对桥接</b>"],
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
const input = readFileSync(0, "utf8");
|
|
148
|
+
let output = input;
|
|
149
|
+
for (const [en, zh] of DICTIONARY) {
|
|
150
|
+
if (output.includes(en)) {
|
|
151
|
+
output = output.split(en).join(zh);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
process.stdout.write(output);
|