@xmanrui/dsh-im 4.19.1 → 4.19.2
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.en.md +18 -4
- package/README.md +18 -4
- package/lib/client.js +73 -1
- package/lib/index.js +261 -260
- package/package.json +5 -1
- package/plugin-src/client/index.js +15 -0
- package/plugin-src/client/interface-language.js +89 -0
- package/plugin-src/host/channels/shared/startup.mjs +23 -0
- package/plugin-src/host/host-language-rpc.mjs +71 -0
- package/plugin-src/host/host-language.mjs +157 -0
- package/plugin-src/host/index.mjs +15 -2
- package/scripts/verify-interface-language.mjs +333 -0
- package/src/channels/discord/discord-runtime.mjs +1 -1
- package/src/channels/shared/i18n-en/discord.mjs +2 -0
- package/src/channels/shared/i18n-en/telegram.mjs +5 -0
- package/src/channels/shared/i18n.mjs +46 -3
- package/src/channels/shared/interface-language-store.mjs +127 -0
- package/src/channels/shared/interface-language.mjs +51 -0
- package/src/channels/shared/token-bot-controller.mjs +26 -0
- package/src/channels/telegram/telegram-runtime.mjs +75 -13
|
@@ -524,10 +524,10 @@ export class TelegramBotClient {
|
|
|
524
524
|
|
|
525
525
|
const fallback = await this.#sendPlain(target, text);
|
|
526
526
|
const terminalText = fallback.deliveryOutcome === 'sent'
|
|
527
|
-
? '回复已发送。'
|
|
527
|
+
? t('回复已发送。')
|
|
528
528
|
: fallback.deliveryOutcome === 'unknown'
|
|
529
|
-
? '回复发送结果未能确认。'
|
|
530
|
-
: '消息发送失败,请稍后重试。';
|
|
529
|
+
? t('回复发送结果未能确认。')
|
|
530
|
+
: t('消息发送失败,请稍后重试。');
|
|
531
531
|
try {
|
|
532
532
|
await this.#api.editMessageText({
|
|
533
533
|
chatId: target.chatId,
|
|
@@ -717,13 +717,13 @@ export class TelegramBotClient {
|
|
|
717
717
|
keepalive: true,
|
|
718
718
|
logger: this.#logger,
|
|
719
719
|
});
|
|
720
|
-
await stream.update(createTextDeliveryBlock('正在处理…', 'plain'));
|
|
720
|
+
await stream.update(createTextDeliveryBlock(t('正在处理…'), 'plain'));
|
|
721
721
|
return stream;
|
|
722
722
|
}
|
|
723
723
|
|
|
724
724
|
const placeholder = await this.#api.sendMessage({
|
|
725
725
|
chatId: target.chatId,
|
|
726
|
-
text: '正在处理…',
|
|
726
|
+
text: t('正在处理…'),
|
|
727
727
|
replyToMessageId: target.replyToMessageId,
|
|
728
728
|
messageThreadId: target.messageThreadId,
|
|
729
729
|
signal: this.#signal,
|
|
@@ -824,6 +824,14 @@ export class TelegramRuntime {
|
|
|
824
824
|
#abortController = null;
|
|
825
825
|
#pollTask = null;
|
|
826
826
|
#starting = null;
|
|
827
|
+
// True only while #start() is between "started connecting" and "ready",
|
|
828
|
+
// which is the one window a skipped refresh must be reconciled. A refresh on
|
|
829
|
+
// a never-started or already-stopped runtime stays a no-op.
|
|
830
|
+
#connecting = false;
|
|
831
|
+
// True when a language switch arrived while the runtime was connecting, so a
|
|
832
|
+
// refreshCommandMenu() had nothing to push. Reconciled once startup
|
|
833
|
+
// completes, so the platform always ends up with the latest language.
|
|
834
|
+
#menuDirty = false;
|
|
827
835
|
|
|
828
836
|
constructor({
|
|
829
837
|
config,
|
|
@@ -902,6 +910,7 @@ export class TelegramRuntime {
|
|
|
902
910
|
|
|
903
911
|
async #start() {
|
|
904
912
|
await this.stop();
|
|
913
|
+
this.#connecting = true;
|
|
905
914
|
this.#status.startedAt = new Date().toISOString();
|
|
906
915
|
this.#status.connectionState = 'connecting';
|
|
907
916
|
this.#status.lastError = null;
|
|
@@ -930,14 +939,7 @@ export class TelegramRuntime {
|
|
|
930
939
|
throw error;
|
|
931
940
|
}
|
|
932
941
|
try {
|
|
933
|
-
|
|
934
|
-
// Both operations use the existing default scope and language. Sending
|
|
935
|
-
// the full list replaces old entries; an empty catalog clears that list.
|
|
936
|
-
if (commands.length > 0) {
|
|
937
|
-
await api.setMyCommands({ commands, signal: controller.signal });
|
|
938
|
-
} else {
|
|
939
|
-
await api.deleteMyCommands({ signal: controller.signal });
|
|
940
|
-
}
|
|
942
|
+
await this.#sendCommandMenu(api, controller.signal);
|
|
941
943
|
await api.setChatMenuButton({ menuButton: COMMANDS_MENU_BUTTON, signal: controller.signal });
|
|
942
944
|
} catch (error) {
|
|
943
945
|
this.#logger.warn?.(
|
|
@@ -973,6 +975,21 @@ export class TelegramRuntime {
|
|
|
973
975
|
this.#status.connectionState = 'connected';
|
|
974
976
|
this.#status.lastCheckedAt = now;
|
|
975
977
|
this.#status.lastConnectedAt = now;
|
|
978
|
+
// A language switch that landed while we were connecting was skipped by
|
|
979
|
+
// refreshCommandMenu(); re-send the menu now that the API is usable, so
|
|
980
|
+
// the platform ends with the latest language instead of the one that was
|
|
981
|
+
// current when #sendCommandMenu first ran.
|
|
982
|
+
if (this.#menuDirty) {
|
|
983
|
+
this.#menuDirty = false;
|
|
984
|
+
try {
|
|
985
|
+
await this.#sendCommandMenu(api, controller.signal);
|
|
986
|
+
} catch (error) {
|
|
987
|
+
this.#logger.warn?.(
|
|
988
|
+
`[dsh-im:telegram] bot ${this.#config.botId} command menu catch-up failed:`,
|
|
989
|
+
error,
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
976
993
|
this.#pollTask = this.#poll(cursor, controller.signal);
|
|
977
994
|
this.#pollTask.catch((error) => {
|
|
978
995
|
if (controller.signal.aborted) return;
|
|
@@ -988,6 +1005,51 @@ export class TelegramRuntime {
|
|
|
988
1005
|
this.#status.lastError = error?.message ?? String(error);
|
|
989
1006
|
await this.stop();
|
|
990
1007
|
throw error;
|
|
1008
|
+
} finally {
|
|
1009
|
+
// Whether the bot is now ready or the attempt failed, the connecting
|
|
1010
|
+
// window is over. A failed attempt also drops any unreconciled dirty
|
|
1011
|
+
// flag: a later retry re-sends the menu from scratch in the current
|
|
1012
|
+
// language anyway.
|
|
1013
|
+
this.#connecting = false;
|
|
1014
|
+
this.#menuDirty = false;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// Both operations use the existing default scope and language. Sending the
|
|
1019
|
+
// full list replaces old entries; an empty catalog clears that list.
|
|
1020
|
+
async #sendCommandMenu(api, signal) {
|
|
1021
|
+
const commands = telegramCommandMenu(this.#commandCatalog);
|
|
1022
|
+
if (commands.length > 0) await api.setMyCommands({ commands, signal });
|
|
1023
|
+
else await api.deleteMyCommands({ signal });
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Re-send the command menu in the current host message language.
|
|
1028
|
+
*
|
|
1029
|
+
* The menu Telegram shows is registered once per connection, so a language
|
|
1030
|
+
* change would otherwise stay invisible until the bot reconnected. This
|
|
1031
|
+
* replaces the text only: the chat menu button is owned by connect.
|
|
1032
|
+
* @returns whether a connected bot accepted the refreshed menu.
|
|
1033
|
+
*/
|
|
1034
|
+
async refreshCommandMenu() {
|
|
1035
|
+
const api = this.#api;
|
|
1036
|
+
const signal = this.#abortController?.signal;
|
|
1037
|
+
if (!this.#status.ready || !api || !signal || signal.aborted) {
|
|
1038
|
+
// A refresh during an in-progress connection must not be lost: mark the
|
|
1039
|
+
// menu dirty so #start() reconciles it the moment the bot is ready. A
|
|
1040
|
+
// never-started or stopped runtime stays a no-op, as before.
|
|
1041
|
+
if (this.#connecting) this.#menuDirty = true;
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
try {
|
|
1045
|
+
await this.#sendCommandMenu(api, signal);
|
|
1046
|
+
return true;
|
|
1047
|
+
} catch (error) {
|
|
1048
|
+
this.#logger.warn?.(
|
|
1049
|
+
`[dsh-im:telegram] bot ${this.#config.botId} command menu refresh failed:`,
|
|
1050
|
+
error,
|
|
1051
|
+
);
|
|
1052
|
+
return false;
|
|
991
1053
|
}
|
|
992
1054
|
}
|
|
993
1055
|
|