# `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)