# `7MMM. ,MMF'`7MMM. ,MMF' `7MMM. ,MMF' `7MM # MMMb dPMM MMMb dPMM MMMb dPMM MM # M YM ,M MM M YM ,M MM M YM ,M MM ,pW"Wq. ,M""bMM ,pP"Ybd # M Mb M' MM M Mb M' MM M Mb M' MM 6W' `Wb ,AP MM 8I `" # M YM.P' MM M YM.P' MM mmmmm M YM.P' MM 8M M8 8MI MM `YMMMa. # M `YM' MM M `YM' MM M `YM' MM YA. ,A9 `Mb MM L. I8 # .JML. `' .JMML..JML. `' .JMML. .JML. `' .JMML.`Ybmd9' `Wbmd"MML.M9mmmP' # # (c) 2023 — licensed under Apache 2.0 — https://www.apache.org/licenses/LICENSE-2.0 # meta developer: @mm_mods # meta pic: https://img.icons8.com/color/344/input-latin-letters-emoji.png import contextlib import logging from telethon.tl.types import Message from .. import loader, utils, translations import requests class YandexTranslator: def __init__(self, start_lang: str = "auto", dest_lang: str = "en"): self.start_lang = start_lang self.dest_lang = dest_lang def get_supported_languages(self, as_dict: bool = False) -> list | dict: """Returns a list of supported languages or a dictionary of supported languages with their codes.""" as_dict = 'true' if as_dict else 'false' return requests.get(f'https://somekindofapp-1-j3340894.deta.app/get/ytranslate/supported?asd={as_dict}').json()['res'] def translate(self, text: str) -> str: """Translates the text into the specified language.""" return requests.post('https://somekindofapp-1-j3340894.deta.app/post/ytranslate/translate', json={'from_lang': self.start_lang, 'to_lang': self.dest_lang, 'text': text}).json()['res'] translator = YandexTranslator() available_languages = translator.get_supported_languages(as_dict=True) logger = logging.getLogger(__name__) def get_key(dictionary: dict, needle: str) -> str: return next((key for key, value in dictionary.items() if value == needle), None) def get_num(lst: list, needle: str) -> int: for i in range(len(lst)): if lst[i] == needle: return i @loader.tds class YaTranslateMod(loader.Module): """Guaranteed to be the most advanced and feature-rich message translation module based on Yandex Translate, with many useful features (GoogleTrans, but rewritten). RR-version (Requirements Reduced).""" strings = { "name": "YandexTrans", "load": "🔄 Translating…", "load2": "🔎 Searching… Please, wait.", "se-re": "📘 Search result:\n", "cll": "🔄 Configuring language list…", "args": "🚫 No arguments, no reply…", "args2": "🚫 No arguments…", "no_lang": "📕 No such language!", "setted": "🔤 Your main language is updated!", "silent": "🔇 OK, I won't dispay translation message!", "unsilent": "🔊 OK, I will dispay translation message!", "mark": "🔇 OK, I won't dispay «translated» mark!", "unmark": "🔊 OK, I will dispay «translated» mark!", "tr-ed": "Translated:", "added": "➕ Chat added to autotranslate list!", "changed": "〰️ Autotranslate configuration changed!", "deled": "➖ Chat deleted from autotranslate list!", "alheader": "📃 Chats, in which autotranslate is activated:", "subscribe": "🖋️ Now I'll keep original text while autotranslating.", "unsubscribe": ( "🖋️ Now I won't keep original text while autotranslating." ), "onboard-h": ( "ℹ️ Some useful info about syntax\n\n• .deflang {two-digit lang" " code} sets your language to defined.\n• .markmode, .subsmode," " .silentmode, .atlist takes no arguments.\n• .autotranslate {start;finish}" " takes argument only in such format. You may skip start language to define" " it automatically. Also you may skip finish language to define it from" " your default language.\n• .translate ({start;finish}) [text/reply] have" " same rules while defining languages, as previous command. You may skip" " block in brackets to translate text from autodefined language to your" " default language.\n• .searchlang {two-digit language code/russion or" " english language name} returns following language.\n\n In manual [s-t]" " being used for unnecessary text block. {s-t} — for necessary." ), "tt": "en", "lapi": ( "📥 Language names packet for {} succesfully installed!" ), "lapd": ( "📤 Language names packet for {} succesfully deleted!" ), } strings_de = { "name": "YandexTrans", "load": "🔄 Übersetze…", "load2": "🔎 Suchen… Bitte warten.", "se-re": "📘 Gefunden:\n", "cll": "🔄 Sprachlist konfiguriere…", "args": "🚫 Kein Antwort, kein Argument…", "args2": "🚫 Kein Argument…", "no_lang": "📕 Ich kenne dieser Sprache nicht!", "setted": "🔤 Deine Muttersprache aktualisiert!", "silent": "🔇 Jetzt zeige ich Übersetzungnachricht nicht!", "unsilent": "🔊 Jetzt zeige ich Übersetzungnachricht!", "mark": "🔇 Jetzt ich zeige »Übersetzt« Merkzeichen nicht!", "unmark": "🔊 Jetzt ich zeige »Übersetzt« Merkzeichen!", "tr-ed": "Übersetzt:", "added": "➕ Chat zum Autoübersetzunglist hinzufügt!", "changed": "〰️ Autoübersetzung Konfiguration geändert!", "deled": "➖ Chat aus Autoübersetzunglist entfernt!", "alheader": "📃 Autoübersetzungchatlist:", "subscribe": "🖋️ Jetzt zeige ich Originaltext bei Autoübersetzung.", "unsubscribe": ( "🖋️ Jetzt zeige ich Originaltext bei Autoübersetzung nicht." ), "onboard-h": ( "ℹ️ Syntax-Leitfaden\n\n• .deflang {zweistellig Sprachcode" "} ersetze dein Muttersprache mit eingegebt.\n• .markmode," " .subsmode, .silentmode, .atlist kein Argumente benötigt.\n•" " .autotranslate {Ausgang;Ziel} benötigen Argumente in diesem Format." " Wenn Ausgangsprache nicht eigegebt, er wird automatisch erkannt" " jedes Mal. Wenn Zielsprache nicht eingegebt, es word von deiner Muttersprache definiert.\n•" " .translate [({Ausgang;Ziel})] {текст/ответ} haben desselben Sprachdefinierung Regeln." " Du kannst Blok im Klammern nicht eingegeben" " um von autoerkennt Sprache auf" " deiner Muttersprache zu Übersetzen.\n• .searchlang {zweistellig Sprachcode/Sprachname an" " Englisch, Russisch oder anders installierte Sprache gebe dir Sprachname/Sprachcode" ".\n\nIn Leitfaden [etwas] ist unbenötigt Textblok." " {etwas} — benötigt." ), "tt": "de", "_cls_doc": "Garantiert das fortschrittlichste und funktionsreichste Nachrichtenübersetzungsmodul auf Basis von" " Yandex Translate mit vielen nützlichen Funktionen (geändert Version von GoogleTrans). RR-Version (kein Abhängigkeiten).", "lapi": "📥 Sprachesuchpaket für {} Sprache erfolgreich installiert!", "lapd": "📤 Sprachesuchpaket für {} Sprache erfolgreich deinstalliert!", '_cmd_doc_yonboardh': 'Syntaxanleitung.', '_cmd_doc_ydllap': 'Ermöglicht die Suche in der eingegebenen Sprache, nachdem die Liste erstellt wurde.', '_cmd_doc_ydellap': 'Entfernt das Sprachesuchpaket', '_cmd_doc_yautotranslate': 'Aktiviert die Autoübersetzung in diesem Chat. Lesen Sie die Hilfe von hier.', '_cmd_doc_yatlist': 'Liste der automatisch übersetzten Chats und der dort verwendeten Sprachen', '_cmd_doc_ydeflang': 'Legt die Muttersprache fest.', '_cmd_doc_ysearchlang': 'Sucht die Sprache nach dem Namen in einer der eingestellten Sprachen — standardmäßig ' 'Englisch und Russisch — oder Sprachcode.', '_cmd_doc_ymarkmode': 'Aktiviert/deaktiviert die Markierung »Übersetzt«', '_cmd_doc_ysubsmode': 'Aktiviert/deaktiviert die Textspeicherung bei der automatischen Übersetzung', '_cmd_doc_ysilentmode': 'Aktiviert/deaktiviert die Anzeige der Fangmeldung beim Übersetzen.', '_cmd_doc_ytranslate': 'Wie unerwartet, übersetzt. Verwenden Sie (start;final), um die zu Übersetzung Sprachen ' 'festzulegen. Verwenden Sie die Hilfe für weitere Informationen.' } strings_ru = { "name": "YandexTrans", "load": "🔄 Перевожу…", "load2": "🔎 Ищу… Ожидайте.", "se-re": "📘 Найдено:\n", "cll": "🔄 Конфигурирую список языков…", "args": "🚫 Ни аргумента, ни ответа…", "args2": "🚫 Нет аргумента…", "no_lang": "📕 Я не знаю такого языка!", "setted": "🔤 Ваш основной язык обновлён!", "silent": "🔇 Хорошо, теперь не отображаю сообщение о переводе!", "unsilent": "🔊 Хорошо, теперь отображаю сообщение о переводе!", "mark": "🔇 Хорошо, теперь не отображаю пометку «переведено»!", "unmark": "🔊 Хорошо, теперь отображаю пометку «переведено»!", "tr-ed": "Переведено:", "added": "➕ Чат добавлен в список автоперевода!", "changed": "〰️ Конфигурация автоперевода изменена!", "deled": "➖ Чат убран из списка автоперевода!", "alheader": "📃 Список чатов, в которых активен автоперевод:", "subscribe": "🖋️ Теперь я сохраняю оригинальный текст при автопереводе.", "unsubscribe": ( "🖋️ Теперь я не сохраняю оригинальный текст при автопереводе." ), "onboard-h": ( "ℹ️ Руководство по синтаксису\n\n• .deflang {двузначный языковой" " код} установит ваш язык по умолчанию на введённый.\n• .markmode," " .subsmode, .silentmode, .atlist не принимают аргументов.\n•" " .autotranslate {старт;финал} принимает аргументы только в таком формате." " При пропуске начального языка, он будет определяться автоматически каждый" " раз. Финальный язык при пропуске его будет взят из языка по умолчанию.\n•" " .translate [({старт;финал})] {текст/ответ} имеет те же правила по" " обозначению языков, что и прошлая команда. Можно пропустить блок в" " круглых скобках чтобы перевести с автопереведённого языка на язык по" " умолчанию.\n• .searchlang {двузначный языковой код/название языка на" " русском или английском} выдаёт язык, соотвтетствующий названию или" " коду.\n\nВ руководстве [что-то] обозначает необязательный текстовый блок." " {что-то} — обязательный." ), "tt": "ру", "lapi": "📥 Языковой пакет для языка {} успешно установлен!", "lapd": "📤 Языковой пакет для языка {} успешно удалён!", '_cmd_doc_yonboardh': 'Справка по синтаксису.', '_cmd_doc_ydllap': 'Даёт возможность после построения списка искать на введённом языке.', '_cmd_doc_ydellap': 'Удаляет языковой пакет для поиска.', '_cmd_doc_yautotranslate': 'Включает автоперевод в данном чате. Дальше — читай справку.', '_cmd_doc_yatlist': 'Список чатов с автопереводом и языков, там используемых.', '_cmd_doc_ydeflang': 'Устанавливает язык по умолчанию.', '_cmd_doc_ysearchlang': 'Ищет язык по названию на одном из установленных языков — по умолчанию ' 'английский и русский.', '_cmd_doc_ymarkmode': 'Включает/выключает пометку «Переведено».', '_cmd_doc_ysubsmode': 'Включает/выключает сохранение текста при автопереводе.', '_cmd_doc_ysilentmode': 'Включает/выключает показ сообщения загрузки при переводе.', '_cmd_doc_ytranslate': 'Как неожиданно — переводит. Используй (start;final) чтоб установить языки для перевода. ' 'Для дальнейшей информации используй справку.', "_cls_doc": "Гарантированно самый продвинутый и многофункциональный модуль для перевода сообщений, основанный " "на Yandex Translate, с множеством полезных функций. (переписанный GoogleTrans) RR-версия (без зависимостей).", } async def client_ready(self, client, db): self._client = client self._db = db if not self.get("deflang", False): self.set("deflang", "en") if not self.get("silence", False): self.set("silence", False) if not self.get("mark", False): self.set("mark", True) if not self.get("s-script", False): self.set("s-script", False) if not self.get("tr_cha", False): self.set("tr_cha", {}) if not self.get("addla", False): self.set("addla", []) async def yautotranslatecmd(self, message: Message): """Use language code with this command to add this chat to autotranslate list.""" lang = utils.get_args_raw(message) if (str(utils.get_chat_id(message)) in self.get("tr_cha")) and not lang: tr_cha = self.get("tr_cha") del tr_cha[str(utils.get_chat_id(message))] self.set("tr_cha", tr_cha) await utils.answer(message, self.strings("deled")) return if ";" not in lang: stla = "auto" fila = self.get("deflang") else: stla, fila = lang.split(";", 1) stla, fila = stla.strip(), fila.strip() if not stla: stla = "auto" if not fila: fila = self.get("deflang") if fila not in available_languages.values(): await utils.answer(message, self.strings("no_lang")) return if (stla != "auto") and (stla not in available_languages.values()): await utils.answer(message, self.strings("no_lang")) return lang = f"{stla};{fila}" tr_cha = tco = self.get("tr_cha") tr_cha.update({str(utils.get_chat_id(message)): lang}) self.set("tr_cha", tr_cha) if str(utils.get_chat_id(message)) not in tco.keys(): await utils.answer(message, self.strings("added")) else: await utils.answer(message, self.strings("changed")) async def yonboardhcmd(self, m: Message): """Syntax manual.""" await utils.answer(m, self.strings("onboard-h")) async def ydllapcmd(self, m: Message): """Downloads languages name pack for entered language. Allows to search languages through .searchlang on your own language.""" lang = utils.get_args_raw(m) if lang == "": return await utils.answer(m, self.strings("args2")) if lang not in available_languages.values(): await utils.answer(m, self.strings("nolang")) if not self.get(f"{lang}langdb", False): await utils.answer(m, self.strings("cll")) rld = {} langword = ( YandexTranslator("en", lang) .translate("a language") .casefold() ) if " " in langword: langword = ( YandexTranslator("en", lang) .translate("language") .casefold() ) for z in available_languages: ru_n = f"{z} language" ru_n = ( YandexTranslator("en", lang) .translate(ru_n) .casefold() .replace(langword, "") ) if ru_n[-1] == " ": ru_n = ru_n[:-1] if ru_n[-1] == "-": ru_n = ru_n[:-1] if ru_n[0] == " ": ru_n = ru_n.replace(" ", "", 1) if ru_n[0] == "-": ru_n = ru_n.replace("-", "", 1) if (lang == 'de') and (ru_n[-1] == 'e'): ru_n = ru_n[:-1] rld[ru_n.casefold()] = available_languages[z] self.set(f"{lang}langdb", rld) addla = self.get("addla") addla.append(lang) addla = self.set("addla", addla) return await utils.answer(m, self.strings("lapi").format(lang)) async def ydellapcmd(self, m: Message): """Deletes custom language pack.""" lang = utils.get_args_raw(m) if lang == "": return await utils.answer(m, self.strings("args2")) if lang not in self.get("addla"): await utils.answer(m, self.strings("no_lang")) try: del self._db[self.__class__.__name__][f"{lang}langdb"] except Exception as e: return await utils.answer(m, self.strings("no_lang")) addla = self.get("addla") del addla[get_num(addla, lang)] addla = self.set("addla", addla) return await utils.answer(m, self.strings("lapd").format(lang)) async def ydeflangcmd(self, message: Message): """Use language code with this command to switch basic translation language.""" lang = utils.get_args_raw(message) if lang not in available_languages.values(): await utils.answer(message, self.strings("nolang")) else: self.set("deflang", lang) await utils.answer(message, self.strings("setted")) async def ysearchlangcmd(self, m: Message): """Searching language by code or name (RU and EN names avaliable — if you downloaded others, you may use them; first usage takes some time to configure database).""" query = utils.get_args_raw(m) if query == "": return await utils.answer(m, self.strings("args2")) if not self.get("rulangdb", False): await utils.answer(m, self.strings("cll")) rld = {} for z in available_languages: ru_n = f"{z} language" ru_n = ( YandexTranslator("en", "ru") .translate(ru_n) .replace("язык", "") .replace(" ", "") ) rld[ru_n] = available_languages[z] self.set("rulangdb", rld) rld = self.get("rulangdb") for x in range(len(self.get("addla"))): try: res = self.get(f'{self.get("addla")[x]}langdb')[query] return await utils.answer( m, f'{self.strings("se-re")}{query} ->' f" {res}", ) except Exception: continue try: res = available_languages[query] except Exception: try: res = rld[query] except Exception: if self.strings("tt") == "ру": res = get_key(rld, query) elif self.strings("tt") == "de": if not self.get("delangdb", False): try: res = get_key(available_languages, query) + ' (du kannst Deutsche Namen durch ".dllap de" installieren)' except: return await utils.answer(m, self.strings("no_lang")) else: try: res = get_key(self.get('delangdb'), query) except: return await utils.answer(m, self.strings("no_lang")) else: res = get_key(available_languages, query) if res is None: return await utils.answer(m, self.strings("no_lang")) return await utils.answer( m, f'{self.strings("se-re")}{query} -> {res}' ) async def ysilentmodecmd(self, message): """Use this command to switch between silent/unsilent mode.""" if self.get("silence"): self.set("silence", False) await utils.answer(message, self.strings("unsilent")) else: self.set("silence", True) await utils.answer(message, self.strings("silent")) async def ysubsmodecmd(self, message): """Use this command to switch autotranslate subscription mode.""" if self.get("s-script"): self.set("s-script", False) await utils.answer(message, self.strings("unsubscribe")) else: self.set("s-script", True) await utils.answer(message, self.strings("subscribe")) async def ymarkmodecmd(self, message): """Use this command to switch between showing/unshowing «translated» mark.""" if self.get("mark"): self.set("mark", False) await utils.answer(message, self.strings("mark")) else: self.set("mark", True) await utils.answer(message, self.strings("unmark")) async def yatlistcmd(self, message: Message): """Sends a list of chats, in which autotranslate is turned on.""" laco = self.strings('tt') autotranslate = self.get("tr_cha") alist = self.strings("alheader") + "\n" avlad = YandexTranslator().get_supported_languages(as_dict=True) for i in autotranslate.keys(): st_la, fi_la = autotranslate[i].split(";") if st_la == "auto": if laco == "ru": st_la = "авто" elif laco == "ru": st_la = f"{get_key(avlad, st_la)} language" st_la = ( YandexTranslator("en", "ru") .translate(st_la) .replace("язык", "") ) elif (laco == 'de') and (self.get('delangdb')): st_la = get_key(self.get('delangdb'), st_la) else: st_la = get_key(avlad, st_la) if laco == "ru": fi_la = f"{get_key(avlad, fi_la)} language" fi_la = ( YandexTranslator("en", "ru") .translate(fi_la) .replace("язык", "") ) elif (laco == 'de') and (self.get('delangdb')): fi_la = get_key(self.get('delangdb'), fi_la) else: fi_la = get_key(avlad, fi_la) type_ = ( "user" if getattr(await self._client.get_entity(int(i)), "first_name", False) else "chat" ) alist += ( f'id{i.replace("-100", "")}:' f" {st_la} » {fi_la}" + "\n" ) if (laco == 'de') and (not self.get('delangdb', False)): alist += '\nDu kannst Deutsche Namen durch .dllap de installieren.' await utils.answer(message, alist) async def ytranslatecmd(self, message: Message): """In fact, it translates. Use (start;final) to mark the start and end language of the translation. Leave the start language blank to define it automatically.""" reply = await message.get_reply_message() prompt = utils.get_args_raw(message) if not prompt and reply is None: await utils.answer(message, self.strings("args")) if prompt and prompt.startswith("("): lafo, prompt = prompt.split(")", 1) if ";" not in lafo: prompt = f"({lafo}){prompt}" stal = "auto" finl = self.get("deflang") else: lafo = lafo.replace("(", "", 1) stal, finl = lafo.split(";", 1) stal, finl = stal.strip(), finl.strip() if not stal: stal = "auto" if not finl: finl = self.get("deflang") if ( (stal or finl) not in available_languages.values() and (stal != "auto") and (finl not in available_languages.values()) ): await utils.answer( message, self.strings("no_lang") + "\n" + stal + " " + finl, ) return else: stal = "auto" finl = self.get("deflang") if not self.get("silence"): await utils.answer(message, self.strings("load")) if not prompt: if reply is None: await utils.answer(message, self.strings("args")) return else: prompt = reply.raw_text translator = YandexTranslator(stal, finl) translated = translator.translate(prompt) if self.get("mark"): translated = f'{self.strings("tr-ed")}\n{translated}' await utils.answer(message, translated) async def watcher(self, message: Message): pr = self.get_prefix() if not message.text: return if not message.out: return if message.text[0] in ['/', pr]: return if str(utils.get_chat_id(message)) not in self.get("tr_cha").keys(): return stla, fila = self.get("tr_cha")[str(utils.get_chat_id(message))].split(";") tren = YandexTranslator(stla, fila) translated = "".join( [ await utils.run_sync(lambda: tren.translate(chunk)) for chunk in utils.chunks(message.raw_text, 512) ] ) if translated == message.raw_text: return if self.get("s-script"): translated = ( message.raw_text + "\n\n" + self.strings("tr-ed") + "\n\n" + translated ) with contextlib.suppress(Exception): await utils.answer(message, translated)