# `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 import requests from telethon.tl.types import Message from copy import deepcopy from .. import loader, translations, utils class GoogleTranslator: 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://trmr-1-f8335856.deta.app/supported?asd={as_dict}').json()['res'] def translate(self, text: str) -> str: """Translates the text into the specified language.""" return requests.post('https://trmr-1-f8335856.deta.app/translate/', json={'from_lang': self.start_lang, 'to_lang': self.dest_lang, 'text': text}).json()['result'] translator = GoogleTranslator() 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 GoogleTranslateMod(loader.Module): """Guaranteed to be the most advanced and feature-rich message translation module based on Google Translate, with many useful features.""" strings = { "name": "GoogleTrans", "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": "GoogleTrans", "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 Google Translate mit vielen" " nützlichen Funktionen." ), "lapi": ( "📥 Sprachesuchpaket für {} Sprache erfolgreich" " installiert!" ), "lapd": ( "📤 Sprachesuchpaket für {} Sprache erfolgreich" " deinstalliert!" ), "_cmd_doc_onboardh": "Syntaxanleitung.", "_cmd_doc_dllap": ( "Ermöglicht die Suche in der eingegebenen Sprache, nachdem die Liste" " erstellt wurde." ), "_cmd_doc_dellap": "Entfernt das Sprachesuchpaket", "_cmd_doc_autotranslate": ( "Aktiviert die Autoübersetzung in diesem Chat. Lesen Sie die Hilfe von" " hier." ), "_cmd_doc_atlist": ( "Liste der automatisch übersetzten Chats und der dort verwendeten Sprachen" ), "_cmd_doc_deflang": "Legt die Muttersprache fest.", "_cmd_doc_searchlang": ( "Sucht die Sprache nach dem Namen in einer der eingestellten Sprachen —" " standardmäßig Englisch und Russisch — oder Sprachcode." ), "_cmd_doc_markmode": "Aktiviert/deaktiviert die Markierung »Übersetzt«", "_cmd_doc_subsmode": ( "Aktiviert/deaktiviert die Textspeicherung bei der automatischen" " Übersetzung" ), "_cmd_doc_silentmode": ( "Aktiviert/deaktiviert die Anzeige der Fangmeldung beim Übersetzen." ), "_cmd_doc_translate": ( "Wie unerwartet, übersetzt. Verwenden Sie (start;final), um die zu" " Übersetzung Sprachen festzulegen. Verwenden Sie die Hilfe für weitere" " Informationen." ), } strings_ru = { "name": "GoogleTrans", "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_onboardh": "Справка по синтаксису.", "_cmd_doc_dllap": ( "Даёт возможность после построения списка искать на введённом языке." ), "_cmd_doc_dellap": "Удаляет языковой пакет для поиска.", "_cmd_doc_autotranslate": ( "Включает автоперевод в данном чате. Дальше — читай справку." ), "_cmd_doc_atlist": "Список чатов с автопереводом и языков, там используемых.", "_cmd_doc_deflang": "Устанавливает язык по умолчанию.", "_cmd_doc_searchlang": ( "Ищет язык по названию на одном из установленных языков — по умолчанию " "английский и русский." ), "_cmd_doc_markmode": "Включает/выключает пометку «Переведено».", "_cmd_doc_subsmode": "Включает/выключает сохранение текста при автопереводе.", "_cmd_doc_silentmode": ( "Включает/выключает показ сообщения загрузки при переводе." ), "_cmd_doc_translate": ( "Как неожиданно — переводит. Используй (start;final) чтоб установить языки" " для перевода. Для дальнейшей информации используй справку." ), "_cls_doc": ( "Гарантированно самый продвинутый и многофункциональный модуль для перевода" " сообщений, основанный на Google Translate, с множеством полезных функций." ), } 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 autotranslatecmd(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 = self.get("tr_cha") tco = deepcopy(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 onboardhcmd(self, m: Message): """Syntax manual.""" await utils.answer(m, self.strings("onboard-h")) async def dllapcmd(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 = GoogleTranslator("en", lang).translate("a language").casefold() if " " in langword: langword = GoogleTranslator("en", lang).translate("language").casefold() for z in available_languages: ru_n = f"{z} language" ru_n = ( GoogleTranslator("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 dellapcmd(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 deflangcmd(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 searchlangcmd(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 = ( GoogleTranslator("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 silentmodecmd(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 subsmodecmd(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 markmodecmd(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 atlistcmd(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 = GoogleTranslator().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 = ( GoogleTranslator("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 = ( GoogleTranslator("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 translatecmd(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 = ' '.join(utils.get_args(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.text translator = GoogleTranslator(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 = GoogleTranslator(stla, fila) translated = "".join( [ await utils.run_sync(lambda: tren.translate(chunk)) for chunk in utils.chunks(message.text, 512) ] ) if translated == message.text: return if self.get("s-script"): translated = ( message.text + "\n\n" + self.strings("tr-ed") + "\n\n" + translated ) with contextlib.suppress(Exception): await utils.answer(message, translated)