mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-10 11:40:45 +08:00
Wrap all user-facing chat text in src/channels with a host-side t() translator (Chinese is the identity default, so untranslated text is always sent verbatim in Chinese). English is selected via the host plugin config `language: en` or the DSH_IM_LANGUAGE environment variable, and the settings UI keeps following the DSH web locale. - add src/channels/shared/i18n.mjs with setImHostLanguage/t() and a Chinese-key -> English dictionary split by area (shared + channels) - complete the English dictionary for every t() key in src/channels, including the feishu channel (welcome/help, repair flow, interactive cards, watch flow) and the shared command/approval/stream text - wrap previously untranslated chat-facing strings in feishu bridge, feishu runtime, editable-message-stream, harness-client, connection-test and image-prompt - dedupe dictionary entries that existed in multiple channel files and canonicalize conflicting translations into the shared dictionaries - extend the i18n unit test to decode string-literal escapes so keys containing \n are checked correctly - document the language switch in README.md, README.en.md and CHANGELOG.md, and rebuild lib/index.js Added English support for bot chat messages across all nine IM channels and AI Office without changing any Chinese output.
86 lines
2.7 KiB
JavaScript
86 lines
2.7 KiB
JavaScript
import { t } from '../shared/i18n.mjs';
|
|
import {
|
|
deriveTokenBotIdentity,
|
|
maskPlatformId,
|
|
TokenBotConfigStore,
|
|
} from '../shared/token-config-store.mjs';
|
|
|
|
const IDENTITY_OPTIONS = Object.freeze({
|
|
botPrefix: 'telegram',
|
|
tokenRefPrefix: 'DSH_TELEGRAM_BOT_TOKEN',
|
|
});
|
|
|
|
export const TELEGRAM_ACCESS_MODES = Object.freeze({
|
|
compatible: 'compatible',
|
|
privateAllowlist: 'private-allowlist',
|
|
});
|
|
|
|
const TELEGRAM_USER_ID = /^[1-9]\d{0,15}$/;
|
|
|
|
export function normalizeTelegramAllowedUsers(value) {
|
|
if (value === undefined) return Object.freeze([]);
|
|
if (!Array.isArray(value)) {
|
|
throw new TypeError('allowedUsers must be an array of numeric Telegram User IDs');
|
|
}
|
|
const normalized = value.map((entry) => {
|
|
const userId = typeof entry === 'number' && Number.isSafeInteger(entry)
|
|
? String(entry) : typeof entry === 'string' ? entry.trim() : '';
|
|
if (!TELEGRAM_USER_ID.test(userId)) {
|
|
throw new TypeError('allowedUsers contains an invalid Telegram User ID');
|
|
}
|
|
return userId;
|
|
});
|
|
return Object.freeze([...new Set(normalized)]);
|
|
}
|
|
|
|
export function normalizeTelegramAccessPolicy(value = {}) {
|
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
throw new TypeError('Telegram access policy must be an object');
|
|
}
|
|
const accessMode = value.accessMode ?? TELEGRAM_ACCESS_MODES.compatible;
|
|
if (!Object.values(TELEGRAM_ACCESS_MODES).includes(accessMode)) {
|
|
throw new TypeError('Telegram accessMode must be compatible or private-allowlist');
|
|
}
|
|
return Object.freeze({
|
|
accessMode,
|
|
allowedUsers: normalizeTelegramAllowedUsers(value.allowedUsers),
|
|
});
|
|
}
|
|
|
|
function normalizeTelegramBotExtension(value) {
|
|
const hasAccessMode = Object.hasOwn(value, 'accessMode');
|
|
const hasAllowedUsers = Object.hasOwn(value, 'allowedUsers');
|
|
if (!hasAccessMode && !hasAllowedUsers) return {};
|
|
try {
|
|
const policy = normalizeTelegramAccessPolicy(value);
|
|
return {
|
|
...(hasAccessMode ? { accessMode: policy.accessMode } : {}),
|
|
...(hasAllowedUsers || hasAccessMode ? { allowedUsers: policy.allowedUsers } : {}),
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function deriveTelegramBotIdentity(platformId) {
|
|
return deriveTokenBotIdentity(platformId, IDENTITY_OPTIONS);
|
|
}
|
|
|
|
export function maskTelegramBotId(platformId) {
|
|
return maskPlatformId(platformId, t('Telegram机器人'));
|
|
}
|
|
|
|
export class TelegramConfigStore extends TokenBotConfigStore {
|
|
constructor(path) {
|
|
super(path, {
|
|
channel: 'Telegram',
|
|
...IDENTITY_OPTIONS,
|
|
normalizeBotExtension: normalizeTelegramBotExtension,
|
|
});
|
|
}
|
|
|
|
async save(value) {
|
|
const previous = value?.platformId ? this.getByPlatformId(String(value.platformId)) : null;
|
|
return super.save({ ...previous, ...value });
|
|
}
|
|
}
|