Files
tg-chat-bot/src/commands/transliteration.ts
T
2026-05-18 13:31:37 +03:00

107 lines
2.9 KiB
TypeScript

import {Command} from "../base/command";
import {Message} from "typescript-telegram-bot-api";
import {logError, oldReplyToMessage, randomValue} from "../util/utils";
import {Environment} from "../common/environment";
const EN =
"`qwertyuiop[]asdfghjkl;'zxcvbnm,./" +
"~QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?" +
"1234567890-=" +
"!@#$%^&*()_+";
const RU =
"ёйцукенгшщзхъфывапролджэячсмитьбю." +
"ЁЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ," +
"1234567890-=" +
"!\"№;%:?*()_+";
function makeMap(from: string, to: string): Map<string, string> {
if (from.length !== to.length) {
throw new Error(`Layout maps must be same length: ${from.length} vs ${to.length}`);
}
const m = new Map<string, string>();
for (let i = 0; i < from.length; i++) m.set(from[i], to[i]);
return m;
}
const enToRu = makeMap(EN, RU);
const ruToEn = makeMap(RU, EN);
function swapLayout(text: string, map: Map<string, string>): string {
let out = "";
for (const ch of text) out += map.get(ch) ?? ch;
return out;
}
export const toRuLayout = (text: string) => swapLayout(text, enToRu);
export const toEnLayout = (text: string) => swapLayout(text, ruToEn);
const reCyr = /\p{Script=Cyrillic}/u;
const reLat = /\p{Script=Latin}/u;
export type ScriptGuess = "ru" | "en" | "mixed" | "other";
export function detectScript(text: string): ScriptGuess {
let cyr = 0, lat = 0;
for (const ch of text) {
if (reCyr.test(ch)) cyr++;
else if (reLat.test(ch)) lat++;
}
if (cyr === 0 && lat === 0) return "other";
if (cyr === lat) return "mixed";
return cyr > lat ? "ru" : "en";
}
export function fixLayoutAuto(
text: string,
toRuLayout: (s: string) => string,
toEnLayout: (s: string) => string,
): string {
let guess = detectScript(text);
if (guess === "mixed") {
guess = (randomValue([true, false]) ?? false) ? "ru" : "en";
}
if (guess === "en") {
return toRuLayout(text);
}
if (guess === "ru") {
return toEnLayout(text);
}
return text;
}
export class Transliteration extends Command {
command = ["transliteration", "tr"];
title = Environment.commandTitles.transliteration;
description = Environment.commandDescriptions.transliteration;
async execute(msg: Message): Promise<void> {
if (!msg.text && !msg.caption) return;
let text: string = "";
if (msg.reply_to_message) {
text = (msg.reply_to_message.text || msg.reply_to_message.caption || "");
} else {
const split = (<string>(msg.text || msg.caption)).split("/tr ");
if (split.length > 1) {
text = split[1].trim();
}
}
if (text.length === 0) {
return;
}
const newText = fixLayoutAuto(text, toRuLayout, toEnLayout);
await oldReplyToMessage(msg, newText).catch(logError);
}
}