diff --git a/SunnexGB/Heroku-Modules/ASCII.py b/SunnexGB/Heroku-Modules/ASCII.py new file mode 100644 index 0000000..7dc49ab --- /dev/null +++ b/SunnexGB/Heroku-Modules/ASCII.py @@ -0,0 +1,97 @@ +# requires: Pillow numpy +# meta developer: @SunnexGB +# meta banner: https://i.pinimg.com/control1/1200x/24/8d/40/248d40b6afa5bd3c3764556b50635691.jpg +__version__ = (1, 0, 0) + +import io +import logging +from herokutl.types import Message +from .. import loader, utils + +logger = logging.getLogger(__name__) + +@loader.tds +class ASCII(loader.Module): + """Convert images to braille ASCII""" + + strings = { + "name": "ASCII", + "no_lib": "🚫 | Library not loaded", + "no_image": "⚠️ | Reply to image", + "processing": "®️ | Processing...", + "empty": "🤬 | Empty result", + "result": "
{art}
", + "Failed_to_load_library": "Failed to load library", + "Conversion_error": "Conversion error", + } + + strings_ru = { + "_cls_doc": "Конвертирует картинку в braille ASCII", + "no_lib": "🚫 | Библиотека не была загружена", + "no_image": "⚠️ | Ответьте на картинку", + "processing": "®️ | Обработка...", + "empty": "🤬 | Пустой результат", + "result": "
{art}
", + "Failed_to_load_library": "Не удалось загрузить библиотеку", + "Conversion_error": "Ошибка конвертации", + } + + def __init__(self): + self.config = loader.ModuleConfig( + loader.ConfigValue("width", 50), + loader.ConfigValue("threshold", 0.65), + loader.ConfigValue("contrast", 2.0), + loader.ConfigValue("chars", 464), + loader.ConfigValue("invert", False), + ) + self.lib = None + + async def client_ready(self): + try: + self.lib = await self.import_lib("https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/Assets/ASCII/ascii-lib.py", suspend_on_error=True) + except Exception: + logger.exception(self.strings["Failed_to_load_library"]) + self.lib = None + + @loader.command(ru_doc="- Отрисовать ASCII-ART (аргумент -f, отправляет файлом)") + async def dotcmd(self, message: Message): + """- Draw ASCII-ART (argument -f, sends as a file)""" + if not self.lib: + return await utils.answer(message, self.strings["no_lib"]) + args = utils.get_args_raw(message) + force_file = "-f" in args.lower() + reply = await message.get_reply_message() or message + if not reply or not ( + reply.photo + or ( + reply.document + and str(getattr(reply.document, "mime_type", "")).startswith("image/") + ) + ): + return await utils.answer(message, self.strings["no_image"]) + msg = await utils.answer(message, self.strings["processing"]) + + try: + image_bytes = await reply.download_media(bytes) + art = self.lib.convert( + image_bytes, + width=self.config["width"], + threshold=self.config["threshold"], + contrast_boost=self.config["contrast"], + invert=self.config["invert"], + target_chars=self.config["chars"], + ) + + except Exception as e: + logger.exception(self.strings["Conversion_error"]) + return await utils.answer(msg, f"
{e}
") + if not art or not art.strip(): + return await utils.answer(msg, self.strings["empty"]) + formatted_art = self.strings("result").format(art=art) + if force_file or len(formatted_art) > 4096: + file = io.BytesIO(art.encode("utf-8")) + file.name = "ascii.txt" + await message.client.send_file(message.peer_id, file) + await msg.delete() + else: + await utils.answer(msg, formatted_art) \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/Assets/ASCII/ascii-lib.py b/SunnexGB/Heroku-Modules/Assets/ASCII/ascii-lib.py new file mode 100644 index 0000000..457093b --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/ASCII/ascii-lib.py @@ -0,0 +1,110 @@ +# requires: Pillow numpy +# Дикие оправдания по поводу именно этого ассета а точнее кода в нем,честно я не знаю что сказать была попытка переписать JS на Py и как бы особых проблем не было, +# до момента пост-обработки на помощь я позвал Claude и он не решил мою проблему от слова совсем,так как в целом я своего рода призираю пилоу,а модуль мне хотелось +# написать я примерно вайб-кодил около 50 минут и я уверен из за этого будет возможно много проблем,в итоге благодаря немного копанию в коде,я нашел проблему и уже +# начал ее решать,НО я опять же вообще не понимал как сделать то что мне нужно,в интернете были сюрсы но будто бы тот или иной мне не подходили? Я не знаю почему я +# дропнул эту идею. Потом я стал искать в JS-е что там вообще можно сделать,в итоге я там импортировал модель какую то блядскую не нужную и опять впустую время +# потратил,думал что тут определено есть решение и снова пошел к ии,вывод опятьь 0 помощи,я не знаю почему я так вцепился лишь в 1 идею.Как бы я мог упростить все, +# даже наверное просто попросив какую то флагмен ии написать модуль и переписать его,но я уже на тот момент по моему мнению сделал много и не хотел ни каким образом +# оставлять это,поэтому через время я нашел сайты которые в целом давали возможность настраивать фильтр,была переделана логика(в целом ее переделал на 60 процентов +# клод,я просто убирал мусор который он испражнял.И вот дальше точно бред я убил более дня на решение проблем которые были решены мной,но результат мне не нравился +# И ОПЯТЬ я пошел просить помощи у гугла,потом понял что возможно даже будет легко(по факту легко,но я ленивый) пока искал,мне перехотелось и я уже потом пытался +# сделать режимы в модуле,что оказалось ужасом ведь они работали,но при возможности гармонично вписать их в код были конфликты И Я В ОЧЕРЕДНОЙ РАЗ ПОШЕЛ К ИИ,спойлер +# он не смог написать лучше чем я,в итоге я отбросил эту идею и думаю в целом никак больше не апдейтать модуль по крупному. +# Да это были оправдания,но зато какие! +import io +import numpy as np +from PIL import Image, ImageFilter, ImageEnhance, ImageOps +from .. import loader + +BASE = 0x2800 +INVERT_MAP = {chr(BASE + c): chr(BASE + (c ^ 0xFF)) for c in range(256)} + + +class AsciiLib(loader.Library): + developer = "@SunnexGB" + + def resize(self, img): + if img.width > 768: + img = img.resize((768, int(img.height * 768 / img.width)), Image.LANCZOS) + w = img.width - img.width % 4 + h = img.height - img.height % 4 + if w != img.width or h != img.height: + img = img.resize((w, h), Image.LANCZOS) + return img + + def mode(self, img, threshold, contrast): + gray = img.convert("L") + edges = ImageOps.invert(gray.filter(ImageFilter.FIND_EDGES)) + contrast_img = ImageEnhance.Contrast(img).enhance(contrast).convert("L") + e = np.array(edges, dtype=np.float32) / 255.0 + c = np.array(contrast_img, dtype=np.float32) / 255.0 + blended = Image.fromarray((e * c * 255).astype(np.uint8), "L") + t = int(threshold * 255) + processed = blended.point(lambda p: 255 if p > t else 0, "L") + return processed, t + + def braille(self, img, threshold, width): + cw = width * 2 + o = -(-round(cw * img.height / img.width) // 4) + ch = 4 * o + px = np.array(img.resize((cw, ch), Image.LANCZOS).convert("L")) + order = [(0,0),(1,0),(2,0),(0,1),(1,1),(2,1),(3,0),(3,1)] + rows = [] + for rs in range(0, ch, 4): + line = [] + for cs in range(0, cw, 2): + grays = [ + int(px[rs+dy, cs+dx]) if (rs+dy < ch and cs+dx < cw) else 255 + for dy, dx in order + ] + bits = list(reversed([1 if g < threshold else 0 for g in grays])) + code = int("".join(str(b) for b in bits), 2) + line.append(chr(BASE + code)) + rows.append("".join(line)) + return rows + + def trim(self, lines): + blank = "\u2800" + while lines and all(c == blank for c in lines[0]): + lines = lines[1:] + while lines and all(c == blank for c in lines[-1]): + lines = lines[:-1] + if not lines: + return lines + left = min(next((i for i,c in enumerate(r) if c!=blank), len(r)) for r in lines) + right = min(next((i for i,c in enumerate(reversed(r)) if c!=blank), len(r)) for r in lines) + return [r[left: len(r)-right if right else len(r)] for r in lines] + + def invert(self, lines): + return ["".join(INVERT_MAP.get(c,c) for c in l) for l in lines] + + def fit(self, img, threshold, chars, width): + lo, hi = 5, 200 + best = "" + for _ in range(14): + mid = (lo + hi)//2 + lines = self.trim(self.braille(img, threshold, mid)) + art = "\n".join(lines) + if len(art) <= chars: + best = art + lo = mid + 1 + else: + hi = mid - 1 + return best + + def convert(self, data, width=50, threshold=0.65, contrast_boost=2.0, invert=False, target_chars=0): + buf = io.BytesIO(data) + img = Image.open(buf) + img.load() + buf.close() + img = img.convert("RGB") + img = self.resize(img) + processed, t = self.mode(img, threshold, contrast_boost) + if target_chars > 0: + art = self.fit(processed, t, target_chars, width) + else: + art = "\n".join(self.trim(self.braille(processed, t, width))) + if invert and art: + art = "\n".join(self.invert(art.split("\n"))) + return art diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/ddialogs/prologue_only.json b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/ddialogs/prologue_only.json new file mode 100644 index 0000000..6dda73a --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/ddialogs/prologue_only.json @@ -0,0 +1,832 @@ +{ + "prologue": [ + { + "type": "label", + "name": "prologue" + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_1", + "action": "load_asset", + "location": "anim/prolog_1", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_1.jpg?raw=true" + }, + { + "type": "narration", + "text": "Мне опять снился сон." + }, + { + "type": "narration", + "text": "Этот сон..." + }, + { + "type": "narration", + "text": "Каждую ночь одно и то же." + }, + { + "type": "narration", + "text": "Но наутро, как обычно, всё забудется." + }, + { + "type": "narration", + "text": "Может быть, оно и к лучшему..." + }, + { + "type": "narration", + "text": "Останутся только туманные воспоминания о приоткрытых, словно приглашающих куда-то воротах, рядом с которыми в камне застыли два пионера." + }, + { + "type": "narration", + "text": "А ещё странная девочка...{w} которая постоянно спрашивает:" + }, + { + "type": "scene", + "kind": "bg", + "name": "anim_prolog1_off", + "action": "load_asset", + "location": "anim/anim_prolog1_off", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/anim_prolog1_off.gif?raw=true" + }, + { + "type": "dialogue", + "char_id": "dreamgirl", + "character": "...", + "text": "Ты пойдёшь со мной?" + }, + { + "type": "narration", + "text": "Пойду?.." + }, + { + "type": "narration", + "text": "Но куда?" + }, + { + "type": "narration", + "text": "И зачем?.." + }, + { + "type": "narration", + "text": "Да и где я вообще нахожусь?" + }, + { + "type": "narration", + "text": "Конечно, случись всё на самом деле, наяву, стоило бы непременно испугаться." + }, + { + "type": "narration", + "text": "Как же иначе!" + }, + { + "type": "narration", + "text": "Но это – всего лишь сон.{w} Тот самый, который я вижу каждую ночь." + }, + { + "type": "narration", + "text": "А ведь всё это неспроста!" + }, + { + "type": "narration", + "text": "Необязательно знать где и почему, чтобы понять – что-то происходит." + }, + { + "type": "narration", + "text": "Нечто, отчаянно требующее моего внимания." + }, + { + "type": "narration", + "text": "Ведь всё окружающее меня здесь – реально!" + }, + { + "type": "narration", + "text": "Реально настолько, насколько реальны вещи в моей квартире; я бы мог открыть ворота, услышать скрип петель, смахнуть рукой осыпающуюся ржавчину, потянуть носом свежий прохладный воздух и поёжиться от холода." + }, + { + "type": "narration", + "text": "Мог бы, но для этого надо сдвинуться с места, сделать шаг, пошевелить рукой..." + }, + { + "type": "narration", + "text": "А ведь это сон – я понимаю, но что дальше, что изменит моё понимание?" + }, + { + "type": "narration", + "text": "Ведь здесь – словно по ту сторону потрескавшегося экрана старого телевизора, который из последних сил борется с помехами и силится показать зрителям всё, не упустив ни малейшей детали." + }, + { + "type": "narration", + "text": "Но вот картинка теряет чёткость...{w} Наверное, скоро просыпаться." + }, + { + "type": "narration", + "text": "..." + }, + { + "type": "narration", + "text": "Может быть, спросить у неё что-то?{w} У девочки." + }, + { + "type": "narration", + "text": "Как же её зовут..." + }, + { + "type": "narration", + "text": "Например про звёзды..." + }, + { + "type": "narration", + "text": "Хотя почему про звёзды?" + }, + { + "type": "narration", + "text": "Можно же спросить про ворота!{w} Да, про ворота!" + }, + { + "type": "narration", + "text": "Вот она удивится." + }, + { + "type": "narration", + "text": "Или лучше про букву ё." + }, + { + "type": "narration", + "text": "Хорошая была буква..." + }, + { + "type": "narration", + "text": "Как будто её больше нет!" + }, + { + "type": "narration", + "text": "И какое отношение буквы, ворота и звёзды имеют к этому месту?" + }, + { + "type": "narration", + "text": "Ведь если мне каждую ночь снится этот сон, который потом всё равно забудется, надо искать разгадку здесь и сейчас!" + }, + { + "type": "narration", + "text": "А вот, если присмотреться, можно увидеть Магелланово Облако..." + }, + { + "type": "narration", + "text": "Словно попал в южное полушарие!" + }, + { + "type": "narration", + "text": "..." + }, + { + "type": "narration", + "text": "Во сне всегда больше волнуют мелочи: неестественный цвет травы, невозможная кривизна прямых или своё перекошенное отражение – а реальная опасность, готовая оборвать всё здесь и сейчас, кажется пустяком." + }, + { + "type": "narration", + "text": "Естественно, ведь здесь нельзя умереть." + }, + { + "type": "narration", + "text": "Я точно знаю – я делал это сотни раз." + }, + { + "type": "narration", + "text": "Но если нельзя умереть, нет смысла жить?" + }, + { + "type": "narration", + "text": "Надо будет спросить у девочки: она местная – должна знать!" + }, + { + "type": "narration", + "text": "Да, именно!{w} Спросить, например, про сову." + }, + { + "type": "narration", + "text": "Больно уж птица странная..." + }, + { + "type": "narration", + "text": "А впрочем, неважно..." + }, + { + "type": "narration", + "text": "..." + }, + { + "type": "dialogue", + "char_id": "dreamgirl", + "character": "...", + "text": "Ты пойдёшь со мной?" + }, + { + "type": "narration", + "text": "И каждый раз надо отвечать." + }, + { + "type": "narration", + "text": "Иначе никак, иначе сон не закончится, а я – не проснусь." + }, + { + "type": "route", + "id": "prologue_choice_1" + }, + { + "type": "narration", + "text": "Каждый раз так сложно решить, что же ответить." + }, + { + "type": "narration", + "text": "Где я, что я здесь делаю, кто она такая?" + }, + { + "type": "narration", + "text": "И почему от ответа на этот вопрос зависит так много в моей жизни?" + }, + { + "type": "narration", + "text": "Или не зависит?.." + }, + { + "type": "narration", + "text": "Ведь это просто сон..." + }, + { + "type": "narration", + "text": "Просто сон..." + }, + { + "type": "scene", + "kind": "bg", + "name": "black", + "action": "load_asset", + "location": "bg/black", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/black.png?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "1_prologue", + "action": "load_asset", + "location": "cg/p_kb_1", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_1.png?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "2_prologue", + "action": "load_asset", + "location": "cg/p_kb_2", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_2.png?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "3_prologue", + "action": "load_asset", + "location": "cg/p_kb_3", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_3.png?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "4_prologue", + "action": "load_asset", + "location": "cg/p_kb_4", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_4.png?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "5_prologue", + "action": "load_asset", + "location": "cg/p_kb_5", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_5.png?raw=true" + }, + { + "type": "narration", + "text": "Экран монитора смотрел на меня словно живой." + }, + { + "type": "narration", + "text": "Иногда мне правда казалось, что он обладает сознанием, своими мыслями и желаниями, стремлениями; умеет чувствовать, любить и страдать." + }, + { + "type": "narration", + "text": "Словно в наших отношениях инструмент не он – неодушевлённый кусок пластика и текстолита, – а я." + }, + { + "type": "narration", + "text": "Наверное, в этом есть доля правды, ведь компьютер на 90% обеспечивает моё общение с внешним миром." + }, + { + "type": "narration", + "text": "Анонимные имиджборды, иногда какие-то чаты, редко – аська или джаббер, ещё реже – форумы." + }, + { + "type": "narration", + "text": "А людей, сидящих по ту сторону сетевого кабеля, попросту не существует!" + }, + { + "type": "narration", + "text": "Все они – всего лишь плод его больной фантазии, ошибка в программном коде или баг ядра, зажившего собственной жизнью." + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_15", + "action": "load_asset", + "location": "anim/prolog_15", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_15.png?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_3", + "action": "load_asset", + "location": "anim/prolog_3", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_3.png?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_4", + "action": "load_asset", + "location": "anim/prolog_4", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_4.png?raw=true" + }, + { + "type": "narration", + "text": "Если посмотреть со стороны на моё существование, то такие мысли покажутся не столь уж бредовыми, а какой-нибудь психолог наверняка поставит мне кучу заумных диагнозов и, возможно, выпишет направление в жёлтый дом." + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_5", + "action": "load_asset", + "location": "anim/prolog_5", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_5.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_14", + "action": "load_asset", + "location": "anim/prolog_14", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_14.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_11", + "action": "load_asset", + "location": "anim/prolog_11", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_11.jpg?raw=true" + }, + { + "type": "narration", + "text": "Маленькая квартирка без следов какого бы то ни было ремонта или даже подобия порядка, и вечно одинаковый вид из окна на серый, день и ночь куда-то бегущий мегаполис, – вот условия моей жизни." + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_2", + "action": "load_asset", + "location": "anim/prolog_2", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_2.jpg?raw=true" + }, + { + "type": "narration", + "text": "Конечно, всё начиналось не так..." + }, + { + "type": "narration", + "text": "Я родился, пошёл в школу, закончил её – всё как у людей." + }, + { + "type": "narration", + "text": "Поступил в институт, где кое-как промучился полтора курса." + }, + { + "type": "narration", + "text": "Работал на паре-тройке разных работ.{w} Иногда даже и неплохо, иногда даже получая за это достойные деньги." + }, + { + "type": "narration", + "text": "Однако всё это казалось чужим, словно списанным с биографии другого человека." + }, + { + "type": "narration", + "text": "Я не ощущал полноту жизни – она словно зациклилась и продолжала идти по кругу.{w} Как в фильме «День сурка»." + }, + { + "type": "narration", + "text": "Только у меня не было выбора, как именно провести этот день, и каждый раз всё повторялось по одной и той же схеме.{w} Схеме пустоты, уныния и отчаяния." + }, + { + "type": "narration", + "text": "Последние несколько лет я просто целыми днями сидел за компьютером." + }, + { + "type": "narration", + "text": "Иногда подворачивались какие-то халтурки, иногда помогали родители." + }, + { + "type": "narration", + "text": "В общем, на жизнь хватало." + }, + { + "type": "narration", + "text": "Это и немудрено, ведь потребности у меня небольшие." + }, + { + "type": "narration", + "text": "На улицу я практически не выхожу, а всё моё общение с людьми сводится к интернет-переписке с анонимами, у которых нет ни реального имени, ни пола, ни возраста." + }, + { + "type": "narration", + "text": "Короче говоря, достаточно типичная жизнь достаточно типичного асоциального человека своего времени.{w} Этакий Обломов XXI века." + }, + { + "type": "narration", + "text": "Может быть, маститый писатель напишет обо мне роман, который станет классикой современной литературы.{w} Или напишу я сам…" + }, + { + "type": "narration", + "text": "Впрочем нет, что себя обманывать – уже не раз пытался, но меня не хватало даже на короткий рассказ." + }, + { + "type": "narration", + "text": "Изучал я и множество других вещей." + }, + { + "type": "narration", + "text": "Рисовать – не дано от природы.{w} Программирование – надоело.{w} Иностранные языки – долго и скучно…" + }, + { + "type": "narration", + "text": "Любил я разве что читать, но даже при этом никогда бы не назвал себя эрудированным человеком." + }, + { + "type": "narration", + "text": "Возможно, я был асом в просмотре аниме и гроссмейстером неумелых шуточек в интернете." + }, + { + "type": "narration", + "text": "Плати мне за это деньги, я бы обрадовался (да и заработал неплохо), но вряд ли так просто можно заполнить пустоту в душе." + }, + { + "type": "scene", + "kind": "bg", + "name": "semen_room_window", + "action": "load_asset", + "location": "bg/semen_room_window", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/semen_room_window.jpg?raw=true" + }, + { + "type": "narration", + "text": "Сегодня очередной типичный день моей типичной жизни типичного неудачника." + }, + { + "type": "narration", + "text": "И именно сегодня мне нужно ехать на встречу институтских товарищей." + }, + { + "type": "narration", + "text": "По правде говоря, совершенно не хотелось." + }, + { + "type": "narration", + "text": "Да и какой смысл, если вместе с ними я отучился всего ничего?" + }, + { + "type": "narration", + "text": "Однако меня всё же уговорил друг, бывший одногруппник, один из немногих, с кем я поддерживал контакт не только в интернете." + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_1", + "action": "load_asset", + "location": "anim/intro_1", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_1.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_2", + "action": "load_asset", + "location": "anim/intro_2", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_2.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_3", + "action": "load_asset", + "location": "anim/intro_3", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_3.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_4", + "action": "load_asset", + "location": "anim/intro_4", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_4.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_5", + "action": "load_asset", + "location": "anim/intro_5", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_5.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_6", + "action": "load_asset", + "location": "anim/intro_6", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_6.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_8", + "action": "load_asset", + "location": "anim/intro_8", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_8.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_7", + "action": "load_asset", + "location": "anim/intro_7", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_7.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "bg", + "name": "bus_stop", + "action": "load_asset", + "location": "bg/bus_stop", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/bus_stop.jpg?raw=true" + }, + { + "type": "narration", + "text": "Вечер. Мороз.{w} Остановка и ожидание автобуса." + }, + { + "type": "narration", + "text": "Я никогда не любил зиму.{w} Впрочем, и жаркое лето – тоже не моя стихия." + }, + { + "type": "narration", + "text": "Просто не вижу смысла выделять какое-то одно время года – не столь важно, какая погода на улице, если ты целыми днями сидишь дома." + }, + { + "type": "narration", + "text": "Автобус сегодня задерживался так сильно, что я уже был готов плюнуть на всё и потратить последнюю пару сотен на такси (совсем не ехать мне почему-то в голову не пришло)." + }, + { + "type": "narration", + "text": "В мозгу, как всегда, роились миллионы мыслей, из которых совершенно невозможно выудить хотя бы одну стоящую." + }, + { + "type": "narration", + "text": "Такую, которую можно закончить, привести в порядок, облечь в форму идеи и претворить в жизнь." + }, + { + "type": "narration", + "text": "Может быть, заняться бизнесом?{w} Но откуда я возьму деньги?" + }, + { + "type": "narration", + "text": "Или пойти опять работать в офис?{w} Нет уж!" + }, + { + "type": "narration", + "text": "Может, стоит попробовать фриланс?{w} Да что я умею, и кому я нужен…" + }, + { + "type": "scene", + "kind": "anim", + "name": "prolog_2", + "action": "load_asset", + "location": "anim/prolog_2", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_2.jpg?raw=true" + }, + { + "type": "narration", + "text": "Вдруг мне вспомнилось детство…{w} Или скорее юношество – 15-17 лет." + }, + { + "type": "narration", + "text": "Почему именно это время?{w} Не знаю." + }, + { + "type": "narration", + "text": "Наверное, потому что тогда всё было проще." + }, + { + "type": "narration", + "text": "Было проще принимать такие сложные сейчас и такие простые тогда решения." + }, + { + "type": "narration", + "text": "Проснувшись с утра, я чётко знал, как пройдёт мой день, а выходных ждал с нетерпением – смогу отдохнуть, заняться любимыми делами: компьютер, футбол, встречи с друзьями." + }, + { + "type": "narration", + "text": "А потом, когда наступит новая неделя, вновь примусь за учёбу." + }, + { + "type": "narration", + "text": "Ведь раньше не возникало этих мучительных вопросов «зачем», «кому это надо», «что изменится, если я это сделаю» или «что не изменится»." + }, + { + "type": "narration", + "text": "Простой поток жизни, такой привычный для любого нормального человека и такой чуждый для меня теперешнего." + }, + { + "type": "narration", + "text": "Время беззаботного детства…{w} Тогда же я и встретил свою первую любовь." + }, + { + "type": "narration", + "text": "Стёрлись из памяти её внешность, характер." + }, + { + "type": "narration", + "text": "Как строчка из профиля в социальной сети осталось лишь имя, да те чувства, которые захлёстывали меня, когда я был с ней.{w} Теплота, нежность, желание заботиться, защитить…" + }, + { + "type": "narration", + "text": "Жаль, что это продолжалось так недолго." + }, + { + "type": "narration", + "text": "Сейчас я уже с трудом могу себе представить что-то подобное." + }, + { + "type": "narration", + "text": "Наверное, и хочется познакомиться с девушкой, только не знаю, как начать диалог, о чём вообще с ней говорить, чем её заинтересовать." + }, + { + "type": "narration", + "text": "Да и подходящих девушек я давно не встречал.{w} Хотя где мне их встретить…" + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_9", + "action": "load_asset", + "location": "anim/intro_9", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_9.jpg" + }, + { + "type": "narration", + "text": "Звук работающего двигателя вернул меня к реальности." + }, + { + "type": "narration", + "text": "Подъехал автобус." + }, + { + "type": "narration", + "text": "«Какой-то он не такой» – мелькнула мысль." + }, + { + "type": "narration", + "text": "Впрочем, какая разница – по этому маршруту ходит только 410-ый." + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_10", + "action": "load_asset", + "location": "anim/intro_10", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_10.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_11", + "action": "load_asset", + "location": "anim/intro_11", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_11.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "anim", + "name": "intro_13", + "action": "load_asset", + "location": "anim/intro_13", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_13.jpg?raw=true", + "duration": null + }, + { + "type": "scene", + "kind": "bg", + "name": "intro_xx", + "action": "load_asset", + "location": "bg/intro_xx", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/intro_xx.jpg?raw=true" + }, + { + "type": "narration", + "text": "Огни пролетают мимо, их холодный свет словно зажигает внутри давно погасшие чувства." + }, + { + "type": "narration", + "text": "Или не зажигает, а просто пробуждает…" + }, + { + "type": "narration", + "text": "Ведь «они» уже давно живут во мне, то затихая, то просыпаясь вновь." + }, + { + "type": "narration", + "text": "Какая-то очень известная мелодия играла в радиоприёмнике у водителя.{w} Но я её не слушал." + }, + { + "type": "narration", + "text": "Я смотрел в запотевшее окно автобуса на проезжающие мимо машины." + }, + { + "type": "narration", + "text": "Ведь люди куда-то спешат, ведь им что-то нужно, и, погружённые в свои дела, они не задумываются о вопросах, мучающих меня." + }, + { + "type": "narration", + "text": "Наверное, у них тоже есть свои серьёзные проблемы, а может, им живётся куда легче." + }, + { + "type": "narration", + "text": "Знать наверняка нельзя, так как все люди разные.{w} Или не разные?" + }, + { + "type": "narration", + "text": "Бывает, поступки человека легко предсказуемы, но, пытаясь заглянуть к нему в душу, видишь лишь непроглядную тьму." + }, + { + "type": "narration", + "text": "..." + }, + { + "type": "narration", + "text": "Автобус приближался к центру, и мои мысли прервал яркий свет огней большого города." + }, + { + "type": "narration", + "text": "Сотни рекламных вывесок, тысячи машин, миллионы людей." + }, + { + "type": "narration", + "text": "Я смотрел на это светопреставление, и мне почему-то безумно захотелось спать." + }, + { + "type": "narration", + "text": "Глаза закрылись всего на полсекунды и…" + }, + { + "type": "scene", + "kind": "bg", + "name": "black", + "action": "load_asset", + "location": "bg/black", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/int_bus_black.jpg?raw=true" + }, + { + "type": "opening", + "kind": "opening", + "name": "opening", + "action": "load_asset", + "location": "opening/opening", + "raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/opening/opening.mp4?raw=true" + } + ] +} \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/ddialogs/routes_prologue.json b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/ddialogs/routes_prologue.json new file mode 100644 index 0000000..0563376 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/ddialogs/routes_prologue.json @@ -0,0 +1,62 @@ +{ + "prologue_choice_1": { + "question": "Иначе никак, иначе сон не закончится, а я – не проснусь. — что выбрать?", + "chapter": "prologue", + "options": { + "Да, я пойду с тобой": { + "effects": {} + }, + "Нет, я останусь здесь": { + "effects": {} + } + } + }, + "endings": { + "labels": [ + "main_good_ending", + "main_bad_ending", + "sl_good_ending", + "sl_bad_ending", + "dv_good_ending", + "dv_bad_ending", + "un_good_ending", + "un_bad_ending", + "us_good_ending", + "us_bad_ending", + "mi_ending", + "uv_ending", + "harem_ending" + ], + "routes": { + "sl": { + "good": "sl_good_ending", + "bad": "sl_bad_ending", + "point_key": "sl_points" + }, + "dv": { + "good": "dv_good_ending", + "bad": "dv_bad_ending", + "point_key": "dv_points" + }, + "un": { + "good": "un_good_ending", + "bad": "un_bad_ending", + "point_key": "un_points" + }, + "us": { + "good": "us_good_ending", + "bad": "us_bad_ending", + "point_key": "us_points" + }, + "mi": { + "single": "mi_ending", + "point_key": "mi_points" + }, + "uv": { + "single": "uv_ending", + "point_key": "uv_points" + } + }, + "fallback": "main_bad_ending" + } +} diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/anim_prolog1_off.gif b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/anim_prolog1_off.gif new file mode 100644 index 0000000..776d00b Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/anim_prolog1_off.gif differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_1.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_1.jpg new file mode 100644 index 0000000..cef9501 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_1.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_10.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_10.jpg new file mode 100644 index 0000000..15a162e Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_10.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_11.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_11.jpg new file mode 100644 index 0000000..54ceb2f Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_11.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_13.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_13.jpg new file mode 100644 index 0000000..1098426 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_13.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_2.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_2.jpg new file mode 100644 index 0000000..acc384b Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_2.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_3.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_3.jpg new file mode 100644 index 0000000..c37580f Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_3.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_4.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_4.jpg new file mode 100644 index 0000000..581db49 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_4.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_5.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_5.jpg new file mode 100644 index 0000000..0782559 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_5.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_6.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_6.jpg new file mode 100644 index 0000000..cb835fc Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_6.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_7.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_7.jpg new file mode 100644 index 0000000..85ac729 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_7.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_8.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_8.jpg new file mode 100644 index 0000000..f418f23 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_8.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_9.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_9.jpg new file mode 100644 index 0000000..4d16e85 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/intro_9.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_1.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_1.jpg new file mode 100644 index 0000000..5a45e62 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_1.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_11.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_11.jpg new file mode 100644 index 0000000..6e6e5cb Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_11.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_14.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_14.jpg new file mode 100644 index 0000000..21d49c4 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_14.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_15.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_15.png new file mode 100644 index 0000000..54c7614 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_15.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_2.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_2.jpg new file mode 100644 index 0000000..c525295 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_2.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_3.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_3.png new file mode 100644 index 0000000..179872c Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_3.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_4.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_4.png new file mode 100644 index 0000000..57d7345 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_4.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_5.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_5.jpg new file mode 100644 index 0000000..c59767e Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/anim/prolog_5.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/black.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/black.png new file mode 100644 index 0000000..e382139 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/black.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/bus_stop.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/bus_stop.jpg new file mode 100644 index 0000000..51bdfa7 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/bus_stop.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/int_bus_black.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/int_bus_black.jpg new file mode 100644 index 0000000..46ef32f Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/int_bus_black.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/intro_xx.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/intro_xx.jpg new file mode 100644 index 0000000..6af1f8e Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/intro_xx.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/semen_room_window.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/semen_room_window.jpg new file mode 100644 index 0000000..63f95ac Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/bg/semen_room_window.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_1.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_1.png new file mode 100644 index 0000000..693cd17 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_1.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_2.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_2.png new file mode 100644 index 0000000..f182134 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_2.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_3.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_3.png new file mode 100644 index 0000000..6d708e4 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_3.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_4.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_4.png new file mode 100644 index 0000000..4e8fbba Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_4.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_5.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_5.png new file mode 100644 index 0000000..d847f0e Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/cg/p_kb_5.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/Save_Menu.png b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/Save_Menu.png new file mode 100644 index 0000000..15e51fc Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/Save_Menu.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/Start_Menu.jpg b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/Start_Menu.jpg new file mode 100644 index 0000000..856fcf2 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/Start_Menu.jpg differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/logo.webp b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/logo.webp new file mode 100644 index 0000000..79bd64a Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/images/1920/in_telegram_images/logo.webp differ diff --git a/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/opening/opening.mp4 b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/opening/opening.mp4 new file mode 100644 index 0000000..fabbee1 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Everlasting_Summer/opening/opening.mp4 differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/1.png b/SunnexGB/Heroku-Modules/Assets/Hangman/1.png new file mode 100644 index 0000000..5aec2db Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/1.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/10.png b/SunnexGB/Heroku-Modules/Assets/Hangman/10.png new file mode 100644 index 0000000..d3726a9 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/10.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/2.png b/SunnexGB/Heroku-Modules/Assets/Hangman/2.png new file mode 100644 index 0000000..be79b1f Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/2.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/3.png b/SunnexGB/Heroku-Modules/Assets/Hangman/3.png new file mode 100644 index 0000000..d62c480 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/3.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/4.png b/SunnexGB/Heroku-Modules/Assets/Hangman/4.png new file mode 100644 index 0000000..f4805d6 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/4.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/5.png b/SunnexGB/Heroku-Modules/Assets/Hangman/5.png new file mode 100644 index 0000000..dd9e6f0 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/5.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/6.png b/SunnexGB/Heroku-Modules/Assets/Hangman/6.png new file mode 100644 index 0000000..f4f9d28 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/6.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/7.png b/SunnexGB/Heroku-Modules/Assets/Hangman/7.png new file mode 100644 index 0000000..4f84d6d Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/7.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/8.png b/SunnexGB/Heroku-Modules/Assets/Hangman/8.png new file mode 100644 index 0000000..8ad057b Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/8.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/9.png b/SunnexGB/Heroku-Modules/Assets/Hangman/9.png new file mode 100644 index 0000000..a1da503 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/9.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/full_hp.png b/SunnexGB/Heroku-Modules/Assets/Hangman/full_hp.png new file mode 100644 index 0000000..c133063 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/Hangman/full_hp.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/Hangman/words.txt b/SunnexGB/Heroku-Modules/Assets/Hangman/words.txt new file mode 100644 index 0000000..3ad2cb6 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/Hangman/words.txt @@ -0,0 +1,1488 @@ +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА +КОШКА +СОБАКА +ПИТОН +ПРИВЕТ +СОЛНЦЕ +ЛУНА +ЗВЕЗДА +ЗЕМЛЯ +ВОДА +ОГОНЬ +ВЕТЕР +ДЕРЕВО +КАМЕНЬ +НЕБО +АЛМАЗ +БЕРЕЗА +ГРОЗА +ДОРОГА +ЕЖЕВИКА +ЖЕЛЕЗО +ЗЕРКАЛО +ИСКРА +ЙОГУРТ +КАРАНДАШ +ЛАБИРИНТ +МОЛОКО +НОЧЬ +ОБЛАКО +ПЕСОК +РАДУГА +СНЕЖИНКА \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/Assets/Mikuru/cultural_words_en.txt b/SunnexGB/Heroku-Modules/Assets/Mikuru/cultural_words_en.txt new file mode 100644 index 0000000..397d620 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/Mikuru/cultural_words_en.txt @@ -0,0 +1,400 @@ +2g1c +2 girls 1 cup +acrotomophilia +alabama hot pocket +alaskan pipeline +anal +anilingus +anus +apeshit +arsehole +ass +asshole +assmunch +auto erotic +autoerotic +babeland +baby batter +baby juice +ball gag +ball gravy +ball kicking +ball licking +ball sack +ball sucking +bangbros +bangbus +bareback +barely legal +barenaked +bastard +bastardo +bastinado +bbw +bdsm +beaner +beaners +beaver cleaver +beaver lips +beastiality +bestiality +big black +big breasts +big knockers +big tits +bimbos +birdlock +bitch +bitches +black cock +blonde action +blonde on blonde action +blowjob +blow job +blow your load +blue waffle +blumpkin +bollocks +bondage +boner +boob +boobs +booty call +brown showers +brunette action +bukkake +bulldyke +bullet vibe +bullshit +bung hole +bunghole +busty +butt +buttcheeks +butthole +camel toe +camgirl +camslut +camwhore +carpet muncher +carpetmuncher +chocolate rosebuds +cialis +circlejerk +cleveland steamer +clit +clitoris +clover clamps +clusterfuck +cock +cocks +coprolagnia +coprophilia +cornhole +coon +coons +creampie +cum +cumming +cumshot +cumshots +cunnilingus +cunt +darkie +date rape +daterape +deep throat +deepthroat +dendrophilia +dick +dildo +dingleberry +dingleberries +dirty pillows +dirty sanchez +doggie style +doggiestyle +doggy style +doggystyle +dog style +dolcett +domination +dominatrix +dommes +donkey punch +double dong +double penetration +dp action +dry hump +dvda +eat my ass +ecchi +ejaculation +erotic +erotism +escort +eunuch +fag +faggot +fecal +felch +fellatio +feltch +female squirting +femdom +figging +fingerbang +fingering +fisting +foot fetish +footjob +frotting +fuck +fuck buttons +fuckin +fucking +fucktards +fudge packer +fudgepacker +futanari +gangbang +gang bang +gay sex +genitals +giant cock +girl on +girl on top +girls gone wild +goatcx +goatse +god damn +gokkun +golden shower +goodpoop +goo girl +goregasm +grope +group sex +g-spot +guro +hand job +handjob +hard core +hardcore +hentai +homoerotic +honkey +hooker +horny +hot carl +hot chick +how to kill +how to murder +huge fat +humping +incest +intercourse +jack off +jail bait +jailbait +jelly donut +jerk off +jigaboo +jiggaboo +jiggerboo +jizz +juggs +kike +kinbaku +kinkster +kinky +knobbing +leather restraint +leather straight jacket +lemon party +livesex +lolita +lovemaking +make me come +male squirting +masturbate +masturbating +masturbation +menage a trois +milf +missionary position +mong +motherfucker +mound of venus +mr hands +muff diver +muffdiving +nambla +nawashi +negro +neonazi +nigga +nigger +nig nog +nimphomania +nipple +nipples +nsfw +nsfw images +nude +nudity +nutten +nympho +nymphomania +octopussy +omorashi +one cup two girls +one guy one jar +orgasm +orgy +paedophile +paki +panties +panty +pedobear +pedophile +pegging +penis +phone sex +piece of shit +pikey +pissing +piss pig +pisspig +playboy +pleasure chest +pole smoker +ponyplay +poof +poon +poontang +punany +poop chute +poopchute +porn +porno +pornography +prince albert piercing +pthc +pubes +pussy +queaf +queef +quim +raghead +raging boner +rape +raping +rapist +rectum +reverse cowgirl +rimjob +rimming +rosy palm +rosy palm and her 5 sisters +rusty trombone +sadism +santorum +scat +schlong +scissoring +semen +sex +sexcam +sexo +sexy +sexual +sexually +sexuality +shaved beaver +shaved pussy +shemale +shibari +shit +shitblimp +shitty +shota +shrimping +skeet +slanteye +slut +s&m +smut +snatch +snowballing +sodomize +sodomy +spastic +spic +splooge +splooge moose +spooge +spread legs +spunk +strap on +strapon +strappado +strip club +style doggy +suck +sucks +suicide girls +sultry women +swastika +swinger +tainted love +taste my +tea bagging +threesome +throating +thumbzilla +tied up +tight white +tit +tits +titties +titty +tongue in a +topless +tosser +towelhead +tranny +tribadism +tub girl +tubgirl +tushy +twat +twink +twinkie +two girls one cup +undressing +upskirt +urethra play +urophilia +vagina +venus mound +viagra +vibrator +violet wand +vorarephilia +voyeur +voyeurweb +voyuer +vulva +wank +wetback +wet dream +white power +whore +worldsex +wrapping men +wrinkled starfish +yaoi +yellow showers +yiffy +zoophilia diff --git a/SunnexGB/Heroku-Modules/Assets/Mikuru/cultural_words_ru.txt b/SunnexGB/Heroku-Modules/Assets/Mikuru/cultural_words_ru.txt new file mode 100644 index 0000000..b7c5618 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/Mikuru/cultural_words_ru.txt @@ -0,0 +1,2187 @@ +анафемовоёбый +архипиздоит +архипиздрит +безмудовый +безмудый +беспезды +беспиздая +бздение +бздеть +бздех +бзднуть +бздун +бздунья +бздюх +бздюха +блежник +блёжник +блудовместилище +блудодей +блудодейский +блудугонище +бля +блябу +блябуду +бляд +блядва +блядво +блядевина +блядей +блядепиздопроёбище +блядеха +бляди +блядиада +блядина +блядистка +блядистость +блядища +блядище +блядки +бляднота +блядование +блядовать +блядовитый +блядовозка +блядогандонский +блядогон +блядодерьмо +блядок +блядолиз +блядопростервозная +блядословник +блядоход +блядоящерица +блядская +блядский +блядского +блядское +блядство +блядствовать +блядун +блядунья +блядь +блядюга +блядюра +блядюшка +блядюшник +блядями +блять +бляхомудия +болтохуярой +бордель +вафлеотстойник +вафлист +вертохуй +вжопить +вжопиться +взбляд +вздрачивание +вздрачивать +вздрачиваться +вздрочить +вздрочиться +вздрючивание +вздрючивать +вздрючить +вз'ебывать +взъебка +взъёбка +взъебнуть +взъебщик +взъёбывать +взьебка +взьебывать +вислозадая +воспиздозаолупоклинившаяся +восьмиблядское +восьмихуй +впиздить +впиздиться +впиздохать +впиздохивать +впиздохиваться +впиздошиваться +впиздошить +впиздронивать +впиздрониваться +впиздюливать +впиздюливаться +впиздюлить +впиздюривать +впиздюриваться +впиздюрить +впиздякать +впиздякивать +впиздяривать +впиздяриваться +впиздярить +впиздяхать +впиздяхивать +впиздяхиваться +впиздячивать +впиздячил +впиздячила +впиздячить +впиздяшивать +впиздяшиваться +впиздяшить +впизживать +впизживаться +всхуяренный +вхуематери +вхуйкать +вхуйнуть +вхуйнуться +вхуя +вхуякать +вхуякаться +вхуякивать +вхуякиваться +вхуякнуть +вхуякнуться +вхуяривание +вхуяривать +вхуяриваться +вхуярить +вхуяриться +вхуячивание +вхуячивать +вхуячиваться +вхуячить +вхуячиться +вхуяшивать +вхуяшиваться +вхуяшить +вхуяшиться +въебать +въебаться +въебашивать +въебашиваться +въебашить +въебашиться +въебенивать +въебениваться +въебенить +въебениться +въёбывать +выблядовал +выблядовала +выблядовать +выблядодерьмо +выблядок +выблядыш +выебанный +выебат +выебать +выебаться +выебок +выебон +выебопропиздище +выебу +выёбывается +выебываешся +выёбывать +выёбываться +выёбываются +выперданный +выпердоватое +выпердоговенный +выпердопроссаная +выпиздеться +выпиздить +высераться +высраная +высраномудоватое +высранохуевое +высрать +высраться +выссаногнойное +выссанохуеплёт +выссать +выссаться +выссереть +выхуякивание +выхуякивать +выхуяривание +выхуяривать +выхуячивание +выхуячивать +гавнопрягопизд +гандон +гандонный +гандоноскотская +гандоносрака +гандоносучий +гандонохеровая +гандонский +глупизди +глыбоебливое +гнидоблядун +гнидопаскудная +гнидское +гнобилище +гноепадла +гноепромандоватое +говенный +говнецо +говнистый +говниться +говно +говновоз +говнодав +говнодерьмище +говноеб +говноёб +говноебский +говноед +говнозалупский +говномер +говномес +говноперданный +говносерка +говночист +говнюк +говняк +говняный +голоёбица +голожопая +голомудый +голопиздая +гомик +гомосек +гондон +гонореей +гонорея +греблядь +гужеёб +гусоёб +давалка +дароёб +двужопник +двужопостворчатый +дерьмо +дерьмовый +дерьмоед +дерьмопрозалупское +дерьмохеропиздократ +дерьмохеропиздократия +дерьмохеропиздократка +дерьмохеропиздократы +дилдо +додрочить +додрочиться +доебалась +доебались +доебался +доебать +доебаться +доебенивать +доебениваться +доебенить +доебениться +доёбывать +долбоеб +долбоёб +долбоебатина +долбоебическая +допиздеться +допиздить +допиздиться +допиздоболивать +допиздоболиваться +допиздоболиться +допиздовать +допиздоваться +допиздовывать +допиздовываться +допиздохать +допиздохаться +допиздохивать +допиздохиваться +допиздошивать +допиздошиваться +допиздошить +допиздошиться +допиздюкать +допиздюкаться +допиздюкивать +допиздюкиваться +допиздюливать +допиздюливаться +допиздюлить +допиздюлиться +допиздюривать +допиздюриваться +допиздюрить +допиздюриться +допиздюхать +допиздюхаться +допиздюхивать +допиздюхиваться +допиздякать +допиздякаться +допиздякивать +допиздякиваться +допиздяривать +допиздяриваться +допиздярить +допиздяриться +допиздяхать +допиздяхаться +допиздяхивать +допиздяхиваться +допиздячивать +допиздячиваться +допиздячить +допиздячиться +допиздяшивать +допиздяшиваться +допиздяшить +допиздяшиться +допизживать +дотрахать +дотрахаться +дохуйнуть +дохуякать +дохуякаться +дохуякивать +дохуякиваться +дохуяривать +дохуяриваться +дохуярить +дохуяриться +дохуячивать +дохуячиваться +дохуячить +дохуячиться +дохуяшивать +дохуяшиваться +дохуяшить +драноебливая +дрисня +дристать +дристун +дристуха +дрочена +дроченная +дроченье +дрочепиздище +дрочепроговенное +дрочепропердоватая +дрочепросволота +дроческотина +дрочила +дрочилка +дрочилыцик +дрочить +дрочиться +дрочка +дрючить +дрючиться +дурапиздия +дуроеб +дуроёб +дядеёб +ёб +ёбака +ебаквакнутый +ебал +ебалка +ебало +ебалово +ебалом +ебальник +ебальные +ебальный +ебанагандонный +ебанатик +ебанашка +ебаная +ебандей +ебанёшься +ебанная +ебанул +ебанула +ебанулась +ебанулись +ебанулось +ебанулся +ебанутые +ебанутый +ебануть +ебануться +ебаный +ёбаный +ебаных +ебанько +ебапира +ебаришка +ебарь +ёбарь +ебат +ебаторий +ебатория +ебатый +ебать +ебатьс +ебаться +ебашит +ебеня +ебёт +ебец +еби +ебиблядская +ебистика +ебись +ебитесь +ёбкость +еблан +ебланистый +ебланить +ебливая +ебливый +еблище +ебло +еблом +еблоухий +еблысь +ебля +ёбля +ёбнул +ебнутый +ёбнутый +ебнуть +ёбнуть +ебнуться +ёбнуться +ебня +ебоблядище +ебоватая +еболожье +ёбс +ебу +ебукентий +ебун +ебунище +ебучее +ебучий +ебущегося +елда +елдак +елдачить +елдище +елду +елупень +женщина-заебись +жидоёб +жидоёбка +жидоёбский +жирнозадый +жопа +жопастая +жопенци +жопища +жопка +жопник +жоповатое +жопоеб +жопой +жополиз +жополизание +жопоногий +жопопроебина +жопосер +жопочка +жопочник +жопство +жопу +забздеть +заблядовать +заблядоваться +задница +задрачивать +задрачиваться +задристать +задрока +задроченное тримандище +задроченный +задрочепидер +задрочепрогнидище +задрочепроговно +задроческоготическое +задроческотское +задрочила +задрочить +задрочиться +задрочун +задрючить +задрючиться +заёб +заеба +заебал +заебала +заебали +заебанец +заебанный +заёбанный +заебательская +заебать +заебаться +заебашивать +заебашиваться +заебашить +заебашиться +заебенивать +заебениваться +заебенить +заебениться +заебёшь +заебись +заебла +заебучестью +заебцовый +заебываться +залупа +залупаться +залупенец +залупенить +залупень +залупистый +залупить +залупиться +залупляться +залупоглазая +залупоглазое +залупоголовая +залуполиз +залупская +залупский +залупу +замудоебина +замудохаться +запиздадуриная +запиздарить +запизденевать +запизденелый +запизденная +запиздеть +запиздие +запиздить +запиздиться +запиздоболивать +запиздоболиваться +запиздоболить +запиздоболиться +запиздовать +запиздоваться +запиздовывать +запиздовываться +запиздомедузь +запиздохать +запиздошивать +запиздошиваться +запиздошить +запиздошиться +запиздюкать +запиздюкаться +запиздюкивать +запиздюкиваться +запиздюливать +запиздюливаться +запиздюлить +запиздюлиться +запиздюривать +запиздюриваться +запиздюрить +запиздюриться +запиздюхать +запиздюхаться +запиздюхивать +запиздюхиваться +запиздючивать +запиздючиваться +запиздючить +запиздючиться +запизживаться +засерать +засеря +засондряченный +засракомондохуй +засранец +засранка +засранный +засратый +засрать +засраться +засрун +зассать +затраханный +затрахать +затрахаться +затрахивать +затрахиваться +захуить +захуйнуть +захуйнуться +захуякать +захуякаться +захуякивать +захуякиваться +захуялась +захуяривать +захуяриваться +захуярить +захуяриться +захуяченный +захуячивать +захуячиваться +захуячить +захуячиться +захуяшивать +захуяшиваться +захуяшить +захуяшиться +злоебучая +злоебучее +злоебучести +злоебучий +злоебучим +злопиздии +изговнять +изговняться +издрочиться +измандить +измандиться +измандовать +измандоваться +измандовывать +измандовываться +изъеб +изъебать +изъебаться +изъебашивать +изъебашиваться +изъебашить +изъебашиться +изъебенивать +изъебениваться +изъебенить +изъебениться +изъебнулась +изъебнулся +изъебнуться +илда +испизделась +испизделся +испиздеться +испиздить +испражнение +испражняться +исхуякать +исхуякаться +исхуякивать +исхуякиваться +исхуяривать +исхуярить +исхуяриться +исхуячить +какать +какашка +кастрат +кастрировать +клитор +клоака +клоповыёбистый +кнахт +козлоблядской +козлоёб +козлоёбина +козлоёбиться +козлоёбище +козоёб +козоёбиться +коноёб +коноёбиться +кончить +коростоебина +косоёбится +косоебить +косоебиться +кривохуй +курва +курвенный +курвиный +куроёб +лахудра +лох +лохматка +лохудра +малоебущий +манда +мандавоха +мандавошка +мандавошный +мандей +мандеть +мандилищи +мандить +мандиться +мандища +мандище +мандоватая +мандовать +мандогнидища +мандопроблядоватая +мандопродерьмище +мандопроеб +мандопростерва +мандопроушечная +мандопроушина +мандоскотина +мандохать +мандохаться +мандохвост +мандохивать +мандохиваться +мандошить +мандюк +мастурбатор +минет +минетить +минетка +минетчик +минетчица +многоблядопропадловый +многоблядопросволота +многоблядская +многоблядь +многоебоватая +многоебоный +многоебошлюхская +многоебучий +многожопа +многомудоватое +многопизда +многопиздище +многопиздная +многопиздоблядун +многопиздопропадла +многостерводерьмо +многостервопропидрила +многостервохеровый +многостервохуенная +мозгоеб +мозгоёб +мозгоебатель +мозгоебать +мозгоебка +мокрожопый +мокропиздая +мондавошь +мондило +мондозалупленной +мордоблядина +мочиться +мудаблядин +мудагавнопердь +мудак +мудами +мудах +мудаханная +мудачьё +мудашвили +муде +мудеть +мудила +мудило +мудильщик +мудистое +мудистый +мудить +мудище +мудня +мудоблядскую +мудовафлоебище +мудодей +мудоеб +мудоёб +мудоеблю +мудозвон +мудой +мудопроеб +мудопроебное +мудопрошлюхское +мудорваней +мудями +муйня +набздеть +наблядовал +наблядоваться +надристать +надроченный +надрочивать +надрочить +надрочиться +наебалово +наебанный +наебать +наебаться +наебашился +наебениться +наебка +наёбка +наебнулась +наебнулся +наебнуть +наебнуться +наебщик +наёбывает +наебывать +наебываться +наебыш +найвыебенищее +накакать +накакаться +накакивать +напиздеть +напиздить +напиздошить +напиздюрить +напиздюриться +насрать +насраться +нассать +нассаться +настоебать +натрахать +натрахаться +натрахивать +натрахиваться +нахуевертеть +нахуй +нахуйник +нахуякать +нахуякаться +нахуякивать +нахуякиваться +нахуяривать +нахуяриваться +нахуярить +нахуяриться +нахуячивать +нахуячиваться +нахуячить +нахуячиться +нахуяшить +невъебения +невъебенной +невъебенный +невъебенным +невьебенно +недоеба +недоебанная +недоебанный +недоебок +недоносок +неебущий +нехуёвый +нехуй +нищеебство +обдристанный +обдристать +обдристаться +обдрочиться +обебать +оберблядь +облядипизденелости +облямудевшая +облямуденный +обосранец +обосранная +обосраный +обосрать +обосраться +обоссанец +обоссаный +обоссать +обоссаться +обоссывать +обоссываться +обпиздить +обпиздиться +обпиздовать +обпиздоваться +обпиздовывать +обпиздовываться +обпиздохать +обпиздохаться +обпиздохивать +обпиздохиваться +обпиздошить +обтрахать +обтрахаться +обтрахивать +обтрахиваться +обхуярить +обхуяриться +обхуячить +объебал +объебала +объебалово +объебательство +объебать +объебаться +объебенить +объебешь +объебнуть +объебон +объебос +одинхуй +однапизда +однохуйственно +оебать +оебашивать +оебашить +оебенивать +оебенить +оебыват +опедерастить +опизде +опизденевать +опизденеть +опизденно +опизденный +опиздеть +опиздить +опиздихуительный +опиздоумел +оскотоёбился +ослоёб +ослоёбиться +остоебал +остоебала +остоебали +остоебать +остоебенило +остоебенить +остоебеть +остопиздело +остопиздеть +остохуело +остохуеть +отдрачивать +отдрачиваться +отдрочить +отдрочиться +отпиздить +отпизднутый +отпиздошить +отпиздячить +отпиздяшивание +отпиздяшивать +отпиздяшиваться +отпиздяшить +отпиздяшиться +отсандаленный +отсасывать +отсасываться +отсосать +отсосаться +оттраханная +оттрахать +оттрахаться +оттрахивать +оттрахиваться +отхерачить +отхуякать +отхуякаться +отхуякивать +отхуякиваться +отхуяривать +отхуяриваться +отхуярить +отхуяриться +отхуячивать +отхуячиваться +отхуячить +отхуячиться +отхуяшивать +отхуяшиваться +отхуяшить +отхуяшиться +отшмаренная +отъебать +отъебаться +отъебашивание +отъебашивать +отъебашиваться +отъебашить +отъебенивать +отъебениваться +отъебенить +отъебениться +отъебись +отъебнуть +отъебывание +отъебывать +отъебываться +отьебаться +отьебашиться +отьебенивание +отьебись +отьебнуться +охлуебень +охуебаннейшая +охуевательский +охуевать +охуевающая +охуевающее +охуевающий +охуевшее +охуевший +охуение +охуения +охуенно +охуенные +охуенный +охуеть +охуительно +охуительный +охуякать +охуякаться +охуякивать +охуякиваться +охуякнуть +охуякнуться +охуяривать +охуяриваться +охуярить +охуяриться +охуячивать +охуячиваться +охуячить +охуячиться +охуяшивать +охуяшиваться +охуяшить +охуяшиться +оххуетительно +очко +падла +падловый +падлопроскотское +падлопросраное +падлопрошлюха +падлюка +педераст +педерастина +педерастический +педерастия +педик +педрик +педрило +пежить +пезды +пёзды +пердеж +пердеть +перднуть +пердодроченное +пердомудоватый +пердопрогнойная +пердопромудище +пердун +пердунец +пердь +перебздеть +передрачивать +передрочить +передрочиться +переёб +переёба +переебать +переебаться +переебашить +перекосоебленным +перемондоебленная +перепиздрюченный +перепиздюханный +перетраханной +перетрахать +перетрахаться +перетрахивать +перетрахиваться +перехуйнуть +перехуйнуться +перехуякать +перехуякаться +перехуякивать +перехуякиваться +перехуякнуть +перехуякнуться +перехуяривать +перехуяриваться +перехуярить +перехуяриться +перехуячивать +перехуячить +перехуячиться +пидарас +пидарастической +пидарасы +пидероперданное +пидор +пидорас +пидорестической +пидористический +пидрасня +пидрила +пизввда +пизда +пиздабол +пиздавлетины +пиздагрыз +пиздадавленный +пиздаёб +пиздакнутый +пиздакрыл +пиздалон +пизданутая +пиздануть +пиздануться +пиздапроебина +пиздарванка +пиздасер +пиздатая +пиздато +пиздатое +пиздатый +пиздаш +пизде +пиздёж +пизделиться +пиздельник +пизделякает +пизделякать +пизденея +пизденка +пизденочка +пизденыш +пиздёныш +пиздень +пиздеть +пиздец +пиздецкая +пиздецкий +пиздецкое +пиздий +пиздилища +пиздилищный +пиздиной +пиздину +пиздить +пиздища +пиздоаллюренный +пиздобездонная +пиздобесины +пиздоблошка +пиздоблятское +пиздобол +пиздобрат +пиздобратия +пиздобрюхой +пиздобузина +пиздоварнаковый +пиздоватый +пиздовать +пиздоверзилище +пиздовладелец +пиздоворот +пиздовыдло +пиздовый +пиздоглазая +пиздоглист +пиздогрыз +пиздогундливый +пиздодержец +пиздодраченая +пиздодрачильник +пиздодрачливище +пиздодушие +пиздодырищи +пиздодырная +пиздодырявина +пиздоебательный +пиздоёбищность +пиздоебливая +пиздоеблю +пиздожал +пиздожоп +пиздожопская +пиздозолотарь +пиздой +пиздокач +пиздоквашни +пиздокопатель +пиздолет +пиздолетная +пиздолиз +пиздоломина +пиздомаздливый +пиздомания +пиздомахалово +пиздомол +пиздомотина +пиздоногая +пиздоноздря +пиздообрез +пиздопертая +пиздопляска +пиздопроeбище +пиздопроговенная +пиздопроебина +пиздопроебинами +пиздопрозоид +пиздопролазный +пиздопроситель +пиздопроушина +пиздорванец +пиздорванка +пиздорвань +пиздосей +пиздосербало +пиздоскал +пиздосов +пиздосос +пиздострадалец +пиздострадания +пиздострадатель +пиздостремливое +пиздотень +пиздотёрый +пиздотолковища +пиздотрескучий +пиздотыренный +пиздохайловый +пиздоход +пиздохочь +пиздохуй +пиздочет +пиздошевка +пиздошить +пиздоямина +пиздрик +пизду +пиздуй +пиздулия +пиздун +пиздуянистый +пизды +пиздырь +пиздьей +пиздью +пиздюга +пиздюк +пиздюкать +пиздюкаться +пиздюлей +пиздюли +пиздюлина +пиздюлька +пиздюля +пиздюрить +пиздюхать +пиздюшка +пиздюшки +пиздюшник +пиздя +пиздякать +пиздятина +пиздятиной +пиздятины +пиздячий +пиздячина +пиздячить +пиздячья +писька +писюлек +плоскозадая +поблудить +поблядовать +поблядушка +подговнять +подзаебать +подзаебенить +подзалупная +поднаебнуть +поднаебнуться +поднаёбывать +подосрать +подосраться +подоссать +подпёздывать +подпиздить +подпиздовать +подпиздоваться +подпиздовывать +подпиздовываться +подпиздок +подпиздохать +подпиздохаться +подпиздохивать +подпиздохиваться +подпиздошивать +подпиздошить +подпиздошиться +подпиздывает +подпиздывать +подпиздякать +подпиздякаться +подпиздякивать +подпиздякиваться +подпиздяривать +подпиздяриваться +подпиздярить +подпиздяриться +подпиздяхать +подпиздяхаться +подпиздяхивать +подпиздяхиваться +подпиздячивать +подпиздячиваться +подпиздячить +подпиздячиться +подпиздяшивать +подпиздяшиваться +подпиздяшить +подпиздяшиться +подристывать +подрочить +подсирать +подхуякать +подхуякаться +подхуякивать +подхуякиваться +подхуякнуть +подхуякнуться +подхуяривать +подхуяриваться +подхуярить +подхуяриться +подхуячивать +подхуячиваться +подхуячиться +подхуяшивать +подхуяшиваться +подхуяшить +подхуяшиться +подъеб +подъебайка +подъебала +подъебалка +подъебать +подъебаться +подъебашить +подъебка +подъёбка +подъёбки +подъебнуть +подъебнуться +подъебывать +подъёбывать +подъябывать +поебанный +поебать +поебаться +поебень +поебистика +поебон +поебончик +поебочка +поебывать +поебываться +попердеть +попердеться +попердывать +попизденная +попиздеть +попиздили +попиздистее +попиздить +попиздиться +попиздоболивать +попиздоболиваться +попиздоболить +попиздоболиться +попиздоватей +попиздовать +попиздоваться +попиздовывать +попиздовываться +попиздохать +попиздохаться +попиздохивать +попиздохиваться +попиздошивать +попиздошиваться +попиздошить +попиздошиться +попиздюкать +попиздюкаться +попиздюкивать +попиздюкиваться +попиздюливать +попиздюливаться +попиздюлить +попиздюлиться +попиздюривать +попиздюриваться +попиздюрить +попиздюриться +попиздюхать +попиздюхаться +попиздюхивать +попиздюхиваться +попиздякать +попиздякаться +попиздякивать +попиздякиваться +попиздяривать +попиздяриваться +попиздярить +попиздяриться +попиздяхать +попиздяхаться +попиздяхивать +попиздяхиваться +попиздячивать +попиздячиваться +попиздячить +попиздячиться +попиздяшивать +попиздяшиваться +попиздяшить +попиздяшиться +попизживать +попизживаться +посрать +потаскун +потаскуха +потраханная +потрахать +потрахаться +потрахивать +потрахиваться +похер +похуист +похуй +похую +похуякать +похуякаться +похуякивать +похуякиваться +похуяривать +похуяриваться +похуярили +похуярить +похуяриться +похуячивать +похуячиваться +похуячить +похуячиться +похуяшивать +похуяшиваться +похуяшить +похуяшиться +поц +пошмариться +поябывать +приебать +приебаться +приебашивать +приебашиваться +приебашить +приебашиться +приебенивать +приебениваться +приебенить +приебениться +приебехать +приебехаться +приебехивать +приебехиваться +приебистый +приебуривать +приебуриваться +приебурить +приебуриться +приебывать +приебываться +прижопить +прижопывать +прикинуть +примавдовывать +примандехать +примандехаться +примандехивать +примандехиваться +примандить +примандиться +примандовать +примандоваться +примандовываться +примандохать +примандохаться +примандохивать +примандохиваться +примандошивать +примандошиваться +примандошить +примандошиться +примандюкать +примандюкаться +примандюкивать +примандюкиваться +примандюливать +примандюливаться +примандюлить +примандюлиться +примандюривать +примандюриваться +примандюрить +примандюриться +примандякать +примандякаться +примандякивать +примандякиваться +примандяривать +примандяриваться +примандярить +примандяриться +примандяхать +примандяхаться +примандяхивать +примандяхиваться +примандячивать +примандячиваться +примандячить +примандячиться +примандяшивать +примандяшиваться +примандяшить +примандяшиться +примудохать +примудохаться +примудохивать +примудохиваться +припизденный +припиздень +припиздеть +припиздить +припиздиться +припиздовать +припиздоваться +припиздовывать +припиздовываться +припиздохать +припиздохаться +припиздохивать +припиздохиваться +припиздошивать +припиздошиваться +припиздошить +припиздошиться +припиздронивать +припиздрониваться +припиздронить +припиздрониться +припиздывать +припиздываться +припиздюкать +припиздюкаться +припиздюкивать +припиздюкиваться +припиздюливать +припиздюливаться +припиздюлить +припиздюлиться +припиздюривать +припиздюриваться +припиздюрить +припиздюриться +припиздюхать +припиздюхаться +припиздюхивать +припиздюхиваться +припиздякать +припиздякаться +припиздякивать +припиздякиваться +припиздяривать +припиздяриваться +припиздярить +припиздяриться +припиздяхать +припиздяхаться +припиздяхивать +припиздяхиваться +припиздячивать +припиздячиваться +припиздячить +припиздячиться +припиздяшивать +припиздяшиваться +припиздяшить +припиздяшиться +припизживать +припизживаться +притрахаться +прихуевать +прихуеть +прихуякать +прихуякаться +прихуякивать +прихуякиваться +прихуяривать +прихуяриваться +прихуярить +прихуяриться +прихуячивать +прихуячиваться +прихуячить +прихуячиться +прихуяшивать +прихуяшиваться +прихуяшить +прихуяшиться +пробиздоблядская +пробиздом +проблядовать +проблядушка +проблядь +прогандонский +прогнидопрохуевающее +прогнидская +продрачивать +продрачиваться +продрочить +продрочиться +проёб +проёбанной +проебать +проебаться +проебашивать +проебашиваться +проебашить +проебашиться +проебенить +проебениться +проебом +проебывать +проебываться +промандеть +промандище +промандоговенная +промонодблядская +промудак +промудеть +промудище +промудоватая +промудопердун +промудохуеблядская +промудохуина +проперданутое +пропердок +пропердомандища +пропиздеть +пропиздить +пропиздиться +пропиздоболивать +пропиздоболиваться +пропиздоболить +пропиздоболиться +пропиздовать +пропиздоваться +пропиздовывать +пропиздовываться +пропиздон +пропиздохать +пропиздохаться +пропиздохивать +пропиздохиваться +пропиздошивать +пропиздошиваться +пропиздошить +пропиздошиться +пропиздюкать +пропиздюкаться +пропиздюкивать +пропиздюкиваться +пропиздюливать +пропиздюливаться +пропиздюлить +пропиздюлиться +пропиздюривать +пропиздюриваться +пропиздюрить +пропиздюриться +пропиздюхать +пропиздюхаться +пропиздюхивать +пропиздюхиваться +пропиздякать +пропиздякаться +пропиздякивать +пропиздякиваться +пропиздяривать +пропиздяриваться +пропиздярить +пропиздяриться +пропиздяхать +пропиздяхивать +пропиздяхиваться +пропиздячивать +пропиздячиваться +пропиздячить +пропиздячиться +пропиздяшивать +пропиздяшиваться +пропиздяшить +пропиздяшиться +пропизживать +пропизживаться +просволота +просволотопрохуевое +просволотопрошлюхский +просволотохуеватое +просволотская +проскотошлюха +просраноперданутая +просраносука +проссаное +проссаномудище +простервоблядовое +простервопрохерун +прохуякать +прохуякаться +прохуякивать +прохуякиваться +прохуяривать +прохуяриваться +прохуярить +прохуяриться +прохуячивать +прохуячиваться +прохуячить +прохуячиться +прохуяшивать +прохуяшиваться +прохуяшить +прохуяшиться +прошмандовочный +пятихуй +разблядоваться +раздрочить +раздрочиться +разёбанный +раззалупаться +разнохуйственно +разъеба +разъёба +разъебай +разъёбанное +разъёбанный +разъебать +разъебаться +разъебашивать +разъебашиваться +разъебашить +разъебашиться +разъебенивать +разъебениваться +разъебенить +разъебениться +разъебись +распиздаеб +распиздаёб +распиздай +распиздеться +распизди +распиздить +распиздиться +распиздоблятство +распиздовать +распиздоваться +распиздовывать +распиздовываться +распиздон +распиздохать +распиздохаться +распиздохивать +распиздохиваться +распиздошивать +распиздошиваться +распиздошил +распиздошила +распиздошить +распиздошиться +распиздюляченный +распиздяй +распиздяйка +распиздяйство +распроеб +расхуюжить +расхуяривать +расхуяриваться +расхуярить +расхуяриться +расхуячивать +расхуячиваться +расхуячить +расхуячиться +свиноёб +свиноёбиться +сволотопродерьмо +сволотосука +сговнять +сдрочить +семиблядским +сестроеб +сетсроёб +сифилитик +сифилюга +склипездень +скотложец +скотоёб +скотоёбина +скотопродерьмища +скурвиться +смандить +смандиться +сосихуйская +сосихуйский +сперматоблятская +сперматозавр +спермоблевотина +спиздеть +спиздил +спиздила +спиздить +сраногнойная +сранье +ссанопиздище +стерва +стервоза +стервопиздище +стервопроблядское +стоебучее +страпиздихуюлина +страхоёбище +страхопиздище +страхопизднутый +сука +суки +сукин +сукины +суходрочка +суходрочкой +сухопиздая +сучара +сучий +сучка +сучье +сучьемудища +сучьескотина +схуёженный +схуякать +схуякаться +схуякивать +схуякиваться +схуяли +схуяривать +схуяриваться +схуярить +схуяриться +схуячивать +схуячить +схуячиться +сцать +сциха +сцыха +съебать +съебаться +съебашивать +съебашиваться +съебашить +съебашиться +съебенивать +съебенить +съебениться +съебывать +съебываться +тварь +толстожопый +толстозадая +торчило +траханье +трахать +трахаться +трахнуть +трахнуться +трепак +трепездон +трепездонит +тригнидопроговно +тригнидопрохерище +тригнидская +триговноперданутое +триеблоостомондовевшая +триебучим +тризлоебучим +тримандаблядская +тримандоблядская +тримандопростервозный +тримондоеби +тримудище +трипездоклятый +трипердоватая +трипиздие +трипиздоблядская +трипиздоблядское +трипиздоблятский +трипиздодрочун +трипиздопроманда +трипиздопроскотложец +трипиздохерище +трипиздыпроебина +триппер +трипперных +трисраное +триссака +триссанохуина +триссаный +трисука +трисучьепадловая +трихломидозопиздоеблохуеблядеперепиздическая +трихуемандаблядский +троебучим +троепиздием +троепиздище +туебень +тупиздень +убить +ублюдоёб +ублюдок +уебалась +уебался +уебальник +уебанский +уебать +уебаться +уебашивать +уебашить +уебенить +уебище +уёбище +уёбищенски +уёбок +уёбывает +уебывать +уёбывать +уебываться +уебыш +упиздить +урод +усраться +усрачка +уссать +уссаться +ухуякать +ухуякаться +ухуякивать +ухуякиваться +ухуяривать +ухуяриваться +ухуярить +ухуяриться +ухуячивать +ухуячиваться +ухуячить +ухуячиться +ухуяшивать +ухуяшиваться +ухуяшить +ухуяшиться +фаллос +фекал +фекалии +фекалий +фуй +хер +херами +херня +херов +херовина +херовый +хероговнюк +херопроскотское +херун +хитровыебанная +хитровыебанный +хитрожопый +хлюха +хрен +хреново +хреновое +хреновый +худоебина +худоёбина +хуе +хуебарный +хуебень +хуеблища +хуеблядипиздожабья +хуеблядская +хуеблядский +хуебратия +хуебун +хуебур +хуебыдловый +хуев +хуёв +хуева +хуеватенький +хуевато +хуеватый +хуеверзоха +хуевина +хуёвина +хуёвищный +хуёвничать +хуево +хуёво +хуеворот +хуевотараканево +хуевый +хуёвый +хуег +хуеглот +хуеговно +хуеголовое +хуеголовый +хуегрыз +хуедав +хуедин +хуедрыга +хуезагнутие +хуездоватый +хуек +хуелептический +хуелес +хуеловица +хуем +хуём +хуеман +хуемандовина +хуемудрие +хуемырло +хуемыслие +хуеньки +хуеоглобель +хуепедераст +хуепиздрическое +хуеплет +хуеплёт +хуепромышленник +хуепропадла +хуепуполо +хуепутало +хуерик +хуероломом +хуерык +хуерыльная +хуесос +хуесосина +хуессаная +хуесучий +хуета +хуетень +хуетертое +хуеть +хуец +хуечек +хуи +хуидло +хуила +хуилище +хуило +хуиный +хуистая +хуистый +хуишко +хуище +хуй +хуйло +хуйнуть +хуйню +хуйня +хуйрик +хуйство +хули +хую +хуюга +хуюживать +хуюживаться +хуюжить +хуюжиться +хуюла +хуюльское +хуюшки +хуя +хуяк +хуяка +хуякать +хуякнуть +хуяли +хуяляга +хуям +хуями +хуярез +хуярить +хуяриться +хуясе +хуястый +хуях +хуяха +хуяция +хуячий +хуячить +хуячиться +хуячья +хуяшить +целка +целку +целочка +черножопые +чернозадый +член +шалава +шароёбится +шелудивая +шелупина +широкопиздая +шлюха +шлюхский +шлюхское +шмара +шмарить +шмариться +ялдак +анус +аборт +блудилище +вагина +влагалище +дурак +моча +пенис \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/first.png b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/first.png new file mode 100644 index 0000000..660babf Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/first.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/last.png b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/last.png new file mode 100644 index 0000000..cd4f641 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/last.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-flip.svg b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-flip.svg new file mode 100644 index 0000000..843d710 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-flip.svg @@ -0,0 +1,3 @@ + + + diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-history.svg b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-history.svg new file mode 100644 index 0000000..87d94b2 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-history.svg @@ -0,0 +1,3 @@ + + + diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-share.svg b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-share.svg new file mode 100644 index 0000000..50b34dd --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/menu-share.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/next.png b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/next.png new file mode 100644 index 0000000..61eefd1 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/next.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/pgn_moves.svg b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/pgn_moves.svg new file mode 100644 index 0000000..72ba596 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/pgn_moves.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/prev.png b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/prev.png new file mode 100644 index 0000000..0e4e744 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/prev.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-crown.svg b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-crown.svg new file mode 100644 index 0000000..c4cc119 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-crown.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-draw.svg b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-draw.svg new file mode 100644 index 0000000..25d67ed --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-draw.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-flag.svg b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-flag.svg new file mode 100644 index 0000000..219b8e7 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/icons/result-flag.svg @@ -0,0 +1,4 @@ + + + + diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/mr_granstandercleang.otf b/SunnexGB/Heroku-Modules/Assets/NoChess/mr_granstandercleang.otf new file mode 100644 index 0000000..554bd15 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/mr_granstandercleang.otf differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/other/bg.png b/SunnexGB/Heroku-Modules/Assets/NoChess/other/bg.png new file mode 100644 index 0000000..e7b9a2b Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/other/bg.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/other/noise.png b/SunnexGB/Heroku-Modules/Assets/NoChess/other/noise.png new file mode 100644 index 0000000..315084a Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/other/noise.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-bishop.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-bishop.png new file mode 100644 index 0000000..d450ca0 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-bishop.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-king.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-king.png new file mode 100644 index 0000000..fa27585 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-king.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-knight.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-knight.png new file mode 100644 index 0000000..76bb3cc Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-knight.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-pawn.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-pawn.png new file mode 100644 index 0000000..a67597e Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-pawn.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-queen.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-queen.png new file mode 100644 index 0000000..9a23ce1 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-queen.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-rook.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-rook.png new file mode 100644 index 0000000..53c1a0a Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/black-rook.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-bishop.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-bishop.png new file mode 100644 index 0000000..d65e581 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-bishop.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-king.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-king.png new file mode 100644 index 0000000..8e93abc Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-king.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-knight.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-knight.png new file mode 100644 index 0000000..4d807d1 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-knight.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-pawn.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-pawn.png new file mode 100644 index 0000000..ad84cc0 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-pawn.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-queen.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-queen.png new file mode 100644 index 0000000..d41bd92 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-queen.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-rook.png b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-rook.png new file mode 100644 index 0000000..bfa7343 Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/NoChess/pieces/white-rook.png differ diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/index.html b/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/index.html new file mode 100644 index 0000000..f9d7af4 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/index.html @@ -0,0 +1,113 @@ + + + + + + + NoChess + + +
+
+
+
+
+
+
+
+
+ first +
+
+ prev +
+
+ next +
+
+ last +
+
+
+
+ +
+
+
+
+
guest-acc
+
+
+
+ + + + + + + + + + + + + +
+
--:--
+
+ + + + +
+
--:--
+
+ + + + + + + + + + + + + +
+
+
+
Sunnex <3
+
+
+
+ + + + + + diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/javascript.js b/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/javascript.js new file mode 100644 index 0000000..e978f20 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/javascript.js @@ -0,0 +1,1675 @@ +const board_size = 8; +const block_size = 107; +const theme_config = window.nochess_theme || {}; +const block_light = theme_config.block_light || '#D8E3E7'; +const block_dark = theme_config.block_dark || '#7699AF'; +const select_block = theme_config.select_block || '#FFDF5A'; +const block_select_alpha_light = 0.34; +const block_select_alpha_dark = 0.58; +const move_pieces_color = theme_config.move_pieces_color || '#58B4FF'; +const block_move_from_alpha = 0.56; +const block_move_to_alpha = 0.62; +const move_highlight_delay_ms = 25; +const result_overlay_duration_ms = 300; +const result_win = theme_config.result_win || '#00BE16'; +const result_lose = theme_config.result_lose || '#BE0000'; +const result_draw = theme_config.result_draw || '#434343'; +const result_overlay_alpha = 0.64; +const arrow_color = theme_config.arrow_color || '#BD3667'; +const asset_root = window.nochess_asset_root || 'https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/NoChess'; +const noise_src = `${asset_root}/other/noise.png`; +const start_fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'; + +const chess_pieces = { + wP: `${asset_root}/pieces/white-pawn.png`, + wR: `${asset_root}/pieces/white-rook.png`, + wN: `${asset_root}/pieces/white-knight.png`, + wB: `${asset_root}/pieces/white-bishop.png`, + wQ: `${asset_root}/pieces/white-queen.png`, + wK: `${asset_root}/pieces/white-king.png`, + bP: `${asset_root}/pieces/black-pawn.png`, + bR: `${asset_root}/pieces/black-rook.png`, + bN: `${asset_root}/pieces/black-knight.png`, + bB: `${asset_root}/pieces/black-bishop.png`, + bQ: `${asset_root}/pieces/black-queen.png`, + bK: `${asset_root}/pieces/black-king.png` +}; + +const sound_urls = { + game_start: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/game-start.mp3', + game_end: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/game-end.mp3', + capture: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/capture.mp3', + castle: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/castle.mp3', + premove: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/premove.mp3', + move_self: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/move-self.mp3', + move_opponent: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/move-opponent.mp3', + move_check: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/move-check.mp3', + promote: 'https://images.chesscomfiles.com/chess-themes/sounds/_MP3_/default/promote.mp3' +}; + +const ui_text = { + no_history: 'No history', + unknown_date: 'Unknown date', + chess_library_not_ready: 'Chess library not ready', + pgn_empty: 'Paste PGN text first', + pgn_invalid: 'Invalid PGN format', + pgn_loaded: 'PGN loaded', + pgn_copied: 'PGN copied', + copy_failed: 'Copy failed' +}; + +const crown_icon_src = `${asset_root}/icons/result-crown.svg`; +const flag_icon_src = `${asset_root}/icons/result-flag.svg`; +const draw_icon_src = `${asset_root}/icons/result-draw.svg`; + +const canvas = document.getElementById('chessBoard'); +const ctx = canvas.getContext('2d'); +canvas.width = board_size * block_size; +canvas.height = board_size * block_size; + +const moves_scroll = document.querySelector('.moves_list_mobile'); +const pgn_moves_board = document.querySelector('.pgn_moves_board'); +const timer_black = document.querySelector('.timer_black'); +const timer_white = document.querySelector('.timer_white'); +const player_name_white = document.querySelector('.player_name_white'); +const player_name_black = document.querySelector('.player_name_black'); +const avatar_white = document.querySelector('.avatar_white'); +const avatar_black = document.querySelector('.avatar_black'); +const history_btn = document.getElementById('history_btn'); +const share_btn = document.getElementById('share_btn'); +const more_btn = document.getElementById('more_btn'); +const app_layout = document.querySelector('.web_chess'); +const first_move_icon = document.querySelector('.first_move_btn img'); +const submenu_overlay = document.getElementById('submenu_overlay'); +const history_panel = document.getElementById('history_panel'); +const share_panel = document.getElementById('share_panel'); +const history_games = document.getElementById('history_games'); +const share_pgn_text = document.getElementById('share_pgn_text'); +const load_pgn_btn = document.getElementById('load_pgn_btn'); +const copy_pgn_btn = document.getElementById('copy_pgn_btn'); +const share_status = document.getElementById('share_status'); + +let board = []; +let marked_cells = new Set(); +let arrows = []; +let piece_images = {}; +let noise_image = null; +let right_drag_start = null; +let is_flipped = false; +let parsed_games = []; +let current_game_index = 0; +let current_ply = 0; +let share_status_timeout = null; +let chess_ready = false; +let highlighted_move_cells = null; +let piece_animation = null; +let highlight_delay_timeout = null; +let result_markers = null; +let result_animation = null; +let nochess_profile = window.nochess_profile || null; +const mobile_breakpoint = window.matchMedia('(max-width: 572px)'); +let was_mobile_layout = false; +const crown_icon_image = new Image(); +const flag_icon_image = new Image(); +const draw_icon_image = new Image(); +crown_icon_image.src = crown_icon_src; +flag_icon_image.src = flag_icon_src; +draw_icon_image.src = draw_icon_src; + +function hexToRgba(hex, alpha) { + const value = hex.replace('#', ''); + const full = value.length === 3 + ? value.split('').map((c) => c + c).join('') + : value; + const int = Number.parseInt(full, 16); + const r = (int >> 16) & 255; + const g = (int >> 8) & 255; + const b = int & 255; + return `rgba(${r}, ${g}, ${b}, ${alpha})`; +} + +function loadScript(src) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = src; + script.async = true; + script.onload = () => resolve(true); + script.onerror = () => reject(new Error(`failed: ${src}`)); + document.head.appendChild(script); + }); +} + +async function loadChessLibrary() { + if (typeof window.Chess !== 'undefined') { + return true; + } + + const classic_sources = [ + 'https://cdn.jsdelivr.net/npm/chess.js@0.13.4/chess.min.js', + 'https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.13.4/chess.min.js' + ]; + + for (const src of classic_sources) { + try { + await loadScript(src); + if (typeof window.Chess !== 'undefined') { + return true; + } + } catch (error) { + } + } + + try { + const module = await import('https://cdn.jsdelivr.net/npm/chess.js@1.4.0/dist/esm/chess.js'); + window.Chess = module.Chess || module.default; + return typeof window.Chess !== 'undefined'; + } catch (error) { + return false; + } +} + +function createChess() { + if (typeof Chess === 'undefined') { + return null; + } + return new Chess(); +} + +function loadPgnToChess(chess, pgn_text) { + if (!chess) { + return false; + } + + if (typeof chess.load_pgn === 'function') { + let loaded = chess.load_pgn(pgn_text, { newline_char: '\n', sloppy: true }); + if (!loaded) { + loaded = chess.load_pgn(pgn_text, { sloppy: true }); + } + if (!loaded) { + loaded = chess.load_pgn(pgn_text); + } + return loaded; + } + + if (typeof chess.loadPgn === 'function') { + try { + chess.loadPgn(pgn_text, { strict: false }); + return true; + } catch (error) { + try { + chess.loadPgn(pgn_text); + return true; + } catch (second_error) { + return false; + } + } + } + + return false; +} + +function moveOnChess(chess, move_text) { + if (!chess || !move_text) { + return null; + } + + try { + const sloppy_move = chess.move(move_text, { sloppy: true }); + if (sloppy_move) { + return sloppy_move; + } + } catch (error) { + } + + try { + return chess.move(move_text); + } catch (error) { + return null; + } +} + +const sound_players = Object.fromEntries(Object.entries(sound_urls).map(([name, url]) => { + const base = new Audio(url); + base.preload = 'auto'; + return [name, () => { + const sound = base.cloneNode(true); + sound.volume = 0.9; + sound.play().catch(() => {}); + }]; +})); + +function updateScrollShadow() { + const at_bottom = moves_scroll.scrollTop + moves_scroll.clientHeight >= moves_scroll.scrollHeight - 5; + const has_overflow = moves_scroll.scrollHeight > moves_scroll.clientHeight; + pgn_moves_board.classList.toggle('has_scroll', has_overflow && !at_bottom); +} + +function parseHeaders(pgn_text) { + const headers = {}; + const header_regex = /^\[(\w+)\s+"([^"]*)"\]$/gm; + let match = header_regex.exec(pgn_text); + while (match) { + headers[match[1]] = match[2]; + match = header_regex.exec(pgn_text); + } + return headers; +} + +function normalizeClock(clock) { + if (!clock) { + return null; + } + const parts = clock.split(':'); + if (parts.length === 3) { + const hours = Number(parts[0]); + const minutes = Number(parts[1]); + const seconds = Number(parts[2]); + if (!Number.isNaN(hours) && !Number.isNaN(minutes) && !Number.isNaN(seconds)) { + if (hours === 0) { + return `${minutes}:${String(seconds).padStart(2, '0')}`; + } + return `${hours}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; + } + } + if (parts.length === 2) { + const minutes = Number(parts[0]); + const seconds = Number(parts[1]); + if (!Number.isNaN(minutes) && !Number.isNaN(seconds)) { + return `${minutes}:${String(seconds).padStart(2, '0')}`; + } + } + return clock; +} + +function isMoveToken(token) { + return /^(O-O(-O)?|[KQRBN]?[a-h]?[1-8]?x?[a-h][1-8](=[QRBN])?[+#]?|[a-h]x[a-h][1-8](=[QRBN])?[+#]?|[a-h][1-8](=[QRBN])?[+#]?)$/.test(token); +} + +function extractMoveClocks(pgn_text) { + const cleaned = pgn_text.replace(/^\[[^\]]*\]\s*$/gm, ''); + const tokens = cleaned.match(/\{[^}]*\}|[^\s]+/g) || []; + const clocks_by_ply = {}; + let ply = 0; + let last_move_ply = 0; + + for (const token of tokens) { + if (!token) { + continue; + } + + if (token.startsWith('{')) { + const clk_match = token.match(/\[%clk\s+([^\]]+)\]/i); + if (clk_match && last_move_ply > 0) { + clocks_by_ply[last_move_ply] = normalizeClock(clk_match[1].trim()); + } + continue; + } + + if (/^\d+\.{1,3}$/.test(token)) { + continue; + } + + if (token === '1-0' || token === '0-1' || token === '1/2-1/2' || token === '*') { + continue; + } + + if (isMoveToken(token)) { + ply += 1; + last_move_ply = ply; + } + } + + return clocks_by_ply; +} + +function fenCharToPiece(char) { + const is_white = char === char.toUpperCase(); + const color = is_white ? 'w' : 'b'; + const type = char.toUpperCase(); + if (!'PRNBQK'.includes(type)) { + return null; + } + return `${color}${type}`; +} + +function boardFromFen(fen) { + const rows = fen.split(' ')[0].split('/'); + return rows.map((row) => { + const parsed = []; + for (const char of row) { + const count = Number(char); + if (!Number.isNaN(count) && count > 0) { + for (let i = 0; i < count; i += 1) { + parsed.push(null); + } + } else { + parsed.push(fenCharToPiece(char)); + } + } + return parsed; + }); +} + +function squareToCoords(square) { + if (!square || square.length < 2) { + return null; + } + const file = square.charCodeAt(0) - 97; + const rank = Number(square[1]); + if (file < 0 || file > 7 || Number.isNaN(rank) || rank < 1 || rank > 8) { + return null; + } + return { row: 8 - rank, col: file }; +} + +function setHighlightedMove(from_square, to_square) { + const from = squareToCoords(from_square); + const to = squareToCoords(to_square); + highlighted_move_cells = from && to ? { from, to } : null; +} + +function scheduleHighlightedMove(from_square, to_square) { + if (highlight_delay_timeout) { + clearTimeout(highlight_delay_timeout); + highlight_delay_timeout = null; + } + + highlighted_move_cells = null; + + if (!from_square || !to_square) { + return; + } + + highlight_delay_timeout = setTimeout(() => { + setHighlightedMove(from_square, to_square); + highlight_delay_timeout = null; + render(); + }, move_highlight_delay_ms); +} + +window.highlightMoveSquares = function highlightMoveSquares(from_square, to_square) { + setHighlightedMove(from_square, to_square); + render(); +}; + +function createAnimationState(from, to, piece_code) { + return { + from, + to, + piece_code, + start_time: performance.now(), + duration: 180 + }; +} + +function animateMoveTransition(animation_state) { + piece_animation = animation_state; + + function step() { + if (!piece_animation) { + return; + } + + const elapsed = performance.now() - piece_animation.start_time; + if (elapsed >= piece_animation.duration) { + piece_animation = null; + render(); + return; + } + + render(); + requestAnimationFrame(step); + } + + requestAnimationFrame(step); +} + +function parsePgnToGameState(pgn_text) { + if (!chess_ready || typeof Chess === 'undefined') { + return null; + } + + const parser = createChess(); + if (!parser) { + return null; + } + + const loaded = loadPgnToChess(parser, pgn_text); + + if (!loaded) { + const fallback = parseMovesWithSloppyParser(pgn_text); + if (!fallback) { + return null; + } + return fallback; + } + + const headers = parseHeaders(pgn_text); + const verbose_moves = parser.history({ verbose: true }); + const clocks_by_ply = extractMoveClocks(pgn_text); + const simulator = createChess(); + if (!simulator) { + return null; + } + const positions = [boardFromFen(simulator.fen())]; + + for (const move of verbose_moves) { + moveOnChess(simulator, move.san); + positions.push(boardFromFen(simulator.fen())); + } + + const moves = verbose_moves.map((move, index) => ({ + ...move, + clock: clocks_by_ply[index + 1] || null, + is_capture: move.flags.includes('c') || move.flags.includes('e'), + is_castle: move.flags.includes('k') || move.flags.includes('q'), + is_promotion: move.flags.includes('p'), + is_check: move.san.includes('+'), + is_mate: move.san.includes('#') + })); + + return { + pgn_text, + headers, + moves, + positions + }; +} + +function normalizeMoveToken(token) { + if (!token) { + return ''; + } + + let value = token.trim(); + value = value.replace(/^\d+\.\.\./, ''); + value = value.replace(/^\d+\./, ''); + value = value.replace(/[!?]+$/g, ''); + value = value.replace(/^0-0-0$/, 'O-O-O'); + value = value.replace(/^0-0$/, 'O-O'); + value = value.replace(/=([qrbn])/g, (_, p1) => `=${p1.toUpperCase()}`); + return value; +} + +function extractSanTokens(movetext) { + const regex = /(?:O-O-O|O-O|0-0-0|0-0|[KQRBN]?[a-h]?[1-8]?x?[a-h][1-8](?:=[QRBNqrbn])?|[a-h]x[a-h][1-8](?:=[QRBNqrbn])?|[a-h][1-8](?:=[QRBNqrbn])?)(?:[+#])?|1-0|0-1|1\/2-1\/2|\*/g; + return movetext.match(regex) || []; +} + +function tryApplySanMove(game, token) { + const first_try = moveOnChess(game, token); + if (first_try) { + return first_try; + } + + const without_suffix = token.replace(/[+#]$/, ''); + if (without_suffix !== token) { + const second_try = moveOnChess(game, without_suffix); + if (second_try) { + return second_try; + } + } + + return null; +} + +function parseMovesWithSloppyParser(pgn_text) { + if (!chess_ready || typeof Chess === 'undefined') { + return null; + } + + const headers = parseHeaders(pgn_text); + const clocks_by_ply = extractMoveClocks(pgn_text); + const move_section = pgn_text + .replace(/^\[[^\]]*\]\s*$/gm, '') + .replace(/\{[^}]*\}/g, ' ') + .replace(/\([^)]*\)/g, ' ') + .replace(/\$\d+/g, ' ') + .replace(/\r\n/g, '\n') + .trim(); + + const start_index = move_section.search(/\b\d+\./); + const mandatory_movetext = start_index >= 0 ? move_section.slice(start_index) : move_section; + const normalized_movetext = mandatory_movetext + .replace(/\s+/g, ' ') + .trim(); + + const raw_tokens = extractSanTokens(normalized_movetext); + const game = createChess(); + if (!game) { + return null; + } + const moves = []; + const positions = [boardFromFen(game.fen())]; + let ply = 0; + + for (const raw of raw_tokens) { + if (!raw || raw === '1-0' || raw === '0-1' || raw === '1/2-1/2' || raw === '*') { + continue; + } + + const split_tokens = raw.includes('.') ? raw.split('.').filter(Boolean) : [raw]; + for (const token of split_tokens) { + const clean_token = normalizeMoveToken(token); + if (!clean_token || /^\d+$/.test(clean_token)) { + continue; + } + + if (clean_token === '...' || /^\.+$/.test(clean_token)) { + continue; + } + + if (clean_token === '1-0' || clean_token === '0-1' || clean_token === '1/2-1/2' || clean_token === '*') { + continue; + } + + const move = tryApplySanMove(game, clean_token); + if (!move) { + return null; + } + + ply += 1; + moves.push({ + ...move, + clock: clocks_by_ply[ply] || null, + is_capture: move.flags.includes('c') || move.flags.includes('e'), + is_castle: move.flags.includes('k') || move.flags.includes('q'), + is_promotion: move.flags.includes('p'), + is_check: move.san.includes('+'), + is_mate: move.san.includes('#') + }); + positions.push(boardFromFen(game.fen())); + } + } + + if (moves.length === 0) { + return null; + } + + return { + pgn_text, + headers, + moves, + positions + }; +} + +function sanitizePgnInput(raw_text) { + if (!raw_text) { + return ''; + } + + let text = raw_text.replace(/^\uFEFF/, '').trim(); + const fenced = text.match(/^```(?:pgn)?\s*([\s\S]*?)\s*```$/i); + if (fenced) { + text = fenced[1].trim(); + } + + return text + .replace(/\u00A0/g, ' ') + .replace(/\r\n/g, '\n') + .replace(/\t/g, ' ') + .replace(/\n{3,}/g, '\n\n'); +} + +function autoResizeShareTextarea() { + share_pgn_text.style.height = 'auto'; + const next_height = Math.min(share_pgn_text.scrollHeight, Math.floor(window.innerHeight * 0.55)); + share_pgn_text.style.height = `${Math.max(next_height, 170)}px`; +} + +function setShareStatus(message, is_error) { + if (share_status_timeout) { + clearTimeout(share_status_timeout); + share_status_timeout = null; + } + + share_status.textContent = message || ''; + share_status.classList.remove('show', 'error'); + if (!message) { + return; + } + if (is_error) { + share_status.classList.add('error'); + } + requestAnimationFrame(() => { + share_status.classList.add('show'); + }); + + share_status_timeout = setTimeout(() => { + share_status.classList.remove('show', 'error'); + share_status.textContent = ''; + share_status_timeout = null; + }, 2200); +} + +function clearResultVisuals() { + result_markers = null; + result_animation = null; +} + +function isSameResultMarkers(first, second) { + if (!first && !second) { + return true; + } + if (!first || !second) { + return false; + } + return first.winner.row === second.winner.row + && first.winner.col === second.winner.col + && first.loser.row === second.loser.row + && first.loser.col === second.loser.col; +} + +function getResultOverlayProgress() { + if (!result_animation) { + return 1; + } + return Math.min(1, (performance.now() - result_animation.start_time) / result_animation.duration); +} + +function getResultIconScale(progress) { + const start = 0.52; + const overshoot = 1.08; + const ease_out_back = 1 + (2.1 * ((progress - 1) ** 3)) + (1.1 * ((progress - 1) ** 2)); + + if (progress < 0.78) { + return start + ((overshoot - start) * ease_out_back); + } + + const settle_progress = (progress - 0.78) / 0.22; + return overshoot + ((1 - overshoot) * settle_progress); +} + +function animateResultOverlay() { + function step() { + if (!result_animation) { + return; + } + + const progress = getResultOverlayProgress(); + render(); + + if (progress >= 1) { + result_animation = null; + return; + } + + requestAnimationFrame(step); + } + + requestAnimationFrame(step); +} + +function getWinnerFromResult(result) { + if (result === '1-0') { + return 'w'; + } + if (result === '0-1') { + return 'b'; + } + if (result === '1/2-1/2') { + return 'draw'; + } + return null; +} + +function updateResultVisuals() { + const previous_markers = result_markers; + clearResultVisuals(); + + const game = getCurrentGame(); + if (!game || current_ply !== game.moves.length) { + return; + } + + const winner = getWinnerFromResult(game.headers.Result || ''); + if (!winner) { + return; + } + + let winner_king = null; + let loser_king = null; + for (let row = 0; row < board_size; row += 1) { + for (let col = 0; col < board_size; col += 1) { + const piece = board[row][col]; + if (winner === 'draw') { + if (piece === 'wK') { + winner_king = { row, col }; + } else if (piece === 'bK') { + loser_king = { row, col }; + } + } else { + if (piece === (winner === 'w' ? 'wK' : 'bK')) { + winner_king = { row, col }; + } else if (piece === (winner === 'w' ? 'bK' : 'wK')) { + loser_king = { row, col }; + } + } + } + } + + if (winner_king && loser_king) { + const next_markers = { + type: winner === 'draw' ? 'draw' : 'winlose', + winner: winner_king, + loser: loser_king + }; + + result_markers = next_markers; + if (!isSameResultMarkers(previous_markers, next_markers)) { + result_animation = { + start_time: performance.now(), + duration: result_overlay_duration_ms + }; + animateResultOverlay(); + } + } +} + +async function copyTextToClipboard(text) { + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(text); + return; + } + + const helper = document.createElement('textarea'); + helper.value = text; + helper.setAttribute('readonly', ''); + helper.style.position = 'fixed'; + helper.style.top = '-1000px'; + helper.style.left = '-1000px'; + document.body.appendChild(helper); + helper.focus(); + helper.select(); + const success = document.execCommand('copy'); + document.body.removeChild(helper); + if (!success) { + throw new Error('copy-failed'); + } +} + +function getCurrentGame() { + return parsed_games[current_game_index] || null; +} + +function normalizePlayerName(value) { + return (value || '').toString().trim().toLowerCase(); +} + +function playerNameMatches(side_name, variants) { + const side = normalizePlayerName(side_name); + if (!side) { + return false; + } + + return variants.some((name) => { + const candidate = normalizePlayerName(name); + if (!candidate) { + return false; + } + return side === candidate || side.includes(candidate) || candidate.includes(side); + }); +} + +function detectMyColor(game) { + if (!game || !nochess_profile || !game.headers) { + return null; + } + + const names = [ + nochess_profile.name, + nochess_profile.username, + nochess_profile.first_name, + nochess_profile.last_name + ]; + + const white_match = playerNameMatches(game.headers.White, names); + const black_match = playerNameMatches(game.headers.Black, names); + + if (white_match && !black_match) { + return 'w'; + } + if (black_match && !white_match) { + return 'b'; + } + return null; +} + +function applyPlayerAvatars() { + if (!avatar_white || !avatar_black || !nochess_profile) { + return; + } + + const me_photo = nochess_profile.photo || ''; + const enemy_photo = nochess_profile.enemy_photo || ''; + if (!me_photo && !enemy_photo) { + return; + } + + const game = getCurrentGame(); + const my_color = detectMyColor(game) || 'w'; + const white_photo = my_color === 'b' ? enemy_photo : me_photo; + const black_photo = my_color === 'b' ? me_photo : enemy_photo; + + if (white_photo) { + avatar_white.style.backgroundImage = `url('${white_photo}')`; + } + if (black_photo) { + avatar_black.style.backgroundImage = `url('${black_photo}')`; + } +} + +function setNoChessProfile(profile) { + nochess_profile = profile || null; + applyPlayerAvatars(); +} + +window.setNoChessProfile = setNoChessProfile; + +function getTimerTextForColor(color) { + const game = getCurrentGame(); + if (!game) { + return '--:--'; + } + + let latest = null; + for (let i = 0; i < current_ply; i += 1) { + const move = game.moves[i]; + if (move.color === color && move.clock) { + latest = move.clock; + } + } + return latest || '--:--'; +} + +function updateTimers() { + timer_white.textContent = getTimerTextForColor('w'); + timer_black.textContent = getTimerTextForColor('b'); +} + +function updatePlayers() { + const game = getCurrentGame(); + if (!game) { + applyPlayerAvatars(); + return; + } + player_name_white.textContent = game.headers.White || 'White'; + player_name_black.textContent = game.headers.Black || 'Black'; + applyPlayerAvatars(); +} + +function renderMoves() { + const game = getCurrentGame(); + moves_scroll.innerHTML = ''; + + if (!game) { + updateScrollShadow(); + return; + } + + for (let i = 0; i < game.moves.length; i += 2) { + const move_number = Math.floor(i / 2) + 1; + const white = game.moves[i] || null; + const black = game.moves[i + 1] || null; + const row = document.createElement('div'); + row.className = 'move_row'; + + const number = document.createElement('div'); + number.className = 'move_number'; + number.textContent = `${move_number}.`; + + const white_cell = document.createElement('div'); + white_cell.className = 'move_white'; + white_cell.textContent = white ? white.san : ''; + if (white) { + white_cell.dataset.ply = String(i + 1); + } + + const black_cell = document.createElement('div'); + black_cell.className = 'move_black'; + black_cell.textContent = black ? black.san : ''; + if (black) { + black_cell.dataset.ply = String(i + 2); + } + + row.append(number, white_cell, black_cell); + moves_scroll.appendChild(row); + } + + moves_scroll.querySelectorAll('.move_white[data-ply], .move_black[data-ply]').forEach((cell) => { + cell.addEventListener('click', () => { + const next_ply = Number(cell.dataset.ply || '0'); + if (next_ply > 0) { + setPly(next_ply, true); + } + }); + }); + + updateCurrentMoveHighlight(); + updateScrollShadow(); +} + +function updateCurrentMoveHighlight() { + moves_scroll.querySelectorAll('.current_move').forEach((cell) => { + cell.classList.remove('current_move'); + }); + + if (current_ply <= 0) { + return; + } + + const active = moves_scroll.querySelector(`[data-ply="${current_ply}"]`); + if (!active) { + return; + } + + active.classList.add('current_move'); + active.scrollIntoView({ block: 'nearest' }); +} + +function playSound(name) { + const player = sound_players[name]; + if (player) { + player(); + } +} + +function playMoveSound(move) { + if (!move) { + return; + } + + if (move.is_mate) { + playSound('move_check'); + setTimeout(() => playSound('game_end'), 70); + return; + } + + if (move.is_promotion) { + playSound('promote'); + return; + } + + if (move.is_castle) { + playSound('castle'); + return; + } + + if (move.is_capture) { + playSound('capture'); + return; + } + + if (move.is_check) { + playSound('move_check'); + return; + } + + playSound(move.color === 'w' ? 'move_self' : 'move_opponent'); +} + +function setPly(next_ply, with_sound) { + const game = getCurrentGame(); + if (!game) { + return; + } + + const previous_ply = current_ply; + const previous_board = board.map((row) => [...row]); + const clamped = Math.max(0, Math.min(next_ply, game.moves.length)); + const delta = clamped - previous_ply; + const moved_forward_by_one = delta === 1; + current_ply = clamped; + + if (current_ply > 0) { + const move = game.moves[current_ply - 1]; + scheduleHighlightedMove(move.from, move.to); + } else { + scheduleHighlightedMove(null, null); + } + + board = game.positions[current_ply].map((row) => [...row]); + + if (Math.abs(delta) === 1) { + const move_index = delta > 0 ? current_ply - 1 : previous_ply - 1; + const move = game.moves[move_index]; + if (move) { + const from_square = delta > 0 ? move.from : move.to; + const to_square = delta > 0 ? move.to : move.from; + const from = squareToCoords(from_square); + const to = squareToCoords(to_square); + const piece_code = from ? previous_board[from.row][from.col] : null; + if (from && to && piece_code) { + animateMoveTransition(createAnimationState(from, to, piece_code)); + } else { + render(); + } + } else { + render(); + } + } else { + piece_animation = null; + render(); + } + + updateCurrentMoveHighlight(); + updateTimers(); + updateResultVisuals(); + + if (delta !== 0) { + marked_cells.clear(); + arrows = []; + } + + if (!with_sound || delta === 0) { + return; + } + + if (moved_forward_by_one && current_ply > 0) { + playMoveSound(game.moves[current_ply - 1]); + return; + } + + playSound('premove'); +} + +function loadGame(index) { + if (index < 0 || index >= parsed_games.length) { + return; + } + current_game_index = index; + current_ply = 0; + marked_cells.clear(); + arrows = []; + updatePlayers(); + renderMoves(); + setPly(0, false); + share_pgn_text.value = parsed_games[current_game_index].pgn_text; + playSound('game_start'); +} + +function buildHistoryList() { + history_games.innerHTML = ''; + + if (parsed_games.length === 0) { + history_games.classList.add('empty'); + const empty = document.createElement('div'); + empty.className = 'history_empty'; + empty.textContent = ui_text.no_history; + history_games.appendChild(empty); + return; + } + + history_games.classList.remove('empty'); + parsed_games.forEach((game, index) => { + const button = document.createElement('button'); + button.className = 'history_game_btn'; + button.type = 'button'; + const white = game.headers.White || 'White'; + const black = game.headers.Black || 'Black'; + const date = game.headers.Date || ui_text.unknown_date; + const result = game.headers.Result || '*'; + button.textContent = `${white} vs ${black} • ${result} • ${date}`; + button.addEventListener('click', () => { + closePanels(); + loadGame(index); + }); + history_games.appendChild(button); + }); +} + +function openPanel(panel) { + submenu_overlay.classList.add('open'); + history_panel.classList.remove('open'); + share_panel.classList.remove('open'); + panel.classList.add('open'); +} + +function closePanels() { + submenu_overlay.classList.remove('open'); + history_panel.classList.remove('open'); + share_panel.classList.remove('open'); +} + +function loadPieceImages(on_ready) { + let loaded = 0; + const total = Object.keys(chess_pieces).length; + + for (const [piece, src] of Object.entries(chess_pieces)) { + const img = new Image(); + img.onload = () => { + piece_images[piece] = img; + loaded += 1; + if (loaded === total) { + on_ready(); + } + }; + img.onerror = () => { + loaded += 1; + if (loaded === total) { + on_ready(); + } + }; + img.src = src; + } +} + +function loadNoise(on_ready) { + const img = new Image(); + img.onload = () => { + noise_image = img; + on_ready(); + }; + img.onerror = () => on_ready(); + img.src = noise_src; +} + +function toVisual(row, col) { + if (is_flipped) { + return { vrow: board_size - 1 - row, vcol: board_size - 1 - col }; + } + return { vrow: row, vcol: col }; +} + +function fromVisual(vrow, vcol) { + if (is_flipped) { + return { row: board_size - 1 - vrow, col: board_size - 1 - vcol }; + } + return { row: vrow, col: vcol }; +} + +function drawBoard() { + for (let vrow = 0; vrow < board_size; vrow += 1) { + for (let vcol = 0; vcol < board_size; vcol += 1) { + const { row, col } = fromVisual(vrow, vcol); + const is_light = (row + col) % 2 === 0; + const is_marked = marked_cells.has(`${row},${col}`); + const is_move_from = highlighted_move_cells + && highlighted_move_cells.from.row === row + && highlighted_move_cells.from.col === col; + const is_move_to = highlighted_move_cells + && highlighted_move_cells.to.row === row + && highlighted_move_cells.to.col === col; + + if (is_move_to) { + ctx.fillStyle = hexToRgba(move_pieces_color, block_move_to_alpha); + } else if (is_move_from) { + ctx.fillStyle = hexToRgba(move_pieces_color, block_move_from_alpha); + } else if (is_marked) { + const alpha = is_light ? block_select_alpha_light : block_select_alpha_dark; + ctx.fillStyle = hexToRgba(select_block, alpha); + } else { + ctx.fillStyle = is_light ? block_light : block_dark; + } + ctx.fillRect(vcol * block_size, vrow * block_size, block_size, block_size); + } + } + + if (noise_image) { + ctx.save(); + ctx.globalCompositeOperation = 'soft-light'; + ctx.drawImage(noise_image, 0, 0, canvas.width, canvas.height); + ctx.restore(); + } +} + +function drawResultOverlays() { + if (!result_markers) { + return; + } + + const progress = getResultOverlayProgress(); + const winner = toVisual(result_markers.winner.row, result_markers.winner.col); + const loser = toVisual(result_markers.loser.row, result_markers.loser.col); + + if (result_markers.type === 'draw') { + ctx.fillStyle = hexToRgba(result_draw, result_overlay_alpha * progress); + ctx.fillRect(winner.vcol * block_size, winner.vrow * block_size, block_size, block_size); + ctx.fillRect(loser.vcol * block_size, loser.vrow * block_size, block_size, block_size); + return; + } + + ctx.fillStyle = hexToRgba(result_win, result_overlay_alpha * progress); + ctx.fillRect(winner.vcol * block_size, winner.vrow * block_size, block_size, block_size); + + ctx.fillStyle = hexToRgba(result_lose, result_overlay_alpha * progress); + ctx.fillRect(loser.vcol * block_size, loser.vrow * block_size, block_size, block_size); +} + +function drawResultIcons() { + if (!result_markers) { + return; + } + + const progress = getResultOverlayProgress(); + const scale = getResultIconScale(progress); + + const draw_icon = (cell, icon) => { + if (!cell || !icon || !icon.complete) { + return; + } + const { vrow, vcol } = toVisual(cell.row, cell.col); + const max_size = 62; + const icon_ratio = (icon.naturalWidth || 61) / (icon.naturalHeight || 54); + const base_w = icon_ratio >= 1 ? max_size : max_size * icon_ratio; + const base_h = icon_ratio >= 1 ? max_size / icon_ratio : max_size; + const icon_w = base_w * scale; + const icon_h = base_h * scale; + const center_x = vcol * block_size + (block_size / 2); + const center_y = vrow * block_size + (block_size / 2); + const x = center_x - (icon_w / 2); + const y = center_y - (icon_h / 2); + ctx.save(); + ctx.globalAlpha = Math.min(1, progress * 1.15); + ctx.drawImage(icon, x, y, icon_w, icon_h); + ctx.restore(); + }; + + if (result_markers.type === 'draw') { + draw_icon(result_markers.winner, draw_icon_image); + draw_icon(result_markers.loser, draw_icon_image); + return; + } + + draw_icon(result_markers.winner, crown_icon_image); + draw_icon(result_markers.loser, flag_icon_image); +} + +function drawPiece(img, vcol, vrow) { + const max_size = block_size * 0.82; + const ratio = img.naturalWidth / img.naturalHeight; + const draw_w = ratio >= 1 ? max_size : max_size * ratio; + const draw_h = ratio >= 1 ? max_size / ratio : max_size; + const x = vcol * block_size + (block_size - draw_w) / 2; + const y = vrow * block_size + (block_size - draw_h) / 2; + ctx.drawImage(img, x, y, draw_w, draw_h); +} + +function drawPieces() { + const hide_row = piece_animation ? piece_animation.to.row : -1; + const hide_col = piece_animation ? piece_animation.to.col : -1; + + for (let row = 0; row < board_size; row += 1) { + for (let col = 0; col < board_size; col += 1) { + if (row === hide_row && col === hide_col) { + continue; + } + + const piece = board[row][col]; + if (!piece || !piece_images[piece]) { + continue; + } + const { vrow, vcol } = toVisual(row, col); + drawPiece(piece_images[piece], vcol, vrow); + } + } +} + +function drawAnimatedPiece() { + if (!piece_animation || !piece_images[piece_animation.piece_code]) { + return; + } + + const progress = Math.min(1, (performance.now() - piece_animation.start_time) / piece_animation.duration); + const eased = 1 - ((1 - progress) * (1 - progress)); + const row = piece_animation.from.row + ((piece_animation.to.row - piece_animation.from.row) * eased); + const col = piece_animation.from.col + ((piece_animation.to.col - piece_animation.from.col) * eased); + const visual = toVisual(row, col); + drawPiece(piece_images[piece_animation.piece_code], visual.vcol, visual.vrow); +} + +function drawArrow(from_col, from_row, to_col, to_row) { + const from = toVisual(from_row, from_col); + const to = toVisual(to_row, to_col); + const from_x = from.vcol * block_size + block_size / 2; + const from_y = from.vrow * block_size + block_size / 2; + const to_x = to.vcol * block_size + block_size / 2; + const to_y = to.vrow * block_size + block_size / 2; + const angle = Math.atan2(to_y - from_y, to_x - from_x); + const head_size = 28; + const line_end_x = to_x - Math.cos(angle) * (head_size * 0.6); + const line_end_y = to_y - Math.sin(angle) * (head_size * 0.6); + + ctx.save(); + ctx.strokeStyle = arrow_color; + ctx.fillStyle = arrow_color; + ctx.lineWidth = 10; + ctx.lineCap = 'round'; + ctx.globalAlpha = 0.85; + + ctx.beginPath(); + ctx.moveTo(from_x, from_y); + ctx.lineTo(line_end_x, line_end_y); + ctx.stroke(); + + ctx.translate(to_x, to_y); + ctx.rotate(angle); + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.lineTo(-head_size, -head_size / 2.2); + ctx.lineTo(-head_size, head_size / 2.2); + ctx.closePath(); + ctx.fill(); + ctx.restore(); +} + +function isMobileLayout() { + return mobile_breakpoint.matches; +} + +function setMobileMovesOpen(is_open) { + if (!app_layout) { + return; + } + app_layout.classList.toggle('mobile_moves_open', is_open); +} + +function syncFirstMoveButtonIcon() { + if (!first_move_icon) { + return; + } + first_move_icon.src = isMobileLayout() + ? `${asset_root}/icons/pgn_moves.svg` + : `${asset_root}/icons/first.png`; +} + +function setMobileMenuOpen(is_open) { + if (!app_layout) { + return; + } + app_layout.classList.toggle('mobile_menu_open', is_open); + if (more_btn) { + more_btn.classList.toggle('active', is_open); + } +} + +function syncMobileUiState() { + const is_mobile = isMobileLayout(); + syncFirstMoveButtonIcon(); + if (!is_mobile) { + setMobileMovesOpen(false); + setMobileMenuOpen(false); + was_mobile_layout = false; + return; + } + + if (!was_mobile_layout) { + setMobileMovesOpen(true); + setMobileMenuOpen(false); + was_mobile_layout = true; + } +} + +function drawArrows() { + for (const arrow of arrows) { + drawArrow(arrow.from_col, arrow.from_row, arrow.to_col, arrow.to_row); + } +} + +function render() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + drawBoard(); + drawPieces(); + drawAnimatedPiece(); + drawResultOverlays(); + drawResultIcons(); + drawArrows(); +} + +function flipBoard() { + is_flipped = !is_flipped; + app_layout.classList.toggle('board_flipped', is_flipped); + applyPlayerAvatars(); + render(); +} + +function getCellFromEvent(event) { + const rect = canvas.getBoundingClientRect(); + const scale_x = canvas.width / rect.width; + const scale_y = canvas.height / rect.height; + const canvas_x = (event.clientX - rect.left) * scale_x; + const canvas_y = (event.clientY - rect.top) * scale_y; + const vcol = Math.floor(canvas_x / block_size); + const vrow = Math.floor(canvas_y / block_size); + return fromVisual(vrow, vcol); +} + +function setupNavigation() { + document.querySelector('.first_move_btn').addEventListener('click', () => { + if (isMobileLayout()) { + setMobileMovesOpen(!app_layout.classList.contains('mobile_moves_open')); + return; + } + setPly(0, true); + }); + document.querySelector('.prev_move_btn').addEventListener('click', () => setPly(current_ply - 1, true)); + document.querySelector('.next_move_btn').addEventListener('click', () => setPly(current_ply + 1, true)); + document.querySelector('.last_move_btn').addEventListener('click', () => { + const game = getCurrentGame(); + if (game) { + setPly(game.moves.length, true); + } + }); + + document.addEventListener('keydown', (event) => { + if (event.key === 'ArrowLeft') { + setPly(current_ply - 1, true); + } else if (event.key === 'ArrowRight') { + setPly(current_ply + 1, true); + } else if (event.key === 'Home') { + setPly(0, true); + } else if (event.key === 'End') { + const game = getCurrentGame(); + if (game) { + setPly(game.moves.length, true); + } + } + }); +} + +function setupMobileButtons() { + if (!more_btn) { + return; + } + + more_btn.addEventListener('click', () => { + if (!isMobileLayout()) { + return; + } + setMobileMenuOpen(!app_layout.classList.contains('mobile_menu_open')); + }); + + const mobile_menu_buttons = [ + document.getElementById('flip_board_btn'), + history_btn, + share_btn + ]; + + for (const button of mobile_menu_buttons) { + if (!button) { + continue; + } + button.addEventListener('click', () => { + if (isMobileLayout()) { + setMobileMenuOpen(false); + } + }); + } + + if (typeof mobile_breakpoint.addEventListener === 'function') { + mobile_breakpoint.addEventListener('change', syncMobileUiState); + } else { + mobile_breakpoint.addListener(syncMobileUiState); + } + + window.addEventListener('resize', syncMobileUiState); + syncMobileUiState(); +} + +function requestFullscreenIfPossible() { + if (document.fullscreenElement) { + return; + } + + const root = document.documentElement; + const request = root.requestFullscreen + || root.webkitRequestFullscreen + || root.msRequestFullscreen; + + if (typeof request === 'function') { + Promise.resolve(request.call(root)).catch(() => {}); + } +} + +function setupAutoFullscreen() { + requestFullscreenIfPossible(); + + const on_first_interaction = () => { + requestFullscreenIfPossible(); + window.removeEventListener('pointerdown', on_first_interaction); + window.removeEventListener('touchstart', on_first_interaction); + window.removeEventListener('click', on_first_interaction); + }; + + window.addEventListener('pointerdown', on_first_interaction, { once: true }); + window.addEventListener('touchstart', on_first_interaction, { once: true }); + window.addEventListener('click', on_first_interaction, { once: true }); +} + +function setupPanels() { + history_btn.addEventListener('click', () => openPanel(history_panel)); + share_btn.addEventListener('click', () => { + const game = getCurrentGame(); + share_pgn_text.value = game ? game.pgn_text : ''; + autoResizeShareTextarea(); + setShareStatus('', false); + openPanel(share_panel); + }); + submenu_overlay.addEventListener('click', closePanels); + + load_pgn_btn.addEventListener('click', () => { + if (!chess_ready) { + setShareStatus(ui_text.chess_library_not_ready, true); + return; + } + + const pgn_text = sanitizePgnInput(share_pgn_text.value); + if (!pgn_text) { + setShareStatus(ui_text.pgn_empty, true); + return; + } + + const parsed_game = parsePgnToGameState(pgn_text); + if (!parsed_game) { + setShareStatus(ui_text.pgn_invalid, true); + return; + } + + setShareStatus(ui_text.pgn_loaded, false); + share_pgn_text.value = pgn_text; + autoResizeShareTextarea(); + parsed_games.unshift(parsed_game); + buildHistoryList(); + closePanels(); + loadGame(0); + }); + + share_pgn_text.addEventListener('input', autoResizeShareTextarea); + window.addEventListener('resize', autoResizeShareTextarea); + + copy_pgn_btn.addEventListener('click', async () => { + try { + await copyTextToClipboard(share_pgn_text.value); + setShareStatus(ui_text.pgn_copied, false); + } catch (error) { + setShareStatus(ui_text.copy_failed, true); + } + }); +} + +function initializeGames() { + parsed_games = []; + current_game_index = 0; + current_ply = 0; + highlighted_move_cells = null; + piece_animation = null; + clearResultVisuals(); + buildHistoryList(); + board = boardFromFen(start_fen); + moves_scroll.innerHTML = ''; + render(); + updateTimers(); + app_layout.classList.remove('board_flipped'); + applyPlayerAvatars(); +} + +document.getElementById('flip_board_btn').addEventListener('click', flipBoard); +moves_scroll.addEventListener('scroll', updateScrollShadow); +new MutationObserver(updateScrollShadow).observe(moves_scroll, { childList: true, subtree: true }); +window.addEventListener('resize', updateScrollShadow); + +canvas.addEventListener('contextmenu', (event) => event.preventDefault()); + +canvas.addEventListener('mousedown', (event) => { + if (event.button !== 2) { + return; + } + right_drag_start = getCellFromEvent(event); +}); + +canvas.addEventListener('mouseup', (event) => { + if (event.button !== 2 || !right_drag_start) { + return; + } + + const { row, col } = getCellFromEvent(event); + + if (row === right_drag_start.row && col === right_drag_start.col) { + const key = `${row},${col}`; + if (marked_cells.has(key)) { + marked_cells.delete(key); + } else { + marked_cells.add(key); + } + } else { + const existing = arrows.findIndex((arrow) => ( + arrow.from_row === right_drag_start.row + && arrow.from_col === right_drag_start.col + && arrow.to_row === row + && arrow.to_col === col + )); + + if (existing >= 0) { + arrows.splice(existing, 1); + } else { + arrows.push({ + from_row: right_drag_start.row, + from_col: right_drag_start.col, + to_row: row, + to_col: col + }); + } + } + + right_drag_start = null; + render(); +}); + +canvas.addEventListener('click', () => { + marked_cells.clear(); + arrows = []; + render(); +}); + +loadNoise(() => { + loadPieceImages(async () => { + chess_ready = await loadChessLibrary(); + setupNavigation(); + setupMobileButtons(); + setupAutoFullscreen(); + setupPanels(); + initializeGames(); + updateScrollShadow(); + }); +}); diff --git a/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/style.css b/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/style.css new file mode 100644 index 0000000..6a5ef05 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/NoChess/raw_assets/style.css @@ -0,0 +1,967 @@ +@font-face { + font-family: 'mr_GranstanderCleanG'; + src: url('https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/NoChess/mr_granstandercleang.otf') format('opentype'); + font-weight: 400; + font-style: normal; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background-color: #1A1224; + background-image: url('bg.png'); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + min-height: 100vh; + font-family: 'mr_GranstanderCleanG', Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; +} + +.web_chess { + display: flex; + align-items: stretch; + gap: 20px; +} + +@media (min-width: 573px) and (max-width: 1562px) { + body { + overflow: hidden; + } + + .web_chess { + width: 1562px; + position: fixed; + left: 50%; + top: 50%; + margin: 0; + --desktop-scale: min(calc(100vw / 1562px), calc(100vh / 856px), 1); + transform-origin: center center; + transform: translate(-50%, -50%) scale(var(--desktop-scale)); + } +} + +.board-wrapper { + position: relative; + display: block; +} + +#chessBoard { + width: 856px; + height: 856px; + display: block; +} + +.pgn_moves_board { + width: 333px; + height: 856px; + position: relative; + overflow: hidden; +} + +.move_board_bg { + position: absolute; + inset: 0; + background: #1E1E1E; + border-radius: 15px; +} + +.move_board_bg::after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 80px; + height: 48px; + background: linear-gradient(180deg, #00000000 0%, #00000040 100%); + opacity: 0; + transition: opacity 0.2s; + pointer-events: none; +} + +.pgn_moves_board.has_scroll .move_board_bg::after { + opacity: 1; +} + +.moves_list { + position: absolute; + top: 20px; + bottom: 80px; + left: 0; + right: 0; + padding: 0 20px; +} + +.moves_list_mobile { + height: 100%; + overflow-y: auto; + display: flex; + flex-direction: column; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.moves_list_mobile::-webkit-scrollbar { + width: 0; + height: 0; + display: none; +} + +.move_row { + display: grid; + grid-template-columns: 40px 1fr 1fr; + align-items: center; + min-height: 52px; + font-size: 20px; + color: #FFFFFF; +} + +.move_number { + opacity: 0.6; +} + +.move_white, +.move_black { + padding: 10px; + display: flex; + align-items: center; + border-radius: 8px; +} + +.current_move { + background: #B7B7B71A; + min-height: 52px; +} + +.move_white, +.move_black { + cursor: pointer; +} + +.move_white:empty, +.move_black:empty { + cursor: default; +} + +.moves_control_buttons { + position: absolute; + bottom: 10px; + left: 0; + width: 100%; + display: flex; + justify-content: space-around; +} + +.move_btn { + width: 75px; + height: 37px; + background: #2D2D2D; + border-radius: 8px; + box-shadow: 0px 4px 3.7px -1px #00000040; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background 0.2s, transform 0.2s; +} + +.move_btn img { + width: 20px; + height: 20px; + object-fit: contain; +} + +.move_btn:hover { + background: #3A3A3A; + transform: translateY(-2px); +} + +.move_btn:active { + background: #252525; + transform: translateY(0); +} + +.main_board { + width: 333px; + height: 856px; + position: relative; +} + +.main_board_bg { + position: absolute; + inset: 0; + background: #00000054; + border-radius: 15px; +} + +.player_block { + position: absolute; + width: 100%; + text-align: center; +} + +.player_black { + top: 20px; +} + +.player_white { + bottom: 20px; +} + +.web_chess.board_flipped .player_black { + top: auto; + bottom: 20px; + display: flex; + flex-direction: column; + align-items: center; +} + +.web_chess.board_flipped .player_white { + top: 20px; + bottom: auto; + display: flex; + flex-direction: column; + align-items: center; +} + +.web_chess.board_flipped .player_black .player_avatar { + order: 1; +} + +.web_chess.board_flipped .player_black .player_name_black { + order: 2; + margin-top: 10px; + margin-bottom: 0; +} + +.web_chess.board_flipped .player_white .player_name_white { + order: 1; + margin-top: 0; + margin-bottom: 10px; +} + +.web_chess.board_flipped .player_white .player_avatar { + order: 2; +} + +.player_avatar { + position: relative; + overflow: hidden; + width: 189px; + height: 189px; + aspect-ratio: 1 / 1; + margin: 0 auto; + background-size: cover; + background-repeat: no-repeat; + background-position: center; + border: 5px solid #FFFFFF; + border-radius: 50%; + clip-path: circle(50% at 50% 50%); +} + +.avatar_black { + background-image: url('https://i.pinimg.com/736x/6e/0a/0c/6e0a0cf688b30ba9de81b81bb32e49f9.jpg'); +} + +.avatar_white { + background-image: url('https://i.pinimg.com/736x/6e/0a/0c/6e0a0cf688b30ba9de81b81bb32e49f9.jpg'); +} + +.player_name_black { + margin-bottom: 10px; + font-size: 20px; + color: #FFFFFF; +} + +.player_name_white { + margin-top: 10px; + font-size: 20px; + color: #FFFFFF; +} + +.divider_line { + position: absolute; + left: 50%; + transform: translateX(-50%); +} + +.top_line { + top: 260px; +} + +.bottom_line { + bottom: 260px; +} + +.divider_line svg { + display: block; +} + +.timer { + position: absolute; + left: 50%; + transform: translateX(-50%); + width: 139px; + height: 41px; + display: flex; + align-items: center; + justify-content: center; + background: #00000054; + border-radius: 15px; + color: #FFFFFF; + font-size: 20px; +} + +.timer_black { + top: 280px; +} + +.timer_white { + bottom: 280px; +} + +.web_chess.board_flipped .timer_black { + top: auto; + bottom: 280px; +} + +.web_chess.board_flipped .timer_white { + top: 280px; + bottom: auto; +} + +.board_menu { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: flex; + gap: 15px; +} + +.menu_btn { + width: 50px; + height: 42px; + background: #00000054; + border-radius: 15px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background 0.2s, transform 0.2s; +} + +.menu_btn:hover { + background: #FFFFFF1A; + transform: translateY(-2px); +} + +.menu_btn:active { + background: #00000080; + transform: translateY(0); +} + +.menu_btn img { + display: block; + width: 28px; + height: 24px; + object-fit: contain; +} + +.menu_more_btn { + display: none; +} + +.more_dots { + position: relative; + width: 22px; + height: 22px; +} + +.more_dot { + position: absolute; + left: 50%; + top: 50%; + width: 8px; + height: 8px; + border-radius: 50%; + background: #FFFFFF; + transform: translate(-50%, -50%); +} + +.more_dot_top { + transform: translate(-50%, calc(-50% - var(--dot-gap, 10px))); + transition: transform 0.32s ease; +} + +.more_dot_mid { + transition: transform 0.32s ease; +} + +.more_dot_bottom { + transform: translate(-50%, calc(-50% + var(--dot-gap, 10px))); + transition: transform 0.32s ease; +} + +.menu_more_btn.active .more_dot_top { + transform: translate(calc(-50% - var(--dot-gap, 10px)), -50%); +} + +.menu_more_btn.active .more_dot_bottom { + transform: translate(calc(-50% + var(--dot-gap, 10px)), -50%); +} + +.web_chess.mobile_menu_open .menu_more_btn .more_dot_mid { + transform: translate(-50%, -50%) scale(1.05); +} + +.submenu_overlay { + position: fixed; + inset: 0; + background: #00000080; + opacity: 0; + pointer-events: none; + transition: opacity 0.2s; +} + +.submenu_overlay.open { + opacity: 1; + pointer-events: auto; +} + +.submenu_panel { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.98); + width: min(540px, 92vw); + max-height: 80vh; + background: #1e1e1e; + border-radius: 15px; + border: 1px solid #FFFFFF14; + padding: 18px; + color: #FFFFFF; + opacity: 0; + pointer-events: none; + transition: opacity 0.2s, transform 0.2s; + z-index: 20; +} + +.submenu_panel.open { + opacity: 1; + pointer-events: auto; + transform: translate(-50%, -50%) scale(1); +} + +.submenu_title { + font-size: 20px; + margin-bottom: 12px; +} + +.history_games { + display: flex; + flex-direction: column; + gap: 10px; +} + +.history_games.empty { + min-height: 120px; + justify-content: center; + align-items: center; +} + +.history_empty { + font-size: 24px; + font-weight: 700; + text-align: center; +} + +.history_game_btn { + width: 100%; + background: #2d2d2d; + color: #FFFFFF; + border: 1px solid #FFFFFF14; + border-radius: 10px; + padding: 10px 12px; + text-align: left; + cursor: pointer; +} + +.history_game_btn:hover { + background: #383838; +} + +#share_pgn_text { + width: 100%; + min-height: 170px; + max-height: 55vh; + border-radius: 10px; + border: 1px solid #FFFFFF1A; + background: #121212; + color: #FFFFFF; + padding: 10px; + resize: none; + overflow: auto; +} + +.share_actions { + margin-top: 12px; + display: flex; + gap: 10px; + justify-content: center; +} + +#load_pgn_btn, +#copy_pgn_btn { + height: 38px; + padding: 0 14px; + border-radius: 10px; + border: 0; + background: #2d2d2d; + color: #FFFFFF; + cursor: pointer; + min-width: 130px; +} + +#load_pgn_btn { + background: #2d2d2d; +} + +#load_pgn_btn:hover, +#copy_pgn_btn:hover { + filter: brightness(1.08); +} + +#share_status { + margin-top: 10px; + min-height: 20px; + color: #d9d9d9; + font-size: 14px; + text-align: center; + opacity: 0; + transform: translateY(4px); + transition: opacity 0.2s ease, transform 0.2s ease; +} + +#share_status.show { + opacity: 1; + transform: translateY(0); +} + +#share_status.error { + color: #ff8f8f; +} + +@media (max-width: 572px) { + body { + justify-content: center; + align-items: flex-start; + overflow: hidden; + } + + body::after { + content: ''; + position: fixed; + left: 0; + right: 0; + bottom: 0; + height: clamp(72px, 17.7vw, 101px); + background: linear-gradient(180deg, #00000000 0%, #0000008C 100%); + pointer-events: none; + z-index: 5; + } + + .web_chess { + --mobile-layout-width: 750px; + --mobile-board-size: 750px; + --mobile-board-top: 233px; + --mobile-player-gap: clamp(24px, 5vw, 32px); + --mobile-panel-height: clamp(64px, 17.13vw, 98px); + --mobile-timer-height: clamp(30px, 7.17vw, 41px); + --mobile-pgn-space: clamp(360px, 86vw, 492px); + --mobile-layout-height: calc(var(--mobile-board-top) + var(--mobile-board-size) + var(--mobile-pgn-space)); + --mobile-fit-scale: min(1, calc((100vw - 2px) / var(--mobile-layout-width)), calc((100dvh - 2px) / var(--mobile-layout-height))); + width: var(--mobile-layout-width); + height: var(--mobile-layout-height); + min-height: 0; + margin: 0; + position: fixed; + left: 50%; + top: 0; + display: block; + padding: 0; + overflow: hidden; + transform-origin: top center; + transform: translateX(-50%) scale(var(--mobile-fit-scale)); + } + + .web_chess::after { + content: none; + } + + .board-wrapper { + position: absolute; + left: 50%; + top: var(--mobile-board-top); + transform: translateX(-50%); + width: var(--mobile-board-size); + z-index: 2; + } + + #chessBoard { + width: 100%; + max-width: none; + height: auto; + } + + .main_board { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + z-index: 3; + pointer-events: none; + } + + .main_board_bg, + .divider_line { + display: none; + } + + .board_menu { + top: 4.2vw; + right: 3.1vw; + left: auto; + transform: none; + gap: 2.2vw; + align-items: center; + pointer-events: auto; + } + + .board_menu .menu_btn:not(.menu_more_btn) { + opacity: 1; + transform: translateX(0); + transition: transform 0.28s ease, opacity 0.28s ease, background 0.2s; + } + + .web_chess:not(.mobile_menu_open) .board_menu .menu_btn:not(.menu_more_btn) { + opacity: 0; + transform: translateX(11vw) scale(0.86); + pointer-events: none; + } + + .web_chess.mobile_menu_open .board_menu .menu_btn:not(.menu_more_btn) { + opacity: 1; + transform: translateX(0); + pointer-events: auto; + } + + .menu_btn { + width: 50px; + height: 42px; + border-radius: 15px; + } + + .menu_btn img { + width: 28px; + height: 24px; + } + + .menu_more_btn { + --dot-gap: 14px; + display: flex; + width: 80px; + height: 80px; + border-radius: 50%; + } + + .menu_more_btn .more_dots { + width: 34px; + height: 34px; + min-width: 34px; + min-height: 34px; + } + + .menu_more_btn .more_dot { + width: 9px; + height: 9px; + } + + .player_block { + width: calc(100% - 4.2vw); + left: 2.1vw; + height: var(--mobile-panel-height); + min-height: 64px; + border-radius: clamp(10px, 2.6vw, 15px); + display: flex; + align-items: center; + gap: clamp(10px, 2.8vw, 18px); + padding: 0 calc(clamp(96px, 24.3vw, 139px) + max(6vw, 22px)) 0 max(2.4vw, 12px); + text-align: left; + background: #00000054; + pointer-events: auto; + } + + .player_black { + top: calc(var(--mobile-board-top) - var(--mobile-player-gap) - var(--mobile-panel-height)); + } + + .player_black .player_avatar { + order: 1; + } + + .player_black .player_name_black { + order: 2; + text-align: left; + } + + .player_white { + top: calc(var(--mobile-board-top) + var(--mobile-board-size) + var(--mobile-player-gap)); + bottom: auto; + } + + .player_avatar { + width: min(13.99vw, 80px); + height: min(13.99vw, 80px); + margin: 0; + border-width: 2px; + flex-shrink: 0; + } + + .player_name_black, + .player_name_white { + margin: 0; + min-width: 0; + max-width: min(39vw, 220px); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: clamp(14px, 3.8vw, 20px); + color: #FFFFFF; + } + + .timer { + left: auto; + right: 6vw; + transform: none; + width: clamp(96px, 24.3vw, 139px); + height: var(--mobile-timer-height); + border-radius: 15px; + font-size: clamp(12px, 3.8vw, 20px); + background: #00000066; + color: #FFFFFF; + text-shadow: 0 1px 1px #0000004D; + z-index: 7; + pointer-events: auto; + } + + .timer_black { + top: calc(var(--mobile-board-top) - var(--mobile-player-gap) - var(--mobile-panel-height) + (var(--mobile-panel-height) - var(--mobile-timer-height)) / 2); + } + + .timer_white { + top: calc(var(--mobile-board-top) + var(--mobile-board-size) + var(--mobile-player-gap) + (var(--mobile-panel-height) - var(--mobile-timer-height)) / 2); + bottom: auto; + } + + .pgn_moves_board { + position: absolute; + top: calc(var(--mobile-board-top) + var(--mobile-board-size) + clamp(24px, 5vw, 32px) + var(--mobile-panel-height) + clamp(18px, 4vw, 28px)); + left: 50%; + width: calc(100% - clamp(10px, 2.7vw, 20px)); + height: clamp(180px, 56vw, 320px); + z-index: 3; + opacity: 1; + transform: translateX(-50%); + transition: opacity 0.24s ease; + } + + .move_board_bg { + display: none; + } + + .moves_list { + top: 0; + bottom: clamp(66px, 14vw, 84px); + left: 50%; + right: auto; + width: clamp(260px, 44vw, 360px); + transform: translateX(-50%); + padding: 0; + transition: opacity 0.24s ease; + } + + .moves_list::after { + content: none; + } + + .moves_list_mobile { + height: 140%; + gap: 0.6vw; + pointer-events: auto; + padding-right: 0; + align-items: center; + transform: translateY(clamp(10px, 2.2vh, 18px)); + scrollbar-width: none; + -ms-overflow-style: none; + } + + .moves_list_mobile::-webkit-scrollbar { + width: 0; + height: 0; + display: none; + } + + .move_row { + min-height: 9vw; + font-size: clamp(18px, 4.2vw, 24px); + width: max-content; + margin: 0 auto; + grid-template-columns: auto auto auto; + column-gap: clamp(10px, 2.8vw, 16px); + } + + .move_number { + opacity: 1; + } + + .move_white, + .move_black { + padding: 0; + min-height: 0; + width: max-content; + max-width: 100%; + justify-self: start; + justify-content: flex-start; + } + + .current_move { + min-height: 0; + width: max-content; + padding: clamp(4px, 1vw, 8px) clamp(6px, 1.4vw, 10px); + } + + .moves_control_buttons { + left: 2.6vw; + right: 2.6vw; + bottom: 0; + width: auto; + justify-content: flex-end; + gap: 1.5vw; + pointer-events: auto; + } + + .move_btn { + width: 80px; + height: 80px; + border-radius: 50%; + background: #00000054; + box-shadow: none; + } + + .menu_btn, + .move_btn { + transition: background 0.2s, transform 0.2s; + -webkit-tap-highlight-color: #00000000; + } + + .menu_btn:hover { + background: #FFFFFF1A; + transform: translateY(-2px); + } + + .move_btn:hover { + background-color: #00000054; + transform: none; + } + + .menu_btn:active { + background: #00000080; + transform: translateY(0); + } + + .menu_more_btn:hover { + background-color: #00000054; + transform: none; + } + + .menu_more_btn:active { + background-color: #00000054; + transform: scale(1.06); + } + + .move_btn:active { + background-color: #00000054; + transform: scale(1.06); + } + + .move_btn img { + width: 46%; + height: 46%; + } + + .first_move_btn { + margin-right: auto; + } + + .last_move_btn { + display: none; + } + + .web_chess:not(.mobile_moves_open) .moves_list { + opacity: 0; + pointer-events: none; + } + + .web_chess.board_flipped .player_black { + top: calc(var(--mobile-board-top) + var(--mobile-board-size) + var(--mobile-player-gap)); + bottom: auto; + flex-direction: row; + align-items: center; + justify-content: flex-start; + text-align: left; + } + + .web_chess.board_flipped .player_white { + top: calc(var(--mobile-board-top) - var(--mobile-player-gap) - var(--mobile-panel-height)); + bottom: auto; + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + text-align: left; + } + + .web_chess.board_flipped .player_white .player_avatar { + order: 1; + } + + .web_chess.board_flipped .player_white .player_name_white { + order: 2; + margin: 0; + align-self: center; + line-height: 1.2; + } + + .web_chess.board_flipped .player_black .player_name_black { + margin: 0; + align-self: center; + line-height: 1.2; + } + + .web_chess.board_flipped .timer_black { + top: calc(var(--mobile-board-top) + var(--mobile-board-size) + var(--mobile-player-gap) + (var(--mobile-panel-height) - var(--mobile-timer-height)) / 2); + bottom: auto; + } + + .web_chess.board_flipped .timer_white { + top: calc(var(--mobile-board-top) - var(--mobile-player-gap) - var(--mobile-panel-height) + (var(--mobile-panel-height) - var(--mobile-timer-height)) / 2); + bottom: auto; + } +} diff --git a/SunnexGB/Heroku-Modules/Assets/XOCheat/Opening_book.txt b/SunnexGB/Heroku-Modules/Assets/XOCheat/Opening_book.txt new file mode 100644 index 0000000..45a1674 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Assets/XOCheat/Opening_book.txt @@ -0,0 +1,1782 @@ +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.7 +2.4 +3.8 +4.5 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.8 +3.1 +4.3 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.2 +3.8 +4.1 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.1 +3.6 +4.2 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.2 +3.3 +4.6 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.5 +2.7 +3.4 +4.6 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.0 +3.2 +4.4 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.3 +3.2 +4.0 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.7 +3.8 +4.5 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.1 +2.7 +3.0 +4.3 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.8 +3.6 +4.7 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.0 +3.3 +4.7 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.0 +3.6 +4.7 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.3 +3.2 +4.4 +5.1 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.4 +3.6 +4.1 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.5 +3.8 +4.6 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.5 +2.3 +3.2 +4.1 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.2 +3.8 +4.1 +5.4 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.2 +3.0 +4.1 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.3 +3.0 +4.2 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.1 +2.0 +3.4 +4.3 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.4 +3.1 +4.7 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.4 +3.0 +4.7 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.0 +3.2 +4.7 +5.4 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.1 +3.6 +4.0 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.4 +3.1 +4.3 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.3 +3.4 +4.0 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.3 +3.0 +4.6 +5.1 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.6 +3.5 +4.0 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.2 +3.1 +4.6 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.5 +2.3 +3.8 +4.0 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.5 +3.1 +4.2 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.7 +2.1 +3.8 +4.0 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.7 +2.3 +3.6 +4.4 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.8 +3.3 +4.5 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.1 +2.2 +3.4 +4.6 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.1 +2.6 +3.0 +4.4 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.8 +3.2 +4.3 +5.1 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.5 +3.1 +4.6 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.7 +3.2 +4.0 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.8 +3.2 +4.1 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.7 +2.1 +3.6 +4.5 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.1 +2.5 +3.2 +4.8 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.1 +3.5 +4.4 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.3 +2.5 +3.0 +4.1 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.5 +3.2 +4.3 +5.1 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.5 +3.2 +4.1 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.2 +3.5 +4.6 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.6 +3.0 +4.1 +5.4 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.3 +2.2 +3.0 +4.1 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.3 +2.8 +3.6 +4.7 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.0 +3.5 +4.7 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.3 +3.1 +4.4 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.1 +2.8 +3.2 +4.4 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.1 +3.5 +4.8 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.7 +3.4 +4.3 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.1 +3.6 +4.7 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.7 +3.6 +4.0 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.8 +3.6 +4.1 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.0 +3.6 +4.3 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.3 +3.7 +4.2 +5.1 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.6 +3.5 +4.4 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.5 +3.1 +4.6 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.4 +3.3 +4.2 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.1 +3.4 +4.3 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.5 +3.0 +4.4 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.4 +3.5 +4.0 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.4 +3.2 +4.0 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.8 +3.6 +4.0 +5.4 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.1 +3.2 +4.3 +5.6 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.3 +3.4 +4.1 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.1 +3.6 +4.7 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.1 +3.4 +4.6 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.5 +3.0 +4.2 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.0 +3.5 +4.6 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.8 +3.5 +4.6 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.8 +3.6 +4.5 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.5 +2.7 +3.4 +4.0 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.1 +3.3 +4.8 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.7 +3.4 +4.5 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.1 +2.2 +3.4 +4.0 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.5 +3.4 +4.6 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.3 +3.1 +4.6 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.3 +2.1 +3.4 +4.2 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.8 +3.6 +4.5 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.0 +3.6 +4.5 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.3 +3.8 +4.2 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.6 +3.1 +4.4 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.6 +3.1 +4.2 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.5 +2.8 +3.4 +4.7 +5.3 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.7 +3.0 +4.5 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.2 +2.0 +3.8 +4.6 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.7 +3.2 +4.0 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.7 +3.5 +4.0 +5.2 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.3 +3.1 +4.2 +5.7 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.3 +3.8 +4.1 +5.0 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.3 +3.4 +4.7 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.5 +3.0 +4.6 +5.8 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.0 +3.3 +4.1 +5.5 + +#3x3 +[Author "Zero Engine"] +[Site "t.me/H_SunMods"] +[X "Zero Bot"] +[O "Zero Opp"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.4 +2.0 +3.7 +4.5 +5.1 + +#3x3 +[Author "@SunnexGB"] +[Site "t.me/H_SunMods"] +[X "Sunnex"] +[O "Tester"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.4 +3.8 + +#3x3 +[Author "@SunnexGB"] +[Site "t.me/H_SunMods"] +[X "Sunnex"] +[O "Tester"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.0 +2.4 +3.8 + +#5x5 +[Author "@SunnexGB"] +[Site "t.me/H_SunMods"] +[X "Sunnex"] +[O "PRO"] +[Result "1/2-1/2"] +[Variant "down O"] +[Opening] +1.12 +2.8 +3.19 +4.17 + + +#5x5 +[Author "@SunnexGB"] +[Site "t.me/H_SunMods"] +[X "Sunnex"] +[O "PRO"] +[Result "1-0"] +[Variant "First X"] +[Opening] +1.12 +2.8 +3.19 +4.13 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.12 +2.2 +3.8 +4.16 +5.6 +6.7 +7.18 +8.24 +9.0 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.1 +2.12 +3.7 +4.13 +5.11 +6.6 +7.3 +8.15 +9.2 +10.0 +11.4 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.12 +3.18 +4.16 +5.8 +6.7 +7.13 +8.23 +9.3 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.7 +2.17 +3.1 +4.11 +5.13 +6.19 +7.3 +8.16 +9.18 +10.8 +11.2 +12.4 +13.0 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.8 +2.1 +3.13 +4.3 +5.2 +6.7 +7.18 +8.23 +9.16 +10.17 +11.12 +12.11 +13.6 +14.24 +15.0 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.12 +2.8 +3.19 +4.17 +5.13 +6.7 +7.11 +8.14 +9.10 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.12 +2.8 +3.19 +4.17 +5.13 +6.11 +7.7 +8.1 +9.6 +10.5 +11.23 +12.15 +13.10 +14.9 +15.14 +16.3 +17.2 +18.22 +19.21 +20.0 +21.24 +22.4 +23.20 +24.16 +25.18 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1/2-1/2"] +[Variant "zero index"] +[Opening] +1.12 +2.8 +3.19 +4.17 +5.13 +6.11 +7.7 +8.1 +9.6 +10.5 +11.23 +12.18 +13.16 +14.21 +15.15 +16.9 +17.3 +18.2 +19.10 +20.14 +21.22 +22.0 +23.24 +24.4 +25.20 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1/2-1/2"] +[Variant "zero index"] +[Opening] +1.6 +2.12 +3.17 +4.11 +5.13 +6.18 +7.8 +8.7 +9.19 +10.9 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1-0"] +[Variant "zero index"] +[Opening] +1.6 +2.12 +3.17 +4.11 +5.13 +6.18 +7.8 +8.7 +9.19 +10.5 +11.9 +12.14 +13.21 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "1/2-1/2"] +[Variant "zero index"] +[Opening] +1.23 +2.12 +3.21 +4.22 +5.17 +6.16 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "0-1"] +[Variant "zero index"] +[Opening] +1.0 +2.12 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "0-1"] +[Variant "zero index"] +[Opening] +1.4 +2.12 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "0-1"] +[Variant "zero index"] +[Opening] +1.20 +2.12 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "0-1"] +[Variant "zero index"] +[Opening] +1.24 +2.12 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "0-1"] +[Variant "zero index"] +[Opening] +1.2 +2.12 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "0-1"] +[Variant "zero index"] +[Opening] +1.22 +2.12 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "0-1"] +[Variant "zero index"] +[Opening] +1.10 +2.12 + +#5x5 +[Author "Zero"] +[Site "t.me/H_SunMods"] +[X "Player X"] +[O "Player O"] +[Result "0-1"] +[Variant "zero index"] +[Opening] +1.14 +2.12 \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/Assets/tmp_assets/chalkboardse.ttf b/SunnexGB/Heroku-Modules/Assets/tmp_assets/chalkboardse.ttf new file mode 100644 index 0000000..7e179af Binary files /dev/null and b/SunnexGB/Heroku-Modules/Assets/tmp_assets/chalkboardse.ttf differ diff --git a/SunnexGB/Heroku-Modules/DevStats.py b/SunnexGB/Heroku-Modules/DevStats.py new file mode 100644 index 0000000..620af1b --- /dev/null +++ b/SunnexGB/Heroku-Modules/DevStats.py @@ -0,0 +1,323 @@ +# meta developer: @H_SunMods +# meta banner: https://r2.fakecrime.bio/uploads/7c43eb05-4387-48f8-bbb2-20c5fad2f85f.jpg +# current ver +__version__ = (1, 0, 1) + +from .. import loader, utils +from herokutl.types import Message +from ..types import InlineCall +import asyncio +import aiohttp +import math + +FHETA_URL = "https://api.fixyres.com/grates" +VECTOR_URL = "https://vector-three-sooty.vercel.app/api/devstats" +VECTOR_TOPMOD_URL = "https://vector-three-sooty.vercel.app/api/usertopmod?users=" + +@loader.tds +class DevStats(loader.Module): + """developers stats module""" + + strings = { + "name": "DevStats", + "loading": "Loading...", + "no_data": "Failed to fetch data. Try again later.", + "dev_header": "Most popular developers:\n\n", + "devtop_not_found": "Your not found.", + "topmod_not_found": "No modules found.", + "no_usernames": "No usernames configured. Set them in .fcfg DevStats usernames @username", + "select_page": "Select page:", + "btn_prev": "◄", + "btn_next": "►", + "btn_back": "Back", + "btn_close": "Close", + "like_singl": "like", + "just_likes": "likes", + "just_dislikes": "dislikes", + "devtop_desc": "Your rank in developer leaderboard", + "topmod_desc": "Your most popular module and its rank", + } + + strings_ru = { + "_cls_doc": "Модуль статистики разработчиков", + "loading": "Загрузка...", + "no_data": "Не удалось получить данные. Попробуйте позже.", + "dev_header": "Самые популярные разработчики:\n\n", + "devtop_not_found": "Вы не были найдены.", + "topmod_not_found": "Модули не найдены.", + "no_usernames": "Юзернеймы не настроены. Укажите в .fcfg DevStats usernames @username", + "select_page": "Выберите страницу:", + "btn_prev": "◄", + "btn_next": "►", + "btn_back": "Назад", + "btn_close": "Закрыть", + "like_singl": "Лайк", + "just_likes": "Лайков", + "just_dislikes": "Дизлайков", + "devtop_desc": "Ваше место в рейтинге разработчиков", + "topmod_desc": "Ваш самый популярный модуль и его место в топе", + } + + def __init__(self): + self.config = loader.ModuleConfig( + loader.ConfigValue( + "provider", + "multi", + "Data source: multi (fheta + vector combined) | fheta | vector", + validator=loader.validators.Choice(["multi", "fheta", "vector"]), + ), + loader.ConfigValue( + "display_mode", + "likes", + "Display mode: likes | both", + validator=loader.validators.Choice(["likes", "both"]), + ), + loader.ConfigValue( + "usernames", + [], + "Your usernames with @ for placeholders", + validator=loader.validators.Series(loader.validators.String()), + ), + loader.ConfigValue( + "excluded_authors", + ["unknown"], + "Authors to exclude from leaderboard", + validator=loader.validators.Series(loader.validators.String()), + ), + loader.ConfigValue( + "rank1_emoji", + "👑", + "Emoji for rank №1", + ), + loader.ConfigValue( + "rank2_emoji", + "🌟", + "Emoji for rank №2", + ), + loader.ConfigValue( + "rank3_emoji", + "", + "Emoji for rank №3", + ), + ) + + async def client_ready(self, client, db): + utils.register_placeholder("devtop", self.placeholder_devtop, self.strings("devtop_desc")) + utils.register_placeholder("topmod", self.placeholder_topmod, self.strings("topmod_desc")) + + async def request_api(self, url: str, token: str = None): + headers = {"Authorization": token} if token else {} + try: + async with aiohttp.ClientSession() as session: + async with session.get( + url, + headers=headers, + timeout=aiohttp.ClientTimeout(total=15), + ) as resp: + return await resp.json() if resp.status == 200 else None + except Exception: + return None + + def aggregate_devs(self, data: dict) -> list: + excluded = {u.lower() for u in self.config["excluded_authors"]} + devs = {} + items = data.items() if isinstance(data, dict) else ( + (e.get("url", i), e) for i, e in enumerate(data) + ) + for _, info in items: + author = info.get("author", "").lstrip("@") + if not author or author.lower() in excluded: + continue + if author not in devs: + devs[author] = {"likes": 0, "dislikes": 0} + devs[author]["likes"] += int(info.get("likes", 0) or 0) + devs[author]["dislikes"] += int(info.get("dislikes", 0) or 0) + return sorted(devs.items(), key=lambda x: x[1]["likes"], reverse=True) + + def aggregate_vector(self, data: list) -> list: + excluded = {u.lower() for u in self.config["excluded_authors"]} + devs = {} + for entry in data: + author = entry.get("author", "").lstrip("@") + if not author or author.lower() in excluded: + continue + if author not in devs: + devs[author] = {"likes": 0, "dislikes": 0} + devs[author]["likes"] += int(entry.get("likes", 0) or 0) + devs[author]["dislikes"] += int(entry.get("dislikes", 0) or 0) + return sorted(devs.items(), key=lambda x: x[1]["likes"], reverse=True) + + def merge_sources(self, fheta_devs: list, vector_devs: list) -> list: + merged = {} + for username, stats in fheta_devs: + merged[username.lower()] = {"name": username, "likes": stats["likes"], "dislikes": stats["dislikes"]} + for username, stats in vector_devs: + key = username.lower() + if key in merged: + merged[key]["likes"] += stats["likes"] + merged[key]["dislikes"] += stats["dislikes"] + else: + merged[key] = {"name": username, "likes": stats["likes"], "dislikes": stats["dislikes"]} + result = [(v["name"], {"likes": v["likes"], "dislikes": v["dislikes"]}) for v in merged.values()] + return sorted(result, key=lambda x: x[1]["likes"], reverse=True) + + async def fetch_sorted_devs(self) -> list: + provider = self.config["provider"] + if provider == "fheta": + data = await self.request_api(FHETA_URL) + return self.aggregate_devs(data) if data else [] + if provider == "vector": + data = await self.request_api(VECTOR_URL) + return self.aggregate_vector(data) if isinstance(data, list) else [] + # multi + fheta_data, vector_data = await asyncio.gather( + self.request_api(FHETA_URL), + self.request_api(VECTOR_URL), + ) + fheta_devs = self.aggregate_devs(fheta_data) if fheta_data else [] + vector_devs = self.aggregate_vector(vector_data) if isinstance(vector_data, list) else [] + if not fheta_devs and not vector_devs: + return [] + return self.merge_sources(fheta_devs, vector_devs) + + def extract_module_name(self, key: str) -> str: + return key.strip().split("/")[-1].removesuffix(".py") + + def format_stats(self, likes: int, dislikes: int) -> str: + mode = self.config["display_mode"] + lw = self.strings["like_singl"] if likes == 1 else self.strings["just_likes"] + if mode == "both": + return f"({likes} {lw} | {dislikes} {self.strings['just_dislikes']})" + return f"({likes} {lw})" + + def dev_entry(self, rank: int, username: str, likes: int, dislikes: int) -> str: + stats = self.format_stats(likes, dislikes) + emoji = self.config[f"rank{rank}_emoji"] if rank <= 3 else "" + safe = utils.escape_html(username) + if emoji: + return f"{rank}. @{safe} {stats} | {emoji}\n" + return f"{rank}. @{safe} {stats}\n" + + def kb_dev_page(self, sorted_devs: list, page: int) -> str: + start = page * 10 + text = self.strings["dev_header"] + for i, (username, stats) in enumerate(sorted_devs[start:start + 10]): + rank = start + i + 1 + text += self.dev_entry(rank, username, stats["likes"], stats["dislikes"]) + return text + + def nav_markup(self, page: int, total: int, on_prev, on_next, on_page) -> list: + return [ + [ + {"text": self.strings["btn_prev"], "callback": on_prev}, + {"text": f"{page + 1}/{total}", "callback": on_page}, + {"text": self.strings["btn_next"], "callback": on_next}, + ], + [{"text": self.strings["btn_close"], "action": "close"}], + ] + + def page_selector_markup(self, total: int, page_cb_factory, on_back) -> list: + buttons, row = [], [] + for p in range(total): + row.append({"text": str(p + 1), "callback": page_cb_factory(p)}) + if len(row) == 5: + buttons.append(row) + row = [] + if row: + buttons.append(row) + buttons.append([{"text": self.strings["btn_back"], "callback": on_back}]) + return buttons + + async def placeholder_devtop(self) -> str: + usernames = {u.lstrip("@").lower() for u in self.config["usernames"]} + if not usernames: + return self.strings["no_usernames"] + sorted_devs = await self.fetch_sorted_devs() + if not sorted_devs: + return self.strings["no_data"] + for rank, (username, _) in enumerate(sorted_devs, 1): + if username.lower() in usernames: + return f"{rank}" + return self.strings["devtop_not_found"] + + async def placeholder_topmod(self) -> str: + usernames = {u.lstrip("@").lower() for u in self.config["usernames"]} + if not usernames: + return self.strings["no_usernames"] + + provider = self.config["provider"] + joined_usernames = ",".join(sorted(usernames)) + + if provider in {"vector", "multi"}: + data = await self.request_api(f"{VECTOR_TOPMOD_URL}{joined_usernames}") + if isinstance(data, dict) and data.get("name") and data.get("rank"): + return f"{data['name']} ({data['rank']})" + if provider == "vector": + return self.strings["topmod_not_found"] if data else self.strings["no_data"] + + data = await self.request_api(FHETA_URL) + if not data: + return self.strings["no_data"] + all_sorted = sorted( + [(self.extract_module_name(k), v) for k, v in data.items()], + key=lambda x: int(x[1].get("likes", 0) or 0), + reverse=True, + ) + user_mods = [ + (name, val) + for name, val in all_sorted + if val.get("author", "").lstrip("@").lower() in usernames + ] + if not user_mods: + return self.strings["topmod_not_found"] + top_name = user_mods[0][0] + global_rank = next( + (i + 1 for i, (name, _) in enumerate(all_sorted) if name == top_name), + None, + ) + return ( + f"{top_name} ({global_rank})" + if global_rank + else self.strings["topmod_not_found"] + ) + + @loader.command(ru_doc="Статистика топ разработчиков") + async def devstats(self, message: Message): + """Top Developers statistics""" + await utils.answer(message, self.strings["loading"]) + sorted_devs = await self.fetch_sorted_devs() + if not sorted_devs: + return await utils.answer(message, self.strings["no_data"]) + total_pages = max(1, math.ceil(len(sorted_devs) / 10)) + state = {"page": 0} + + def markup(): + return self.nav_markup(state["page"], total_pages, on_prev, on_next, on_page) + + def render(): + return self.kb_dev_page(sorted_devs, state["page"]) + + async def on_prev(call: InlineCall): + state["page"] = max(0, state["page"] - 1) + await call.edit(render(), reply_markup=markup()) + + async def on_next(call: InlineCall): + state["page"] = min(total_pages - 1, state["page"] + 1) + await call.edit(render(), reply_markup=markup()) + + async def on_page(call: InlineCall): + await call.edit( + self.strings["select_page"], + reply_markup=self.page_selector_markup(total_pages, make_page_cb, on_back), + ) + + def make_page_cb(p): + async def go(call: InlineCall): + state["page"] = p + await call.edit(render(), reply_markup=markup()) + return go + + async def on_back(call: InlineCall): + await call.edit(render(), reply_markup=markup()) + + await utils.answer(message, render(), reply_markup=markup()) \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/ForkCircles.py b/SunnexGB/Heroku-Modules/ForkCircles.py new file mode 100644 index 0000000..88acfa2 --- /dev/null +++ b/SunnexGB/Heroku-Modules/ForkCircles.py @@ -0,0 +1,168 @@ +# requires: python-ffmpeg +# meta developer: @SunnexGB +# meta pic: https://r2.fakecrime.bio/uploads/ef6d3ed1-6378-4bc4-aaad-d2bdeeaa4bbd.jpg +# meta banner: https://r2.fakecrime.bio/uploads/ef6d3ed1-6378-4bc4-aaad-d2bdeeaa4bbd.jpg + +# Note +# This is a fork module from @KeyZenD. +# Here is a link to the original module: https://github.com/KeyZenD/modules/blob/master/Circles.py + +from .. import loader, utils +from PIL import Image, ImageDraw, ImageOps, ImageFilter +import io +from telethon.tl.types import DocumentAttributeFilename +import subprocess +import json +import os + +@loader.tds +class ForkCircles(loader.Module): + """rounds everything - reply to message""" + strings = { + "name": "ForkCircles", + "processing_image": "Processing image💬", + "processing_video": "Processing video💬", + "no_reply": "🤚|reply to image/sticker or video/gif!", + "download": "downloading💬", + "ffprobe_failed": "🤚|error`ffmpeg is installed?", + "ffmpeg_failed": "🤚|ffmpeg error`: {error}", + } + + strings_ru = { + "_cls_doc": "Округляет всё - ответом на сообщение", + "processing_image": "Обработка изображения💬", + "processing_video": "Обработка видео💬", + "no_reply": "🤚|ответьте на изображение/стикер или видео/gif!", + "download": "Скачивание💬", + "ffprobe_failed": "🤚|еррорь ffmpeg установил?", + "ffmpeg_failed": "🤚|ffmpeg еррорь: {error}", + } + + def __init__(self): + self.name = self.strings['name'] + + async def client_ready(self, client, db): + self.client = client + + @loader.sudo + async def roundcmd(self, message): + """""" + reply = None + if message.is_reply: + reply = await message.get_reply_message() + data = await check_media(reply) + if isinstance(data, bool): + await utils.answer(message, self.strings['no_reply']) + return + else: + await utils.answer(message, self.strings['no_reply']) + return + data, type = data + if type == "img": + await message.edit(self.strings['processing_image']) + img = io.BytesIO() + bytes = await message.client.download_file(data, img) + im = Image.open(img) + w, h = im.size + img = Image.new("RGBA", (w,h), (0,0,0,0)) + img.paste(im, (0, 0)) + m = min(w, h) + img = img.crop(((w-m)//2, (h-m)//2, (w+m)//2, (h+m)//2)) + w, h = img.size + mask = Image.new('L', (w, h), 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((10, 10, w-10, h-10), fill=255) + mask = mask.filter(ImageFilter.GaussianBlur(2)) + img = ImageOps.fit(img, (w, h)) + img.putalpha(mask) + im = io.BytesIO() + im.name = "img.webp" + img.save(im) + im.seek(0) + await message.client.send_file(message.to_id, im, reply_to=reply) + else: + await message.edit(self.strings['processing_video']) + await message.client.download_file(data, "video.mp4") + try: + cmd = [ + 'ffprobe', '-v', 'error', '-select_streams', 'v:0', + '-show_entries', 'stream=width,height', '-of', 'json', 'video.mp4' + ] + proc = subprocess.run(cmd, capture_output=True, text=True) + if proc.returncode != 0: + return + info = json.loads(proc.stdout or '{}') + streams = info.get('streams', []) + if not streams: + return + w = int(streams[0].get('width', 0)) + h = int(streams[0].get('height', 0)) + m = min(w, h) + x = (w - m) // 2 + y = (h - m) // 2 + await message.edit(self.strings['download']) + crop_filter = f"crop={m}:{m}:{x}:{y}" + is_gif = getattr(reply, 'gif', False) or False + if is_gif: + cmd = [ + 'ffmpeg', '-y', '-i', 'video.mp4', + '-vf', crop_filter, + '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '23', + '-pix_fmt', 'yuv420p', '-an', + 'result.mp4' + ] + else: + cmd = [ + 'ffmpeg', '-y', '-i', 'video.mp4', + '-vf', crop_filter, + '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '23', + '-c:a', 'aac', '-strict', '-2', + 'result.mp4' + ] + proc = subprocess.run(cmd, capture_output=True, text=True) + if proc.returncode != 0: + err = proc.stderr or '' + lines = [l for l in err.splitlines() if l.strip()] + filtered = [] + for l in lines: + low = l.lower() + if low.startswith('ffmpeg version') or low.startswith('built with') or low.startswith('configuration:'): + continue + filtered.append(l) + if not filtered: + safe = err[:300] + else: + safe = '\n'.join(filtered[-6:]) + await utils.answer(message, self.strings['ffmpeg_failed'].format(error=safe)) + return + await message.client.send_file(message.to_id, 'result.mp4', video_note=(not is_gif), reply_to=reply) + finally: + if os.path.exists('video.mp4'): + os.remove('video.mp4') + if os.path.exists('result.mp4'): + os.remove('result.mp4') + await message.delete() + + +async def check_media(reply): + type = "img" + if reply and reply.media: + if reply.photo: + data = reply.photo + elif reply.document: + if DocumentAttributeFilename(file_name='AnimatedSticker.tgs') in reply.media.document.attributes: + return False + if reply.gif or reply.video: + type = "vid" + if reply.audio or reply.voice: + return False + data = reply.media.document + else: + return False + else: + return False + + if not data or data is None: + return False + else: + return (data, type) diff --git a/SunnexGB/Heroku-Modules/Hangman.py b/SunnexGB/Heroku-Modules/Hangman.py new file mode 100644 index 0000000..a213887 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Hangman.py @@ -0,0 +1,171 @@ +# meta developer: @H_SunMods +# meta pic: https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/10.png +# meta banner: https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/10.png +# meta fhsdesc: Game, Игра, Hangman, Висилица +# крутой баннер да? + +#current version +__version__ = ("d", "i", "e") + +import random +import aiohttp +from .. import loader, utils +from herokutl.types import Message +from ..types import InlineCall + +words = "https://github.com/SunnexGB/Heroku-Modules/raw/refs/heads/main/Assets/Hangman/words.txt" + +@loader.tds +class Hangman(loader.Module): + """Висилица""" + + strings = { + "name": "Hangman", + "caption": "{word}", + "won": "{word}", + "over": "Игра окончена. Слово было: {word}", + "already": "Сосиски свои ебаные убрал от этой буквы!", + } + + HangmanLives = [ + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/full_hp.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/1.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/2.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/3.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/4.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/5.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/6.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/7.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/8.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/9.png", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/10.png", + ] + + async def client_ready(self): + await self.load_words() + + async def load_words(self): + try: + async with aiohttp.ClientSession() as session: + async with session.get(words) as resp: + resp.raise_for_status() + text = await resp.text() + self.words = [ + word.strip().upper() + for word in text.splitlines() + if word.strip() + ] + except Exception: + self.words = ["СЛЕНДЕРМЕН", "КАЗИНО", "АЗАРТ"] + + def field_w_letters(self, word, guessed): + return " ".join(l if l in guessed else "_" for l in word) + + def caption(self, state): + return self.strings["caption"].format( + word=self.field_w_letters(state["word"], state["guessed"]), + ) + + def russian_latters(self, state, chat_id): + guessed = state["guessed"] + wrong = state["wrong"] + return [ + [ + {"text": "А", "callback": self.on_letter, "args": (chat_id, "А"), **({"style": "success"} if "А" in guessed else {"style": "danger"} if "А" in wrong else {})}, + {"text": "Б", "callback": self.on_letter, "args": (chat_id, "Б"), **({"style": "success"} if "Б" in guessed else {"style": "danger"} if "Б" in wrong else {})}, + {"text": "В", "callback": self.on_letter, "args": (chat_id, "В"), **({"style": "success"} if "В" in guessed else {"style": "danger"} if "В" in wrong else {})}, + {"text": "Г", "callback": self.on_letter, "args": (chat_id, "Г"), **({"style": "success"} if "Г" in guessed else {"style": "danger"} if "Г" in wrong else {})}, + {"text": "Д", "callback": self.on_letter, "args": (chat_id, "Д"), **({"style": "success"} if "Д" in guessed else {"style": "danger"} if "Д" in wrong else {})}, + {"text": "Е", "callback": self.on_letter, "args": (chat_id, "Е"), **({"style": "success"} if "Е" in guessed else {"style": "danger"} if "Е" in wrong else {})}, + {"text": "Ё", "callback": self.on_letter, "args": (chat_id, "Ё"), **({"style": "success"} if "Ё" in guessed else {"style": "danger"} if "Ё" in wrong else {})}, + {"text": "Ж", "callback": self.on_letter, "args": (chat_id, "Ж"), **({"style": "success"} if "Ж" in guessed else {"style": "danger"} if "Ж" in wrong else {})}, + ], + [ + {"text": "З", "callback": self.on_letter, "args": (chat_id, "З"), **({"style": "success"} if "З" in guessed else {"style": "danger"} if "З" in wrong else {})}, + {"text": "И", "callback": self.on_letter, "args": (chat_id, "И"), **({"style": "success"} if "И" in guessed else {"style": "danger"} if "И" in wrong else {})}, + {"text": "Й", "callback": self.on_letter, "args": (chat_id, "Й"), **({"style": "success"} if "Й" in guessed else {"style": "danger"} if "Й" in wrong else {})}, + {"text": "К", "callback": self.on_letter, "args": (chat_id, "К"), **({"style": "success"} if "К" in guessed else {"style": "danger"} if "К" in wrong else {})}, + {"text": "Л", "callback": self.on_letter, "args": (chat_id, "Л"), **({"style": "success"} if "Л" in guessed else {"style": "danger"} if "Л" in wrong else {})}, + {"text": "М", "callback": self.on_letter, "args": (chat_id, "М"), **({"style": "success"} if "М" in guessed else {"style": "danger"} if "М" in wrong else {})}, + {"text": "Н", "callback": self.on_letter, "args": (chat_id, "Н"), **({"style": "success"} if "Н" in guessed else {"style": "danger"} if "Н" in wrong else {})}, + {"text": "О", "callback": self.on_letter, "args": (chat_id, "О"), **({"style": "success"} if "О" in guessed else {"style": "danger"} if "О" in wrong else {})}, + ], + [ + {"text": "П", "callback": self.on_letter, "args": (chat_id, "П"), **({"style": "success"} if "П" in guessed else {"style": "danger"} if "П" in wrong else {})}, + {"text": "Р", "callback": self.on_letter, "args": (chat_id, "Р"), **({"style": "success"} if "Р" in guessed else {"style": "danger"} if "Р" in wrong else {})}, + {"text": "С", "callback": self.on_letter, "args": (chat_id, "С"), **({"style": "success"} if "С" in guessed else {"style": "danger"} if "С" in wrong else {})}, + {"text": "Т", "callback": self.on_letter, "args": (chat_id, "Т"), **({"style": "success"} if "Т" in guessed else {"style": "danger"} if "Т" in wrong else {})}, + {"text": "У", "callback": self.on_letter, "args": (chat_id, "У"), **({"style": "success"} if "У" in guessed else {"style": "danger"} if "У" in wrong else {})}, + {"text": "Ф", "callback": self.on_letter, "args": (chat_id, "Ф"), **({"style": "success"} if "Ф" in guessed else {"style": "danger"} if "Ф" in wrong else {})}, + {"text": "Х", "callback": self.on_letter, "args": (chat_id, "Х"), **({"style": "success"} if "Х" in guessed else {"style": "danger"} if "Х" in wrong else {})}, + {"text": "Ц", "callback": self.on_letter, "args": (chat_id, "Ц"), **({"style": "success"} if "Ц" in guessed else {"style": "danger"} if "Ц" in wrong else {})}, + ], + [ + {"text": "Ч", "callback": self.on_letter, "args": (chat_id, "Ч"), **({"style": "success"} if "Ч" in guessed else {"style": "danger"} if "Ч" in wrong else {})}, + {"text": "Ш", "callback": self.on_letter, "args": (chat_id, "Ш"), **({"style": "success"} if "Ш" in guessed else {"style": "danger"} if "Ш" in wrong else {})}, + {"text": "Щ", "callback": self.on_letter, "args": (chat_id, "Щ"), **({"style": "success"} if "Щ" in guessed else {"style": "danger"} if "Щ" in wrong else {})}, + {"text": "Ъ", "callback": self.on_letter, "args": (chat_id, "Ъ"), **({"style": "success"} if "Ъ" in guessed else {"style": "danger"} if "Ъ" in wrong else {})}, + {"text": "Ь", "callback": self.on_letter, "args": (chat_id, "Ь"), **({"style": "success"} if "Ь" in guessed else {"style": "danger"} if "Ь" in wrong else {})}, + {"text": "Ы", "callback": self.on_letter, "args": (chat_id, "Ы"), **({"style": "success"} if "Ы" in guessed else {"style": "danger"} if "Ы" in wrong else {})}, + {"text": "Э", "callback": self.on_letter, "args": (chat_id, "Э"), **({"style": "success"} if "Э" in guessed else {"style": "danger"} if "Э" in wrong else {})}, + {"text": "Ю", "callback": self.on_letter, "args": (chat_id, "Ю"), **({"style": "success"} if "Ю" in guessed else {"style": "danger"} if "Ю" in wrong else {})}, + ], + [ + {"text": "Я", "callback": self.on_letter, "args": (chat_id, "Я"), **({"style": "success"} if "Я" in guessed else {"style": "danger"} if "Я" in wrong else {})}, + ], + ] + + async def on_letter(self, call: InlineCall, chat_id: int, letter: str): + letter = letter.upper() + state = self.get(f"pidor_{chat_id}", None) + + if letter in state["guessed"] or letter in state["wrong"]: + await call.answer(self.strings["already"], show_alert=True) + return + + if letter in state["word"]: + state["guessed"].append(letter) + self.set(f"pidor_{chat_id}", state) + + if "_" not in self.field_w_letters(state["word"], state["guessed"]): + self.set(f"pidor_{chat_id}", None) + await call.edit(self.strings["won"].format(word=state["word"])) + return + + await call.edit(self.caption(state), reply_markup=self.russian_latters(state, chat_id)) + + else: + state["wrong"].append(letter) + self.set(f"pidor_{chat_id}", state) + + wrong_count = len(state["wrong"]) + stage = min(wrong_count, len(self.HangmanLives) - 1) + + if wrong_count >= 10: + self.set(f"pidor_{chat_id}", None) + await call.edit( + self.strings["over"].format(word=state["word"]), + photo=self.HangmanLives[stage], + ) + return + + await call.edit( + self.caption(state), + reply_markup=self.russian_latters(state, chat_id), + photo=self.HangmanLives[stage], + ) + + @loader.command(ru_doc="(.oleg) Начать висилицу", alias="oleg") + async def hangman(self, message: Message): + """(.oleg) Start hangman game""" + chat_id = message.chat_id + word = random.choice(self.words) + state = {"word": word, "guessed": [], "wrong": []} + self.set(f"pidor_{chat_id}", state) + + await self.inline.form( + message=message, + text=self.caption(state), + reply_markup=self.russian_latters(state, chat_id), + photo=self.HangmanLives[0], + ) \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/HerokuTime.py b/SunnexGB/Heroku-Modules/HerokuTime.py new file mode 100644 index 0000000..197bbd3 --- /dev/null +++ b/SunnexGB/Heroku-Modules/HerokuTime.py @@ -0,0 +1,50 @@ +# meta developer: @SunnexGB +# meta pic: https://r2.fakecrime.bio/uploads/e19c2179-d2e9-4206-b783-25d6c0eb72eb.jpg +# meta banner: https://r2.fakecrime.bio/uploads/e19c2179-d2e9-4206-b783-25d6c0eb72eb.jpg +# meta fhsdesc: Плейсхолдер, placeholder, Time, Время, Статистика, Stats +# крутой баннер да? +#current version +__version__ = (1, 0, 0) + +import time +from .. import loader, utils + +@loader.tds +class HerokuTime(loader.Module): + """shows how much heroku you use in total (since installing placeholder)""" + + strings = { + "name": "HerokuTime", + "sec": "sec", + "min": "min", + "hour": "h", + } + + strings_ru = { + "cls_doc": "показывает сколько вы используете всего хероку(с момента установки плейсхолдера)", + "sec": "сек", + "min": "мин", + "hour": "ч", + } + + async def client_ready(self): + if not self.get("start_time"): + self.set("start_time", int(time.time())) + utils.register_placeholder("alltime", self.get_uptime, "show heroku time usage") + + def format_time(self, seconds: int) -> str: + if seconds < 60: + return f"{seconds} {self.strings('sec')}" + minutes = seconds // 60 + if minutes < 60: + return f"{minutes} {self.strings('min')} {seconds % 60} {self.strings('sec')}" + hours = minutes // 60 + return f"{hours} {self.strings('hour')} {minutes % 60} {self.strings('min')} {seconds % 60} {self.strings('sec')}" + + async def get_uptime(self): + start_time = self.get("start_time") + if not start_time: + return f"0 {self.strings('sec')}" + now = int(time.time()) + uptime = now - start_time + return self.format_time(uptime) \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/Mikuru.py b/SunnexGB/Heroku-Modules/Mikuru.py new file mode 100644 index 0000000..8e9ae19 --- /dev/null +++ b/SunnexGB/Heroku-Modules/Mikuru.py @@ -0,0 +1,138 @@ +# requires: aiohttp +# meta banner: https://r2.fakecrime.bio/uploads/c389e9b5-9ef1-495d-a37a-e993ef819b4a.mp4 +# meta developer: @SunnexGB +__version__ = (1, 0, 0) + +from .. import loader, utils +import re +import aiohttp + +@loader.tds +class Mikuru(loader.Module): + """Censors words with phrase Mikuru Asahina""" + + strings = { + "name": "Mikuru", + "mikuru": "🤤 | I cant say this is for u, b-because this is c-classified information", + "adult_mikuru": "😥 | Y-you already k-know s-so much...", + "ignored": "😏 | Y-you can talk about c-classified information in this chat", + "unignored": "☺️ | You cant speak in this chat b-because c-classified information", + "classified_information": "classified information" + } + + strings_ru = { + "_cls_doc": "Цензурует слова фразой Микуру Асахины", + "mikuru": "🤤 | Я не могу сказать тебе это, п-п-потому что это с-секретные сведения", + "adult_mikuru": "😥 | Т-ты и так м-много з-знаешь... ", + "ignored": "😏 | Т-ты можешь говорить о с-секретных сведениях в этом чате", + "unignored": "☺️ | Ты не можешь говорить в этом чате п-потому что с-секретные сведения", + "classified_information": "секретные сведения" + } + + def __init__(self): + self.config = loader.ModuleConfig( + loader.ConfigValue( + "Ignored_chats", + [-1002410964167, + -1002341345589, + -1001697279580, + -1001554874075, + -1001984640085], + "Ignored chats", + validator=loader.validators.Series() + ), + ) + self.bad_words = None + + async def client_ready(self, client, db): + self.db = db + await self.load_words() + + async def load_words(self): + cultural_words = [ + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/Assets/Mikuru/cultural_words_ru.txt", + "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/49f6883d03d1d2c15c82bad55ee4d31f708870ed/Assets/Mikuru/cultural_words_en.txt" + ] + + words = set() + + async with aiohttp.ClientSession() as session: + for url in cultural_words: + try: + async with session.get(url) as resp: + if resp.status != 200: + continue + text = await resp.text() + for line in text.splitlines(): + w = line.strip().lower() + if w: + words.add(w) + except Exception: + pass + + if words: + self.bad_words = re.compile( + r"\b(" + "|".join(map(re.escape, words)) + r")\b", + re.IGNORECASE + ) + else: + self.bad_words = None + + @loader.command(ru_doc="- Начать цензурирование") + async def mikuru(self, message): + """- lets go censoring""" + state = self.db.get(self.name, "mikuru_state", False) + if state: + self.db.set(self.name, "mikuru_state", False) + await utils.answer(message, self.strings("adult_mikuru")) + else: + self.db.set(self.name, "mikuru_state", True) + await utils.answer(message, self.strings("mikuru")) + + @loader.command(ru_doc="- Добавить в список игнорируемых чатов(не будет работать в этих чатах) ") + async def ignore(self, message): + """- Add to list ignored chats(will not work in these chats) """ + args = utils.get_args_raw(message) + if not args: + target = str(utils.get_chat_id(message)) + else: + target = args.strip() + ignored = list(self.config["Ignored_chats"]) + if target in ignored: + ignored.remove(target) + self.config["Ignored_chats"] = ignored + await utils.answer(message, self.strings("unignored")) + else: + ignored.append(target) + self.config["Ignored_chats"] = ignored + await utils.answer(message, self.strings("ignored")) + + async def watcher(self, message): + if not self.db.get(self.name, "mikuru_state", False): + return + if not message.text: + return + if not message.out: + return + + chat_id = str(utils.get_chat_id(message)) + user_id = str(getattr(message.sender_id, "id", message.sender_id)) + + if chat_id in self.config["Ignored_chats"] or user_id in self.config["Ignored_chats"]: + return + if not self.bad_words: + return + + if self.bad_words.search(message.text): + text = self.bad_words.sub( + self.strings("classified_information"), + message.text + ) + try: + await message.edit(text) + except Exception: + try: + await message.delete() + await message.respond(text) + except Exception: + pass \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/NoChess.py b/SunnexGB/Heroku-Modules/NoChess.py new file mode 100644 index 0000000..5a6ffc2 --- /dev/null +++ b/SunnexGB/Heroku-Modules/NoChess.py @@ -0,0 +1,487 @@ +# requires: aiohttp pyngrok +# meta developer: @H_SunMods +# meta banner: https://r2.fakecrime.bio/uploads/965a3206-4609-4dff-beb0-6831f8b90e12.jpg +# current ver +__version__ = (0, 1, 0) + +import json +import socket +import asyncio +import secrets +import logging +from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit +from aiohttp import ClientSession, ClientTimeout, web +from herokutl.types import Message +from pyngrok import conf, ngrok +from .. import loader, utils +from ..inline.types import InlineCall + +logging.getLogger("pyngrok").setLevel(logging.WARNING) +logging.getLogger("pyngrok.process").setLevel(logging.WARNING) +logging.getLogger("pyngrok.process.ngrok").setLevel(logging.WARNING) + +html_raw = "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/Assets/NoChess/raw_assets/index.html" +css_raw = "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/Assets/NoChess/raw_assets/style.css" +js_raw = "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/Assets/NoChess/raw_assets/javascript.js" +asset_root_raw = "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/NoChess" +botfather_photo_url = "https://r2.fakecrime.bio/uploads/d3e16245-15a2-43f1-b176-493b4d9f1f21.jpg" + +@loader.tds +class NoChess(loader.Module): + """NoChess - web module that allows u to launch a web page either as a functional HTML page or as a Telegram Mini-App. This is an add-on for Chess module by @nullmod""" + + # я пытался кароче сделать тут перевод делая реплейсы в зависимости от стрингов,но это не работает,поэтому да + strings = { + "name": "NoChess", + "starting": "( ノ・ェ・ )ノ Starting NoChess...", + "online": "(*˘︶˘*) NoChess is running", + "already_running": "ʕᵕᴥᵕʔ NoChess is already running", + "stopped": "・゚・(。>д<。)・゚・ NoChess stopped", + "not_running": "(✿╹◡╹) NoChess is not running", + "ngrok_missing": "Set a ngrok_token", + "ngrok_error": "Ngrok start error: {}", + "asset_read_error": "Failed to load web assets: {}", + "open_button": "Open mini-app", + "stop_button": "Stop", + "about_text": "Important read:\nSometimes the server won't lift cause there's enough processes running, for example on HikkaHost, for this I just rebooted the server\nNext is that cma setups the app by a template and it's rly crooked, so you'll have to set some web app config settings yourself\nAnd also:\n 1. First launch will start straight with a site link, not as a web app\n 2. Use nochess, and then cma to setup the web app\n 3. After that restart the process by typing nochess -kill and nochess again\nYeah it's hacky as hell, but I was so over doing stuff that I started dumping some routine like working with files on ai, which I didn't like so I decided to quick-release the module before it's too late\nWell and maybe soon I'll make an update, right now it's some pre-alpha version, that's why the version name is like this, later I'll change it to 1.0.0, if people actually dig the module as an idea", + "cma_start": "( ノ・ェ・ )ノ Creating mini app in BotFather...", + "cma_need_url": "Set mini app web URL first or run .nochess to get it.", + "cma_done": "(*˘︶˘*) Done.", + "cma_error": "Error: {}", + "RuntimeError": "inline bot username not found", + "not_supported_platform": "(┬┬_┬┬) Unfortunately, it is impossible to install this module on this platform.\n\n(〜^∇^)〜 This is not an error, please do not contact support." + } + + strings_ru = { + "_cls_doc": "NoChess - Веб модуль который позволяет запускать веб-пейдж,как HTML страницу с функционалом,так же в виде Telegram Mini-App. Является дополнением к модулю Chess от @nullmod", + "starting": "( ノ・ェ・ )ノ Запуск NoChess...", + "online": "(*˘︶˘*) NoChess запущен", + "already_running": "ʕᵕᴥᵕʔ NoChess уже запущен", + "stopped": "・゚・(。>д<。)・゚・ NoChess остановлен", + "not_running": "(✿╹◡╹) NoChess не запущен", + "ngrok_missing": "Укажи ngrok_token", + "ngrok_error": "Ошибка запуска ngrok: {}", + "asset_read_error": "Не удалось загрузить веб-ассеты: {}", + "open_button": "Открыть мини-приложение", + "stop_button": "Остановить", + "about_text": "Важно к прочтению:\nИногда сервер не может подниматься из за того что запущено достаточно процессов, например на HikkaHost,для этого я просто перезагружал сервер.\nДалее это то что cma сетапает приложение по шаблону и оч криво, поэтому вам придется выставлять некоторые настройки конфигурации веб приложения самим.\nА еще:\n 1. Первый запуск будет запускаться сразу ссылкой на сайт, а не как веб приложение.\n 2. Используйте nochess, а потом cma чтобы настроить веб приложение.\n 3. После чего перезапустите процесс написав nochess -kill и повторно nochess.\nДа это костыли, но мне уже настолько было в падлу что то делать что я уже стал спихивать рутину по типу работы с файлами на ии, что мне не понравилось и я решил быстро релизать модуль пока не стало поздно.\nНу и может быть в скором времени я уже сделаю апдейт, на данный момент это какая то пре-альфа версия, поэтому и название версии такое, в дальнейшем изменю на 1.0.0, если модуль вообще понравиться людям как идея.", + "cma_start": "( ノ・ェ・ )ノ Создаю эпку через BotFather...", + "cma_need_url": "Сначала укажи URL мини-эпки или запусти .nochess, чтобы получить его", + "cma_done": "(*˘︶˘*) Готово", + "cma_error": "Ошибка: {}", + "RuntimeError": "юз инлайн бота не найден", + "not_supported_platform": "(┬┬_┬┬) К сожалению, на эту платформу невозможно установить этот модуль.\n\n(〜^∇^)〜 Это не ошибка, пожалуйста, не обращайтесь в поддержку." + } + + async def client_ready(self): + platform = utils.get_named_platform() + if platform in ("HikkaHost"): + raise loader.LoadError(self.strings("not_supported_platform")) + + def __init__(self): + self.config = loader.ModuleConfig( + loader.ConfigValue( + "ngrok_token", + None, + "Token from ngrok.com | Токен полученый на ngrok.com", + validator=loader.validators.Hidden(), + ), + loader.ConfigValue( + "mini_app_url", + None, + "Mini app direct url | Директ ссылка на ваше мини приложение", + validator=loader.validators.String(), + ), + loader.ConfigValue( + "block_light", + "#D8E3E7", + "Light board block color | Цвет светлых полей на доске", + validator=loader.validators.String() + ), + loader.ConfigValue("block_dark", + "#7699AF", + "Dark board block color | Цвет тёмных полей на доске", + validator=loader.validators.String() + ), + loader.ConfigValue( + "select_block", + "#FF5A5A", + "Selected block color | Цвет для выделения полей на доске", + validator=loader.validators.String() + ), + loader.ConfigValue( + "move_pieces_color", + "#58B4FF", + "Move highlight color | Цвет подсвечиваниях перехода на другую позицию", + validator=loader.validators.String() + ), + loader.ConfigValue( + "result_win", + "#00BE16", + "Winner color | Блок цвета победителя", + validator=loader.validators.String() + ), + loader.ConfigValue( + "result_lose", + "#BE0000", + "Loser color | Блок цвета проигравшего", + validator=loader.validators.String() + ), + loader.ConfigValue( + "result_draw", + "#434343", + "Draw color | Блок цвета при ничьей", + validator=loader.validators.String() + ), + loader.ConfigValue( + "arrow_color", + "#BD3667", + "Arrow color | Цвет стрелки", + validator=loader.validators.String() + ), + ) + + self.runner = None + self.tunnel_url = None + self.access_token = None + self.games_cache = [] + self.games_dump = "" + + def theme_config_dict(self): + return { + "block_light": self.config["block_light"], + "block_dark": self.config["block_dark"], + "select_block": self.config["select_block"], + "move_pieces_color": self.config["move_pieces_color"], + "result_win": self.config["result_win"], + "result_lose": self.config["result_lose"], + "result_draw": self.config["result_draw"], + "arrow_color": self.config["arrow_color"], + } + + async def refresh_games_cache(self): + chess = self.lookup("chess") + if not chess or not getattr(chess, "games", None): + self.games_cache = [] + self.games_dump = "" + return + + chunks = [] + items = list(chess.games.items()) + + def sort_key(item): + key = str(item[0]) + return (0, int(key)) if key.isdigit() else (1, key) + + for _, game in sorted(items, key=sort_key, reverse=True): + node = None + + if isinstance(game, dict): + game_obj = game.get("game", {}) + if isinstance(game_obj, dict): + node = game_obj.get("root_node") or game_obj.get("node") + if node is None: + node = game.get("root_node") or game.get("node") + + if node is None and hasattr(game, "game"): + game_obj = getattr(game, "game", None) + if isinstance(game_obj, dict): + node = game_obj.get("root_node") or game_obj.get("node") + + if node is None and hasattr(game, "root_node"): + node = getattr(game, "root_node", None) + + if node is None and hasattr(game, "node"): + node = getattr(game, "node", None) + + if node: + chunks.append(str(node).strip()) + + self.games_cache = [x for x in chunks if x] + self.games_dump = "\n\n".join(self.games_cache) + + async def get_me_json(self): + me = await self.client.get_me() + fallback_photo = "https://i.pinimg.com/736x/6e/0a/0c/6e0a0cf688b30ba9de81b81bb32e49f9.jpg" + full_name = (getattr(me, "first_name", "") or "") + ( + (" " + getattr(me, "last_name", "")) if getattr(me, "last_name", None) else "" + ) + return { + "id": getattr(me, "id", None), + "username": getattr(me, "username", None), + "first_name": getattr(me, "first_name", None), + "last_name": getattr(me, "last_name", None), + "name": full_name.strip() or str(getattr(me, "id", "Unknown")), + "photo": fallback_photo, + "enemy_photo": fallback_photo, + } + + def check_access(self, request): + token = request.query.get("token") or request.cookies.get("nochess_token") + return bool(self.access_token and token == self.access_token) + + def ensure_access_token(self): + if self.access_token: + return self.access_token + self.access_token = self.get("access_token") + if not self.access_token: + self.access_token = secrets.token_urlsafe(32) + self.set("access_token", self.access_token) + return self.access_token + + async def read_remote_asset(self, url): + timeout = ClientTimeout(total=15) + async with ClientSession(timeout=timeout) as session: + async with session.get(url) as response: + if response.status != 200: + raise RuntimeError(f"HTTP {response.status}: {url}") + return await response.text() + + async def load_web_assets(self): + html = await self.read_remote_asset(html_raw) + css = await self.read_remote_asset(css_raw) + js = await self.read_remote_asset(js_raw) + return html, css, js + + def localication_script(self): + return ( + "" + ) + + def inject_runtime_config(self, html, css, js): + asset_root = asset_root_raw.rstrip("/") + if asset_root: + css = css.replace("url('bg.png')", f"url('{asset_root}/other/bg.png')") + theme_json = json.dumps(self.theme_config_dict(), ensure_ascii=False) + bootstrap = ( + "" + ) + html = html.replace('', f"") + html = html.replace('', bootstrap + f"") + return html + + async def handle_home(self, request): + try: + html, css, js = await self.load_web_assets() + except Exception as error: + return web.Response( + text=self.strings["asset_read_error"].format(utils.escape_html(str(error))), + status=500, + ) + html = self.inject_runtime_config(html, css, js) + html = html.replace("", self.localication_script() + "") + response = web.Response(text=html, content_type="text/html") + response.set_cookie( + "nochess_token", + self.access_token, + max_age=86400, + httponly=True, + samesite="Lax", + ) + return response + + async def handle_games(self, request): + if not self.check_access(request): + return web.json_response({"error": "Unauthorized"}, status=401) + if not self.games_cache: + await self.refresh_games_cache() + return web.json_response({"games_dump": self.games_dump, "games": list(self.games_cache)}) + + async def handle_me(self, request): + if not self.check_access(request): + return web.json_response({"error": "Unauthorized"}, status=401) + return web.json_response(await self.get_me_json()) + + async def stop_server(self): + was_running = bool(self.runner) + try: + ngrok.kill() + except Exception: + pass + if self.runner: + await self.runner.cleanup() + self.runner = None + self.tunnel_url = None + return was_running + + async def send_form(self, message, url): + await self.inline.form( + self.strings["online"], + message=message, + reply_markup=[ + [{"text": self.strings["open_button"], "url": url}], + [{"text": self.strings["stop_button"], "callback": self.stop_callback}], + ], + ) + + async def stop_callback(self, call: InlineCall): + was_running = await self.stop_server() + await call.answer( + self.strings["stopped"] if was_running else self.strings["not_running"], + show_alert=False, + ) + try: + await call.delete() + except Exception: + try: + await call.edit(self.strings["stopped"] if was_running else self.strings["not_running"]) + except Exception: + pass + + @loader.command(ru_doc="[-kill] Вызываь веб интерфейс для просмотра партии") + async def nochess(self, message: Message): + """[-kill] Call web interface to view chess game""" + try: + return await self.nochess_args(message) + except Exception as error: + await self.stop_server() + return await utils.answer( + message, + self.strings["ngrok_error"].format(utils.escape_html(str(error))), + ) + + async def nochess_args(self, message: Message): + args = (utils.get_args_raw(message) or "").strip().lower() + if args == "-kill": + was_running = await self.stop_server() + return await utils.answer(message, self.strings["stopped"] if was_running else self.strings["not_running"]) + mini_url = (self.config["mini_app_url"] or "").strip().rstrip("/") + is_tg_direct = mini_url.startswith("https://t.me/") + if self.runner: + if is_tg_direct: + access = mini_url + else: + base = (self.tunnel_url or "").rstrip("/") + access = f"{base}/?token={self.access_token}" if base and self.access_token else base + await utils.answer(message, self.strings["already_running"]) + if access: + await self.send_form(message, access) + return + if not self.config["ngrok_token"] and (not mini_url or is_tg_direct): + return await utils.answer(message, self.strings["ngrok_missing"]) + await self.refresh_games_cache() + await utils.answer(message, self.strings["starting"]) + self.ensure_access_token() + sock = socket.socket() + sock.bind(("", 0)) + port = sock.getsockname()[1] + sock.close() + app = web.Application() + app.router.add_get("/", self.handle_home) + app.router.add_get("/api/games", self.handle_games) + app.router.add_get("/api/me", self.handle_me) + self.runner = web.AppRunner(app) + await self.runner.setup() + await web.TCPSite(self.runner, "127.0.0.1", port).start() + try: + if self.config["ngrok_token"]: + conf.get_default().auth_token = self.config["ngrok_token"] + tunnel = ngrok.connect(port) + self.tunnel_url = tunnel.public_url.rstrip("/") + else: + self.tunnel_url = mini_url + except Exception as error: + await self.stop_server() + return await utils.answer( + message, + self.strings["ngrok_error"].format(utils.escape_html(str(error))), + ) + if is_tg_direct: + access_url = mini_url + else: + base = (self.tunnel_url or "").rstrip("/") + access_url = f"{base}/?token={self.access_token}" if base and self.access_token else base + await self.send_form(message, access_url) + + @loader.command(ru_doc="Создает и настраивает эпку") + async def cma(self, message: Message): + """Create and setup mini-app""" + raw_args = (utils.get_args_raw(message) or "").strip() + parts = raw_args.split() + web_url = "" + short_name = "NoChess" + if parts: + web_url = parts[0] + if len(parts) > 1: + short_name = parts[1] + if not web_url: + candidate = (self.tunnel_url or "").strip() + if not candidate: + candidate = (self.config["mini_app_url"] or "").strip() + if candidate.startswith("https://t.me/"): + candidate = "" + web_url = candidate + if not web_url: + return await utils.answer(message, self.strings["cma_need_url"]) + self.ensure_access_token() + if web_url.startswith("http") and "t.me/" not in web_url: + parsed = urlsplit(web_url) + query = dict(parse_qsl(parsed.query, keep_blank_values=True)) + query["token"] = self.access_token + web_url = urlunsplit((parsed.scheme, parsed.netloc, parsed.path, urlencode(query), parsed.fragment)) + await utils.answer(message, self.strings["cma_start"]) + try: + bot_username = (await self.inline.bot.get_me()).username + bot_username = (bot_username or "").strip().lstrip("@") + if not bot_username: + raise RuntimeError(self.strings["RuntimeError"]) + await self.client.send_message("@BotFather", "/cancel") + await asyncio.sleep(0.9) + + async with self.client.conversation("@BotFather", timeout=120) as conv: + await conv.send_message("/newapp") + await conv.get_response() + await asyncio.sleep(0.8) + await conv.send_message(f"@{bot_username}") + await conv.get_response() + await asyncio.sleep(0.8) + await conv.send_message("NoChessModule") + await conv.get_response() + await asyncio.sleep(0.8) + await conv.send_message("NoChess") + await conv.get_response() + await asyncio.sleep(0.8) + await conv.send_file(botfather_photo_url) + await conv.get_response() + await asyncio.sleep(0.8) + await conv.send_message("/empty") + await conv.get_response() + await asyncio.sleep(0.8) + await conv.send_message(web_url) + await conv.get_response() + await asyncio.sleep(0.8) + await conv.send_message(short_name) + await conv.get_response() + + direct_link = f"https://t.me/{bot_username}/{short_name}" + module_ref = None + try: + module_ref = self.lookup("NoChess") + except Exception: + module_ref = None + if module_ref: + module_ref.config["mini_app_url"] = direct_link + else: + self.config["mini_app_url"] = direct_link + await utils.answer(message, self.strings["cma_done"]) + except Exception as error: + await utils.answer(message, self.strings["cma_error"].format(utils.escape_html(str(error)))) + + @loader.command(ru_doc="ВАЖНО К ПРОЧТЕНИЮ") + async def about(self, message: Message): + """IMPORTANT READING""" + await utils.answer(message, self.strings["about_text"]) + async def on_unload(self): + await self.stop_server() diff --git a/SunnexGB/Heroku-Modules/Shazamio.py b/SunnexGB/Heroku-Modules/Shazamio.py new file mode 100644 index 0000000..d8fe5cc --- /dev/null +++ b/SunnexGB/Heroku-Modules/Shazamio.py @@ -0,0 +1,124 @@ +# meta pic: https://r2.fakecrime.bio/uploads/54b3c78d-38cb-4970-b925-18b7ec2b268d.jpg +# meta banner: https://r2.fakecrime.bio/uploads/54b3c78d-38cb-4970-b925-18b7ec2b268d.jpg +# requires: https://files.pythonhosted.org/packages/2f/66/31ecae67c373421db10f250a83d80653d6908f7d95080c46816102bd1fda/shazamio-0.8.1.tar.gz https://files.pythonhosted.org/packages/dd/4d/7ecffb341d646e016be76e36f5a42cb32f409c9ca21a57b68f067fad3fc7/python_ffmpeg-2.0.12.tar.gz +# meta developer: @SunnexGB +#current version +__version__ = (1, 0, 0) + +from .. import loader, utils +import os +import asyncio +from shazamio import Shazam + +@loader.tds +class Shazamio(loader.Module): + """Music recognition module""" + + strings = { + "name": "Shazamio", + "processing": "Processing 🫥", + "shazaming": "🔈| Shazaming...", + "no_reply": "🚫| Reply to a video message.", + "no_video": "🚫| Reply must be to a video message.", + "ffmpeg_error": "🚫| Failed to read audio. Make sure ffmpeg is installed.", + "not_found": "✖️| Sorry, could not recognize the song.", + "result": "🔈| Song recognized:\n\n" + "🔈Artist:{artist}\n" + "🚮Title:{title}", + "result_url": "〰️Song recognized:\n\n" + "🚮Artist:{artist}\n" + "🔈Title:{title}\n\n" + "🔗Listen on Shazam", + "shazam_history": "〰️| Your last 10 recognised songs", # i put it off for later and then forgot i wanted to implement it + "no_history": "〰️| What do you want to see here?", # i put it off for later and then forgot i wanted to implement it + } + + strings_ru = { + "name": "Shazamio", + "_cls_doc": "Модуль для распознования музыки", + "processing": "Обработка 🫥", + "shazaming": "🔈| Шазамлю...", + "no_reply": "🚫| Ответьте на сообщение с видео.", + "no_video": "🚫| Ответ должен быть на видео", + "ffmpeg_error": "🚫| Неудачное чтение аудио. Убедитесь что ffmpeg установлен.Инструкция по установке", + "not_found": "✖️| Простите, песня не была найдена.", + "result": "🔈| Песня найдена:\n\n" + "🔈Исполнитель:{artist}\n" + "🚮Название:{title}", + "result_url": "〰️Песня найдена:\n\n" + "🚮Исполнитель:{artist}\n" + "🔈Название:{title}\n\n" + "🔗Слушайте на Shazam", + "shazam_history": "〰️| Твои 10 последних распознаных треков", # на потом,я забыл что я хотел это реализовать + "no_history": "〰️| Ну и что ты тут хотел увидеть?", # на потом,я забыл что я хотел это реализовать + } + + def __init__(self): + self.config = loader.ModuleConfig( + "ffmpeg_path", + "ffmpeg", + "Path to ffmpeg executable", + ) + + @loader.command(ru_doc="Распознать музыку (Ответом на видео)") + async def shazam(self, message): + """Recognize music (Reply in video)""" + reply = await message.get_reply_message() + if not reply: + await utils.answer(message, self.strings["no_reply"]) + return + + if not reply.video: + await utils.answer(message, self.strings["no_video"]) + return + + await utils.answer(message, self.strings["processing"]) + downloaded_path = await message.client.download_media(reply.video) + video_path = os.path.abspath(downloaded_path) + base, _ = os.path.splitext(video_path) + audio_path = f"{base}.mp3" + + try: + cmd = ( + f'{self.config["ffmpeg_path"]} -i "{video_path}" ' + f'-y -vn -ab 128k -ar 44100 -f mp3 "{audio_path}"' + ) + proc = await asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + await proc.communicate() + + if not os.path.exists(audio_path): + await utils.answer(message, self.strings["ffmpeg_error"]) + return + + await utils.answer(message, self.strings["shazaming"]) + shazam = Shazam() + result = await shazam.recognize(audio_path) + + track = result.get("track") + if track: + title = track.get("title", "Unknown Title") + artist = track.get("subtitle", "Unknown Artist") + url = track.get("url") + + if url: + text = self.strings["result_url"].format( + title=title, artist=artist, url=url + ) + else: + text = self.strings["result"].format( + title=title, artist=artist + ) + + await utils.answer(message, text) + else: + await utils.answer(message, self.strings["not_found"]) + + finally: + if os.path.exists(video_path): + os.remove(video_path) + if os.path.exists(audio_path): + os.remove(audio_path) \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/SpotiSaver.py b/SunnexGB/Heroku-Modules/SpotiSaver.py new file mode 100644 index 0000000..f0ae61d --- /dev/null +++ b/SunnexGB/Heroku-Modules/SpotiSaver.py @@ -0,0 +1,191 @@ +# Спасибо: snfsx, кезу, а так же Gemini +# requires: httpx +# meta developer: @SunnexGB +# meta repo: https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/spotisaver.py +# meta pic: https://r2.fakecrime.bio/uploads/ddf03169-09fe-4eb1-8eea-bad1a4cc4ada.jpg +# meta banner: https://r2.fakecrime.bio/uploads/ddf03169-09fe-4eb1-8eea-bad1a4cc4ada.jpg +# meta fhsdesc: Spotify, downloader, music, музыка, спотифай,скачать музыку +# это не должно было быть в релизе,но ладно я потом пофикшу все и вся в говнокоде. +__version__ = (1, 1, 1) + +import asyncio +import httpx +import os +import re +import logging +from .. import loader, utils +from herokutl.types import Message + +logger = logging.getLogger(__name__) + +headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36", + "Accept": "application/json", + "Content-Type": "application/json", + "Origin": "https://spotmate.online", + "Referer": "https://spotmate.online/en1", +} + +@loader.tds +class SpotiSaver(loader.Module): + """Downloading music from Spotify""" + strings = { + "name": "SpotiSaver", + # "args": " link to song is not specified", + "downloading": "📥 Downloading: {}", + "error": " Error, see logs!", + "done": "✔️ Done!", + "no_spotifymod": "💢 SpotifyMod not found.", + "no_spotify": "😅 Nothing is playing on Spotify.", + "nf_id": " ID key not found!", + "nf_track": " Song not found.", + "timeout": " timeout! Try again.", + } + + strings_ru = { + "name": "SpotiSaver", + "_cls_doc": "Скачивание музыки из Spotify", + # "args": " Ссылка на песню не указана", + "downloading": "📥 Скачиваю: {}", + "error": " Ерорь, смотри логи!", + "done": "✔️ Готово!", + "no_spotifymod": "💢 SpotifyMod не найден.", + "no_spotify": "😅 В Spotify ничего не играет.", + "nf_id": " ID песни не найден", + "nf_track": " Песня не найдена", + "timeout": " Таймаут! Попробуй ещё раз.", + } + + def __init__(self): + self.config = loader.ModuleConfig( + loader.ConfigValue( + "TimeOut", + 60, + "Response timeout in seconds | Время ожидания ответа в секундах", + validator=loader.validators.Integer(minimum=30), + ) + ) + + async def get_session(self, client: httpx.AsyncClient) -> str: + res = await client.get("https://spotmate.online/en1", headers={ + "User-Agent": headers["User-Agent"], + "Accept": "text/html", + }, + timeout=self.config["TimeOut"]) + match = re.search(r'csrf-token[^>]*content="([^"]+)"', res.text) + if not match: + raise ValueError("CSRF token not found") + return match.group(1) + + async def get_current_spotify_url(self) -> str | None: + spotifymod = self.lookup("SpotifyMod") + if not spotifymod or not spotifymod.sp: + return None + current_playback = await asyncio.to_thread(spotifymod.sp.current_playback) + if not current_playback or not current_playback.get("is_playing"): + return None + track_id = current_playback["item"]["id"] + return f"https://open.spotify.com/track/{track_id}" + + @loader.command(ru_doc="<ссылка> — Скачать трек из Spotify") + async def spotsave(self, message: Message): + """ - Download track from Spotify""" + args = utils.get_args_raw(message) + if not args: + spotifymod = self.lookup("SpotifyMod") + if not spotifymod or not spotifymod.sp: + return await utils.answer(message, self.strings["no_spotifymod"]) + args = await self.get_current_spotify_url() + if not args: + return await utils.answer(message, self.strings["no_spotify"]) + if "track/" not in args: + return await utils.answer(message, self.strings["nf_id"]) + track_url = args.split("?")[0] + try: + async with httpx.AsyncClient(follow_redirects=True) as client: + csrf = await self.get_session(client) + hdrs = {**headers, "X-CSRF-TOKEN": csrf} + info_res = await client.post( + "https://spotmate.online/getTrackData", + headers=hdrs, + json={"spotify_url": track_url}, + timeout=self.config["TimeOut"], + ) + + info = info_res.json() + if info.get("type") != "track": + return await utils.answer(message, self.strings["nf_track"]) + track_name = info.get("name", "Unknown") + artists = ", ".join(a["name"] for a in info.get("artists", [])) + full_name = f"{artists} - {track_name}" + track_id = info.get("id", track_url.split("/")[-1]) + conv_res = await client.post( + "https://spotmate.online/convert", + headers=hdrs, + json={"urls": track_url}, + timeout=self.config["TimeOut"], + ) + conv = conv_res.json() + download_url = conv.get("url") or conv.get("download_url") + task_id = conv.get("task_id") or conv.get("taskId") + if not download_url and task_id: + for _ in range(40): + await asyncio.sleep(4.5) + task_res = await client.get( + f"https://spotmate.online/tasks/{task_id}", + headers={**hdrs, "Accept": "application/json"}, + timeout=self.config["TimeOut"], + ) + task = task_res.json() + if task.get("error"): + return await utils.answer(message, self.strings["error"]) + data = task.get("data") or task.get("result") or {} + status = str(data.get("status") or data.get("state") or "").lower() + if status == "finished": + download_url = ( + data.get("url") or data.get("download_url") + or (data.get("result") or {}).get("url") + or (data.get("result") or {}).get("download_url") + ) + break + + if status in ("failed", "error", "expired", "cancelled"): + return await utils.answer(message, self.strings["error"]) + + if not download_url: + return await utils.answer(message, self.strings["timeout"]) + + await utils.answer( + message, + self.strings["downloading"].format(utils.escape_html(full_name)), + ) + + file_res = await client.get( + download_url, + headers={"User-Agent": headers["User-Agent"], "Referer": "https://spotmate.online/en1"}, + timeout=self.config["TimeOut"], + ) + + filename = f"{track_id}.mp3" + with open(filename, "wb") as f: + f.write(file_res.content) + + await self.client.send_file( + message.chat_id, + filename, + caption=self.strings["done"], + reply_to=message.id, + attributes=( + [utils.get_audio_tag(filename, title=track_name, performer=artists)] + if hasattr(utils, "get_audio_tag") + else [] + ), + ) + + await message.delete() + if os.path.exists(filename): + os.remove(filename) + + except Exception: + logger.exception("Download failed") + await utils.answer(message, self.strings["error"]) \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/SpotifyLyrics.py b/SunnexGB/Heroku-Modules/SpotifyLyrics.py new file mode 100644 index 0000000..8c9fd4d --- /dev/null +++ b/SunnexGB/Heroku-Modules/SpotifyLyrics.py @@ -0,0 +1,285 @@ +# meta developer: @SunnexGB +# requires: aiohttp +# meta pic: https://r2.fakecrime.bio/uploads/f49a9294-36ad-4fc4-801f-48cb049111d6.jpg +# meta banner: https://r2.fakecrime.bio/uploads/f49a9294-36ad-4fc4-801f-48cb049111d6.jpg +# meta fhsdesc: Spotify, music, музыка, спотифай,Lyrics, слова, текст, трек, песня +# все же я не знаю трек или сонг, так что пусть будет трек, а не сонг потому что интуитивнее поняттнее,наверное? +# крутой баннер да? +#current version +__version__ = (1, 1, 2) + +from herokutl.types import Message +from .. import loader, utils +from ..types import InlineCall +import aiohttp +import asyncio +import re + + +@loader.tds +class SpotifyLyrics(loader.Module): + """life lyrics current song""" + + strings = { + "name": "SpotifyLyrics", + "no_spotifymod": "💢 SpotifyMod not found,but u can install it. You can also support developer: @ke_mods", + "no_auth": "⁉️ You not authorized in SpotifyMod, visit you Saved Messages.", + "no_spotify": "😅 Nothing is playing on Spotify.", + "no_lyrics": "💢 Lyrics not found for: {}", + "not_synced": "⚠️ Lyrics are not synchronized.\n\n", + "finished": "‼️ Playback ended or track changed.", + "header": "🎤 {} - {}\n\n", + "timeout": " Oopsi, looks like we've got a timeout here.", + } + + strings_ru = { + "cls_doc": "Лайв слова текущей песни.", + "no_spotifymod": "💢 SpotifyMod не найден,но его можно установить. Вы также можете поддержать разработчика: @ke_mods", + "no_auth": "⁉️ Вы не авторизированы в SpotifyMod, перейдите в Избранное.", + "no_spotify": "😅 В Spotify ничего не играет.", + "no_lyrics": "💢 Текст не найден для: {}", + "not_synced": "⚠️ Текст не синхронизирован.\n\n", + "finished": "‼️ Воспроизведение завершено или трек сменился.", + "header": "🎤 {} - {}\n\n", + "timeout": " Упси, похоже кто то словил таймаут..", + } + + def __init__(self): + self._active_tasks: dict = {} + self.config = loader.ModuleConfig( + loader.ConfigValue( + "emoji_current", + "🤯", + "Emoji for current line", + validator=loader.validators.String(), + ), + loader.ConfigValue( + "dot", + "♪", + "instrumental_emoji or text", + validator=loader.validators.String(), + ), + loader.ConfigValue( + "lyrics_delay", + 0.5, + "delay in switching to a new timing sector with words", + ), + loader.ConfigValue( + "request_timeout", + 12, + "timeout value", + ), + ) + + async def install_spotifymod(self, call: InlineCall): + mod_url = "https://raw.githubusercontent.com/radiocycle/Modules/refs/heads/master/SpotifyMod.py" + try: + m = self.lookup("Modules") or self.lookup("loader") + await m.download_and_install(mod_url) + await call.answer("SpotifyMod installed!", show_alert=True) + mod = self.lookup("SpotifyMod") + acs_tkn = mod.get("acs_tkn") if mod else None + if not acs_tkn: + await self.invoke("sauth", " ", "me") + await call.edit( + self.strings("no_auth"), + reply_markup=[[{"text": "Хорошо", "callback": self.close}]], + ) + else: + await call.delete() + except Exception as e: + await call.answer(f"Error! Check logs.\n{e}", show_alert=True) + + def close(self, call: InlineCall): + return call.delete() + + async def _get_lyrics(self, artist: str, track: str): + clean_track = re.sub(r"\(.*?\)|\[.*?\]", "", track).strip() + try: + async with aiohttp.ClientSession() as session: + async with session.get( + "https://lrclib.net/api/search", + params={"track_name": clean_track, "artist_name": artist}, + timeout=aiohttp.ClientTimeout(total=(self.config["request_timeout"])), + ) as resp: + if resp.status == 200: + res = await resp.json() + return res[0] if res else None + except asyncio.TimeoutError: + return {"timeout": True} + except Exception: + pass + return None + + def _parse_synced(self, synced_text: str) -> list: + lines = [] + for line in synced_text.split("\n"): + m = re.search(r"\[(\d+):(\d+\.\d+)\](.*)", line) + if m: + mins, secs, text = m.groups() + lines.append({ + "time": (int(mins) * 60 + float(secs)) * 1000, + "text": text.strip(), + }) + return lines + + def _build_content(self, artist, track, lines, plain, progress_ms, not_synced_str): + header = self.strings("header").format( + utils.escape_html(artist), + utils.escape_html(track), + ) + if lines: + curr_idx = 0 + for i, line in enumerate(lines): + if progress_ms >= line["time"]: + curr_idx = i + win_start = max(0, curr_idx - 1) + win_end = min(len(lines), curr_idx + 6) + rows = [] + for i in range(win_start, win_end): + t = lines[i]["text"] or self.config["dot"] + if i == curr_idx: + rows.append( + f"{self.config['emoji_current']} {utils.escape_html(t)}" + ) + else: + rows.append(f"{utils.escape_html(t)}") + return header + "\n".join(rows) + else: + return header + not_synced_str + f"
{utils.escape_html((plain or '')[:4000])}
" + + def _markup(self, song_url): + return [ + [{"text": "🔗 song.link", "url": song_url}], + [{"text": "❌ Close", "callback": self._close_cb}], + ] + + async def _close_cb(self, call): + for track_id, task in list(self._active_tasks.items()): + task.cancel() + self._active_tasks.pop(track_id, None) + try: + await call.answer() + await call.delete() + except Exception: + pass + + async def run_loop(self, form, mod, track_id, artist_name, track_name, song_url, lines, plain, not_synced_str): + last_display = "" + try: + while True: + pb = mod.sp.current_playback() + if not pb or not pb.get("item") or pb["item"]["id"] != track_id: + try: + await form.edit( + self.strings("finished"), + reply_markup=[[{"text": "❌ Close", "callback": self._close_cb}]], + ) + except Exception: + pass + break + + prog = pb.get("progress_ms", 0) + content = self._build_content( + artist_name, track_name, lines, plain, prog, not_synced_str + ) + + if content != last_display: + try: + await form.edit(content, reply_markup=self._markup(song_url)) + last_display = content + except Exception: + break + + if not lines: + break + + await asyncio.sleep(self.config["lyrics_delay"]) + + except asyncio.CancelledError: + raise + except Exception: + pass + finally: + self._active_tasks.pop(track_id, None) + + @loader.command(ru_doc="- показать синхронизированный текст песни") + async def snowlcmd(self, message: Message): + """- show synchronized lyrics for current Spotify track""" + mod = self.lookup("SpotifyMod") + if not mod: + form = await self.inline.form("⏳", message=message) + await form.edit( + self.strings("no_spotifymod"), + reply_markup=[[{"text": "Install SpotifyMod", "callback": self.install_spotifymod}]], + ) + return + + acs_tkn = mod.get("acs_tkn") + if not acs_tkn: + await self.invoke("sauth", " ", "me") + form = await self.inline.form("⏳", message=message) + await form.edit( + self.strings("no_auth"), + reply_markup=[[{"text": "Хорошо", "callback": self.close}]], + ) + return + + playback = mod.sp.current_playback() + if not playback or not playback.get("item"): + return await utils.answer(message, self.strings("no_spotify")) + + track = playback["item"] + track_id = track["id"] + artist_name = track["artists"][0]["name"] + track_name = track["name"] + song_url = f"https://song.link/s/{track_id}" + + old = self._active_tasks.pop(track_id, None) + if old: + old.cancel() + + data = await self._get_lyrics(artist_name, track_name) + if data and data.get("timeout"): + return utils.answer( + message, + self.strings["timeout"] + ) + if not data or data.get("instrumental"): + track_and_artist = f"{utils.escape_html(track_name)} - {utils.escape_html(artist_name)}" + return await utils.answer( + message, + self.strings("no_lyrics").format(track_and_artist), + ) + + synced_raw = data.get("syncedLyrics") + plain = data.get("plainLyrics", "") + + lines = self._parse_synced(synced_raw) if synced_raw else [] + not_synced_str = self.strings("not_synced") + + prog = playback.get("progress_ms", 0) + initial_content = self._build_content( + artist_name, track_name, lines, plain, prog, not_synced_str + ) + + form = await self.inline.form( + text=initial_content, + message=message, + reply_markup=self._markup(song_url), + ) + + task = asyncio.ensure_future( + self.run_loop( + form=form, + mod=mod, + track_id=track_id, + artist_name=artist_name, + track_name=track_name, + song_url=song_url, + lines=lines, + plain=plain, + not_synced_str=not_synced_str, + ) + ) + self._active_tasks[track_id] = task \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/YandexLyrics.py b/SunnexGB/Heroku-Modules/YandexLyrics.py new file mode 100644 index 0000000..d9d31aa --- /dev/null +++ b/SunnexGB/Heroku-Modules/YandexLyrics.py @@ -0,0 +1,352 @@ +# meta developer: @SunnexGB +# requires: aiohttp +# meta pic: https://r2.fakecrime.bio/uploads/ab42b5e2-91f1-4ed1-8002-51b3184e3839.jpg +# meta banner: https://r2.fakecrime.bio/uploads/ab42b5e2-91f1-4ed1-8002-51b3184e3839.jpg +# meta fhsdesc: YaMusic, music, музыка, яндекс музыка,Lyrics, слова, текст, трек, песня +# все же я не знаю трек или сонг, так что пусть будет трек, а не сонг потому что интуитивнее поняттнее,наверное? +# крутой баннер да? +#current version +__version__ = (1, 1, 2) + +from herokutl.types import Message +from .. import loader, utils +from ..types import InlineCall +import aiohttp +import asyncio +import re + + +@loader.tds +class YandexLyrics(loader.Module): + """life lyrics current song""" + + strings = { + "name": "YandexLyrics", + "no_YaMusicMod": "💢 YaMusicMod not found,but u can install it. You can also support developer: @codrago_m", + "no_auth": "⁉️ You not authorized in SpotifyMod, visit you Saved Messages and setup token to continue.", + "no_ym": "😅 Nothing is playing on YaMusic.", + "no_lyrics": "💢 Lyrics not found for: {}", + "not_synced": "⚠️ Lyrics are not synchronized.\n\n", + "finished": "‼️ Playback ended or track changed.", + "header": "🎤 {} - {}\n\n", + "timeout": " Oopsi, looks like we've got a timeout here.", + + } + + strings_ru = { + "cls_doc": "Лайв слова текущей песни.", + "no_YaMusicMod": "💢 YaMusicMod не найден,но его можно установить. Вы также можете поддержать разработчика: @codrago_m", + "no_auth": "⁉️ Вы не авторизированы в YaMusicMod, перейдите в Избранное и установите токен для продолжения работы.", + "no_ym": "😅 В YaMusic ничего не играет.", + "no_lyrics": "💢 Текст не найден для: {}", + "not_synced": "⚠️ Текст не синхронизирован.\n\n", + "finished": "‼️ Воспроизведение завершено или трек сменился.", + "header": "🎤 {} - {}\n\n", + "timeout": " Упси, похоже кто то словил таймаут..", + } + + + def __init__(self): + self._active_tasks: dict = {} + self.config = loader.ModuleConfig( + loader.ConfigValue( + "emoji_current", + "🤯", + "Emoji for current line", + validator=loader.validators.String(), + ), + loader.ConfigValue( + "dot", + "♪", + "instrumental_emoji or text", + validator=loader.validators.String(), + ), + loader.ConfigValue( + "lyrics_delay", + 0.5, + "delay in switching to a new timing sector with words", + ), + loader.ConfigValue( + "request_timeout", + 12, + "timeout value", + ), + ) + + async def install_yamusic(self, call: InlineCall): + mod_url = "https://raw.githubusercontent.com/coddrago/modules/main/YaMusic.py" + try: + m = self.lookup("Modules") or self.lookup("loader") + await m.download_and_install(mod_url) + await call.answer("YaMusicMod installed!", show_alert=True) + mod = self.lookup("YaMusicMod") + acs_tkn = mod.get("__config__")["token"] if mod else "****" + if not acs_tkn: + await self.invoke("yguide", " ", "me") + await call.edit( + self.strings("no_auth"), + reply_markup=[[{"text": "Хорошо", "callback": self.close}]], + ) + else: + await call.delete() + except Exception as e: + await call.answer(f"Error! Check logs.\n{e}", show_alert=True) + + def close(self, call: InlineCall): + return call.delete() + + async def _get_lyrics(self, artist: str, track: str): + clean_track = re.sub(r"\(.*?\)|\[.*?\]", "", track).strip() + try: + async with aiohttp.ClientSession() as session: + async with session.get( + "https://lrclib.net/api/search", + params={"track_name": clean_track, "artist_name": artist}, + timeout=aiohttp.ClientTimeout(total=(self.config["request_timeout"])), + ) as resp: + if resp.status == 200: + res = await resp.json() + return res[0] if res else None + except asyncio.TimeoutError: + return {"timeout": True} + except Exception: + pass + return None + + def _parse_synced(self, synced_text: str) -> list: + lines = [] + for line in synced_text.split("\n"): + m = re.search(r"\[(\d+):(\d+\.\d+)\](.*)", line) + if m: + mins, secs, text = m.groups() + lines.append({ + "time": (int(mins) * 60 + float(secs)) * 1000, + "text": text.strip(), + }) + return lines + + def _build_content(self, artist, track, lines, plain, progress_ms, not_synced_str): + header = self.strings("header").format( + utils.escape_html(artist), + utils.escape_html(track), + ) + if lines: + curr_idx = 0 + for i, line in enumerate(lines): + if progress_ms >= line["time"]: + curr_idx = i + win_start = max(0, curr_idx - 1) + win_end = min(len(lines), curr_idx + 6) + rows = [] + for i in range(win_start, win_end): + t = lines[i]["text"] or self.config["dot"] + if i == curr_idx: + rows.append( + f"{self.config['emoji_current']} {utils.escape_html(t)}" + ) + else: + rows.append(f"{utils.escape_html(t)}") + return header + "\n".join(rows) + else: + return header + not_synced_str + f"
{utils.escape_html((plain or '')[:4000])}
" + + def _markup(self, song_url): + return [ + [{"text": "🔗 song.link", "url": song_url}], + [{"text": "❌ Close", "callback": self._close_cb}], + ] + + async def _close_cb(self, call): + for track_id, task in list(self._active_tasks.items()): + task.cancel() + self._active_tasks.pop(track_id, None) + try: + await call.answer() + await call.delete() + except Exception: + pass + + async def run_loop(self, form, mod, track_id, artist_name, track_name, song_url, lines, plain, not_synced_str): + last_display = "" + try: + while True: + pb = await mod._YaMusicMod__get_now_playing() + if not pb or not pb.get("track") or pb["track"]["track_id"] != track_id: + try: + await form.edit( + self.strings("finished"), + reply_markup=[[{"text": "❌ Close", "callback": self._close_cb}]], + ) + except Exception: + pass + break + + prog = pb.get("progress_ms", 0) + content = self._build_content( + artist_name, track_name, lines, plain, prog, not_synced_str + ) + + if content != last_display: + try: + await form.edit(content, reply_markup=self._markup(song_url)) + last_display = content + except Exception: + break + + if not lines: + break + + await asyncio.sleep(self.config["lyrics_delay"]) + + except asyncio.CancelledError: + raise + except Exception: + pass + finally: + self._active_tasks.pop(track_id, None) + + @loader.command(ru_doc="- показать синхронизированный текст песни") + async def ynowlcmd(self, message: Message): + """- show synchronized lyrics for current YaMusic track""" + mod = self.lookup("YaMusic") + if not mod: + form = await self.inline.form("⏳", message=message) + await form.edit( + self.strings("no_YaMusicMod"), + reply_markup=[[{"text": "Install YaMusic", "callback": self.install_yamusic}]], + ) + return + + ya_token = mod.get("__config__")["token"] + if not ya_token: + await self.invoke("yguide", " ", "me") + form = await self.inline.form("⏳", message=message) + await form.edit( + self.strings("no_auth"), + reply_markup=[[{"text": "Хорошо", "callback": self.close}]], + ) + return + + playback = await mod._YaMusicMod__get_now_playing() + if not playback or not playback.get("track"): + return await utils.answer(message, self.strings("no_ym")) + + track = playback["track"] + track_id = track["track_id"] + artist_name = ", ".join(track["artist"]) + track_name = track["title"] + song_url = f"https://song.link/s/{track_id}" + + old = self._active_tasks.pop(track_id, None) + if old: + old.cancel() + + data = await self._get_lyrics(artist_name, track_name) + if data and data.get("timeout"): + return utils.answer( + message, + self.strings["timeout"] + ) + if not data or data.get("instrumental"): + track_and_artist = f"{utils.escape_html(track_name)} - {utils.escape_html(artist_name)}" + return await utils.answer( + message, + self.strings("no_lyrics").format(track_and_artist), + ) + + synced_raw = data.get("syncedLyrics") + plain = data.get("plainLyrics", "") + + lines = self._parse_synced(synced_raw) if synced_raw else [] + not_synced_str = self.strings("not_synced") + + prog = playback.get("progress_ms", 0) + initial_content = self._build_content( + artist_name, track_name, lines, plain, prog, not_synced_str + ) + + form = await self.inline.form( + text=initial_content, + message=message, + reply_markup=self._markup(song_url), + ) + + task = asyncio.create_task( + self.run_loop( + form=form, + mod=mod, + track_id=track_id, + artist_name=artist_name, + track_name=track_name, + song_url=song_url, + lines=lines, + plain=plain, + not_synced_str=not_synced_str, + ) + ) + self._active_tasks[track_id] = task + + # Fan-fantasizing + # Fa-fa-fa-fantasizing + # You and I-I-I + # When I close my eyes (my eyes) + # Nothings real + # Fantasizing (fantasizing) + # Bout you and I + # Cos you only hit my line + # When you wanna waste time + # I know you're so busy + # But trust me baby I'm not blind (blind) + # Uh oh, you and I + # We could never be + # Uh oh you and I + # Cos we will never be + # Uh oh, you and I + # No, we will never be + # That pretty picture that I painted in my mind (mind) + # So tell me what (tell me, tell me) + # The view is like + # With your head in the clouds + # And tell me what (tell me, tell me) + # It feels like to be right all the time + # You say that you love me + # But you don't even love yourself (no) + # Wanna get in my head + # But I ain't gonna let you close (no) + # Tryna control me + # But I ain't gon' play your game + # No more + # No I won't + # When I close (when I close) + # My eyes (my eyes) + # Nothing's real (no) + # Fantasizing (fantasize) + # bout you-you-you-you-you + # You-you-you-you + # You-you-you-you + # You-you-you + # And I + # You-you-you-you-you-you-you + # You-you-you-you-you-you-you + # You-you-you-you-you + # And I + # This is the last time I tell you + # Don't come round my way if you're just gon' waste my time + # And no, I won't be there for the long run + # No, not I + # But you never get (never get) + # The message, do you? + # You never seem (never seem) + # To grip an understanding + # That you emulate a ghost + # I pointed out all of your flaws + # But you still came up with excuses for em all + # So typical (so typical) + # You know it all + # So of course, I'm the one that's wrong (right?) + # When I close my eyes + # Nothings real + # Fan- fantasize + # Fa-fa-fa-fa + # Fantasizing + # Fa-fantasizing bout you and I \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/everlastingsummer.py b/SunnexGB/Heroku-Modules/everlastingsummer.py new file mode 100644 index 0000000..16dd3c4 --- /dev/null +++ b/SunnexGB/Heroku-Modules/everlastingsummer.py @@ -0,0 +1,486 @@ +# meta developer: @H_SunMods +#meta banner: https://i.ibb.co/LdN9FXjc/logo.webp +# __version__ +__version__ = ("alpha", "1.0", 0) + +import asyncio +import copy +import json +from urllib.request import Request, urlopen +from herokutl.types import Message +from .. import loader, utils +from ..types import InlineCall + + +prologue_dialogs_url = "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/ddialogs/prologue_only.json" +routes_url = "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/ddialogs/routes_prologue.json" +menu_background_url = "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/in_telegram_images/Start_Menu.jpg" +save_background_url = "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/in_telegram_images/Save_Menu.png" + + +@loader.tds +class EverlastingSummer(loader.Module): + """Встретив Семёна, главного героя игры, вы никогда бы не обратили на него внимания. Просто обычный молодой человек среди тысяч, даже сотен тысяч таких, как он, в каждом обычном городе. Но однажды с ним происходит нечто совершенно необычное: он засыпает в автобусе зимой и просыпается... посреди жаркого лета. Перед ним - "Совёнок" - пионерский лагерь, а за ним - его прежняя жизнь. Чтобы понять, что с ним произошло, Семёну придется познакомиться с местными жителями (и, возможно, даже найти любовь), сориентироваться в сложном лабиринте человеческих отношений и своих собственных проблем, а также разгадать тайны лагеря. И ответить на главный вопрос - как вернуться? Стоит ли ему возвращаться?""" + strings = { + "name": "EverlastingSummer", + "menu": "Пролог", + "disclaimer": ( + "Игра является плодом фантазии её разработчиков\n" + "и не ставит перед собой цели затронуть или иным\n" + "образом оскорбить кого-либо по религиозному,расовому,\n" + "социальному, экономическому или видовому признаку.\n" + "Также любое ущемление чувства прекрасного, активной\n" + "гражданской позиции или иных высоких душевных порывов\n" + "игроков разработчики оставляют на их совести.\n" + "Совпадения героев с вашими реальными (и воображаемыми)\n" + "знакомыми,соседями,коллегами, тульпами считать случайным.\n" + "Все героини достигли восемнадцатилетнего возраста,\n" + "и они дали письменное согласие на участие в игре\n" + "(выписка из истории болезни сценариста предоставляется по требованию).\n" + "При разработке не пострадало ни одного маскота, животного или человека. Приятной игры!" + ), + "bad": "Не удалось загрузить сценарий", + "end": "{}", + "save_header": "Сохранения", + "load_header": "Загрузить игру", + "default_route_question": "Что выберете?", + "or_game": "Игра", + "or_character": "Персонаж", + "cutscene_text": "💫", + "opening_title": "Опенинг", + "opening_next": "Пропустить опенинг", + "saved": "игра сохранена в слот № {}", + "loaded": "Сохранение № {} загружено", + "empty": "Слот {} пуст", + "rewrite": "Слот {} уже занят. Перезаписать?", + "state_slots_from_menu": "slots_from_menu", + "chapter_prologue": "prologue", + "save_action": "save", + "load_action": "load", + "mode_ask_rewrite": "ask_rewrite", + "mode_ended": "ended", + "mode_menu": "menu", + "mode_play": "play", + "mode_slots": "slots", + "type_label": "label", + "type_jump": "jump", + "type_scene": "scene", + "type_dialogue": "dialogue", + "type_narration": "narration", + "type_route": "route", + "type_opening": "opening", + } + + def __init__(self): + self.config = loader.ModuleConfig(loader.ConfigValue("cut_speed", 3)) + self.dialogs_url = prologue_dialogs_url + self.routes_url = routes_url + self.menu_image = menu_background_url + self.save_image = save_background_url + self.dialogs_data = None + self.routes_data = None + self.label_index = {} + + async def json_load(self, url: str): + last_error = None + for i in range(3): + try: + def run(): + req = Request(url, headers={"User-Agent": "Mozilla/5.0 HSunMods"}) + with urlopen(req, timeout=60) as x: + return json.loads(x.read().decode("utf-8")) + return await asyncio.to_thread(run) + except Exception as e: + last_error = e + if i < 2: + await asyncio.sleep(1.5 * (i + 1)) + raise last_error + + async def load_data(self, force: bool = False): + if self.dialogs_data is not None and self.routes_data is not None and not force: + return True + try: + self.dialogs_data = await self.json_load(self.dialogs_url) + self.routes_data = await self.json_load(self.routes_url) + except Exception: + return False + + prologue_nodes = self.dialogs_data.get(self.strings["chapter_prologue"]) + if not isinstance(prologue_nodes, list): + return False + self.dialogs_data = {self.strings["chapter_prologue"]: prologue_nodes} + + self.label_index = {} + for node_index, node in enumerate(prologue_nodes): + if isinstance(node, dict) and node.get("type") == self.strings["type_label"]: + self.label_index[node.get("name")] = (self.strings["chapter_prologue"], node_index) + return True + + def state_get(self): + return self.get( + "state", + { + "mode": self.strings["mode_menu"], + "chapter": self.strings["chapter_prologue"], + "idx": 0, + "pending": None, + "scene": {}, + "vars": {}, + }, + ) + + def state_set(self, state_data): + self.set("state", state_data) + + def slots_get(self): + return self.get("slots", {}) + + def slots_set(self, slots_data): + self.set("slots", slots_data) + + async def ui(self, target, text, kb=None, photo=None): + if isinstance(target, InlineCall): + if photo: + try: + return await target.edit(text, reply_markup=kb, photo=photo) + except TypeError: + try: + return await target.edit(text, reply_markup=kb, file=photo) + except Exception: + pass + except Exception: + pass + try: + return await target.edit(text, reply_markup=kb) + except Exception: + raise + if photo: + try: + return await utils.answer(target, text, reply_markup=kb, photo=photo) + except Exception: + pass + return await utils.answer(target, text, reply_markup=kb) + + def menu_kb(self): + return [ + [{"text": "Начать пролог", "callback": self.new_game}], + [{"text": "Сохранения", "callback": self.save_menu, "args": (self.strings["load_action"],)}], + [{"text": "Дисклеймер", "callback": self.disclaimer_msg}], + ] + + def start_kb(self): + return [ + [{"text": "➤", "callback": self.next_step}], + [{"text": "Сохранить", "callback": self.save_menu, "args": (self.strings["save_action"],)}], + [{"text": "Меню", "callback": self.menu}], + ] + + def save_kb(self, mode: str): + slots = self.slots_get() + row = [] + for i in range(1, 6): + k = str(i) + b = {"text": k, "callback": self.save_action, "args": (mode, i)} + if k in slots: + b["style"] = "success" + row.append(b) + return [row, [{"text": "Назад", "callback": self.back_from_saves}]] + + def choice_kb(self, route_id: str): + opts = self.routes_data.get(route_id, {}).get("options", {}) + rows = [] + for i, txt in enumerate(opts.keys()): + rows.append([{"text": txt, "callback": self.pick_option, "args": (route_id, i)}]) + rows.append([{"text": "Сохранить", "callback": self.save_menu, "args": (self.strings["save_action"],)}]) + rows.append([{"text": "Меню", "callback": self.menu}]) + return rows + + def opening_kb(self): + return [[{"text": self.strings["opening_next"], "callback": self.opening_done}]] + + def state_preservation(self, state_data): + return { + "chapter": state_data.get("chapter"), + "idx": state_data.get("idx", 0), + "part": state_data.get("part", 0), + "pending": copy.deepcopy(state_data.get("pending")), + "scene": copy.deepcopy(state_data.get("scene")), + "vars": copy.deepcopy(state_data.get("vars", {})), + "mode": self.strings["mode_play"], + } + + def scene_photo(self, state_data): + u = (state_data.get("scene") or {}).get("raw_url") + return u if isinstance(u, str) else None + + def wait_text(self, t: str): + return [x.strip() for x in t.split("{w}") if x.strip()] or [t] + + def render_dialogs(self, state_data): + pending_node = state_data.get("pending") or {} + if pending_node.get("type") == self.strings["type_dialogue"]: + who = (pending_node.get("character") or pending_node.get("char_id") or self.strings["or_character"]).strip() + txt = " ".join(pending_node.get("parts", [])[: pending_node.get("part", 1)]) + return f"{who}:\n
{txt}
" + if pending_node.get("type") == self.strings["type_narration"]: + return " ".join(pending_node.get("parts", [])[: pending_node.get("part", 1)]) + return "" + + def is_ending_label(self, name: str): + return name in self.routes_data.get("endings", {}).get("labels", []) + + async def go(self, target, state_data): + cut_scene_speed_fallback = self.config["cut_speed"] + while True: + chapter_nodes = self.dialogs_data.get(self.strings["chapter_prologue"], []) + node_index = state_data.get("idx", 0) + if node_index >= len(chapter_nodes): + ending_name = self.routes_data.get("endings", {}).get("fallback", "main_bad_ending") + state_data["mode"] = self.strings["mode_ended"] + state_data["ending"] = ending_name + self.state_set(state_data) + await self.ui(target, self.strings["end"].format(ending_name), self.menu_kb(), self.scene_photo(state_data)) + return + + current_node = chapter_nodes[node_index] + node_type = current_node.get("type") + + if node_type == self.strings["type_label"]: + if self.is_ending_label(current_node.get("name")): + state_data["mode"] = self.strings["mode_ended"] + state_data["ending"] = current_node.get("name") + self.state_set(state_data) + await self.ui(target, self.strings["end"].format(current_node.get("name")), self.menu_kb(), self.scene_photo(state_data)) + return + state_data["idx"] = node_index + 1 + continue + + if node_type == self.strings["type_jump"]: + jump_target = self.label_index.get(current_node.get("label")) + if jump_target: + state_data["chapter"], state_data["idx"] = jump_target + else: + state_data["idx"] = node_index + 1 + continue + + if node_type == self.strings["type_scene"]: + state_data["scene"] = { + "raw_url": current_node.get("raw_url"), + "location": current_node.get("location"), + "action": current_node.get("action"), + "kind": current_node.get("kind"), + "name": current_node.get("name"), + } + state_data["idx"] = node_index + 1 + next_node = chapter_nodes[state_data["idx"]] if state_data["idx"] < len(chapter_nodes) else None + if isinstance(next_node, dict) and next_node.get("type") == self.strings["type_scene"]: + scene_duration = current_node.get("duration") + if scene_duration is None: + if cut_scene_speed_fallback is None: + scene_delay_seconds = 0.0 + else: + try: + scene_delay_seconds = float(cut_scene_speed_fallback) + except Exception: + scene_delay_seconds = 0.0 + else: + try: + scene_delay_seconds = float(scene_duration) + except Exception: + scene_delay_seconds = 0.0 + if scene_delay_seconds < 0: + scene_delay_seconds = 0.0 + self.state_set(state_data) + await self.ui(target, self.strings["cutscene_text"], None, self.scene_photo(state_data)) + if scene_delay_seconds > 0: + await asyncio.sleep(scene_delay_seconds) + continue + continue + + if node_type in {self.strings["type_dialogue"], self.strings["type_narration"]}: + state_data["pending"] = { + "type": node_type, + "parts": self.wait_text(current_node.get("text", "")), + "part": 1, + "char_id": current_node.get("char_id"), + "character": current_node.get("character"), + } + state_data["mode"] = self.strings["mode_play"] + self.state_set(state_data) + await self.ui(target, self.render_dialogs(state_data), self.start_kb(), self.scene_photo(state_data)) + return + + if node_type == self.strings["type_route"]: + route_id = current_node.get("id") + route_question = self.routes_data.get(route_id, {}).get("question") or self.strings["default_route_question"] + state_data["pending"] = {"type": self.strings["type_route"], "id": route_id} + state_data["mode"] = self.strings["mode_play"] + self.state_set(state_data) + await self.ui(target, route_question, self.choice_kb(route_id), self.scene_photo(state_data)) + return + + if node_type == self.strings["type_opening"] or (node_type == self.strings["type_label"] and current_node.get("kind") == self.strings["type_opening"]): + state_data["scene"] = { + "raw_url": current_node.get("raw_url"), + "location": current_node.get("location"), + "action": current_node.get("action"), + "kind": current_node.get("kind") or self.strings["type_opening"], + "name": current_node.get("name") or self.strings["type_opening"], + } + state_data["pending"] = {"type": self.strings["type_opening"]} + state_data["mode"] = self.strings["mode_play"] + state_data["idx"] = node_index + 1 + self.state_set(state_data) + await self.ui(target, self.strings["opening_title"], self.opening_kb(), self.scene_photo(state_data)) + return + + state_data["idx"] = node_index + 1 + + async def menu(self, call: InlineCall): + state = self.state_get() + state["mode"] = self.strings["mode_menu"] + state["pending"] = None + self.state_set(state) + await self.ui(call, self.strings["menu"], self.menu_kb(), self.menu_image) + + async def disclaimer_msg(self, call: InlineCall): + await self.ui(call, self.strings["disclaimer"], [[{"text": "Назад", "callback": self.menu}]], self.menu_image) + + async def new_game(self, call: InlineCall): + ok = await self.load_data(force=False) + if not ok: + await call.answer(self.strings["bad"], show_alert=True) + return + state = self.state_get() + state.update( + { + "chapter": self.strings["chapter_prologue"], + "idx": 0, + "part": 0, + "pending": None, + "scene": {}, + "vars": {}, + "mode": self.strings["mode_play"], + } + ) + self.state_set(state) + await self.go(call, state) + + async def next_step(self, call: InlineCall): + state = self.state_get() + pending_node = state.get("pending") or {} + if pending_node.get("type") == self.strings["type_opening"]: + await self.menu(call) + return + if pending_node.get("type") in {self.strings["type_dialogue"], self.strings["type_narration"]}: + if pending_node.get("part", 1) < len(pending_node.get("parts", [])): + pending_node["part"] += 1 + state["pending"] = pending_node + self.state_set(state) + await self.ui(call, self.render_dialogs(state), self.start_kb(), self.scene_photo(state)) + return + state["idx"] += 1 + state["pending"] = None + self.state_set(state) + await self.go(call, state) + return + await self.go(call, state) + + async def opening_done(self, call: InlineCall): + state = self.state_get() + state["pending"] = None + state["mode"] = self.strings["mode_menu"] + self.state_set(state) + await self.menu(call) + + async def pick_option(self, call: InlineCall, route_id: str, option_index: int): + state = self.state_get() + option_items = list((self.routes_data.get(route_id, {}).get("options") or {}).items()) + if option_index < 0 or option_index >= len(option_items): + return + _, option_data = option_items[option_index] + jump_label = option_data.get("jump") + if jump_label and jump_label in self.label_index: + state["chapter"], state["idx"] = self.label_index[jump_label] + else: + state["idx"] += 1 + state["pending"] = None + self.state_set(state) + await self.go(call, state) + + async def save_menu(self, call: InlineCall, mode: str): + state = self.state_get() + state[self.strings["state_slots_from_menu"]] = state.get("mode") == self.strings["mode_menu"] + state["mode"] = self.strings["mode_slots"] + self.state_set(state) + title_text = self.strings["save_header"] if mode == self.strings["save_action"] else self.strings["load_header"] + await self.ui(call, title_text, self.save_kb(mode), self.save_image) + + async def back_from_saves(self, call: InlineCall): + state = self.state_get() + if state.get(self.strings["state_slots_from_menu"]): + state[self.strings["state_slots_from_menu"]] = False + state["mode"] = self.strings["mode_menu"] + state["pending"] = None + self.state_set(state) + await self.menu(call) + return + if state.get("chapter") and state.get("mode") != self.strings["mode_menu"]: + state["mode"] = self.strings["mode_play"] + self.state_set(state) + pending_node = state.get("pending") + if pending_node and pending_node.get("type") == self.strings["type_route"]: + route_id = pending_node.get("id") + question_text = self.routes_data.get(route_id, {}).get("question") or self.strings["default_route_question"] + await self.ui(call, question_text, self.choice_kb(route_id), self.scene_photo(state)) + return + await self.ui(call, self.render_dialogs(state) or self.strings["or_game"], self.start_kb(), self.scene_photo(state)) + return + await self.menu(call) + + async def save_action(self, call: InlineCall, mode: str, n: int): + slots = self.slots_get() + state = self.state_get() + slot_key = str(n) + if mode == self.strings["save_action"]: + if slot_key in slots: + state["mode"] = self.strings["mode_ask_rewrite"] + self.state_set(state) + kb = [ + [ + {"text": "Да", "callback": self.rewrite_true, "args": (n,)}, + {"text": "Нет", "callback": self.save_menu, "args": (self.strings["save_action"],)}, + ], + [{"text": "Назад", "callback": self.back_from_saves}], + ] + await self.ui(call, self.strings["rewrite"].format(n), kb, self.save_image) + return + slots[slot_key] = self.state_preservation(state) + self.slots_set(slots) + await call.answer(self.strings["saved"].format(n), show_alert=True) + await self.save_menu(call, self.strings["save_action"]) + return + if slot_key not in slots: + await call.answer(self.strings["empty"].format(n), show_alert=True) + return + loaded_state = copy.deepcopy(slots[slot_key]) + self.state_set(loaded_state) + await call.answer(self.strings["loaded"].format(n), show_alert=True) + await self.go(call, loaded_state) + + async def rewrite_true(self, call: InlineCall, n: int): + slots = self.slots_get() + state = self.state_get() + slots[str(n)] = self.state_preservation(state) + self.slots_set(slots) + await call.answer(self.strings["saved"].format(n), show_alert=True) + await self.save_menu(call, self.strings["save_action"]) + + @loader.command() + async def bl(self, message: Message): + """Запустить ваше бесконечное лето,нууу точнее пока что его пролог.""" + ok = await self.load_data() + if not ok: + await utils.answer(message, self.strings["bad"]) + return + await self.ui(message, self.strings["menu"], self.menu_kb(), self.menu_image) diff --git a/SunnexGB/Heroku-Modules/full.txt b/SunnexGB/Heroku-Modules/full.txt new file mode 100644 index 0000000..9598d80 --- /dev/null +++ b/SunnexGB/Heroku-Modules/full.txt @@ -0,0 +1,9 @@ +SpotiSaver +SpotifyLyrics +YandexLyrics +HerokuTime +Shazamio +ForkCircles +Mikuru +pairavatars +ASCII \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/pairavatars.py b/SunnexGB/Heroku-Modules/pairavatars.py new file mode 100644 index 0000000..da1cbd7 --- /dev/null +++ b/SunnexGB/Heroku-Modules/pairavatars.py @@ -0,0 +1,57 @@ +# requires: https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz +# meta banner: https://i.ibb.co/yFVJ6L5D/pairavs.webp +# meta developer: @SunnexGB +# я хочу красивый баннер,не осуждайте. +# add version +__version__ = (1, 0, 1) + +import io +from PIL import Image +from herokutl.types import Message +from .. import loader, utils + +@loader.tds +class pairavatars(loader.Module): + """Create pair avatars""" + strings = { + "name": "PairAvatars", + "no_reply": "🚫 | Reply to photo!", + "processing": "💫 | Processing...", + "error": "⚠️ | Error`" + } + + strings_ru = { + "_cls_doc": "Создаёт парные авы", + "no_reply": "🚫 | Ответь на фото!", + "processing": "💫 | Обработка...", + "error": "⚠️ | Еррорь" + } + + @loader.command(ru_doc="- Создать парные аватарки (команда работает ТОЛЬКО ответом на сообщение)", only_reply=True) + async def pairavs(self, message: Message): + """- Create pair avatars (command work ONLY reply message)""" + reply = await message.get_reply_message() + processing_msg = await utils.answer(message, self.strings["processing"]) + try: + tmp_data = await message.client.download_media(reply.photo, bytes) + img = Image.open(io.BytesIO(tmp_data)) + w, h = img.size + center = w // 2 + left_part = img.crop((0, 0, center, h)) + right_part = img.crop((center, 0, w, h)) + out_left, out_right = io.BytesIO(), io.BytesIO() + left_part.save(out_left, "JPEG", quality=100) + right_part.save(out_right, "JPEG", quality=100) + out_left.name, out_right.name = "left.jpg", "right.jpg" + out_left.seek(0) + out_right.seek(0) + + await message.client.send_file( + message.chat_id, + [out_left, out_right], + reply_to=reply.id + ) + + await processing_msg.delete() + except Exception: + await utils.answer(processing_msg, self.strings["error"]) \ No newline at end of file diff --git a/SunnexGB/Heroku-Modules/spotify_ph.py b/SunnexGB/Heroku-Modules/spotify_ph.py new file mode 100644 index 0000000..22634f5 --- /dev/null +++ b/SunnexGB/Heroku-Modules/spotify_ph.py @@ -0,0 +1,124 @@ +# meta developer: @H_SunMods +# meta banner: https://r2.fakecrime.bio/uploads/7103b4ca-5fb1-4512-8a70-e720780c29c8.jpg +# current ver +__version__ = (1, 0, 0) + +import logging +from .. import loader, utils + +logger = logging.getLogger(__name__) + +@loader.tds +class spotifyph(loader.Module): + """Progress bar current track in spotify""" + + strings = { + "name": "spotify_ph", + "start_duration": "🎶🎶", + "start_full_duration": "🎶🎶", + "mid_duration": "🎶", + "empty_mid": "🎶", + "end_duration": "🎶", + "end_duration_full": "🎶", + "empty_end": "🎶", + "no_prem_start_duration": "ᵔᴥᵔ [---", + "no_prem_start_full_duration": "ᵔᴥᵔ [~~~", + "no_prem_mid_duration": "~~~", + "no_prem_empty_mid": "---", + "no_prem_end_duration_full": "~~~]", + "no_prem_empty_end": "---]", + "not_installed": "SpotifyMod is not installed", + "nothing_plays": "Nothing plays", + "sp_duration_desc": "Progress bar", + "sp_track_desc": "Artist and song", + "err": "Error`" + } + + strings_ru = { + "_cls_doc": "Прогресс бар играющего трека в спотифай", + "not_installed": "SpotifyMod не установлен", + "nothing_plays": "Ничего не играет", + "sp_duration_desc": "Прогресс бар", + "sp_track_desc": "Автор и песня", + "err": "Еррорь" + } + + async def client_ready(self, client, db): + self._client = client + utils.register_placeholder("sp_duration", self.sp_duration, self.strings("sp_duration_desc")) + utils.register_placeholder("sp_track", self.get_sp_track, self.strings("sp_track_desc")) + + def __init__(self): + self.config = loader.ModuleConfig( + loader.ConfigValue( + "show_text_time", + True, + "show text time", + validator=loader.validators.Boolean(), + ) + ) + + async def get_sp_track(self): + try: + s = self.lookup("SpotifyMod") + if not s or not s.sp: + return self.strings("not_installed") + + p = s.sp.current_user_playing_track() + if not (p and p.get('item')): + return self.strings("nothing_plays") + + artist = p['item']['artists'][0]['name'] + track_name = p['item']['name'] + return utils.escape_html(f"{artist} — {track_name}") + except Exception as e: + logger.error(f"Error in sp_track: {e}") + return self.strings("err") + + async def sp_duration(self): + s = self.lookup("SpotifyMod") + if not s or not s.sp: + return self.strings("not_installed") + + playback = s.sp.current_playback() + if not playback or not playback.get("item"): + return self.strings("nothing_plays") + + prog_ms = playback.get("progress_ms", 0) + dur_ms = playback["item"].get("duration_ms", 0) + + if dur_ms == 0: + return "00:00 / 00:00" + + percent = (prog_ms / dur_ms) * 100 + filled_units = int(percent // 16.66) + + # Логика для нищих + user = getattr(self._client, "heroku_me", getattr(self._client, "me", None)) + is_premium = getattr(user, "premium", False) + pref = "" if is_premium else "no_prem_" + + if filled_units >= 1: + start_pos = f"{pref}start_full_duration" + else: + start_pos = f"{pref}start_duration" + bar = self.strings(start_pos) + + for i in range(2, 6): + if filled_units >= i: + mid_pos = f"{pref}mid_duration" + else: + mid_pos = f"{pref}empty_mid" + bar += self.strings(mid_pos) + + if filled_units >= 6: + end_key = f"{pref}end_duration_full" + else: + end_key = f"{pref}empty_end" + bar += self.strings(end_key) + + if self.config["show_text_time"]: + prog_t = f"{prog_ms//60000:02}:{(prog_ms//1000)%60:02}" + dur_t = f"{dur_ms//60000:02}:{(dur_ms//1000)%60:02}" + return f"{bar} {prog_t} / {dur_t}" + return bar \ No newline at end of file diff --git a/coddrago/modules/YaMusic.py b/coddrago/modules/YaMusic.py index 036e671..01becc2 100644 --- a/coddrago/modules/YaMusic.py +++ b/coddrago/modules/YaMusic.py @@ -26,7 +26,6 @@ from .. import loader, utils logger = logging.getLogger(__name__) - class Banners: def __init__( self, @@ -324,6 +323,16 @@ class YaMusicMod(loader.Module): "name": "YaMusic" } + duration_placeholder = { + "start_duration": "☀️☀️", + "start_full_duration": "☀️☀️", + "closed_duration": "☀️", + "empty_mid": "☀️", + "empty_closed_duration_duration": "☀️", + "end_duration_full": "☀️", + "empty_closed_duration": "☀️", + } + def __init__(self): self.config = loader.ModuleConfig( loader.ConfigValue( @@ -553,67 +562,19 @@ class YaMusicMod(loader.Module): return "0%" percent = (progress / duration) * 100 + fill_logic = int(percent // 16.66) - s_less_10 = ( - "" - "" - "" - "" - "" - "" - ) - - s_10_to_20 = ( - "" - "" - "" - "" - "" - "" - ) - - s_30_to_40 = ( - "" - "" - "" - "" - "" - "" - ) - - s_over_50 = ( - "" - "" - "" - "" - "" - "" - ) - - s_over_80 = ( - "" - "" - "" - "" - "" - "" - ) - - if percent < 10: - return s_less_10 - elif percent < 20: - return s_10_to_20 - elif percent < 30: - return s_10_to_20 - elif percent < 40: - return s_30_to_40 - elif percent < 50: - return s_30_to_40 - elif percent < 80: - return s_over_50 + bar = self.duration_placeholder["start_full_duration"] if fill_logic >= 1 else self.duration_placeholder["start_duration"] + for i in range(2, 6): + if fill_logic >= i: + bar += self.duration_placeholder["closed_duration"] + else: + bar += self.duration_placeholder["empty_mid"] + if fill_logic >= 6: + bar += self.duration_placeholder["end_duration_full"] else: - return s_over_80 - + bar += self.duration_placeholder["empty_closed_duration"] + return bar except Exception as e: return f"Error: {e}" diff --git a/modules.json b/modules.json index 27ae360..dfe3e4c 100644 --- a/modules.json +++ b/modules.json @@ -1,5 +1,203 @@ { "modules": { + "LimokaLegacy.py": { + "name": "Limoka", + "description": "Modules are now in one place with easy searching!", + "cls_doc": { + "ru": "Модули теперь в одном месте с простым и удобным поиском!" + }, + "meta": { + "pic": null, + "banner": null, + "developer": "@limokanews" + }, + "commands": [ + { + "limoka": "[query / nothing] - Search modules | (RU) [запрос / ничего] — Поиск модулей" + }, + { + "lshistory": "[clear] - Show or clear search history | (RU) [clear] — Показать или очистить историю поиска" + } + ], + "new_commands": [ + { + "name": "limoka", + "original_name": "limokacmd", + "description": { + "default": "[query / nothing] - Search modules", + "ru": "[запрос / ничего] — Поиск модулей" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "lshistory", + "original_name": "lshistorycmd", + "description": { + "default": "[clear] - Show or clear search history", + "ru": "[clear] — Показать или очистить историю поиска" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Limoka", + "wait": "
Just wait\n🔍 A search is underway among {count} modules for the query: {query}\n{fact}
", + "found_header": "
🔍 Found module {name} by query: {query}\n\nℹ️ Description: {description}\n🧑‍💻 Developer: {username}\n\n🏷 Tags: {tags}\n\n
", + "found_body": "{commands}", + "found_footer": "
\n🪄 {prefix}dlm {url}{module_path}
", + "caption_short": "
🔍 {safe_name}\nℹ️ Description: {safe_desc}\n🧑‍💻 Dev: {dev_username}\n🪄 {prefix}dlm {module_path}
", + "command_template": "{emoji} {prefix}{command} — {description}\n", + "inline_handler_template": "{inline_bot} {command} — {description}\n", + "emojis": { + "1": "1️⃣", + "2": "2️⃣", + "3": "3️⃣", + "4": "4️⃣", + "5": "5️⃣", + "6": "6️⃣", + "7": "7️⃣", + "8": "8️⃣", + "9": "9️⃣" + }, + "404": "
Not found by query: {query}
", + "noargs": "
No args
", + "?": "
🔎 Request too short / not found
", + "no_info": "
No information
", + "facts": [ + "
🛡 The limoka catalog is carefully moderated!
", + "
🚀 Limoka performance allows you to search for modules quickly!
" + ], + "inline404": "
Not found
", + "inline?": "
Request too short / not found
", + "inlinenoargs": "
Please, enter query
", + "history": "
🔎 Your search history:\n{history}
", + "filter_menu": "Choose filters", + "filter_cat": "📑 Filter by Category", + "apply_filters": "✅ Apply Filters", + "clear_filters": "🗑 Clear Filters", + "back_to_results": "🔙 Back to Results", + "empty_history": "
🔎 Your search history is empty!
", + "enter_query": "
🔍 Enter new search query:
", + "global_search": "
🔍 Global search for {query} — found {count} modules
", + "change_query": "🔍 Change query", + "no_modules": "
No modules available.
", + "filter_title": "🏷 Filters", + "category_title": "📂 Categories", + "selected_categories": "
✅ Selected categories: {categories}
", + "no_categories": "
No categories found in the module database
", + "select_category": "
Select categories for query: {query}\n(You can select multiple)
", + "back": "🔙 Back", + "category": "📁 {category}", + "no_category": "
No category
", + "global_button": "🌍 Results", + "filtered_button": "🏷️ Filtered search", + "inline_search": "🔍 Search in Limoka", + "inline_no_results": "
❌ No modules found
", + "inline_error": "
❌ Search error occurred
", + "inline_short_query": "
❌ Query too short (min 2 chars)
", + "inline_switch_pm": "💬 Open in chat", + "inline_switch_pm_text": "🔍 Results for: {query}", + "inline_start_message": "
🔍 Limoka Search\nType module name or keyword
", + "first_page": "
This is the first page!
", + "last_page": "
This is the last page!
", + "display_error": "
Error displaying module. Please try again.
", + "error_occurred": "
An error occurred. Please try again.
", + "start_search_form": "
🔍 Limoka Search\nEnter your query to search for modules:
", + "global_search_form": "
🔍 Global Search\nEnter your query to search ALL modules without filters:
", + "history_cleared": "
🧹 Search history cleared!
", + "invalid_history_arg": "
Invalid argument for history command. Use:\n.lshistory - show history\n.lshistory clear - clear history
", + "close": "❌ Close", + "watcher_no_tag": "
❌ Invalid message format. No #limoka tag found.
", + "watcher_invalid_format": "
❌ Invalid format. Expected: #limoka:path:signature
", + "watcher_signature_invalid": "
❌ Signature invalid! Installation aborted.
", + "watcher_loader_missing": "
❌ Loader module not found.
", + "watcher_module_not_found": "
❌ Module not found in Limoka database: {path}
", + "watcher_critical": "
❌ Critical error: {error}
", + "tags": { + "herokutrusted": "Heroku Trusted", + "hikkatrusted": "Hikka Trusted", + "nonactive": "Non-Active Repository", + "nonlongermaintained": "No Longer Maintained Repository", + "newbie": "Newbie" + }, + "indexing_in_progress": "
⚠️ Database is busy, try again later. If issue persists, try removing limoka_index in the userbot's root folder. If error persists again, report to developers
", + "body_page": "Commands", + "name_ru": "Limoka", + "wait_ru": "
Подождите\n🔍 Идёт поиск среди {count} модулей по запросу: {query}\n{fact}
", + "found_header_ru": "
🔍 Найден модуль {name} по запросу: {query}
\n\n
ℹ️ Описание: {description}
\n
🧑‍💻 Разработчик: {username}
\n\n
🏷 Теги: {tags}
\n\n", + "found_body_ru": "{commands}", + "found_footer_ru": "\n
🪄 {prefix}dlm {url}{module_path}
", + "caption_short_ru": "
🔍 {safe_name}\nℹ️ Описание: {safe_desc}\n🧑‍💻 Разработчик: {dev_username}\n🪄 {prefix}dlm {module_path}
", + "command_template_ru": "
{emoji} {prefix}{command} — {description}
\n", + "inline_handler_template_ru": "{inline_bot} {command} — {description}\n", + "404_ru": "
Не найдено по запросу: {query}
", + "noargs_ru": "
Нет аргументов
", + "?_ru": "
🔎 Запрос слишком короткий / не найден
", + "no_info_ru": "
Нет информации
", + "inline404_ru": "
Не найдено
", + "inline?_ru": "
Запрос слишком короткий / не найден
", + "inlinenoargs_ru": "
Введите запрос
", + "history_ru": "
🔎 История поиска:\n{history}
", + "filter_menu_ru": "Выберите фильтры", + "filter_cat_ru": "📑 Фильтр по категориям", + "apply_filters_ru": "✅ Применить фильтры", + "clear_filters_ru": "🗑 Очистить фильтры", + "back_to_results_ru": "🔙 Вернуться к результатам", + "empty_history_ru": "
🔎 История поиска пуста!
", + "enter_query_ru": "
🔍 Введите новый поисковый запрос:
", + "global_search_ru": "
🔍 Глобальный поиск по {query} — найдено {count} модулей
", + "change_query_ru": "🔍 Изменить запрос", + "no_modules_ru": "
Модули недоступны.
", + "filter_title_ru": "🏷 Фильтры", + "category_title_ru": "📂 Категории", + "selected_categories_ru": "
✅ Выбранные категории: {categories}
", + "no_categories_ru": "
Категории не найдены в базе модулей
", + "select_category_ru": "
Выберите категории для запроса: {query}\n(Можно выбрать несколько)
", + "back_ru": "🔙 Назад", + "category_ru": "📁 {category}", + "no_category_ru": "
Без категории
", + "global_button_ru": "🌍 Результаты", + "filtered_button_ru": "🏷️ Поиск с фильтрами", + "inline_search_ru": "🔍 Поиск в Limoka", + "inline_no_results_ru": "
❌ Модули не найдены
", + "inline_error_ru": "
❌ Ошибка поиска
", + "inline_short_query_ru": "
❌ Запрос слишком короткий (мин. 2 символа)
", + "inline_switch_pm_ru": "💬 Открыть в чате", + "inline_switch_pm_text_ru": "🔍 Результаты для: {query}", + "inline_start_message_ru": "
🔍 Limoka Поиск\nВведите название модуля или ключевое слово
", + "first_page_ru": "
Это первая страница!
", + "last_page_ru": "
Это последняя страница!
", + "display_error_ru": "
Ошибка отображения модуля. Пожалуйста, попробуйте еще раз.
", + "error_occurred_ru": "
Произошла ошибка. Пожалуйста, попробуйте еще раз.
", + "start_search_form_ru": "
🔍 Limoka Поиск\nВведите ваш запрос для поиска модулей:
", + "global_search_form_ru": "
🔍 Глобальный Поиск\nВведите запрос для поиска ВСЕХ модулей без фильтров:
", + "history_cleared_ru": "
🧹 История поиска очищена!
", + "invalid_history_arg_ru": "
Неверный аргумент для команды истории. Используйте:\n.lshistory - показать историю\n.lshistory clear - очистить историю
", + "close_ru": "❌ Закрыть", + "watcher_no_tag_ru": "
❌ Неверный формат сообщения. Тег #limoka не найден.
", + "watcher_invalid_format_ru": "
❌ Неверный формат. Ожидается: #limoka:path:signature
", + "watcher_signature_invalid_ru": "
❌ Неверная подпись! Установка отменена.
", + "watcher_loader_missing_ru": "
❌ Модуль загрузчика не найден.
", + "watcher_module_not_found_ru": "
❌ Модуль не найден в базе Limoka: {path}
", + "watcher_critical_ru": "
❌ Критическая ошибка: {error}
", + "indexing_in_progress_ru": "
⚠️ База данных занята, попробуйте снова через несколько секунд. Если ошибка сохраняется, попробуйте удалить limoka_index в корневой папке юзербота. Если ошибка сохраняется снова, сообщите разработчикам
", + "body_page_ru": "Команды" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, "Limoka.py": { "name": "ModuleRepository", "description": "Manages module data and filtering.", @@ -17,27 +215,1955 @@ "has_on_unload": false, "class_cmd_names": {} }, - "LimokaLegacy.py": { - "name": "LimokaLegacy", + "SunnexGB/Heroku-Modules/everlastingsummer.py": { + "name": "EverlastingSummer", + "description": "Встретив Семёна, главного героя игры, вы никогда бы не обратили на него внимания. Просто обычный молодой человек среди тысяч, даже сотен тысяч таких, как он, в каждом обычном городе. Но однажды с ним происходит нечто совершенно необычное: он засыпает в автобусе зимой и просыпается... посреди жаркого лета. Перед ним - \"Совёнок\" - пионерский лагерь, а за ним - его прежняя жизнь. Чтобы понять, что с ним произошло, Семёну придется познакомиться с местными жителями (и, возможно, даже найти любовь), сориентироваться в сложном лабиринте человеческих отношений и своих собственных проблем, а также разгадать тайны лагеря. И ответить на главный вопрос - как вернуться? Стоит ли ему возвращаться?", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": "@H_SunMods" + }, + "commands": [ + { + "bl": "Запустить ваше бесконечное лето,нууу точнее пока что его пролог." + } + ], + "new_commands": [ + { + "name": "bl", + "original_name": "bl", + "description": { + "default": "Запустить ваше бесконечное лето,нууу точнее пока что его пролог." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "EverlastingSummer", + "menu": "Пролог", + "disclaimer": "Игра является плодом фантазии её разработчиков\nи не ставит перед собой цели затронуть или иным\nобразом оскорбить кого-либо по религиозному,расовому,\nсоциальному, экономическому или видовому признаку.\nТакже любое ущемление чувства прекрасного, активной\nгражданской позиции или иных высоких душевных порывов\nигроков разработчики оставляют на их совести.\nСовпадения героев с вашими реальными (и воображаемыми)\nзнакомыми,соседями,коллегами, тульпами считать случайным.\nВсе героини достигли восемнадцатилетнего возраста,\nи они дали письменное согласие на участие в игре\n(выписка из истории болезни сценариста предоставляется по требованию).\nПри разработке не пострадало ни одного маскота, животного или человека. Приятной игры!", + "bad": "Не удалось загрузить сценарий", + "end": "{}", + "save_header": "Сохранения", + "load_header": "Загрузить игру", + "default_route_question": "Что выберете?", + "or_game": "Игра", + "or_character": "Персонаж", + "cutscene_text": "💫", + "opening_title": "Опенинг", + "opening_next": "Пропустить опенинг", + "saved": "игра сохранена в слот № {}", + "loaded": "Сохранение № {} загружено", + "empty": "Слот {} пуст", + "rewrite": "Слот {} уже занят. Перезаписать?", + "state_slots_from_menu": "slots_from_menu", + "chapter_prologue": "prologue", + "save_action": "save", + "load_action": "load", + "mode_ask_rewrite": "ask_rewrite", + "mode_ended": "ended", + "mode_menu": "menu", + "mode_play": "play", + "mode_slots": "slots", + "type_label": "label", + "type_jump": "jump", + "type_scene": "scene", + "type_dialogue": "dialogue", + "type_narration": "narration", + "type_route": "route", + "type_opening": "opening" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/ForkCircles.py": { + "name": "ForkCircles", + "description": "rounds everything - reply to message", + "cls_doc": { + "ru": "Округляет всё - ответом на сообщение" + }, + "meta": { + "pic": "https://r2.fakecrime.bio/uploads/ef6d3ed1-6378-4bc4-aaad-d2bdeeaa4bbd.jpg", + "banner": "https://r2.fakecrime.bio/uploads/ef6d3ed1-6378-4bc4-aaad-d2bdeeaa4bbd.jpg", + "developer": "@SunnexGB" + }, + "commands": [ + { + "round": "" + } + ], + "new_commands": [ + { + "name": "round", + "original_name": "roundcmd", + "description": { + "default": "" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "ForkCircles", + "processing_image": "Processing image💬", + "processing_video": "Processing video💬", + "no_reply": "🤚|reply to image/sticker or video/gif!", + "download": "downloading💬", + "ffprobe_failed": "🤚|error`ffmpeg is installed?", + "ffmpeg_failed": "🤚|ffmpeg error`: {error}", + "processing_image_ru": "Обработка изображения💬", + "processing_video_ru": "Обработка видео💬", + "no_reply_ru": "🤚|ответьте на изображение/стикер или видео/gif!", + "download_ru": "Скачивание💬", + "ffprobe_failed_ru": "🤚|еррорь ffmpeg установил?", + "ffmpeg_failed_ru": "🤚|ffmpeg еррорь: {error}" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/HerokuTime.py": { + "name": "HerokuTime", + "description": "shows how much heroku you use in total (since installing placeholder)", + "cls_doc": {}, + "meta": { + "pic": "https://r2.fakecrime.bio/uploads/e19c2179-d2e9-4206-b783-25d6c0eb72eb.jpg", + "banner": "https://r2.fakecrime.bio/uploads/e19c2179-d2e9-4206-b783-25d6c0eb72eb.jpg", + "developer": "@SunnexGB", + "fhsdesc": "Плейсхолдер, placeholder, Time, Время, Статистика, Stats" + }, + "commands": [], + "new_commands": [], + "inline_handlers": [], + "strings": { + "name": "HerokuTime", + "sec": "sec", + "min": "min", + "hour": "h", + "cls_doc_ru": "показывает сколько вы используете всего хероку(с момента установки плейсхолдера)", + "sec_ru": "сек", + "min_ru": "мин", + "hour_ru": "ч" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/Shazamio.py": { + "name": "Shazamio", + "description": "Music recognition module", + "cls_doc": { + "ru": "Модуль для распознования музыки" + }, + "meta": { + "pic": "https://r2.fakecrime.bio/uploads/54b3c78d-38cb-4970-b925-18b7ec2b268d.jpg", + "banner": "https://r2.fakecrime.bio/uploads/54b3c78d-38cb-4970-b925-18b7ec2b268d.jpg", + "developer": "@SunnexGB" + }, + "commands": [ + { + "shazam": "Recognize music (Reply in video) | (RU) Распознать музыку (Ответом на видео)" + } + ], + "new_commands": [ + { + "name": "shazam", + "original_name": "shazam", + "description": { + "default": "Recognize music (Reply in video)", + "ru": "Распознать музыку (Ответом на видео)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Shazamio", + "processing": "Processing 🫥", + "shazaming": "🔈| Shazaming...", + "no_reply": "🚫| Reply to a video message.", + "no_video": "🚫| Reply must be to a video message.", + "ffmpeg_error": "🚫| Failed to read audio. Make sure ffmpeg is installed.", + "not_found": "✖️| Sorry, could not recognize the song.", + "result": "🔈| Song recognized:\n\n🔈Artist:{artist}\n🚮Title:{title}", + "result_url": "〰️Song recognized:\n\n🚮Artist:{artist}\n🔈Title:{title}\n\n🔗Listen on Shazam", + "shazam_history": "〰️| Your last 10 recognised songs", + "no_history": "〰️| What do you want to see here?", + "name_ru": "Shazamio", + "processing_ru": "Обработка 🫥", + "shazaming_ru": "🔈| Шазамлю...", + "no_reply_ru": "🚫| Ответьте на сообщение с видео.", + "no_video_ru": "🚫| Ответ должен быть на видео", + "ffmpeg_error_ru": "🚫| Неудачное чтение аудио. Убедитесь что ffmpeg установлен.Инструкция по установке", + "not_found_ru": "✖️| Простите, песня не была найдена.", + "result_ru": "🔈| Песня найдена:\n\n🔈Исполнитель:{artist}\n🚮Название:{title}", + "result_url_ru": "〰️Песня найдена:\n\n🚮Исполнитель:{artist}\n🔈Название:{title}\n\n🔗Слушайте на Shazam", + "shazam_history_ru": "〰️| Твои 10 последних распознаных треков", + "no_history_ru": "〰️| Ну и что ты тут хотел увидеть?" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/pairavatars.py": { + "name": "pairavatars", + "description": "Create pair avatars", + "cls_doc": { + "ru": "Создаёт парные авы" + }, + "meta": { + "pic": null, + "banner": "https://i.ibb.co/yFVJ6L5D/pairavs.webp", + "developer": "@SunnexGB" + }, + "commands": [ + { + "pairavs": "- Create pair avatars (command work ONLY reply message) | (RU) - Создать парные аватарки (команда работает ТОЛЬКО ответом на сообщение)" + } + ], + "new_commands": [ + { + "name": "pairavs", + "original_name": "pairavs", + "description": { + "default": "- Create pair avatars (command work ONLY reply message)", + "ru": "- Создать парные аватарки (команда работает ТОЛЬКО ответом на сообщение)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "PairAvatars", + "no_reply": "🚫 | Reply to photo!", + "processing": "💫 | Processing...", + "error": "⚠️ | Error`", + "no_reply_ru": "🚫 | Ответь на фото!", + "processing_ru": "💫 | Обработка...", + "error_ru": "⚠️ | Еррорь" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/SpotifyLyrics.py": { + "name": "SpotifyLyrics", + "description": "life lyrics current song", + "cls_doc": {}, + "meta": { + "pic": "https://r2.fakecrime.bio/uploads/f49a9294-36ad-4fc4-801f-48cb049111d6.jpg", + "banner": "https://r2.fakecrime.bio/uploads/f49a9294-36ad-4fc4-801f-48cb049111d6.jpg", + "developer": "@SunnexGB", + "fhsdesc": "Spotify, music, музыка, спотифай,Lyrics, слова, текст, трек, песня" + }, + "commands": [ + { + "snowl": "- show synchronized lyrics for current Spotify track | (RU) - показать синхронизированный текст песни" + } + ], + "new_commands": [ + { + "name": "snowl", + "original_name": "snowlcmd", + "description": { + "default": "- show synchronized lyrics for current Spotify track", + "ru": "- показать синхронизированный текст песни" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "SpotifyLyrics", + "no_spotifymod": "💢 SpotifyMod not found,but u can install it. You can also support developer: @ke_mods", + "no_auth": "⁉️ You not authorized in SpotifyMod, visit you Saved Messages.", + "no_spotify": "😅 Nothing is playing on Spotify.", + "no_lyrics": "💢 Lyrics not found for: {}", + "not_synced": "⚠️ Lyrics are not synchronized.\n\n", + "finished": "‼️ Playback ended or track changed.", + "header": "🎤 {} - {}\n\n", + "timeout": " Oopsi, looks like we've got a timeout here.", + "cls_doc_ru": "Лайв слова текущей песни.", + "no_spotifymod_ru": "💢 SpotifyMod не найден,но его можно установить. Вы также можете поддержать разработчика: @ke_mods", + "no_auth_ru": "⁉️ Вы не авторизированы в SpotifyMod, перейдите в Избранное.", + "no_spotify_ru": "😅 В Spotify ничего не играет.", + "no_lyrics_ru": "💢 Текст не найден для: {}", + "not_synced_ru": "⚠️ Текст не синхронизирован.\n\n", + "finished_ru": "‼️ Воспроизведение завершено или трек сменился.", + "header_ru": "🎤 {} - {}\n\n", + "timeout_ru": " Упси, похоже кто то словил таймаут.." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/Mikuru.py": { + "name": "Mikuru", + "description": "Censors words with phrase Mikuru Asahina", + "cls_doc": { + "ru": "Цензурует слова фразой Микуру Асахины" + }, + "meta": { + "pic": null, + "banner": "https://r2.fakecrime.bio/uploads/c389e9b5-9ef1-495d-a37a-e993ef819b4a.mp4", + "developer": "@SunnexGB" + }, + "commands": [ + { + "mikuru": "- lets go censoring | (RU) - Начать цензурирование" + }, + { + "ignore": "- Add to list ignored chats(will not work in these chats) | (RU) - Добавить в список игнорируемых чатов(не будет работать в этих чатах) " + } + ], + "new_commands": [ + { + "name": "mikuru", + "original_name": "mikuru", + "description": { + "default": "- lets go censoring", + "ru": "- Начать цензурирование" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ignore", + "original_name": "ignore", + "description": { + "default": "- Add to list ignored chats(will not work in these chats) ", + "ru": "- Добавить в список игнорируемых чатов(не будет работать в этих чатах) " + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Mikuru", + "mikuru": "🤤 | I cant say this is for u, b-because this is c-classified information", + "adult_mikuru": "😥 | Y-you already k-know s-so much...", + "ignored": "😏 | Y-you can talk about c-classified information in this chat", + "unignored": "☺️ | You cant speak in this chat b-because c-classified information", + "classified_information": "classified information", + "mikuru_ru": "🤤 | Я не могу сказать тебе это, п-п-потому что это с-секретные сведения", + "adult_mikuru_ru": "😥 | Т-ты и так м-много з-знаешь... ", + "ignored_ru": "😏 | Т-ты можешь говорить о с-секретных сведениях в этом чате", + "unignored_ru": "☺️ | Ты не можешь говорить в этом чате п-потому что с-секретные сведения", + "classified_information_ru": "секретные сведения" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/ASCII.py": { + "name": "ASCII", + "description": "Convert images to braille ASCII", + "cls_doc": { + "ru": "Конвертирует картинку в braille ASCII" + }, + "meta": { + "pic": null, + "banner": "https://i.pinimg.com/control1/1200x/24/8d/40/248d40b6afa5bd3c3764556b50635691.jpg", + "developer": "@SunnexGB" + }, + "commands": [ + { + "dot": "- Draw ASCII-ART (argument -f, sends as a file) | (RU) - Отрисовать ASCII-ART (аргумент -f, отправляет файлом)" + } + ], + "new_commands": [ + { + "name": "dot", + "original_name": "dotcmd", + "description": { + "default": "- Draw ASCII-ART (argument -f, sends as a file)", + "ru": "- Отрисовать ASCII-ART (аргумент -f, отправляет файлом)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "ASCII", + "no_lib": "🚫 | Library not loaded", + "no_image": "⚠️ | Reply to image", + "processing": "®️ | Processing...", + "empty": "🤬 | Empty result", + "result": "
{art}
", + "Failed_to_load_library": "Failed to load library", + "Conversion_error": "Conversion error", + "no_lib_ru": "🚫 | Библиотека не была загружена", + "no_image_ru": "⚠️ | Ответьте на картинку", + "processing_ru": "®️ | Обработка...", + "empty_ru": "🤬 | Пустой результат", + "result_ru": "
{art}
", + "Failed_to_load_library_ru": "Не удалось загрузить библиотеку", + "Conversion_error_ru": "Ошибка конвертации" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/SpotiSaver.py": { + "name": "SpotiSaver", + "description": "Downloading music from Spotify", + "cls_doc": { + "ru": "Скачивание музыки из Spotify" + }, + "meta": { + "pic": "https://r2.fakecrime.bio/uploads/ddf03169-09fe-4eb1-8eea-bad1a4cc4ada.jpg", + "banner": "https://r2.fakecrime.bio/uploads/ddf03169-09fe-4eb1-8eea-bad1a4cc4ada.jpg", + "developer": "@SunnexGB", + "repo": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/spotisaver.py", + "fhsdesc": "Spotify, downloader, music, музыка, спотифай,скачать музыку" + }, + "commands": [ + { + "spotsave": " - Download track from Spotify | (RU) <ссылка> — Скачать трек из Spotify" + } + ], + "new_commands": [ + { + "name": "spotsave", + "original_name": "spotsave", + "description": { + "default": " - Download track from Spotify", + "ru": "<ссылка> — Скачать трек из Spotify" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "SpotiSaver", + "downloading": "📥 Downloading: {}", + "error": " Error, see logs!", + "done": "✔️ Done!", + "no_spotifymod": "💢 SpotifyMod not found.", + "no_spotify": "😅 Nothing is playing on Spotify.", + "nf_id": " ID key not found!", + "nf_track": " Song not found.", + "timeout": " timeout! Try again.", + "name_ru": "SpotiSaver", + "downloading_ru": "📥 Скачиваю: {}", + "error_ru": " Ерорь, смотри логи!", + "done_ru": "✔️ Готово!", + "no_spotifymod_ru": "💢 SpotifyMod не найден.", + "no_spotify_ru": "😅 В Spotify ничего не играет.", + "nf_id_ru": " ID песни не найден", + "nf_track_ru": " Песня не найдена", + "timeout_ru": " Таймаут! Попробуй ещё раз." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/spotify_ph.py": { + "name": "spotifyph", + "description": "Progress bar current track in spotify", + "cls_doc": { + "ru": "Прогресс бар играющего трека в спотифай" + }, + "meta": { + "pic": null, + "banner": "https://r2.fakecrime.bio/uploads/7103b4ca-5fb1-4512-8a70-e720780c29c8.jpg", + "developer": "@H_SunMods" + }, + "commands": [], + "new_commands": [], + "inline_handlers": [], + "strings": { + "name": "spotify_ph", + "start_duration": "🎶🎶", + "start_full_duration": "🎶🎶", + "mid_duration": "🎶", + "empty_mid": "🎶", + "end_duration": "🎶", + "end_duration_full": "🎶", + "empty_end": "🎶", + "no_prem_start_duration": "ᵔᴥᵔ [---", + "no_prem_start_full_duration": "ᵔᴥᵔ [~~~", + "no_prem_mid_duration": "~~~", + "no_prem_empty_mid": "---", + "no_prem_end_duration_full": "~~~]", + "no_prem_empty_end": "---]", + "not_installed": "SpotifyMod is not installed", + "nothing_plays": "Nothing plays", + "sp_duration_desc": "Progress bar", + "sp_track_desc": "Artist and song", + "err": "Error`", + "not_installed_ru": "SpotifyMod не установлен", + "nothing_plays_ru": "Ничего не играет", + "sp_duration_desc_ru": "Прогресс бар", + "sp_track_desc_ru": "Автор и песня", + "err_ru": "Еррорь" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/NoChess.py": { + "name": "NoChess", + "description": "NoChess - web module that allows u to launch a web page either as a functional HTML page or as a Telegram Mini-App. This is an add-on for Chess module by @nullmod", + "cls_doc": { + "ru": "NoChess - Веб модуль который позволяет запускать веб-пейдж,как HTML страницу с функционалом,так же в виде Telegram Mini-App. Является дополнением к модулю Chess от @nullmod" + }, + "meta": { + "pic": null, + "banner": "https://r2.fakecrime.bio/uploads/965a3206-4609-4dff-beb0-6831f8b90e12.jpg", + "developer": "@H_SunMods" + }, + "commands": [ + { + "nochess": "[-kill] Call web interface to view chess game | (RU) [-kill] Вызываь веб интерфейс для просмотра партии" + }, + { + "cma": "Create and setup mini-app | (RU) Создает и настраивает эпку" + }, + { + "about": "IMPORTANT READING | (RU) ВАЖНО К ПРОЧТЕНИЮ" + } + ], + "new_commands": [ + { + "name": "nochess", + "original_name": "nochess", + "description": { + "default": "[-kill] Call web interface to view chess game", + "ru": "[-kill] Вызываь веб интерфейс для просмотра партии" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "cma", + "original_name": "cma", + "description": { + "default": "Create and setup mini-app", + "ru": "Создает и настраивает эпку" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "about", + "original_name": "about", + "description": { + "default": "IMPORTANT READING", + "ru": "ВАЖНО К ПРОЧТЕНИЮ" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "NoChess", + "starting": "( ノ・ェ・ )ノ Starting NoChess...", + "online": "(*˘︶˘*) NoChess is running", + "already_running": "ʕᵕᴥᵕʔ NoChess is already running", + "stopped": "・゚・(。>д<。)・゚・ NoChess stopped", + "not_running": "(✿╹◡╹) NoChess is not running", + "ngrok_missing": "Set a ngrok_token", + "ngrok_error": "Ngrok start error: {}", + "asset_read_error": "Failed to load web assets: {}", + "open_button": "Open mini-app", + "stop_button": "Stop", + "about_text": "Important read:\nSometimes the server won't lift cause there's enough processes running, for example on HikkaHost, for this I just rebooted the server\nNext is that cma setups the app by a template and it's rly crooked, so you'll have to set some web app config settings yourself\nAnd also:\n 1. First launch will start straight with a site link, not as a web app\n 2. Use nochess, and then cma to setup the web app\n 3. After that restart the process by typing nochess -kill and nochess again\nYeah it's hacky as hell, but I was so over doing stuff that I started dumping some routine like working with files on ai, which I didn't like so I decided to quick-release the module before it's too late\nWell and maybe soon I'll make an update, right now it's some pre-alpha version, that's why the version name is like this, later I'll change it to 1.0.0, if people actually dig the module as an idea", + "cma_start": "( ノ・ェ・ )ノ Creating mini app in BotFather...", + "cma_need_url": "Set mini app web URL first or run .nochess to get it.", + "cma_done": "(*˘︶˘*) Done.", + "cma_error": "Error: {}", + "RuntimeError": "inline bot username not found", + "not_supported_platform": "(┬┬_┬┬) Unfortunately, it is impossible to install this module on this platform.\n\n(〜^∇^)〜 This is not an error, please do not contact support.", + "starting_ru": "( ノ・ェ・ )ノ Запуск NoChess...", + "online_ru": "(*˘︶˘*) NoChess запущен", + "already_running_ru": "ʕᵕᴥᵕʔ NoChess уже запущен", + "stopped_ru": "・゚・(。>д<。)・゚・ NoChess остановлен", + "not_running_ru": "(✿╹◡╹) NoChess не запущен", + "ngrok_missing_ru": "Укажи ngrok_token", + "ngrok_error_ru": "Ошибка запуска ngrok: {}", + "asset_read_error_ru": "Не удалось загрузить веб-ассеты: {}", + "open_button_ru": "Открыть мини-приложение", + "stop_button_ru": "Остановить", + "about_text_ru": "Важно к прочтению:\nИногда сервер не может подниматься из за того что запущено достаточно процессов, например на HikkaHost,для этого я просто перезагружал сервер.\nДалее это то что cma сетапает приложение по шаблону и оч криво, поэтому вам придется выставлять некоторые настройки конфигурации веб приложения самим.\nА еще:\n 1. Первый запуск будет запускаться сразу ссылкой на сайт, а не как веб приложение.\n 2. Используйте nochess, а потом cma чтобы настроить веб приложение.\n 3. После чего перезапустите процесс написав nochess -kill и повторно nochess.\nДа это костыли, но мне уже настолько было в падлу что то делать что я уже стал спихивать рутину по типу работы с файлами на ии, что мне не понравилось и я решил быстро релизать модуль пока не стало поздно.\nНу и может быть в скором времени я уже сделаю апдейт, на данный момент это какая то пре-альфа версия, поэтому и название версии такое, в дальнейшем изменю на 1.0.0, если модуль вообще понравиться людям как идея.", + "cma_start_ru": "( ノ・ェ・ )ノ Создаю эпку через BotFather...", + "cma_need_url_ru": "Сначала укажи URL мини-эпки или запусти .nochess, чтобы получить его", + "cma_done_ru": "(*˘︶˘*) Готово", + "cma_error_ru": "Ошибка: {}", + "RuntimeError_ru": "юз инлайн бота не найден", + "not_supported_platform_ru": "(┬┬_┬┬) К сожалению, на эту платформу невозможно установить этот модуль.\n\n(〜^∇^)〜 Это не ошибка, пожалуйста, не обращайтесь в поддержку." + }, + "has_on_load": false, + "has_on_unload": true, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/YandexLyrics.py": { + "name": "YandexLyrics", + "description": "life lyrics current song", + "cls_doc": {}, + "meta": { + "pic": "https://r2.fakecrime.bio/uploads/ab42b5e2-91f1-4ed1-8002-51b3184e3839.jpg", + "banner": "https://r2.fakecrime.bio/uploads/ab42b5e2-91f1-4ed1-8002-51b3184e3839.jpg", + "developer": "@SunnexGB", + "fhsdesc": "YaMusic, music, музыка, яндекс музыка,Lyrics, слова, текст, трек, песня" + }, + "commands": [ + { + "ynowl": "- show synchronized lyrics for current YaMusic track | (RU) - показать синхронизированный текст песни" + } + ], + "new_commands": [ + { + "name": "ynowl", + "original_name": "ynowlcmd", + "description": { + "default": "- show synchronized lyrics for current YaMusic track", + "ru": "- показать синхронизированный текст песни" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "YandexLyrics", + "no_YaMusicMod": "💢 YaMusicMod not found,but u can install it. You can also support developer: @codrago_m", + "no_auth": "⁉️ You not authorized in SpotifyMod, visit you Saved Messages and setup token to continue.", + "no_ym": "😅 Nothing is playing on YaMusic.", + "no_lyrics": "💢 Lyrics not found for: {}", + "not_synced": "⚠️ Lyrics are not synchronized.\n\n", + "finished": "‼️ Playback ended or track changed.", + "header": "🎤 {} - {}\n\n", + "timeout": " Oopsi, looks like we've got a timeout here.", + "cls_doc_ru": "Лайв слова текущей песни.", + "no_YaMusicMod_ru": "💢 YaMusicMod не найден,но его можно установить. Вы также можете поддержать разработчика: @codrago_m", + "no_auth_ru": "⁉️ Вы не авторизированы в YaMusicMod, перейдите в Избранное и установите токен для продолжения работы.", + "no_ym_ru": "😅 В YaMusic ничего не играет.", + "no_lyrics_ru": "💢 Текст не найден для: {}", + "not_synced_ru": "⚠️ Текст не синхронизирован.\n\n", + "finished_ru": "‼️ Воспроизведение завершено или трек сменился.", + "header_ru": "🎤 {} - {}\n\n", + "timeout_ru": " Упси, похоже кто то словил таймаут.." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SunnexGB/Heroku-Modules/Hangman.py": { + "name": "Hangman", + "description": "Висилица", + "cls_doc": {}, + "meta": { + "pic": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/10.png", + "banner": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Hangman/10.png", + "developer": "@H_SunMods", + "fhsdesc": "Game, Игра, Hangman, Висилица" + }, + "commands": [ + { + "hangman": "(.oleg) Start hangman game | (RU) (.oleg) Начать висилицу" + } + ], + "new_commands": [ + { + "name": "hangman", + "original_name": "hangman", + "description": { + "default": "(.oleg) Start hangman game", + "ru": "(.oleg) Начать висилицу" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Hangman", + "caption": "{word}", + "won": "{word}", + "over": "Игра окончена. Слово было: {word}", + "already": "Сосиски свои ебаные убрал от этой буквы!" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/UniversalDownloader.py": { + "name": "UniversalDownloaderMod", + "description": "Downloads media from YouTube, VK, TikTok, and all yt-dlp supported sites", + "cls_doc": { + "default": "Downloads media from YouTube, VK, TikTok, and all yt-dlp supported sites", + "ru": "Скачивает медиа из YouTube, VK, TikTok и всех поддерживаемых yt-dlp сайтов" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/UniversalDownloader.png", + "developer": "@quise_m" + }, + "commands": [ + { + "unidl": "(EN) Download media | (RU) Скачать медиа" + } + ], + "new_commands": [ + { + "name": "unidl", + "original_name": "unidlcmd", + "description": { + "default": "", + "en": "Download media", + "ru": "Скачать медиа" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "UniversalDownloader", + "_cls_doc": "Downloads media from YouTube, VK, TikTok, and all yt-dlp supported sites", + "select_download_type": "⬇️ Select download type:", + "invalid_args": " There is no arguments or they are invalid", + "downloading": "🕐 Downloading...", + "cookie_desc": "Cookie account (helps downloading video with strict age rating restricrions)", + "deno_err": "❗️ Error! The Deno JavaScript engine was not install automatically.\nThis is a required dependency for yt-dlp (a library for downloading video/audio) to work correctly.\n\nTo continue, you need to install the engine manually, or resolve any issues preventing automatic installation and restart the userbot.", + "err": "❗️ Error!\n\nAdditional info: {}", + "video": "video", + "audio": "audio", + "select_download_type_ru": "⬇️ Выберите тип загрузки:", + "invalid_args_ru": " Нет аргументов или они неверны", + "downloading_ru": "🕐 Скачиваю...", + "cookie_desc_ru": "Куки аккаунта (помогает скачивать видео с жесткими возрастными ограничениями)", + "deno_err_ru": "❗️ Ошибка! JS-движок Deno не установился автоматически.\nЭто необходимая зависимость для корректной работы yt-dlp (библиотека для скачивания видео/аудио).\n\nДля продолжения вам необходимо установить движок вручную, или устранить препятствия для автоматической установки и перезагрузить юзербота.", + "err_ru": "❗️ Ошибка!\n\nДоп.информация: {}", + "video_ru": "видео", + "audio_ru": "аудио" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/CodeShare.py": { + "name": "CodeShareMod", + "description": "Uploads your code at the kmi.aeza.net (Pastebin and GitHub Gist alternative).", + "cls_doc": { + "default": "Uploads your code at the kmi.aeza.net (Pastebin and GitHub Gist alternative)", + "ru": "Загружает ваш код на kmi.aeza.net (альтернатива Pastebin и GitHub Gist)" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/CodeShare.png", + "developer": "@quise_m" + }, + "commands": [ + { + "codeshare": "(RU) Загрузка кода на сайт | (EN) Upload code to the site" + } + ], + "new_commands": [ + { + "name": "codeshare", + "original_name": "codesharecmd", + "description": { + "default": "", + "ru": "Загрузка кода на сайт", + "en": "Upload code to the site" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "CodeShare", + "invalid_args": " There is no arguments or reply with a file, or they are invalid", + "_cls_doc": "Uploads your code at the kmi.aeza.net (Pastebin and GitHub Gist alternative)", + "link_ready": " Code uploaded! Link: {}", + "invalid_args_ru": " Нет аргументов или реплая с файлом, или они неверны", + "link_ready_ru": " Код загружен! Ссылка: {}" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/FolderAutoRead.py": { + "name": "FolderAutoReadMod", + "description": "Automatically reads chats in selected folders.", + "cls_doc": { + "default": "Automatically reads chats in selected folders every 60 seconds!", + "ru": "Автоматически читает чаты в выбранных папках каждые 60 секунд!" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/FolderAutoRead.png", + "developer": "@quise_m" + }, + "commands": [ + { + "addfolder": "Adds folder to the tracking list by it's name. Usage: .addfolder FolderName | (R) Добавляет папки в список отслеживания по их названию. Использование: .addfolder НазваниеПапки | (RU) Добавляет папки в список отслеживания по их названию. Использование: .addfolder НазваниеПапки" + }, + { + "delfolder": "Deletes folder from the tracking list | (R) Удаляет папку из списка для отслежнивания | (RU) Удаляет папку из списка для отслежнивания" + }, + { + "listfolders": "Prints list of tracked folders | (R) Выводит список отслеживаемых папок | (RU) Выводит список отслеживаемых папок" + } + ], + "new_commands": [ + { + "name": "addfolder", + "original_name": "addfolder", + "description": { + "default": "Adds folder to the tracking list by it's name. Usage: .addfolder FolderName", + "r": "Добавляет папки в список отслеживания по их названию. Использование: .addfolder НазваниеПапки", + "ru": "Добавляет папки в список отслеживания по их названию. Использование: .addfolder НазваниеПапки" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "delfolder", + "original_name": "delfolder", + "description": { + "default": "Deletes folder from the tracking list", + "r": "Удаляет папку из списка для отслежнивания", + "ru": "Удаляет папку из списка для отслежнивания" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "listfolders", + "original_name": "listfolders", + "description": { + "default": "Prints list of tracked folders", + "r": "Выводит список отслеживаемых папок", + "ru": "Выводит список отслеживаемых папок" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "FolderAutoRead", + "not_exists_or_already_added": "🚫 This folder does not exists or it is already added for tracking!", + "_cls_doc": "Automatically reads chats in selected folders every 60 seconds!", + "_cmd_doc_addfolder": "Adds folder to the tracking list by it's name. Usage: .addfolder FolderName", + "_cmd_doc_listfolders": "Prints list of tracked folders", + "_cmd_doc_delfolder": "Deletes folder from the tracking list", + "wrong_args": "🚫 Wrong arguments! Usage: .addfolder/delfolder FolderName\n\nTip: If you trying to delete the folder from the tracking list, double-check that it really still tracking using .listfolders", + "listfolders": "📁 List of tracked folders:\n", + "delfolder": "🗑 Folder is successfully deleted from the tracking list!", + "addfolder": "📁 Folder is successfully added to the tracking list!", + "not_exists_or_already_added_ru": "🚫 Такой папки не существует, или она уже добавлена для отслеживания!", + "_cmd_doc_ru_addfolder": "Добавляет папки в список отслеживания по их названию. Использование: .addfolder НазваниеПапки", + "_cmd_doc_addfolder_ru": "Добавляет папки в список отслеживания по их названию. Использование: .addfolder НазваниеПапки", + "_cmd_doc_ru_listfolders": "Выводит список отслеживаемых папок", + "_cmd_doc_listfolders_ru": "Выводит список отслеживаемых папок", + "_cmd_doc_ru_delfolder": "Удаляет папку из списка для отслежнивания", + "_cmd_doc_delfolder_ru": "Удаляет папку из списка для отслежнивания", + "wrong_args_ru": "🚫 Неверные аргументы! Использование: .addfolder/delfolder НазваниеПапки\n\nСовет: Если вы пытаетесь удалить папку из списка отслеживания, проверьте, что она вообще отслеживается, используя .listfolders", + "listfolders_ru": "📁 Список отслеживаемых папок:\n", + "delfolder_ru": "🗑 Папка успешно удалена из листа отслеживания!", + "addfolder_ru": "📁 Папка успешно добавлена в лист отслеживания!" + }, + "has_on_load": false, + "has_on_unload": true, + "class_cmd_names": {} + }, + "archquise/q.mods/timezone.py": { + "name": "TimeZoneMod", + "description": "Prints current time in selected timezone (UTC+n and tzdata formats supported).", + "cls_doc": { + "default": "Prints current time in selected timezone (UTC+n and tzdata formats supported)", + "ru": "Выводит текущее время в выбранном часовом поясе (поддерживаются форматы UTC+n и tzdata)" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/timezone.png", + "developer": "@quise_m" + }, + "commands": [ + { + "utc": "(RU) Выводит время по UTC+n | Использование: .utc 4 | (EN) Prints UTC+n time | Usage: .utc 4" + }, + { + "tzdata": "(RU) Выводит время по часовому поясу tzdata | Использование: .tzdata Europe/Moscow | (EN) Prints time by tzdata timezone | Usage: .tzdata Europe/Moscow" + } + ], + "new_commands": [ + { + "name": "utc", + "original_name": "utccmd", + "description": { + "default": "", + "ru": "Выводит время по UTC+n | Использование: .utc 4", + "en": "Prints UTC+n time | Usage: .utc 4" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tzdata", + "original_name": "tzdatacmd", + "description": { + "default": "", + "ru": "Выводит время по часовому поясу tzdata | Использование: .tzdata Europe/Moscow", + "en": "Prints time by tzdata timezone | Usage: .tzdata Europe/Moscow" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "TimeZone", + "invalid_args": " There is no arguments or they are invalid", + "_cls_doc": "Prints current time in selected timezone (UTC+n and tzdata formats supported)", + "time_utc": "🕓 Current time by UTC+{}: {}", + "time_tzdata": "🕓 Current time in {}: {}", + "invalid_args_ru": " Нет аргументов или они неверны", + "tzdata_error_ru": " Произошла ошибка при получении времени по tzdata: {}\n\nУбедитесь, что часовой пояс указан верно", + "time_utc_ru": "🕓 Текущее время по UTC+{}: {}", + "time_tzdata_ru": "🕓 Текущее время в {}: {}" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/ytdl.py": { + "name": "YTDLMod", + "description": "Downloads and sends audio/video from YouTube.", + "cls_doc": { + "default": "Downloads and sends audio/video from YouTube", + "ru": "Скачивает и отправляет аудио/видео с Ютуба" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/ytdl.png", + "developer": "@quise_m" + }, + "commands": [ + { + "ytdlv": "(EN) Download video | (RU) Скачать видео" + }, + { + "ytdla": "(EN) Download audio | (RU) Скачать аудио" + } + ], + "new_commands": [ + { + "name": "ytdlv", + "original_name": "ytdlvcmd", + "description": { + "default": "", + "en": "Download video", + "ru": "Скачать видео" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ytdla", + "original_name": "ytdlacmd", + "description": { + "default": "", + "en": "Download audio", + "ru": "Скачать аудио" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "YTDL", + "_cls_doc": "Downloads and sends audio/video from YouTube", + "invalid_args": " There is no arguments or they are invalid", + "downloading": "🕐 Downloading...", + "done": " Done!", + "cookie_desc": "Cookie account (helps downloading video with strict age rating restricrions)", + "deno_err": "❗️ Error! The Deno JavaScript engine was not install automatically.\nThis is a required dependency for yt-dlp (a library for downloading video/audio) to work correctly.\n\nTo continue, you need to install the engine manually, or resolve any issues preventing automatic installation and restart the userbot.", + "err": "❗️ Error!\n\nAdditional info: {}", + "invalid_args_ru": " Нет аргументов или они неверны", + "downloading_ru": "🕐 Скачиваю...", + "done_ru": " Готово!", + "cookie_desc_ru": "Куки аккаунта (помогает скачивать видео с жесткими возрастными ограничениями)", + "deno_err_ru": "❗️ Ошибка! JS-движок Deno не установился автоматически.\nЭто необходимая зависимость для корректной работы yt-dlp (библиотека для скачивания видео/аудио).\n\nДля продолжения вам необходимо установить движок вручную, или устранить препятствия для автоматической установки и перезагрузить юзербота.", + "err_ru": "❗️ Ошибка!\n\nДоп.информация: {}" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/QNotes.py": { + "name": "QNotes", + "description": "A notes module that just works\nUsage: #notetag in any chat", + "cls_doc": { + "ru": "Модуль для заметок, который просто работает\nИспользование: #тегзаметки в любом чате" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/qnotes.png", + "developer": "@quise_m" + }, + "commands": [ + { + "qnsave": "(RU) Сохраняет заметку под тегом | Пример: .qnsave заметка | (EN) Saves note by tag | Example: .qnsave note" + }, + { + "qnrem": "(RU) Удаляет заметку по тегу | Пример: .qnrem заметка | (EN) Removes note by tag | Example: .qnrem note" + }, + { + "qnlist": "(RU) Выводит список всех заметок и позволяет управлять ими | (EN) Shows note list and allows managing them" + }, + { + "qnp": "(RU) Выводит список доступных плейсхолдеров | (EN) Displays a list of available placeholders" + } + ], + "new_commands": [ + { + "name": "qnsave", + "original_name": "qnsave", + "description": { + "default": "", + "ru": "Сохраняет заметку под тегом | Пример: .qnsave заметка", + "en": "Saves note by tag | Example: .qnsave note" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "qnrem", + "original_name": "qnrem", + "description": { + "default": "", + "ru": "Удаляет заметку по тегу | Пример: .qnrem заметка", + "en": "Removes note by tag | Example: .qnrem note" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "qnlist", + "original_name": "qnlist", + "description": { + "default": "", + "ru": "Выводит список всех заметок и позволяет управлять ими", + "en": "Shows note list and allows managing them" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "qnp", + "original_name": "qnp", + "description": { + "default": "", + "ru": "Выводит список доступных плейсхолдеров", + "en": "Displays a list of available placeholders" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "QNotes", + "topic_desc": "Stores your notes content\nUsage: #notetag in any chat", + "wrongargs": " Wrong arguments. Check command usage.", + "not_exist": "There is no such note!", + "no_reply": "No reply! Reply to the message, which text will become a note.", + "already_exists": "Seems like note with the same tag already exists. Overwrite?", + "show_note_inline": "
#{}
\n\n
{}
", + "notelist": "Note list:", + "msg_not_found_inline": "Message with this note wasn't found. Probably, it was been removed. Note has been removed from the database.", + "remnote_inline": "🗑 Remove", + "close_inline": "❌ Close", + "yes": "✔️ Yes", + "no": "❌ No", + "true": "yes", + "false": "no", + "saved": "Note saved!", + "removed": "Note removed!", + "nonotes": "You don't have any notes!", + "privacy_switch": "Determines whose data will be used by the my_* placeholders\n\nTrue - the account that is issuing the note\nFalse - the account on which the userbot is running", + "note_prefix": "The prefix used to call up notes", + "placeholders": "\n Available placeholders:\n\n about the account on which userbot is installed:\n {my_id} - ID\n @{my_username} - username, tag\n {my_phone} - phone number\n {my_premium} - premium status (yes/no)\n\n about reply author:\n {reply_id} - ID\n {reply_name} - name\n {reply_surname} - surname\n {reply_fullname} - full name (name + surname (if specified))\n @{reply_username} - username, tag\n {reply_phone} - phone number (if not hidden)\n {reply_premium} - premium status (yes/no)\n\n general:\n {today} - current date\n ", + "topic_desc_ru": "Хранит содержимое ваших заметок\nИспользование: #тегзаметки в любом чате", + "wrongargs_ru": " Неверные аргументы. Проверьте использование команды.", + "no_reply_ru": "Нет реплая! Ответьте на сообщение, текст которого станет заметкой.", + "not_exist_ru": "Такой заметки не найдено!", + "already_exists_ru": "Кажется, заметка с таким тегом уже существует. Перезаписать?", + "show_note_inline_ru": "
#{}
\n\n
{}
", + "notelist_ru": "Список заметок:", + "msg_not_found_inline_ru": "Сообщение с этой заметкой не было найдено. Вероятно, оно было удалено. Заметка очищена из базы данных.", + "remnote_inline_ru": "🗑 Удалить", + "close_inline_ru": "❌ Закрыть", + "yes_ru": "✔️ Да", + "no_ru": "❌ Нет", + "saved_ru": "Заметка сохранена!", + "removed_ru": "Заметка удалена!", + "true_ru": "да", + "false_ru": "нет", + "nonotes_ru": "Нет заметок!", + "privacy_switch_ru": "Влияет на то, чьи данные будут использовать my_* плейсхолдеры\n\nTrue - аккаунта, который вызывает заметку\nFalse - аккаунта на котором стоит юзербот", + "note_prefix_ru": "Префикс, с которым вызываются заметки", + "placeholders_ru": "\n Доступные плейсхолдеры:\n\n об аккаунте, на котором стоит юзербот:\n {my_id} - айди\n @{my_username} - юзернейм, тег\n {my_phone} - номер телефона\n {my_premium} - статус премиум (да/нет)\n\n об авторе реплая:\n {reply_id} - айди\n {reply_name} - имя\n {reply_surname} - фамилия\n {reply_fullname} - полное имя (имя + фамилия (если указана))\n @{reply_username} - юзернейм, тег\n {reply_phone} - номер телефона (если не скрыт)\n {reply_premium} - статус премиум (да/нет)\n\n общее:\n {today} - текущая дата\n " + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/shortener.py": { + "name": "ShortenerMod", + "description": "Module for using bit.ly API.", + "cls_doc": { + "default": "Module for using bit.ly API", + "ru": "Модуль для использования API bit.ly" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/Shortener.png", + "developer": "@quise_m" + }, + "commands": [ + { + "shorten": "Shorten URL using bit.ly API. | (RU) Сократить ссылку через bit.ly (ссылка с https://) | (EN) Shorten the link via bit.ly (url with https://)" + }, + { + "statcl": "Get click statistics for shortened URL. | (RU) Посмотреть статистику ссылки через bit.ly (ссылка без https:// | Доступно только на платных аккаунтах) | (EN) View link statistics via bit.ly (link without https:// | Works only on paid accounts)" + } + ], + "new_commands": [ + { + "name": "shorten", + "original_name": "shortencmd", + "description": { + "default": "Shorten URL using bit.ly API.", + "ru": "Сократить ссылку через bit.ly (ссылка с https://)", + "en": "Shorten the link via bit.ly (url with https://)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "statcl", + "original_name": "statclcmd", + "description": { + "default": "Get click statistics for shortened URL.", + "ru": "Посмотреть статистику ссылки через bit.ly (ссылка без https:// | Доступно только на платных аккаунтах)", + "en": "View link statistics via bit.ly (link without https:// | Works only on paid accounts)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Shortener", + "no_api": " You have not specified an API token from the site bit.ly", + "statclcmd": "📊 Statistics on clicks for this link: {c}", + "shortencmd": " Your shortened link is ready: {c}", + "no_args": " Please provide a URL to shorten.", + "invalid_url": " Invalid URL format.", + "api_error": " API error: {error}", + "_cls_doc": "Module for using bit.ly API", + "no_api_ru": " Вы не указали api токен с сайта bit.ly", + "statclcmd_ru": "📊 Статистика о переходе по этой ссылке: {c}", + "shortencmd_ru": " Ваша сокращённая ссылка готова: {c}", + "no_args_ru": " Пожалуйста, укажите URL для сокращения.", + "invalid_url_ru": " Неверный формат URL.", + "api_error_ru": " Ошибка API: {error}" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/face.py": { + "name": "FaceMod", + "description": "Gives you a random kaomoji.", + "cls_doc": { + "ru": "Выдает случайное каомодзи (японские эмодзи)" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/face.png", + "developer": "@quise_m" + }, + "commands": [ + { + "rface": "(RU) Случайное каомодзи | (EN) Random kaomoji" + } + ], + "new_commands": [ + { + "name": "rface", + "original_name": "rfacecmd", + "description": { + "default": "", + "ru": "Случайное каомодзи", + "en": "Random kaomoji" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Face", + "loading": "🔍 I'm looking for you kaomoji", + "random_face": "🗿 Here is your random one kaomoji\n{}", + "error": "An error has occurred!", + "loading_ru": "🔍 Ищу вам kaomoji", + "random_face_ru": "🗿 Вот ваш рандомный kaomoji\n{}", + "error_ru": "Произошла ошибка!" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/WindowsKeys.py": { + "name": "WindowsKeysMod", + "description": "Windows KMS activation keys.", + "cls_doc": { + "ru": "KMS ключи активации Windows" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/WindowsKeys.png", + "developer": "@quise_m" + }, + "commands": [ + { + "winkey": "(RU) Меню ключей Windows | (EN) Windows keys menu" + } + ], + "new_commands": [ + { + "name": "winkey", + "original_name": "winkey", + "description": { + "default": "", + "ru": "Меню ключей Windows", + "en": "Windows keys menu" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "WindowsKeys", + "winkey": " Key: {}\n\n❗️ For KMS activation only", + "error": " Failed to get key", + "select": "🔑 Select version:", + "close": "❌ Close", + "back": "← Back", + "loading": "✍️ Loading...", + "winkey_ru": " Ключ: {}\n\n❗️ Только для KMS активации", + "error_ru": " Ошибка получения", + "select_ru": "🔑 Выберите версию:", + "close_ru": "❌ Закрыть", + "back_ru": "← Назад", + "loading_ru": "✍️ Загрузка..." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/IrisSimpleMod.py": { + "name": "IrisSimpleMod", + "description": "Module for basic interaction with Iris bot.", + "cls_doc": { + "ru": "Модуль для базового взаимодействия с Ирисом!" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/IrisSimpleMod.png", + "developer": "@quise_m" + }, + "commands": [ + { + "bag": "Check bag. | (RU) Проверить мешок | (EN) Check bag" + }, + { + "farm": "Farm iris-coins. | (RU) Зафармить ирис-коины | (EN) Farm iris-coins" + }, + { + "irisstats": "Display user stats. | (RU) Вывести анкету | (EN) Display user stats" + } + ], + "new_commands": [ + { + "name": "bag", + "original_name": "bag", + "description": { + "default": "Check bag.", + "ru": "Проверить мешок", + "en": "Check bag" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "farm", + "original_name": "farm", + "description": { + "default": "Farm iris-coins.", + "ru": "Зафармить ирис-коины", + "en": "Farm iris-coins" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "irisstats", + "original_name": "irisstats", + "description": { + "default": "Display user stats.", + "ru": "Вывести анкету", + "en": "Display user stats" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "IrisSimpleMod", + "checking_bag": "🌎 Checking bag...", + "bag_result": " Your bag: {}", + "farming": "🌎 Farming iris-coins...", + "farm_result": " Farm result: {}", + "getting_stats": "🌎 Getting user stats...", + "stats_result": " User stats: {}", + "bot_stats": "🌎 Getting bot stats...", + "bot_stats_result": " Bot stats: {}", + "error_no_response": " No response from bot. Please try again.", + "error_timeout": " Request timeout. Please try again.", + "error_general": " An error occurred: {error}", + "checking_bag_ru": "🌎 Проверка мешка...", + "bag_result_ru": " Ваш мешок: {}", + "farming_ru": "🌎 Фарм ирис-коинов...", + "farm_result_ru": " Результат фарма: {}", + "getting_stats_ru": "🌎 Получение статистики пользователя...", + "stats_result_ru": " Статистика пользователя: {}", + "bot_stats_ru": "🌎 Получение статистики ботов...", + "bot_stats_result_ru": " Статистика ботов: {}", + "error_no_response_ru": " Нет ответа от бота. Попробуйте еще раз.", + "error_timeout_ru": " Таймаут запроса. Попробуйте еще раз.", + "error_general_ru": " Произошла ошибка: {error}" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/AniLiberty.py": { + "name": "AniLibertyMod", + "description": "Ищет и возвращает случайное аниме из базы Aniliberty.", + "cls_doc": { + "ru": "Ищет и отправляет случайное аниме из базы AniLiberty" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/AniLiberty.png", + "developer": "@quise_m" + }, + "commands": [ + { + "arandom": "(RU) Возвращает случайный релиз из базы | (EN) Returns a random release from the database" + }, + { + "asearchinlinehandler": "" + } + ], + "new_commands": [ + { + "name": "arandom", + "original_name": "arandom", + "description": { + "default": "", + "ru": "Возвращает случайный релиз из базы", + "en": "Returns a random release from the database" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "asearchinlinehandler", + "original_name": "asearch_inline_handler", + "description": { + "default": "" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": true, + "is_inline_handler": true, + "decorators": [] + } + ], + "inline_handlers": [ + { + "name": "asearchinlinehandler", + "description": { + "default": "" + }, + "decorators": [] + } + ], + "strings": { + "name": "AniLiberty", + "announce": "The announcement:", + "ongoing": "Ongoing:", + "type": "Type:", + "genres": "Genres:", + "favorite": "Favourites <3:", + "announce_ru": "Анонс:", + "ongoing_ru": "Онгоинг:", + "type_ru": "Тип:", + "genres_ru": "Жанры:", + "favorite_ru": "Избранное <3:" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "archquise/q.mods/TempChat.py": { + "name": "TempChatMod", + "description": "Creates a temporary private chat with a message forwarding restriction and adds the specified user to it.", + "cls_doc": { + "ru": "Создает временный приватный чат с запретом на пересылку и добавляет туда выбранного человека" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/archquise/qmods_meta/main/TempChat.png", + "developer": "@quise_m" + }, + "commands": [ + { + "tmpchat": "Create temporary chat. Usage: .tmpchat [@user/reply] [time] | (RU) Создает временный чат. Использование: .tmpchat [@user/reply] [time]" + } + ], + "new_commands": [ + { + "name": "tmpchat", + "original_name": "tmpchat", + "description": { + "default": "Create temporary chat. Usage: .tmpchat [@user/reply] [time]", + "ru": "Создает временный чат. Использование: .tmpchat [@user/reply] [time]" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "TempChat", + "selfchat": "You can't create a chat with yourself.", + "wrongargs": " Wrong arguments. Use .tmpchat [@user/reply] [time]", + "alreadychatting": " You already have an active conversation with this person.", + "invalidtime": " Invalid time format. Use combinations like 1h30m.", + "invitemsg": "🛡 You've been invited to a temporary private chat!\n\n⌛️ Auto-deletes in ", + "joinlink": "🔗 Join link: ", + "chatcreated": " The temporary chat has been successfully created!", + "selfchat_ru": "Ты не можешь создать чат сам с собой.", + "wrongargs_ru": " Неверные аргументы. Используй .tmpchat [@user/reply] [время]", + "alreadychatting_ru": " У вас уже есть открытая переписка с этим человеком.", + "invalidtime_ru": " Неверный формат времени. Убедитесь, что вы вводите время в формате 1h, 2h30m.", + "invitemsg_ru": "🛡 Вы были приглашены во временный приватный чат!\n\n⌛️ Авто-удаление через ", + "joinlink_ru": "🔗 Ссылка: ", + "chatcreated_ru": " Временный чат успешно создан!" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "MoriSummerz/ftg-mods/chatgpt.py": { + "name": "ChatGPT", + "description": "ChatGPT AI API interaction", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://i.imgur.com/H1vPM6U.jpg", + "developer": "@morisummermods" + }, + "commands": [ + { + "gpt": " - Ask a question | (RU) <вопрос> - Задать вопрос | (IT) - Fai una domanda | (FR) - Posez une question | (DE) - Stelle eine Frage | (ES) - Haz una pregunta | (TR) - Soru sor | (UZ) - Savol ber" + } + ], + "new_commands": [ + { + "name": "gpt", + "original_name": "gpt", + "description": { + "default": " - Ask a question", + "ru": "<вопрос> - Задать вопрос", + "it": " - Fai una domanda", + "fr": " - Posez une question", + "de": " - Stelle eine Frage", + "es": " - Haz una pregunta", + "tr": " - Soru sor", + "uz": " - Savol ber" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "ChatGPT", + "no_args": "🚫 No arguments provided", + "question": "👤 Question: {question}\n", + "answer": "🤖 Answer: {answer}", + "loading": "Loading...", + "no_api_key": "🚫 No API key provided\nℹ️ Get it from official OpenAI website and add it to config", + "no_args_ru": "🚫 Не указаны аргументы", + "question_ru": "👤 Вопрос: {question}\n", + "answer_ru": "🤖 Ответ: {answer}", + "loading_ru": "Загрузка...", + "no_api_key_ru": "🚫 Не указан API ключ\nℹ️ Получите его на официальном сайте OpenAI и добавьте в конфиг", + "no_args_es": "🚫 No se han proporcionado argumentos", + "question_es": "👤 Pregunta: {question}\n", + "answer_es": "🤖 Respuesta: {answer}", + "loading_es": "Cargando...", + "no_api_key_es": "🚫 No se ha proporcionado una clave API\nℹ️ Obtenga una en el sitio web oficial de OpenAI y agréguela a la configuración", + "no_args_fr": "🚫 Aucun argument fourni", + "question_fr": "👤 Question: {question}\n", + "answer_fr": "🤖 Réponse: {answer}", + "loading_fr": "Chargement...", + "no_api_key_fr": "🚫 Aucune clé API fournie\nℹ️ Obtenez-en un sur le site officiel d'OpenAI et ajoutez-le à la configuration", + "no_args_de": "🚫 Keine Argumente angegeben", + "question_de": "👤 Frage: {question}\n", + "answer_de": "🤖 Antwort: {answer}", + "loading_de": "Laden...", + "no_api_key_de": "🚫 Kein API-Schlüssel angegeben\nℹ️ Holen Sie sich einen auf der offiziellen OpenAI-Website und fügen Sie ihn der Konfiguration hinzu", + "no_args_tr": "🚫 Argümanlar verilmedi", + "question_tr": "👤 Soru: {question}\n", + "answer_tr": "🤖 Cevap: {answer}", + "loading_tr": "Yükleniyor...", + "no_api_key_tr": "🚫 API anahtarı verilmedi\nℹ️ OpenAI'nın resmi websitesinden alın ve yapılandırmaya ekleyin", + "no_args_uz": "🚫 Argumentlar ko'rsatilmadi", + "question_uz": "👤 Savol: {question}\n", + "answer_uz": "🤖 Javob: {answer}", + "loading_uz": "Yuklanmoqda...", + "no_api_key_uz": "🚫 API kalit ko'rsatilmadi\nℹ️ Ofitsial OpenAI veb-saytidan oling", + "no_args_it": "🚫 Nessun argomento fornito", + "question_it": "👤 Domanda: {question}\n", + "answer_it": "🤖 Risposta: {answer}", + "loading_it": "Caricamento...", + "no_api_key_it": "🚫 Nessuna chiave API fornita\nℹ️ Ottienila dal sito ufficiale di OpenAI e aggiungila al tuo file di configurazione" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "MoriSummerz/ftg-mods/lyrics.py": { + "name": "LyricsMod", + "description": "Song lyrics from Genius", + "cls_doc": { + "ru": "Поиск тексов песен с Genius" + }, + "meta": { + "pic": "https://i.imgur.com/pViqDsI.png", + "banner": "https://i.imgur.com/AIjsMoV.jpg", + "developer": "@morisummermods" + }, + "commands": [ + { + "lyrics": "Get lyrics | (R) Получить слова песни | (RU) Получить слова песни" + }, + { + "slyrics": "Get lyrics from your current Spotify playback (Needs SpotifyNow module) | (R) Получить слова песни прослушиваемой в Спотифай, для работоспособности требуется модуль SpotifyNow | (RU) Получить слова песни прослушиваемой в Спотифай, для работоспособности требуется модуль SpotifyNow" + } + ], + "new_commands": [ + { + "name": "lyrics", + "original_name": "lyricscmd", + "description": { + "default": "Get lyrics", + "r": "Получить слова песни", + "ru": "Получить слова песни" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "slyrics", + "original_name": "slyricscmd", + "description": { + "default": "Get lyrics from your current Spotify playback (Needs SpotifyNow module)", + "r": "Получить слова песни прослушиваемой в Спотифай, для работоспособности требуется модуль SpotifyNow", + "ru": "Получить слова песни прослушиваемой в Спотифай, для работоспособности требуется модуль SpotifyNow" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Lyrics", + "type_name": "🚫 Please type name of the song", + "genius": "🎵 Full lyrics on Genius", + "noSpotify": "🚫 Please install SpotifyNow module and proceed auth\n🌃 Install: .dlmod https://mods.hikariatama.ru/spotify.py", + "notFound": "🚫 No results found", + "couldn'tFind": "We couldn't find what are you looking for", + "sauth": "🚫 Execute .sauth before using this action.", + "SpotifyError": "🚫 Spotify error", + "noResults": "🚫 No results found for {}", + "noLyrics": "🚫 Couldn't find the lyrics", + "lyrics": "Lyrics for {} by {}\n{}", + "loading": "Loading lyrics for {} by {}...\n{}", + "_cmd_doc_ru_lyrics": "Получить слова песни", + "_cmd_doc_lyrics_ru": "Получить слова песни", + "_cmd_doc_ru_slyrics": "Получить слова песни прослушиваемой в Спотифай, для работоспособности требуется модуль SpotifyNow", + "_cmd_doc_slyrics_ru": "Получить слова песни прослушиваемой в Спотифай, для работоспособности требуется модуль SpotifyNow", + "_ihandle_doc_ru_lyrics": "Поиск текста песни", + "_ihandle_doc_lyrics_ru": "Поиск текста песни", + "type_name_ru": "🚫 Пожалуйста, введите имя композиции", + "genius_ru": "🎵 Полный текст на Genius", + "noSpotify_ru": "🚫 Пожалуйста установи модуль SpotifyNow и пройди авторизацию.\n🌃 Установка: .dlmod https://mods.hikariatama.ru/spotify.py", + "notFound_ru": "🚫 Результаты не найдены", + "couldn'tFind_ru": "К сожалению мы не нашли, что вы искали", + "sauth_ru": "🚫 Выполни .sauth перед этим действием.", + "SpotifyError_ru": "🚫 Ошибка Спотифай", + "noResults_ru": "🚫 Результаты для {} не найдены", + "noLyrics_ru": "🚫 Не удалось найти текст", + "lyrics_ru": "Текст песни {} от {}\n{}", + "loading_ru": "Загрузка текста песни {} от {}...\n{}" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "MoriSummerz/ftg-mods/magictext.py": { + "name": "MagicTextMod", + "description": "Magic Text generator", + "cls_doc": {}, + "meta": { + "pic": "https://i.imgur.com/nmAXM3k.png", + "banner": "https://i.imgur.com/3joMFwC.jpg", + "developer": "@morisummermods" + }, + "commands": [ + { + "mtset": "Set the symbols for animation (Separated by space. Example: .mtset ✨ 💖)" + }, + { + "mtiset": "Set the text for inline message (Example: .mtiset ❤️‍🔥 I want to tell you something...)" + }, + { + "mt": "Send message with animating text" + }, + { + "mti": "Send inline message with animating text" + } + ], + "new_commands": [ + { + "name": "mtset", + "original_name": "mtsetcmd", + "description": { + "default": "Set the symbols for animation (Separated by space. Example: .mtset ✨ 💖)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "mtiset", + "original_name": "mtisetcmd", + "description": { + "default": "Set the text for inline message (Example: .mtiset ❤️‍🔥 I want to tell you something...)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "mt", + "original_name": "mtcmd", + "description": { + "default": "Send message with animating text" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "mti", + "original_name": "mticmd", + "description": { + "default": "Send inline message with animating text" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "MagicText", + "inline_text": "❤️‍🔥 I want to tell you something...", + "only_2": "🚫 Please type only 2 symbols", + "symbols_success": "✅ Symbols set successfully", + "text_success": "✅ Text set successfully", + "no_text": "🚫 Please type text", + "open": "💖 Open", + "not_supported": "🚫 Not supported symbol", + "inline_text_ru": "❤️‍🔥 Я хочу тебе сказать что-то...", + "only_2_ru": "🚫 Пожалуйста, введите только 2 символа", + "symbols_success_ru": "✅ Символы установлены успешно", + "text_success_ru": "✅ Текст успешно установлен", + "no_text_ru": "🚫 Пожалуйста, введите текст", + "open_ru": "💖 Открыть", + "not_supported_ru": "🚫 Неподдерживаемый символ" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "MoriSummerz/ftg-mods/weather.py": { + "name": "WeatherMod", + "description": "Weather module", + "cls_doc": {}, + "meta": { + "pic": "https://i.imgur.com/iwoskSb.png", + "banner": "https://i.imgur.com/JR6VqYF.png", + "developer": "@morisummermods" + }, + "commands": [ + { + "weathercity": "Set default city for forecast" + }, + { + "weather": "Current forecast for provided city" + } + ], + "new_commands": [ + { + "name": "weathercity", + "original_name": "weathercitycmd", + "description": { + "default": "Set default city for forecast" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "weather", + "original_name": "weathercmd", + "description": { + "default": "Current forecast for provided city" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Weather" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "MoriSummerz/ftg-mods/TimeBot.py": { + "name": "TimerBotMod", "description": "", "cls_doc": {}, - "meta": { - "pic": null, - "banner": null, - "developer": "@limokanews" - }, - "commands": [], - "new_commands": [], - "inline_handlers": [], - "strings": {}, - "has_on_load": false, - "has_on_unload": false, - "class_cmd_names": {} - }, - "Sad0ff/modules-ftg/hitler.py": { - "name": "HitlerMod", - "description": "Hitler", - "cls_doc": {}, "meta": { "pic": null, "banner": null, @@ -45,15 +2171,15 @@ }, "commands": [ { - "h": ".h <реплай на сообщение/свой текст>\n@offsd подпишись-пожалеешь" + "timeb": "Пример ввода: .timeb <задержка появления текста в минутах> <текст>" } ], "new_commands": [ { - "name": "h", - "original_name": "hcmd", + "name": "timeb", + "original_name": "timebcmd", "description": { - "default": ".h <реплай на сообщение/свой текст>\n@offsd подпишись-пожалеешь" + "default": "Пример ввода: .timeb <задержка появления текста в минутах> <текст>" }, "cmd_names": {}, "aliases": [], @@ -65,33 +2191,48 @@ ], "inline_handlers": [], "strings": { - "name": "Hitler", - "usage": "Напиши .help Цитатник Гитлера" + "name": "TimeBot" }, "has_on_load": false, "has_on_unload": false, "class_cmd_names": {} }, - "Sad0ff/modules-ftg/jac.py": { - "name": "JacquesMod", - "description": "Жаконизатор", + "MoriSummerz/ftg-mods/magictext-ftg.py": { + "name": "MagicTextMod", + "description": "Magic Text generator", "cls_doc": {}, "meta": { "pic": null, "banner": null, - "developer": null + "developer": "@morisummermods" }, "commands": [ { - "j": ".j <реплай на сообщение/свой текст>\n@offsd подпишись-пожалеешь" + "mtset": "Set the symbols for animation (Separated by space. Example: .mtset ✨ 💖)" + }, + { + "mt": "Send message with animating text" } ], "new_commands": [ { - "name": "j", - "original_name": "jcmd", + "name": "mtset", + "original_name": "mtsetcmd", "description": { - "default": ".j <реплай на сообщение/свой текст>\n@offsd подпишись-пожалеешь" + "default": "Set the symbols for animation (Separated by space. Example: .mtset ✨ 💖)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "mt", + "original_name": "mtcmd", + "description": { + "default": "Send message with animating text" }, "cmd_names": {}, "aliases": [], @@ -103,33 +2244,65 @@ ], "inline_handlers": [], "strings": { - "name": "Жаконизатор", - "usage": "Напиши .help Жаконизатор" + "name": "MagicText", + "author": "morisummermods" }, "has_on_load": false, "has_on_unload": false, "class_cmd_names": {} }, - "Sad0ff/modules-ftg/DownloadYT.py": { - "name": "DownloadYTMod", - "description": "DownloadYT", + "MoriSummerz/ftg-mods/osu.py": { + "name": "OsuMod", + "description": "\"I'm an osu!bot that can do some things written by @morisummerzxc", "cls_doc": {}, "meta": { - "pic": null, - "banner": null, - "developer": null + "pic": "https://i.imgur.com/fcHCrS2.png", + "banner": "https://i.imgur.com/fPWWFrL.jpg", + "developer": "@morisummermods" }, "commands": [ { - "dyt": "отправляет видеов чат по ссылке из ютуба\n@offsd подпишись-пожалеешь" + "osume": "Remember user's nickname for commands" + }, + { + "osutop": "Get user's 5 best plays" + }, + { + "osuprofile": "Get user's profile" } ], "new_commands": [ { - "name": "dyt", - "original_name": "dytcmd", + "name": "osume", + "original_name": "osumecmd", "description": { - "default": "отправляет видеов чат по ссылке из ютуба\n@offsd подпишись-пожалеешь" + "default": "Remember user's nickname for commands" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "osutop", + "original_name": "osutopcmd", + "description": { + "default": "Get user's 5 best plays" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "osuprofile", + "original_name": "osuprofilecmd", + "description": { + "default": "Get user's profile" }, "cmd_names": {}, "aliases": [], @@ -141,39 +2314,37 @@ ], "inline_handlers": [], "strings": { - "name": "DownloadYT", - "usage": "Напиши .help DownloadYT" + "name": "Osu", + "you_are": " You are {} now", + "you_are_ru": " Теперь ты {}" }, "has_on_load": false, "has_on_unload": false, "class_cmd_names": {} }, - "Sad0ff/modules-ftg/allwithvoice.py": { - "name": "allwithvoiceMod", - "description": "Перевод текста в гс и наоборот", + "MoriSummerz/ftg-mods/picsaver.py": { + "name": "PicsaverMod", + "description": "\"Automatic Self-destructing media saver to Saved Messages", "cls_doc": {}, "meta": { "pic": null, "banner": null, - "developer": null + "developer": "@morisummermods" }, "commands": [ { - "vw": ".vw \nчтобы все работало как я задумывал, смените голос (если хотите) на женский у @Maksobot\n@offsd подпишись-пожалеешь" + "sp": "Reply to self-destructing media to save" }, { - "vm": ".vm " - }, - { - "t": ".t \nчтобы оно работало, включите в боте тихий режим командой /silent" + "ps": "Enable/disable automatic self-destructing media save" } ], "new_commands": [ { - "name": "vw", - "original_name": "vwcmd", + "name": "sp", + "original_name": "spcmd", "description": { - "default": ".vw \nчтобы все работало как я задумывал, смените голос (если хотите) на женский у @Maksobot\n@offsd подпишись-пожалеешь" + "default": "Reply to self-destructing media to save" }, "cmd_names": {}, "aliases": [], @@ -183,23 +2354,10 @@ "decorators": [] }, { - "name": "vm", - "original_name": "vmcmd", + "name": "ps", + "original_name": "pscmd", "description": { - "default": ".vm " - }, - "cmd_names": {}, - "aliases": [], - "usage": null, - "inline": false, - "is_inline_handler": false, - "decorators": [] - }, - { - "name": "t", - "original_name": "tcmd", - "description": { - "default": ".t \nчтобы оно работало, включите в боте тихий режим командой /silent" + "default": "Enable/disable automatic self-destructing media save" }, "cmd_names": {}, "aliases": [], @@ -211,30 +2369,30 @@ ], "inline_handlers": [], "strings": { - "name": "allwithvoice" + "name": "Picsaver" }, "has_on_load": false, "has_on_unload": false, "class_cmd_names": {} }, - "shadowhikka/sh.modules/TableInfo.py": { - "name": "TableMod", - "description": "Information in parents", + "MoriSummerz/ftg-mods/top20.py": { + "name": "Top20Mod", + "description": "", "cls_doc": {}, "meta": { "pic": null, - "banner": "https://i.imgur.com/GLgp9u1.jpeg", - "developer": "@shadow_modules" + "banner": null, + "developer": "@morisummermods" }, "commands": [ { - "tableadd": "" + "top20": "" } ], "new_commands": [ { - "name": "tableadd", - "original_name": "tableaddcmd", + "name": "top20", + "original_name": "top20cmd", "description": { "default": "" }, @@ -248,40 +2406,32 @@ ], "inline_handlers": [], "strings": { - "name": "TableMod", - "no_args": "😥 Arguments not found", - "args_incorrect": "😰 Arguments are not correct\n✔ Example arguments: .tableadd name|age|day|year|hobby|userid|geo", - "success": "😊 Successfully added", - "dont_touch": "💾 Do not touch this chat\n😊It was created for the TableInfo module to work", - "no_args_ru": "😥 Аргументы не найдены", - "args_incorrect_ru": "😰 Аргументы не правильные\n✔ Пример аргументов: .tableadd name|age|day|year|hobby|userid|geo", - "success_ru": "😊 Информация успешно добавлена", - "dont_touch_ru": "💾 Не трогайте этот чат\n😊Он был создан для работы модуля TableInfo" + "name": "Top20" }, "has_on_load": false, "has_on_unload": false, "class_cmd_names": {} }, - "shadowhikka/sh.modules/GPT2.py": { - "name": "GPT2Mod", - "description": "ChatGPT в модуле", + "MoriSummerz/ftg-mods/airalert.py": { + "name": "AirAlertMod", + "description": "🇺🇦 Предупреждение о воздушной тревоге.\nНужно быть подписаным на @air_alert_ua и включены уведомления в вашем боте", "cls_doc": {}, "meta": { - "pic": null, - "banner": "https://i.imgur.com/fdEskim.jpeg", - "developer": "@shadow_modules" + "pic": "https://i.imgur.com/AwKGCQe.png", + "banner": "https://i.imgur.com/V0Qhyi0.jpg", + "developer": "@morisummermods" }, "commands": [ { - "gpt": ".gpt " + "alertforward": "Перенаправление предупреждений в другие чаты.\nДля добавления/удаления введите команду с ссылкой на чат.\nДля просмотра чатов введите команду без аргументов\nДля установки кастомной таблички введите .alertforward set " } ], "new_commands": [ { - "name": "gpt", - "original_name": "gptcmd", + "name": "alertforward", + "original_name": "alertforwardcmd", "description": { - "default": ".gpt " + "default": "Перенаправление предупреждений в другие чаты.\nДля добавления/удаления введите команду с ссылкой на чат.\nДля просмотра чатов введите команду без аргументов\nДля установки кастомной таблички введите .alertforward set " }, "cmd_names": {}, "aliases": [], @@ -293,23 +2443,32417 @@ ], "inline_handlers": [], "strings": { - "name": "ChatGPT", - "wait": "🤖 GPT-2 is generating response, please wait", - "quest": "\n\n\n Your question to GPT-2 was: {args}", - "args_err": "⛔️ You didn't ask a question GPT-2", - "conf_err": "⛔️ You didn't provide an api key for GPT-2", - "wait_ru": "🤖 GPT-2 генерирует ответ, подождите", - "quest_ru": "\n\n\n Ваш вопрос к GPT-2 был: {args}", - "args_err_ru": "⛔️ Вы не задали вопрос GPT-2", - "conf_err_ru": "⛔️ Вы не указали api key для GPT-2" + "name": "AirAlert" }, "has_on_load": false, "has_on_unload": false, "class_cmd_names": {} }, - "shadowhikka/sh.modules/CheckerUsernames.py": { - "name": "CheckerUsernamesMod", - "description": "Check of avaliable usernames", + "SekaiYoneya/Friendly-telegram/Pic.py": { + "name": "PicPhotosMod", + "description": "Фотографии из @pic.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "gow": "" + } + ], + "new_commands": [ + { + "name": "gow", + "original_name": "gowcmd", + "description": { + "default": "" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Pic" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/kickall-addusers.py": { + "name": "addmembersMod", + "description": "", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "addusers": "Добавляет людей с чата в чат." + }, + { + "kickall": "Удаляет всех пользователей из чата." + } + ], + "new_commands": [ + { + "name": "addusers", + "original_name": "adduserscmd", + "description": { + "default": "Добавляет людей с чата в чат." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "kickall", + "original_name": "kickallcmd", + "description": { + "default": "Удаляет всех пользователей из чата." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "kickall & addusers" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/SpamBot.py": { + "name": "SpamBotMod", + "description": "Показывает ваши ограничения.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "spambot": "Смотреть статус ограничений." + }, + { + "thankbot": "Написать 'хорошо, спасибо', когда есть инлайн." + }, + { + "okbot": "Написать 'Ок', когда есть инлайн." + }, + { + "whatbot": "Спросить, почему на Вас могли жаловаться, когда есть инлайн." + }, + { + "plsbot": "Попросить снять Вам ограничения, когда есть инлайн." + }, + { + "ponspsbot": "Написать 'Понятно, спасибо', когда есть инлайн." + }, + { + "infobot": "Узнать больше о спаме, когда есть инлайн." + } + ], + "new_commands": [ + { + "name": "spambot", + "original_name": "spambotcmd", + "description": { + "default": "Смотреть статус ограничений." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "thankbot", + "original_name": "thankbotcmd", + "description": { + "default": "Написать 'хорошо, спасибо', когда есть инлайн." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "okbot", + "original_name": "okbotcmd", + "description": { + "default": "Написать 'Ок', когда есть инлайн." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "whatbot", + "original_name": "whatbotcmd", + "description": { + "default": "Спросить, почему на Вас могли жаловаться, когда есть инлайн." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "plsbot", + "original_name": "plsbotcmd", + "description": { + "default": "Попросить снять Вам ограничения, когда есть инлайн." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ponspsbot", + "original_name": "ponspsbotcmd", + "description": { + "default": "Написать 'Понятно, спасибо', когда есть инлайн." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "infobot", + "original_name": "infobotcmd", + "description": { + "default": "Узнать больше о спаме, когда есть инлайн." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "SpamBot" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/RenameCont.py": { + "name": "RenameMod", + "description": "Переиминовать или добавить в контакт.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "rename": "" + } + ], + "new_commands": [ + { + "name": "rename", + "original_name": "renamecmd", + "description": { + "default": "" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Rename" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/Online.py": { + "name": "EternalOnlineMod", + "description": "Вечный онлайн, который будет читать сообщения в чатах.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "online": "Включить вечный онлайн" + } + ], + "new_commands": [ + { + "name": "online", + "original_name": "onlinecmd", + "description": { + "default": "Включить вечный онлайн" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Online" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/AudioConverter.py": { + "name": "AudioConverterMod", + "description": "Конвертирование в разные форматы", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "tovoice": ".tovoice \nСконвертировать аудио в войс " + }, + { + "toformat": ".toformat [format] \n   Сконвертировать аудио/видео/войс в нужный формат \nПоддерживаются mp3, m4a, ogg, mpeg, wav, oga " + } + ], + "new_commands": [ + { + "name": "tovoice", + "original_name": "tovoicecmd", + "description": { + "default": ".tovoice \nСконвертировать аудио в войс " + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "toformat", + "original_name": "toformatcmd", + "description": { + "default": ".toformat [format] \n   Сконвертировать аудио/видео/войс в нужный формат \nПоддерживаются mp3, m4a, ogg, mpeg, wav, oga " + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "AudioConverter" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/Leo.py": { + "name": "leomatchMod", + "description": "Леонардо Дайвинчик", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "diz": "Дизлайкнуть пользователь." + }, + { + "like": "Лайкнуть пользователь." + }, + { + "spack": "Не нужен мне ваш стикерпак." + }, + { + "tt": "Не нужен мне ваш тик ток." + }, + { + "unafk": "Выйти из АФК и смотреть анкеты." + } + ], + "new_commands": [ + { + "name": "diz", + "original_name": "dizcmd", + "description": { + "default": "Дизлайкнуть пользователь." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "like", + "original_name": "likecmd", + "description": { + "default": "Лайкнуть пользователь." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "spack", + "original_name": "spackcmd", + "description": { + "default": "Не нужен мне ваш стикерпак." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tt", + "original_name": "ttcmd", + "description": { + "default": "Не нужен мне ваш тик ток." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "unafk", + "original_name": "unafkcmd", + "description": { + "default": "Выйти из АФК и смотреть анкеты." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Leo" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/BanMedia.py": { + "name": "BanMediaMod", + "description": "Модуль блокировки стикеров или гифок в чатах.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "banmedia": "Используй: .banmedia чтобы заблокировать стикер или гифку в чате. | аргументы «clear или clearall» (по желанию)" + } + ], + "new_commands": [ + { + "name": "banmedia", + "original_name": "banmediacmd", + "description": { + "default": "Используй: .banmedia чтобы заблокировать стикер или гифку в чате. | аргументы «clear или clearall» (по желанию)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "BanMedia" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/TikTok.py": { + "name": "TikTok", + "description": "", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [], + "new_commands": [], + "inline_handlers": [], + "strings": {}, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/x0-Uploader.py": { + "name": "x0Mod", + "description": "Uploader", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "x0": "" + } + ], + "new_commands": [ + { + "name": "x0", + "original_name": "x0cmd", + "description": { + "default": "" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "x0 Uploader" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/Quotes.py": { + "name": "QuotesMod", + "description": "Quotes a message", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "mquote": "" + }, + { + "quote": ".quote - quote a message" + }, + { + "fquote": ".fquote @ or - fake quote" + } + ], + "new_commands": [ + { + "name": "mquote", + "original_name": "mquotecmd", + "description": { + "default": "" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "quote", + "original_name": "quotecmd", + "description": { + "default": ".quote - quote a message" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "fquote", + "original_name": "fquotecmd", + "description": { + "default": ".fquote @ or - fake quote" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Quotes", + "silent_processing_cfg_doc": "Process quote silently(mostly w/o editing)", + "module_endpoint_cfg_doc": "Module endpoint URL", + "quote_limit_cfg_doc": "Limit for messages per quote", + "max_width_cfg_doc": "Maximum quote width in pixels", + "scale_factor_cfg_doc": "Quote quality (up to 5.5)", + "square_avatar_cfg_doc": "Square avatar in quote", + "text_color_cfg_doc": "Color of text in quote", + "reply_line_color_cfg_doc": "Reply line color", + "reply_thumb_radius_cfg_doc": "Reply media thumbnail radius in pixels", + "admintitle_color_cfg_doc": "Admin title color", + "message_radius_cfg_doc": "Message radius in px", + "picture_radius_cfg_doc": "Media picture radius in px", + "background_color_cfg_doc": "Quote background color", + "quote_limit_reached": "The maximum number of messages in multiquote - {}.", + "fq_incorrect_args": "Args incorrect. \"@$username (ID)$text\" or \"$reply $text\"", + "updating": "Updating...", + "update_error": "Update error", + "processing": "Processing...", + "unreachable_error": "API Host is unreachable now. Please try again later.", + "server_error": "API Error occured :)", + "no_reply": "You didn't reply to a message.", + "creator": "Owner", + "admin": "Admin", + "channel": "Channel", + "media_type_photo": "Photo", + "media_type_video": "📹Video", + "media_type_videomessage": "📹Video message", + "media_type_voice": "🎵Voice message", + "media_type_audio": "🎧Music: {} - {}", + "media_type_contact": "👤Contact: {}", + "media_type_poll": "📊Poll: ", + "media_type_quiz": "📊Quiz: ", + "media_type_location": "📍Location", + "media_type_gif": "🖼GIF", + "media_type_sticker": "Sticker", + "media_type_file": "💾File", + "dice_type_dice": "Dice", + "dice_type_dart": "Dart", + "ball_thrown": "Ball thrown", + "ball_kicked": "Ball kicked", + "dart_thrown": "Dart thrown", + "dart_almostthere": "almost there!", + "dart_missed": "missed!", + "dart_bullseye": "bullseye!" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/Pinger.py": { + "name": "PingerMod", + "description": "более точный пинг", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "ping": "пингует" + } + ], + "new_commands": [ + { + "name": "ping", + "original_name": "pingcmd", + "description": { + "default": "пингует" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Pinger" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/WelcomeLeft.py": { + "name": "WelcomeLeftMod", + "description": "Вход и выход пользователей в чате.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "welcome": "Включить/выключить приветствие новых пользователей в чате. Используй: .welcome ." + }, + { + "setwelcome": "Установить приветствие новых пользователей в чате.\nИспользуй: .setwelcome <текст (можно использовать {name}; {chat})>; ничего." + }, + { + "left": "Включить/выключить выход пользователей из чата. Используй: .left ." + }, + { + "setleft": "Установить новое сообщение при выходе из чата пользователей.\nИспользуй: .setleft <текст (можно использовать {name}; {chat})>; ничего." + } + ], + "new_commands": [ + { + "name": "welcome", + "original_name": "welcomecmd", + "description": { + "default": "Включить/выключить приветствие новых пользователей в чате. Используй: .welcome ." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "setwelcome", + "original_name": "setwelcomecmd", + "description": { + "default": "Установить приветствие новых пользователей в чате.\nИспользуй: .setwelcome <текст (можно использовать {name}; {chat})>; ничего." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "left", + "original_name": "leftcmd", + "description": { + "default": "Включить/выключить выход пользователей из чата. Используй: .left ." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "setleft", + "original_name": "setleftcmd", + "description": { + "default": "Установить новое сообщение при выходе из чата пользователей.\nИспользуй: .setleft <текст (можно использовать {name}; {chat})>; ничего." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Welcome & Left" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/AutoBlackList.py": { + "name": "AutoBlackListMod", + "description": "Кидает всех неконтактов в ЧС.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "autobl": "Включить/выключить режим" + }, + { + "autoblstatus": "Проверить статус AutoBlackList" + }, + { + "autodelchat": "Автоматически удаляет диалог после того, как кинет в ЧС" + } + ], + "new_commands": [ + { + "name": "autobl", + "original_name": "autoblcmd", + "description": { + "default": "Включить/выключить режим" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "autoblstatus", + "original_name": "autoblstatuscmd", + "description": { + "default": "Проверить статус AutoBlackList" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "autodelchat", + "original_name": "autodelchatcmd", + "description": { + "default": "Автоматически удаляет диалог после того, как кинет в ЧС" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "AutoBlackList" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/ChatStats.py": { + "name": "ChatStatisticMod", + "description": "Статистика чата", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "stata": "" + } + ], + "new_commands": [ + { + "name": "stata", + "original_name": "statacmd", + "description": { + "default": "" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "ChatStatistic" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/SendPhotos.py": { + "name": "GetPPMod", + "description": "Description for module", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "poto": "Кинуть фоточки" + } + ], + "new_commands": [ + { + "name": "poto", + "original_name": "potocmd", + "description": { + "default": "Кинуть фоточки" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "SendPhotos" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/Anti-PM.py": { + "name": "AntiPMMod", + "description": "", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "pm": "Используй: .pm : чтобы включить/отключить авто ответ на личные сообщения." + }, + { + "allow": "Используй: .allow чтобы разрешить этому пользователю писать вам в личку." + }, + { + "deny": "Используй: .deny чтобы запретить этому пользователю писать вам в личку." + }, + { + "allowed": "Используй: .allowed : чтобы посмотреть список пользователей которым вы разрешили писать в личку." + }, + { + "block": "Используй: .block чтобы заблокировать этого пользователя." + }, + { + "unblock": "Используй: .unblock чтобы разблокировать этого пользователя." + }, + { + "addcont": "Используй: .addcont чтобы добавить пользователя в свои контакты." + }, + { + "delcont": "Используй: .delcont чтобы удалить пользователя из своих контактов." + }, + { + "rename": "" + } + ], + "new_commands": [ + { + "name": "pm", + "original_name": "pmcmd", + "description": { + "default": "Используй: .pm : чтобы включить/отключить авто ответ на личные сообщения." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "allow", + "original_name": "allowcmd", + "description": { + "default": "Используй: .allow чтобы разрешить этому пользователю писать вам в личку." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "deny", + "original_name": "denycmd", + "description": { + "default": "Используй: .deny чтобы запретить этому пользователю писать вам в личку." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "allowed", + "original_name": "allowedcmd", + "description": { + "default": "Используй: .allowed : чтобы посмотреть список пользователей которым вы разрешили писать в личку." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "block", + "original_name": "blockcmd", + "description": { + "default": "Используй: .block чтобы заблокировать этого пользователя." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "unblock", + "original_name": "unblockcmd", + "description": { + "default": "Используй: .unblock чтобы разблокировать этого пользователя." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "addcont", + "original_name": "addcontcmd", + "description": { + "default": "Используй: .addcont чтобы добавить пользователя в свои контакты." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "delcont", + "original_name": "delcontcmd", + "description": { + "default": "Используй: .delcont чтобы удалить пользователя из своих контактов." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "rename", + "original_name": "renamecmd", + "description": { + "default": "" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Anti-PM", + "pm_off": "Теперь вы принимаете сообщения от всех пользователей.", + "pm_on": "Вы перестали принимать сообщения от пользователей.", + "pm_go_away": "Здравствуй! К сожалению, я не принимаю личные сообщения.\n\nСвяжитесь со мной в @sekai_pmbot, или ожидайте пока я одобрю вас.", + "pm_allowed": "Я разрешил {} писать мне.", + "pm_deny": "Я запретил {} писать мне.", + "blocked": "{} был(-а) занесен(-а) в Черный Список.", + "unblocked": "{} удален(-а) из Черного Списка.", + "addcontact": "{} был(-а) добавлен(-а) в контакты.", + "delcontact": "{} был(-а) удален(-а) из контактов.", + "who_to_allow": "Кому разрешить писать в личку ?", + "who_to_deny": "Кому запретить писать в личку ?", + "who_to_block": "Укажите, кого блокировать.", + "who_to_unblock": "Укажите, кого разблокировать.", + "who_to_contact": "Укажите, кого добавить в контакт.", + "who_to_delcontact": "Укажите, кого удалить из контактов." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/AudioEditor.py": { + "name": "AudioEditorMod", + "description": "Модуль для работы со звуком(???)", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "bass": ".bass [уровень bass'а 2-100 (Default 2)] \nBassBoost" + }, + { + "fv": ".fv [уровень шакала 2-100 (Default 25)] \nШакалинг" + }, + { + "echos": ".echos \nЭхо эффект" + }, + { + "volup": ".volup \nУвеличить громкость на 10dB" + }, + { + "voldw": ".voldw \nУменьшить громкость на 10dB" + }, + { + "revs": ".revs \nРазвернуть аудио" + }, + { + "reps": ".reps \nПовторить аудио 2 раза подряд" + }, + { + "slows": ".slows \nЗамедлить аудио 0.5x" + }, + { + "fasts": ".fasts \nУскорить аудио 1.5x" + }, + { + "rights": ".rights \nВесь звук в правый канал" + }, + { + "lefts": ".lefts \nВесь звук в левый канал" + }, + { + "norms": ".norms \nНормализовать звук (Из тихого - нормальный)" + }, + { + "byroberts": ".byroberts \nДобавить в конец аудио \"Directed by Robert B Weide\"" + } + ], + "new_commands": [ + { + "name": "bass", + "original_name": "basscmd", + "description": { + "default": ".bass [уровень bass'а 2-100 (Default 2)] \nBassBoost" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "fv", + "original_name": "fvcmd", + "description": { + "default": ".fv [уровень шакала 2-100 (Default 25)] \nШакалинг" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "echos", + "original_name": "echoscmd", + "description": { + "default": ".echos \nЭхо эффект" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "volup", + "original_name": "volupcmd", + "description": { + "default": ".volup \nУвеличить громкость на 10dB" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "voldw", + "original_name": "voldwcmd", + "description": { + "default": ".voldw \nУменьшить громкость на 10dB" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "revs", + "original_name": "revscmd", + "description": { + "default": ".revs \nРазвернуть аудио" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "reps", + "original_name": "repscmd", + "description": { + "default": ".reps \nПовторить аудио 2 раза подряд" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "slows", + "original_name": "slowscmd", + "description": { + "default": ".slows \nЗамедлить аудио 0.5x" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "fasts", + "original_name": "fastscmd", + "description": { + "default": ".fasts \nУскорить аудио 1.5x" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "rights", + "original_name": "rightscmd", + "description": { + "default": ".rights \nВесь звук в правый канал" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "lefts", + "original_name": "leftscmd", + "description": { + "default": ".lefts \nВесь звук в левый канал" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "norms", + "original_name": "normscmd", + "description": { + "default": ".norms \nНормализовать звук (Из тихого - нормальный)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "byroberts", + "original_name": "byrobertscmd", + "description": { + "default": ".byroberts \nДобавить в конец аудио \"Directed by Robert B Weide\"" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "AudioEditor" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/DeleteTimer.py": { + "name": "DeleteTimer", + "description": "", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [], + "new_commands": [], + "inline_handlers": [], + "strings": {}, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/Frazes.py": { + "name": "FrazesMod", + "description": "Госу, пикапы, подкаты.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "gosu": "Выебать чью-то мамку" + }, + { + "pikap": "Пикап" + }, + { + "podkat": "Подкат" + }, + { + "ayf": "АУФ!!!" + } + ], + "new_commands": [ + { + "name": "gosu", + "original_name": "gosucmd", + "description": { + "default": "Выебать чью-то мамку" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pikap", + "original_name": "pikapcmd", + "description": { + "default": "Пикап" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "podkat", + "original_name": "podkatcmd", + "description": { + "default": "Подкат" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ayf", + "original_name": "ayfcmd", + "description": { + "default": "АУФ!!!" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Фразы" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/Sender.py": { + "name": "SenderMod", + "description": "", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "snd": ".snd <канал/чат/id> \nОтправить сообщение в чат/канал(без авторства)" + } + ], + "new_commands": [ + { + "name": "snd", + "original_name": "sndcmd", + "description": { + "default": ".snd <канал/чат/id> \nОтправить сообщение в чат/канал(без авторства)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Sender" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/MyRep.py": { + "name": "MyRepMod", + "description": "Модуль с вашей репутацией", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "rep": "Включить режим репутаций." + }, + { + "myrep": "Посмотреть свою репутацию. Используй: .myrep clear (очистка репутации)." + } + ], + "new_commands": [ + { + "name": "rep", + "original_name": "repcmd", + "description": { + "default": "Включить режим репутаций." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "myrep", + "original_name": "myrepcmd", + "description": { + "default": "Посмотреть свою репутацию. Используй: .myrep clear (очистка репутации)." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Репутация" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/ChatModule.py": { + "name": "ChatMod", + "description": "Чат модули", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "userid": "Команда .userid <@ или реплай> показывает ID выбранного пользователя." + }, + { + "chatid": "Команда .chatid показывает ID чата." + }, + { + "invite": "Используйте .invite <@ или реплай>, чтобы добавить пользователя в чат." + }, + { + "leave": "Используйте команду .leave, чтобы выйти из чата." + }, + { + "users": "Команда .users <имя>; ничего выводит список всех пользователей в чате." + }, + { + "admins": "Команда .admins показывает список всех админов в чате." + }, + { + "bots": "Команда .bots показывает список всех ботов в чате." + } + ], + "new_commands": [ + { + "name": "userid", + "original_name": "useridcmd", + "description": { + "default": "Команда .userid <@ или реплай> показывает ID выбранного пользователя." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "chatid", + "original_name": "chatidcmd", + "description": { + "default": "Команда .chatid показывает ID чата." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "invite", + "original_name": "invitecmd", + "description": { + "default": "Используйте .invite <@ или реплай>, чтобы добавить пользователя в чат." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "leave", + "original_name": "leavecmd", + "description": { + "default": "Используйте команду .leave, чтобы выйти из чата." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "users", + "original_name": "userscmd", + "description": { + "default": "Команда .users <имя>; ничего выводит список всех пользователей в чате." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "admins", + "original_name": "adminscmd", + "description": { + "default": "Команда .admins показывает список всех админов в чате." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "bots", + "original_name": "botscmd", + "description": { + "default": "Команда .bots показывает список всех ботов в чате." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "ChatModule" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/SearchMusic.py": { + "name": "SearchMusicMod", + "description": "Модуль SearchMusic - поиск музыки \nРаботает через бота @lybot ", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "sm": "Используй: .sm «название» чтобы найти музыку по названию." + } + ], + "new_commands": [ + { + "name": "sm", + "original_name": "smcmd", + "description": { + "default": "Используй: .sm «название» чтобы найти музыку по названию." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "SearchMusic" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/Whois.py": { + "name": "WhoIsMod", + "description": "Получает информацию о пользователе.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "whois": "Используй .whois <@ или реплай>; ничего" + } + ], + "new_commands": [ + { + "name": "whois", + "original_name": "whoiscmd", + "description": { + "default": "Используй .whois <@ или реплай>; ничего" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Whois" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "SekaiYoneya/Friendly-telegram/GroupCreator.py": { + "name": "GroupCreatorMod", + "description": "Создать чат или канал.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "create": "Используй .create <название>, чтобы создать группу, супергруппу или канал." + } + ], + "new_commands": [ + { + "name": "create", + "original_name": "createcmd", + "description": { + "default": "Используй .create <название>, чтобы создать группу, супергруппу или канал." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "GroupCreator" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "1jpshiro/hikka-modules/Autotime.py": { + "name": "Autotime", + "description": "Automatic stuff for your profile", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://0x0.st/s/FIR0RnhUN5pZV5CZ6sNFEw/8KBz.jpg", + "developer": "@shiro_hikka" + }, + "commands": [ + { + "cfgset": " - specify a timezone\nRegarding to UTC+0" + }, + { + "autoname": " - autotime in nickname | {time} must be placed in the text\nWrite without argument to disable" + }, + { + "autobio": " - autotime in bio | {time} must be placed in the text\nWrite without argument to disable" + } + ], + "new_commands": [ + { + "name": "cfgset", + "original_name": "cfgsetcmd", + "description": { + "default": " - specify a timezone\nRegarding to UTC+0" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "autoname", + "original_name": "autonamecmd", + "description": { + "default": " - autotime in nickname | {time} must be placed in the text\nWrite without argument to disable" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "autobio", + "original_name": "autobiocmd", + "description": { + "default": " - autotime in bio | {time} must be placed in the text\nWrite without argument to disable" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Autotime", + "no_time": "😒 You didn't place a {time}", + "cfg": "Positive or negative integer from -12 to 12 inclusively" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "1jpshiro/hikka-modules/MessageEraser.py": { + "name": "MessageEraser", + "description": "Delete your messages in the current chat", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://0x0.st/s/FIR0RnhUN5pZV5CZ6sNFEw/8KBz.jpg", + "developer": "@shiro_hikka" + }, + "commands": [ + { + "stoppurge": "Interrupt the deletion process\nUse in the chat where you've previously started deletion" + }, + { + "purge": "[reply] [10s / 10m / 10h / 10d] [-all] - delete all your messages in the current chat or only ones up to the message you replied to\nPossible to do with a delay\n-all - to delete messages from each topic if this is a forum otherwise flag'll just be ignored\nExample: 10h 3d" + } + ], + "new_commands": [ + { + "name": "stoppurge", + "original_name": "stoppurgecmd", + "description": { + "default": "Interrupt the deletion process\nUse in the chat where you've previously started deletion" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "purge", + "original_name": "purgecmd", + "description": { + "default": "[reply] [10s / 10m / 10h / 10d] [-all] - delete all your messages in the current chat or only ones up to the message you replied to\nPossible to do with a delay\n-all - to delete messages from each topic if this is a forum otherwise flag'll just be ignored\nExample: 10h 3d" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "MessageEraser", + "enabled": "😒 It's not operational now anyway", + "disabled": "❄️ Operation status changed to disabled", + "interrupted": "😀 The deletion was interrupted because you changed your mind", + "none": "👁️ You didn't even intend to delete anything here, but anyway it's disabled now" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "1jpshiro/hikka-modules/Counter.py": { + "name": "Counter", + "description": "Inline Clicks Counter", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://0x0.st/s/FIR0RnhUN5pZV5CZ6sNFEw/8KBz.jpg", + "developer": "@shiro_hikka" + }, + "commands": [ + { + "creset": "[-u] [-c] - reset the counter\n-u (users list) -c (counts list)" + }, + { + "count": "Creates an inline button for counting a presses" + } + ], + "new_commands": [ + { + "name": "creset", + "original_name": "cresetcmd", + "description": { + "default": "[-u] [-c] - reset the counter\n-u (users list) -c (counts list)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "count", + "original_name": "countcmd", + "description": { + "default": "Creates an inline button for counting a presses" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Counter", + "count": "Counter: {}" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "1jpshiro/hikka-modules/StickerStealer.py": { + "name": "StickerStealer", + "description": "Emoji / Sticker pickpocket", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://0x0.st/s/FIR0RnhUN5pZV5CZ6sNFEw/8KBz.jpg", + "developer": "@shiro_hikka" + }, + "commands": [ + { + "steal": " - add an emoji or sticker to your pack\nEmoji: one type of emoji only is possible to be used at time" + } + ], + "new_commands": [ + { + "name": "steal", + "original_name": "stealcmd", + "description": { + "default": " - add an emoji or sticker to your pack\nEmoji: one type of emoji only is possible to be used at time" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "StickerStealer", + "incorrect": "🤨 It's not a sticker or emoji" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "1jpshiro/hikka-modules/ChannelImitator.py": { + "name": "ChannelImitator", + "description": "", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://0x0.st/s/FIR0RnhUN5pZV5CZ6sNFEw/8KBz.jpg", + "developer": "@shiro_hikka" + }, + "commands": [], + "new_commands": [], + "inline_handlers": [], + "strings": {}, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "1jpshiro/hikka-modules/PMStat.py": { + "name": "PMStat", + "description": "Defines how many messages did you and your chat partner write", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://0x0.st/s/FIR0RnhUN5pZV5CZ6sNFEw/8KBz.jpg", + "developer": "@shiro_hikka" + }, + "commands": [ + { + "stat": "[-p] [-s] - (-p - counts your chat partner messages) (-s - send result to the saved messages)" + } + ], + "new_commands": [ + { + "name": "stat", + "original_name": "statcmd", + "description": { + "default": "[-p] [-s] - (-p - counts your chat partner messages) (-s - send result to the saved messages)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "PMStat", + "q": "👨‍💻 All in all, {} messages were counted from {}", + "pm": "🤨 Use in PM only" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "1jpshiro/hikka-modules/Tracker.py": { + "name": "Tracker", + "description": "Tracks the change history of usernames and nicknames of users", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://0x0.st/s/FIR0RnhUN5pZV5CZ6sNFEw/8KBz.jpg", + "developer": "@shiro_hikka" + }, + "commands": [ + { + "track": "Enable / Disable the tracking" + }, + { + "addtrack": " - add a new user to track" + }, + { + "deltrack": "Remove user from the track list" + }, + { + "trackstat": "View the statistic about users you're tracking" + } + ], + "new_commands": [ + { + "name": "track", + "original_name": "trackcmd", + "description": { + "default": "Enable / Disable the tracking" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "addtrack", + "original_name": "addtrackcmd", + "description": { + "default": " - add a new user to track" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "deltrack", + "original_name": "deltrackcmd", + "description": { + "default": "Remove user from the track list" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "trackstat", + "original_name": "trackstatcmd", + "description": { + "default": "View the statistic about users you're tracking" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Tracker", + "enabled": "The tracker successfully enabled", + "disabled": "The tracker successfully disabled", + "no_user": "It seems this user doesn't exist, try another ID/Username", + "change_status": "You just changed a status of tracking the user", + "new_user": "You've successfully added a new user to track", + "no_stat": "You're currently tracking no user", + "only_one": "You're currently tracking only one user", + "removed": "You've removed this user from the track list and each ID was descendingly replaced", + "not_removed": "This user isn't added to the list so there's nobody to remove", + "exists": "This user's already included in the track list, he's ID is {}", + "cfg": "Specify a period of the cooldown between checks" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "1jpshiro/hikka-modules/Timer.py": { + "name": "Timer", + "description": "Creates fine adorned timer", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://0x0.st/s/FIR0RnhUN5pZV5CZ6sNFEw/8KBz.jpg", + "developer": "@shiro_hikka" + }, + "commands": [ + { + "timer": "[5h 5m 5s] - launch the timer" + } + ], + "new_commands": [ + { + "name": "timer", + "original_name": "timercmd", + "description": { + "default": "[5h 5m 5s] - launch the timer" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Timer", + "q": "Current Timer for {}\n👾 {} left" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/komarumod.py": { + "name": "KomaruMod", + "description": "Random picture/video/gif from the @komarueveryday", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/Komaru.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "komaru": "- choose a random picture/gif/video | (RU) - подобрать рандом картинку(пикчу)/видео/гиф" + } + ], + "new_commands": [ + { + "name": "komaru", + "original_name": "komaru", + "description": { + "default": "- choose a random picture/gif/video", + "ru": "- подобрать рандом картинку(пикчу)/видео/гиф" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Komaru", + "choosing": "🔴 Choosing {}...", + "gif": "gif", + "video": "video", + "photo": "photo", + "choosing_ru": "🔴 Подбираем {}...", + "gif_ru": "ваш гиф", + "video_ru": "ваше видео", + "photo_ru": "вашу картинку(пикчу)" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/shazam.py": { + "name": "ShazamMod", + "description": "Use to search for a song using audio.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/AstroShazam.png", + "developer": "@AstroModules" + }, + "commands": [ + { + "sh": " - recognize track | (RU) - распознать трек" + } + ], + "new_commands": [ + { + "name": "sh", + "original_name": "sh", + "description": { + "default": " - recognize track", + "ru": " - распознать трек" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Shazam", + "Downloading": "📥 Downloading...", + "Searching": "🔎 Searching...", + "no_reply": "🎙 Please reply to an audio message.", + "not_found": "🚫 Song not found.", + "track_info": " Song found\n📝 Name \"{}\"", + "Downloading_ru": "📥 Загрузка..", + "Searching_ru": "🔎 Поиск..", + "no_reply_ru": "🎙 Oтветьте на аудио сообщение", + "not_found_ru": "🚫 Не удалось найти песню", + "track_info_ru": " Песня найдена\n📝 Название: \"{}\"" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/astroweather.py": { + "name": "AstroWeatherMod", + "description": "Модуль для получения информации о погоде в Вашем городе, в красивом формате", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/AstroWeather.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "aw": "<город> - узнать погоду в указанном городе" + } + ], + "new_commands": [ + { + "name": "aw", + "original_name": "aw", + "description": { + "default": "<город> - узнать погоду в указанном городе" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "AstroWeather", + "error": "⚠️ Ошибка", + "api_error": "🚫 Вы не указали API ключ!\n👀 Пожалуйста, укажите его в конфиге ниже", + "search": "{} Поиск информации о погоде в городе {}.." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/achievements.py": { + "name": "AchievementsMod", + "description": "Create the achievement from https://minecraft-inside.ru/achievements/\n\nIdea from @Den4ikSOP & @boyhao", + "cls_doc": { + "ru": "Создает достижение из https://minecraft-inside.ru/achievements/\n\nIdea from @Den4ikSOP & @boyhao" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/Achievements.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "achievement": "[title] \"\" [-icon ] [-title-color #] [-text-color #] | (RU) [Заголовок] \"<текст>\" [-icon ] [-title-color #<цвет>] [-text-color #<цвет>]" + } + ], + "new_commands": [ + { + "name": "achievement", + "original_name": "achievement", + "description": { + "default": "[title] \"\" [-icon ] [-title-color #] [-text-color #]", + "ru": "[Заголовок] \"<текст>\" [-icon ] [-title-color #<цвет>] [-text-color #<цвет>]" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Achievements", + "text_color_cfg": "Text color in format #RRGGBB (hex, default #ffffff)", + "title_color_cfg": "Title color in format #RRGGBB (hex, default #ffff00)", + "icon_id_cfg": "Icon ID (1-1099, default 893) from https://minecraft-inside.ru/achievements/ (row number * 15 + icon number in row from 1 to 15)", + "invalid_length": "🚫 The length of the text must be between 1 and 45 characters", + "invalid_icon_id": "🚫 The icon ID must be between 1 and 1099", + "invalid_color": "🚫 The color must be in the format #RRGGBB", + "title_cfg": "Title of achievement (no more than 45 characters)", + "text_color_cfg_ru": "Цвет текста в формате #RRGGBB (hex, по умолчанию #ffffff)", + "title_color_cfg_ru": "Цвет заголовка в формате #RRGGBB (hex, по умолчанию #ffff00)", + "icon_id_cfg_ru": "ID иконки (от 1 до 1099) из https://minecraft-inside.ru/achievements/ (номер строки * 15 + номер иконки в строке от 1 до 15)", + "invalid_length_ru": "🚫 Длина текста должна быть между 1 и 45 символами", + "invalid_icon_id_ru": "🚫 ID иконки должен быть между 1 и 1099", + "invalid_color_ru": "🚫 Цвет должен быть в формате #RRGGBB", + "title_cfg_ru": "Заголовок достижения (не более 45 символов)" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/YandexMusic.py": { + "name": "YaMusicMod", + "description": "Поиск музыки через музыкального бота от Яндекса", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/YaMusic.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "ym": "- найти трек по названию" + } + ], + "new_commands": [ + { + "name": "ym", + "original_name": "ymcmd", + "description": { + "default": "- найти трек по названию" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "YandexMusic", + "na": "😅 А что искать то?", + "searching": "Поиск..." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/TwinkManager.py": { + "name": "TwinkManagerMod", + "description": "Управление твинками через основной аккаунт.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/TwinkManager.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "trestart": "- перезагрузить аккаунты" + }, + { + "tping": "- проверить пинг" + }, + { + "tdlmod": " - загрузить модули на аккаунты" + }, + { + "tterminal": " - выполнить действие в терминале" + }, + { + "tupdate": "- обновить хикку на аккаунтах" + }, + { + "thelp": "- список модулей либо информация о модуле" + }, + { + "tloadmod": " - загрузить файл модуля на аккаунты" + } + ], + "new_commands": [ + { + "name": "trestart", + "original_name": "trestart", + "description": { + "default": "- перезагрузить аккаунты" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tping", + "original_name": "tping", + "description": { + "default": "- проверить пинг" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tdlmod", + "original_name": "tdlmod", + "description": { + "default": " - загрузить модули на аккаунты" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tterminal", + "original_name": "tterminal", + "description": { + "default": " - выполнить действие в терминале" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tupdate", + "original_name": "tupdate", + "description": { + "default": "- обновить хикку на аккаунтах" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "thelp", + "original_name": "thelp", + "description": { + "default": "- список модулей либо информация о модуле" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tloadmod", + "original_name": "tloadmod", + "description": { + "default": " - загрузить файл модуля на аккаунты" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Twink-Manager", + "pref1": "Префикс первого вашего твинка", + "pref2": "Префикс второго вашего твинка (если имеется)", + "pref3": "Префикс третьего вашего твинка (если имеется)", + "all_t": "Выполнять действия также и с вашего основного аккаунта?" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/astroafk.py": { + "name": "AstroAfkMod", + "description": "Полностью настраиваемый модуль для ухода в АФК режим! Обновление TxAFK!", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/AstroAFK.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "asst": "- открыть настройки модуля" + }, + { + "goafk": "- войти в АФК режим" + }, + { + "ungoafk": "- выйти из режима АФК" + } + ], + "new_commands": [ + { + "name": "asst", + "original_name": "asst", + "description": { + "default": "- открыть настройки модуля" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "goafk", + "original_name": "goafk", + "description": { + "default": "- войти в АФК режим" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ungoafk", + "original_name": "ungoafk", + "description": { + "default": "- выйти из режима АФК" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "AstroAFK", + "lname": "| afk.", + "bt_off_afk": "🚫 АФК режим отключен!", + "_cfg_cst_btn": "Ссылка на чат, которая будет находиться под текстом АФК. Чтобы вовсе убрать, напишите None", + "feedback_bot__text": "Юзернейм вашего feedback бота. Если нету - не трогайте", + "button__text": "Добавить инлайн кнопку отключения АФК режима?", + "custom_text__afk_text": "Кастомный текст афк. Используй {time} для вывода последнего времени нахождения в сети и {reason} для указания причины ухода в афк" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/demotivator.py": { + "name": "DemotivatorMod", + "description": "Demotivate picture with text, arguments and config.", + "cls_doc": { + "ru": "Демотивировает картинку по параметрам(текст, аргументы и конфиг)." + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/Demotivator.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "demotivate": "\n[-bottom/-btm-text/-bottom-text - add. text below]\n[-wt/-watermark - add watermark]\n[-font-color/-ftc (red/while/blue/yellow/...) - font color (white by default)]\n[-fill-color/-flc (red/while/blue/yellow/...) - background color (black by default)]\n[-font/-font-name/-font-link (not zip, but ttf) - font for text]\n[-top-size/-topsize/-tpsz (default 80) - main text size]\n[-bottom-size/-btmsz (default 60) - extra size text]\n[-arrange - adjust photo frames]\n- demotivate a picture according to the given text and arguments | (RU) <текст>\n [-bottom/-btm-text/-bottom-text <текст> - доп. текст внизу]\n [-wt/-watermark <текст> - добавить водяной знак]\n [-font-color/-ftc <цвет> (red/while/blue/yellow/...) - цвет шрифта (по дефолту white)]\n [-fill-color/-flc <цвет> (red/while/blue/yellow/...) - цвет заднего фона (по дефолту black)]\n [-font/-font-name/-font-link <ссылка на файл со шрифтами> (не zip, а ttf) - шрифт для текста]\n [-top-size/-topsize/-tpsz <размер> (по дефолту 80) - размер главного текста]\n [-bottom-size/-btmsz <размер> (по дефолту 60) - размер доп.(нижнего) текста]\n [-arrange - регулировать рамки под фотографию]\n - демотивировать картинку по заданному тексту и аргументам\n " + } + ], + "new_commands": [ + { + "name": "demotivate", + "original_name": "demotivate", + "description": { + "default": "\n[-bottom/-btm-text/-bottom-text - add. text below]\n[-wt/-watermark - add watermark]\n[-font-color/-ftc (red/while/blue/yellow/...) - font color (white by default)]\n[-fill-color/-flc (red/while/blue/yellow/...) - background color (black by default)]\n[-font/-font-name/-font-link (not zip, but ttf) - font for text]\n[-top-size/-topsize/-tpsz (default 80) - main text size]\n[-bottom-size/-btmsz (default 60) - extra size text]\n[-arrange - adjust photo frames]\n- demotivate a picture according to the given text and arguments", + "ru": "<текст>\n [-bottom/-btm-text/-bottom-text <текст> - доп. текст внизу]\n [-wt/-watermark <текст> - добавить водяной знак]\n [-font-color/-ftc <цвет> (red/while/blue/yellow/...) - цвет шрифта (по дефолту white)]\n [-fill-color/-flc <цвет> (red/while/blue/yellow/...) - цвет заднего фона (по дефолту black)]\n [-font/-font-name/-font-link <ссылка на файл со шрифтами> (не zip, а ttf) - шрифт для текста]\n [-top-size/-topsize/-tpsz <размер> (по дефолту 80) - размер главного текста]\n [-bottom-size/-btmsz <размер> (по дефолту 60) - размер доп.(нижнего) текста]\n [-arrange - регулировать рамки под фотографию]\n - демотивировать картинку по заданному тексту и аргументам\n " + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Demotivator", + "require_photo": "Reply with a photo, attach it to the team.", + "require_text": "Text required!", + "require_args": "Args required!", + "error": "An error occurred...", + "success": "Result:", + "demotivation": "Demotivation, please wait...", + "watermark_cfg": "Default watermark.", + "font_color_cfg": "Default text font color.", + "fill_color_cfg": "Default background color", + "font_name_cfg": "Link to font file (.ttf, not .zip)", + "top_size_cfg": "Default top text size.", + "bottom_size_cfg": "Default additional (bottom) text size.", + "arrange_cfg": "Adjust photo frames or not", + "require_photo_ru": "Ответьте на фото, приложите его к команде.", + "require_text_ru": "Необходим текст!", + "require_args_ru": "Необходимы аргументы!", + "error_ru": "Произошла ошибка...", + "success_ru": "Результат:", + "demotivation_ru": "Демотивация, подождите, пожалуйста...", + "watermark_cfg_ru": "Водяной знак по умолчанию.", + "font_color_cfg_ru": "Цвет шрифта текста по умолчанию.", + "fill_color_cfg_ru": "Цвет фона по умолчанию", + "font_name_cfg_ru": "Ссылка на файл со шрифтом (.ttf, не .zip)", + "top_size_cfg_ru": "Размер главного текста по умолчанию.", + "bottom_size_cfg_ru": "Размер дополнительного (нижнего) текста по умолчанию.", + "arrange_cfg_ru": "Регулировать рамки под фотографию или нет" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/dl_yt_previews.py": { + "name": "YTPreviewMod", + "description": "Скачивает превью с ютуба", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/YouTubePreviews.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "ytp": " --> download YouTube video preview | (RU) --> скачивает превью" + } + ], + "new_commands": [ + { + "name": "ytp", + "original_name": "ytpcmd", + "description": { + "default": " --> download YouTube video preview", + "ru": " --> скачивает превью" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "YT-Preview", + "choice": "Select YouTube preview extension:", + "caption": "You have selected an extension: {}", + "error": "There doesn't seem to be an extension for this video...Choose another.", + "choice_ru": "Выберите расширение для превью ролика YouTube:", + "caption_ru": "Вы выбрали расширение: {}", + "error_ru": "Кажется этого расширения для этого видео нету...Выберите другое." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/Emotions.py": { + "name": "EmotionsMod", + "description": "Выражение эмоций в чате", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/Emotions.jpg", + "developer": "@AstroModules, @HikariMods" + }, + "commands": [ + { + "emogo": "- вкл/выкл эмоции в данном чате" + }, + { + "emoclear": " - сбросить список эмоций" + }, + { + "emolist": "- список доступных эмоций" + }, + { + "emo": "<символ|слово> <эмоция> - добавить эмоцию в базу модуля" + } + ], + "new_commands": [ + { + "name": "emogo", + "original_name": "emogocmd", + "description": { + "default": "- вкл/выкл эмоции в данном чате" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "emoclear", + "original_name": "emoclearcmd", + "description": { + "default": " - сбросить список эмоций" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "emolist", + "original_name": "emolistcmd", + "description": { + "default": "- список доступных эмоций" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "emo", + "original_name": "emocmd", + "description": { + "default": "<символ|слово> <эмоция> - добавить эмоцию в базу модуля" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Emotions", + "delete_msg": "Удалять сообщение которое вызывает эмоцию?", + "on": "🥺 Emotions успешно активирован в этом чате.", + "off": "🥺 Emotions успешно деактивирован в этом чате", + "ok": "☑️Эмоция успешно добавлена", + "list": "🥺 Доступные эмоции:\n\n{}\n\n❗️ Для добавления своих эмоций введите команду:\n .emo <символ/слово> <эмоция>" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/pcmanager.py": { + "name": "PCManagerMod", + "description": "Управление вашим компьютером через юзербота", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/PCManager.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "addbot": "- добавить бота\n\n💎 Основные команды:" + }, + { + "tutor": "- туториал по подключению" + }, + { + "pcoff": "- выключить компьютер" + }, + { + "pcreboot": "- перезагрузить компьютер" + }, + { + "pcinfo": "- просмотреть характеристики системы" + }, + { + "pcip": "- просмотреть информацию об айпи адресе" + }, + { + "pcscreen": "- сделать скриншот экрана" + }, + { + "pcweb": "<ссылка> - открыть ссылку в браузере\n \n🔑 Дополнительно:" + }, + { + "pcalert": "<сообщение> - вывести на экран сообщение" + }, + { + "pcvol": "- управление звуком" + }, + { + "pcmedia": "- управление музыкой" + } + ], + "new_commands": [ + { + "name": "addbot", + "original_name": "addbot", + "description": { + "default": "- добавить бота\n\n💎 Основные команды:" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tutor", + "original_name": "tutor", + "description": { + "default": "- туториал по подключению" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcoff", + "original_name": "pcoff", + "description": { + "default": "- выключить компьютер" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcreboot", + "original_name": "pcreboot", + "description": { + "default": "- перезагрузить компьютер" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcinfo", + "original_name": "pcinfo", + "description": { + "default": "- просмотреть характеристики системы" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcip", + "original_name": "pcip", + "description": { + "default": "- просмотреть информацию об айпи адресе" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcscreen", + "original_name": "pcscreen", + "description": { + "default": "- сделать скриншот экрана" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcweb", + "original_name": "pcweb", + "description": { + "default": "<ссылка> - открыть ссылку в браузере\n \n🔑 Дополнительно:" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcalert", + "original_name": "pcalert", + "description": { + "default": "<сообщение> - вывести на экран сообщение" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcvol", + "original_name": "pcvol", + "description": { + "default": "- управление звуком" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pcmedia", + "original_name": "pcmedia", + "description": { + "default": "- управление музыкой" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "PC-Manager" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/mindtalk.py": { + "name": "MindTalkMod", + "description": "Your little psychologist Based on MindTalk by Hikamoru", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/MindTalk.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "login": " - log in and save token" + }, + { + "ask": " - ask a psychologist a question" + }, + { + "mtclear": "- clear MindTalk history" + } + ], + "new_commands": [ + { + "name": "login", + "original_name": "login", + "description": { + "default": " - log in and save token" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ask", + "original_name": "ask", + "description": { + "default": " - ask a psychologist a question" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "mtclear", + "original_name": "mtclear", + "description": { + "default": "- clear MindTalk history" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "MindTalk", + "args_error": "❗️ Missing or invalid arguments!", + "successful_login": "✔️ Login completed successfully. Token saved in config", + "not_token": " Missing access token. Please login using the {}login command", + "wait": "☕️ Waiting answer from a psychologist...", + "login_error": " Login failed. You may have entered the wrong password or you are not registered. Try again, or go through the authorization again using this link.", + "answer": "👩‍💻 Your question: {}\n\n👩‍⚕️ Answer from psychologist: {}", + "history_cleared": "🛡 Your history has been successfully cleared" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/TxAFK.py": { + "name": "TxAFKMod", + "description": "Афк модуль от AstroModules с изменением био и имени", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": "@AstroModules" + }, + "commands": [ + { + "txcfg": "- открыть конфиг модуля" + }, + { + "goafk": "- войти в AFK режим" + }, + { + "ungoafk": "- выйти из режима AFK" + } + ], + "new_commands": [ + { + "name": "txcfg", + "original_name": "txcfgcmd", + "description": { + "default": "- открыть конфиг модуля" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "goafk", + "original_name": "goafkcmd", + "description": { + "default": "- войти в AFK режим" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ungoafk", + "original_name": "ungoafkcmd", + "description": { + "default": "- выйти из режима AFK" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "TxAFK", + "lname": "| afk.", + "lname0": " ", + "bt_off_afk": "⚠️ АФК режим отключен", + "bt_on_afk": "💤 АФК режим снова активен", + "_cfg_cst_btn": "Ссылка на чат которая будет отоброжаться вместе с уведомлением. (Чтобы вообще убрать напишите None)", + "standart_bio_text": "Кастомное описание профиля", + "feedback_bot__text": "Юзер вашего фидбэк бота (если имеется)", + "button__text": "Добавить инлайн кнопку отключения АФК режима?", + "custom_text__afk_text": "Кастомный текст афк. Используй {time} для вывода последнего времени нахождения в сети" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/RandomTrack.py": { + "name": "RandomTrackMod", + "description": "Получить рандомный трек. \nИспользуйте категории чтобы сгенерировать трек на свой вкус", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/RandomTrack.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "rt": "- сгенерировать трек.\n\n 🫠 Категории:\n - фонки\n - ремиксы\n - мемные звуки\n - грустная музыка\n - хайперпоп музыка\n

- популярная музыка\n - ностальгические треки\n - треки из вашего плейлиста\n\n 🤫 По желанию, в конфиге, можно указать свои каналы откуда будет отбираться музыка\n " + } + ], + "new_commands": [ + { + "name": "rt", + "original_name": "rt", + "description": { + "default": "- сгенерировать трек.\n\n 🫠 Категории:\n - фонки\n - ремиксы\n - мемные звуки\n - грустная музыка\n - хайперпоп музыка\n

- популярная музыка\n - ностальгические треки\n - треки из вашего плейлиста\n\n 🤫 По желанию, в конфиге, можно указать свои каналы откуда будет отбираться музыка\n " + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "RandomTrack" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/akinator.py": { + "name": "AkinatorGame", + "description": "Akinator will guess any character you have in mind,\nYou just need to answer a couple of questions)", + "cls_doc": { + "ru": "Акинатор угадает любого вами загаданного персонажа, стоит лишь ответить на пару вопросов)" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/Akinator.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "akinator": "- start the game | (RU) - начать игру" + } + ], + "new_commands": [ + { + "name": "akinator", + "original_name": "akinator", + "description": { + "default": "- start the game", + "ru": "- начать игру" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Akinator", + "child_mode": "Child mode. If enabled, it will be easier to guess 18+ heroes", + "failed": "❌ Failed", + "start": "🔮 Start", + "text": "🔮 Guess any character you have in mind, and click on the Start button", + "target_lang": "Target language", + "yes": "Yes", + "no": "No", + "idk": "I don't know", + "probably": "Probably", + "probably_not": "Probably not", + "this_is": "This is {name}\n{description}", + "not_right": "Not right", + "child_mode_ru": "Детский режим. Если включен, то будет сложнее отгадать 18+ героев", + "failed_ru": "🚫 Не удалось угадать персонажа", + "start_ru": "🔮 Начать", + "text_ru": "🔮 Задумайте реального или вымышленного персонажа, и нажмите начать", + "target_lang_ru": "Язык для перевода", + "yes_ru": "Да", + "no_ru": "Нет", + "idk_ru": "Не знаю", + "probably_ru": "Возможно", + "probably_not_ru": "Скорее нет", + "this_is_ru": "Это {name}\n{description}", + "not_right_ru": "Это не он" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/minesweeper.py": { + "name": "MineSwepperModule", + "description": "Minesweeper game", + "cls_doc": { + "ru": "Игра \"Сапёр\"" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/MineSwepper.png", + "developer": "@AstroModules" + }, + "commands": [ + { + "minesweeper": "- start the game \"Minesweeper\" | (RU) - начать игру \"Сапёр\"" + } + ], + "new_commands": [ + { + "name": "minesweeper", + "original_name": "minesweeper", + "description": { + "default": "- start the game \"Minesweeper\" ", + "ru": "- начать игру \"Сапёр\"" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "MineSweeper", + "mines": "Number of mines", + "rows": "Number of rows", + "cols": "Number of columns", + "game": "🎮 MineSweeper game\n\n💣 {mines} mines\n🧮 {rows} rows\n💈 {cols} columns\n🚩 {flags} flags", + "game-over": "❌ Game over!\n💣 You hit a mine.\n\n💣 {mines} mines\n🧮 {rows} rows\n💈 {cols} columns\n🚩 {flags} flags", + "game-end": "❌ Game over!\n🎉 You win!\n\n💣 {mines} mines\n🧮 {rows} rows\n💈 {cols} columns\n🚩 {flags} flags", + "game-ended": "❌ Game over!", + "continue-or-start-new-game": "⚠️ Game already started!\n❔ Continue or start new game?", + "continue": "▶️ Continue", + "start-new-game": "🆕 Start new game", + "game-title": "🎮 MineSweeper game", + "game-not-found": "❌ Game not found!", + "cell-flagged": "🚩 Cell flagged", + "switch-mode": "🔄 Switch to «🚩» mode", + "mines_ru": "Количество мин", + "rows_ru": "Количество строк", + "cols_ru": "Количество столбцов", + "game_ru": "🎮 Игра \"Сапёр\"\n\n💣 {mines} мин\n🧮 {rows} строк\n💈 {cols} столбцов\n🚩 {flags} флагов", + "game-over_ru": "❌ Игра окончена!\n💣 Вы попали на мину.\n\n💣 {mines} мин\n🧮 {rows} строк\n💈 {cols} столбцов\n🚩 {flags} флагов", + "game-end_ru": "❌ Игра окончена! \n🎉 Вы выиграли!\n\n💣 {mines} мин\n🧮 {rows} строк\n💈 {cols} столбцов\n🚩 {flags} флагов", + "game-ended_ru": "❌ Игра окончена!", + "continue-or-start-new-game_ru": "⚠️ В этом чате уже идёт игра!\n❔ Продолжить или начать новую игру?", + "continue_ru": "▶️ Продолжить", + "start-new-game_ru": "🆕 Начать новую игру", + "game-title_ru": "🎮 Игра \"Сапёр\"", + "game-not-found_ru": "❌ Игра не найдена! Возможно она уже завершена.", + "cell-flagged_ru": "🚩 Поле помечено", + "switch-mode_ru": "🔄 Переключиться на режим «{}»" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/Steam.py": { + "name": "Steam", + "description": "Get now played game", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/AstroSteamNow.png", + "developer": "@AstroModules" + }, + "commands": [ + { + "steamnow": "- get what I'm playing at | (RU) - получить, во что я сейчас играю" + }, + { + "sme": "- my steam account | (RU) - открыть аккаунт Steam" + }, + { + "game": " - get game info | (RU) - получить инфо об игре" + }, + { + "steamtoggle": "- toggle widgets updates | (RU) - вкл/выкл виджеты SteamNow" + } + ], + "new_commands": [ + { + "name": "steamnow", + "original_name": "steamnow", + "description": { + "default": "- get what I'm playing at", + "ru": " - получить, во что я сейчас играю" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "sme", + "original_name": "sme", + "description": { + "default": "- my steam account", + "ru": "- открыть аккаунт Steam" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "game", + "original_name": "game", + "description": { + "default": " - get game info", + "ru": " - получить инфо об игре" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "steamtoggle", + "original_name": "steamtoggle", + "description": { + "default": "- toggle widgets updates", + "ru": "- вкл/выкл виджеты SteamNow" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "SteamNow", + "_api_key": "Enter your SteamAPI Key. You can get it by following this tutorial:\n\nhttps://t.me/help_code/20", + "_account_id": "Enter your Steam Account ID. More details in the tutorial: \n\nhttps://t.me/help_code/21", + "no_api_key_or_id": "❌ You did not specify your API_KEY or ACCOUNT_ID in the config.\n🚨 Correct this for further module operation", + "noGame": "❌ The game is not running or you do not have access", + "steamNow": "💻 At the moment you are playing:\n\n🎮 Title: {}\n🆔 Game ID: {}", + "lite_gameInfo": "🎮 Game information:\n\nTitle: {}\nPrice: {}\nDescription:\n- {}", + "steamMe": "🎮 Your account:\n\nName: {} ({})\nOnline: {}\nCreated: {}\nRecent games:\n • {}", + "gameNotFound": "🆔 There is no game with such an identifier, try again", + "state": "🙂 Steam widgets: {}\n{}", + "error": "Steam error\n\n{}", + "tutorial": "ℹ️ To enable the widget, send this text to any chat: {STEAMNOW}", + "configuring": "🙂 Steam widget will be ready soon...", + "_api_key_ru": "Введите ваш SteamAPI Key. Получить его можно по туториалу:\n\n", + "_account_id_ru": "Введите ваш Steam Account ID. Подробнее в туториале: \n\n", + "no_api_key_or_id_ru": "❌ Вы не указали ваш API_KEY или ACCOUNT_ID в конфиге.\n🚨 Исправьте это для дальнейшей работы модуля", + "noGame_ru": "❌ Игра не запущена, или у вас нет доступа", + "steamNow_ru": "💻 В данный момент вы играете:\n\n🎮 Название: {}\n🆔 ID Игры: {}", + "lite_gameInfo_ru": "🎮 Информация об игре:\n\nНазвание: {}\nЦена: {}\nОписание:\n- {}", + "steamMe_ru": "🎮 Ваш аккаунт:\n\nИмя: {} ({})\nВ сети: {}\nСоздан: {}\nПоследние игры:\n • {}", + "gameNotFound_ru": "🆔 Игры с таким идентификатором нет, попробуйте снова", + "state_ru": "🙂 Steam виджеты: {}\n{}", + "error_ru": "Steam error\n\n{}", + "tutorial_ru": "ℹ️ Для включения виджета отправьте данный текст в любой чат: {STEAMNOW}", + "configuring_ru": "🙂 Steam виджет скоро будет готов..." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/YmLive.py": { + "name": "YmLive", + "description": "Модуль для демонстрации играющей песни в Яндекс.Музыке", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": null + }, + "commands": [ + { + "yalive": "Включение или выключение автоматического обновления названия канала | (RU) - включить/выключить YaLive" + } + ], + "new_commands": [ + { + "name": "yalive", + "original_name": "yalive", + "description": { + "default": "Включение или выключение автоматического обновления названия канала", + "ru": "- включить/выключить YaLive" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "YandexMusicLive", + "_text_token": "Токен аккаунта Яндекс Музыки", + "_text_id": "ID канала, который будет использоваться для показа треков...", + "on/off": "YandexMusicLive теперь {}", + "channel_id_error": "В конфиге не указан ID канала. Исправь это!", + "_from_bot_channel_error": "Не найден ID канала в конфиге. Пожалуйста исправь это для дальнейшего использования модуля...", + "token_from_YmNow": "У вас установлен модуль YmNow и в его конфиге я нашел токен. Для вашего удобства токен автоматически выставлен в конфиг. Приятного использования :)", + "tutor": "🎉 Добро пожаловать в модуль YandexMusicLive!\nВы успешно загрузили модуль, который позволяет отображать играющую музыку из Яндекс.Музыки прямо в названии вашего канала!\n\n🌟 Чтобы модуль начал работать, выполните следующие шаги:\n1) Создайте канал: Cоздайте новый канал, в котором будет отображаться играющий сейчас трек, и закрепите этот канал в своем профиле.\n\n2) Настройка токена Яндекс.Музыки: Перейдите в {}config YandexMusicLive -> YandexMusicToken и вставьте ваш токен Яндекс.Музыки. (Туториал на получение токена)\n\n3) Настройка ID канала: Перейдите в {}config YandexMusicLive -> channel_id и вставьте ID вашего канала. \n Если вы не знаете, как получить ID канала - Напишите в канал сообщение с текстом {}e m.chat.id и вставьте в конфиг то, что вам выдаст ЮзерБот\n\n4) Переустановите модуль После выполнения всех настроек переустановите модуль, чтобы завершить процесс настройки." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/inline_bot_manager.py": { + "name": "InlineBotManagerMod", + "description": "Control over your Inline bot!", + "cls_doc": { + "ru": "Управление над своим Inline ботом!" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/InlineBotManager.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "inlinebothelp": "--> Check help for this module | (RU) --> Просмотреть помощь по этому модулю" + }, + { + "ibsetname": " --> change Name for your Inline-Bot | (RU) <имя> --> изменить имя для вашего Инлайн-Бота" + }, + { + "ibsetqtext": " --> change text in InlineQuery for your Inline-Bot | (RU) <текст> --> изменить текст в InlineQuery для вашего Инлайн-Бота" + }, + { + "ibsetdescription": " --> change inline-bot description | (RU) <текст> --> изменить информацию о инлайн-боте" + }, + { + "ibsetabout": " --> change inline-bot about text | (RU) <текст> --> изменить текст об информации о инлайн-боте" + }, + { + "ibcheckname": "-->check bot name to be: \"🌘 Hikka Userbot of {your nickname}\" | (RU) -->проверить имя бота, чтобы оно было: \"🌘 Hikka Userbot of {ваш ник}\" " + } + ], + "new_commands": [ + { + "name": "inlinebothelp", + "original_name": "inlinebothelpcmd", + "description": { + "default": "--> Check help for this module", + "ru": "--> Просмотреть помощь по этому модулю" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ibsetname", + "original_name": "ibsetnamecmd", + "description": { + "default": " --> change Name for your Inline-Bot", + "ru": "<имя> --> изменить имя для вашего Инлайн-Бота" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ibsetqtext", + "original_name": "ibsetqtextcmd", + "description": { + "default": " --> change text in InlineQuery for your Inline-Bot", + "ru": "<текст> --> изменить текст в InlineQuery для вашего Инлайн-Бота" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ibsetdescription", + "original_name": "ibsetdescriptioncmd", + "description": { + "default": " --> change inline-bot description", + "ru": "<текст> --> изменить информацию о инлайн-боте" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ibsetabout", + "original_name": "ibsetaboutcmd", + "description": { + "default": " --> change inline-bot about text", + "ru": "<текст> --> изменить текст об информации о инлайн-боте" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ibcheckname", + "original_name": "ibchecknamecmd", + "description": { + "default": "-->check bot name to be: \"🌘 Hikka Userbot of {your nickname}\" ", + "ru": "-->проверить имя бота, чтобы оно было: \"🌘 Hikka Userbot of {ваш ник}\" " + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "InlineBotManager", + "no_args": "No arguments :( | Read, how to use the module, command: {}>/code>", + "...-set": "{} for your inline bot(@{}) successfully set to {}", + "error": "An error has occurred.", + "namea": "Name", + "inline-text": "Inline-Text", + "about-text": "About", + "description-text": "Description", + "help-mod": "Instructions for the module:\n------------------------------------------------\n•Information about the module:\n •Module name --> InlineBotManager\n •Module description --> Control over your Inline bot!\n •Link to the module(to download) --> \n •Unload the module --> {prefix}unloadmod InlineBotManager\n •Your inline botname --> {}\n •Your inline bot username --> @{}\n------------------------------------------------\n• Commands:\n • {prefix}ibcheckname --> check bot name to be: \"🌘 Hikka Userbot of {your nickname}\"\n --------------------------------------------\n • {prefix}ibsetname --> set a name for your Inline Bot\n Command example:\n {prefix}ibsetname DSOP-UserBot\n --------------------------------------------\n • {prefix}ibsetqtext --> set text instead of \"InlineQuery\" for your Inline Bot\n Command example:\n {prefix}ibsetqtext UserBot-Inline-Query\n --------------------------------------------\n • {prefix}ibsetdescription --> change the information Description the inline bot\n Command example:\n {prefix}ibsetdescription DSOP-UserBot\n --------------------------------------------\n • {prefix}ibsetabout --> change the text about the information about the inline bot\n Command example:\n {prefix}ibsetabout DSOP-UserBot-about\n------------------------------------------------", + "check-yes": "Bot name checked successfully!\nIt's correct.", + "check-no": "Your inline bot name(@{}) was successfully checked! Result: bot name didn't match account name, bot name was changed from {} to {}", + "_cfg_check_name": "Check and change the name of your inline bot after every restart?", + "no_args_ru": "Нет аргументов :( | Прочитайте, как пользоваться модулем, командой: {}", + "...-set_ru": "{} для вашего инлайн-бота(@{}) успешно установлен(-о/-а) на {}", + "namea_ru": "Имя", + "inline-text_ru": "Inline-Текст", + "about-text_ru": "Текст об информации", + "description-text_ru": "Информация", + "error_ru": "Произошла ошибка.", + "help-mod_ru": "Инструкция к модулю:\n------------------------------------------------\n•Информация о модуле:\n •Название модуля --> InlineBotManager\n •Описание модуля --> Управление над своим Inline ботом!\n •Ссылка на модуль(для загрузки) --> \n •Выгрузить модуль --> {prefix}unloadmod InlineBotManager\n------------------------------------------------\n•Информация о вашем Инлайн-Боте:\n ------\n •Имя бота --> {}\n ----------------------\n •Юзернейм бота --> @{}\n------------------------------------------------\n•Команды:\n • {prefix}ibcheckname --> проверить имя бота, чтобы оно было: \"🌘 Hikka Userbot of (ваш ник-нейм)\"\n------------------------------------------------\n • {prefix}ibsetname <имя> --> установить имя для вашего Инлайн-Бота\n Пример команды:\n {prefix}ibsetname DSOP-UserBot\n------------------------------------------------\n • {prefix}ibsetqtext <текст> --> установить текст вместо \"InlineQuery\" для вашего Инлайн-Бота\n Пример команды:\n {prefix}ibsetqtext UserBot-Inline-Query\n------------------------------------------------\n • {prefix}ibsetdescription <текст> --> изменить информацию о инлайн-боте\n Пример команды:\n {prefix}ibsetdescription DSOP-UserBot\n------------------------------------------------\n • {prefix}ibsetabout <текст> --> изменить текст об информации о инлайн-боте\n Пример команды:\n {prefix}ibsetabout DSOP-UserBot-about\n------------------------------------------------", + "ib-help_ru": "----------------------\n", + "check-yes_ru": "Имя бота успешно проверено!\nОно верное.", + "check-no_ru": "Имя вашего инлайн-бота(@{}) было успешно проверено! Результат: имя бота не соответствовало имени аккаунта, имя бота было сменено с {} на {}", + "_cfg_check_name_ru": "Проверять и изменять имя вашего инлайн-бота после каждого рестарта?" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/gamecheat.py": { + "name": "GameeCheatMod", + "description": "Читы для игр в @gamee", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/GameeCheat.jpg", + "developer": "@astromodules", + "designer": "@XizurK" + }, + "commands": [ + { + "chg": "<ссылка> <рекорд> - запустить чит" + } + ], + "new_commands": [ + { + "name": "chg", + "original_name": "chg", + "description": { + "default": "<ссылка> <рекорд> - запустить чит" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "GameeCheats", + "result": "🪄 Рекорд накручен!\n Новый рекорд: {}", + "err_args": "🚫 Введите нужные аргументы!\nℹ️ Пример: {}chg <ссылка> <рекорд>", + "banned": "⛔️ Вы были заблокированы в боте!\nℹ️ Вы не можете ставить новые рекорды в течении 24 часов", + "banned_perm": "⛔️ Вы были заблокированы в боте навсегда!\nℹ️ Вы не можете ставить новые рекорды в @gamee", + "error_link": "🚫 Вы ввели неправильную ссылку!\nℹ️ Введите правильную ссылку или же посмотрите туториал" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/RandomStatuses.py": { + "name": "RandomStatusesMod", + "description": "Рандомные статусы для описания аккаунта в ТГ/Вацап/ВК и т.д.", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/RandomStatuses.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "rstatus": "Рандомный статус на описание аккаунта в ТГ/ВК/Вацап и т.д." + } + ], + "new_commands": [ + { + "name": "rstatus", + "original_name": "rstatuscmd", + "description": { + "default": "Рандомный статус на описание аккаунта в ТГ/ВК/Вацап и т.д." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "RandomStatuses", + "generate_st": "✨ Рандомный статус 🌺", + "support_chat_btn": "🎩 Чат поддержки 🎓", + "more_modules_btn": "🌌 Больше Модулей ✨", + "set_status": "⚙️ Сохранить в био 📥" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/dialogs_manager.py": { + "name": "DialogsManagerMod", + "description": "Check your all info for dialogs, chats, PMs\nDelete definite dialog,\ndelete all dialogs by arguments,\nleave the chats, ids/usernames which you specify\nDialogs Manager!", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/DialogsManager.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "dialogsinfo": "➪ all info for your dialogs, chats, PMs... | (RU) ➪ полная информация о ваших диалогах, чатах лс и т.д." + }, + { + "dialogclear": " ➪ delete dialog with user | (RU) ➪ удалить чат(диалог) с юзером" + }, + { + "dclear": "➪ alias for command 'dialog_clear' | (RU) ➪ алиас для команды 'dialogs_clear'" + }, + { + "alldialogsclear": "Arguments:\n-deleted ➪ delete all dialogs PM with deleted accounts\n-fake ➪ delete all dialogs PM with fake accounts\n-scam ➪ delete all dialogs PM with scam accounts\n-bots ➪ delete all dialog with bots\n-allpms ➪ delete all dialogs PM\n-prem ➪ delete all dialogs PM with PREMIUM⭐️ users | (RU) Аргументы:\n\t\t-deleted ➪ очистить все ЛС с удаленными аккаунтами\n\t\t-fake ➪ очистить все ЛС с аккаунтами с пометкой \"FAKE\"\n\t\t-scam ➪ очистить все ЛС со скам аккаунтами\n\t\t-bots ➪ очистить все ЛС с ботами\n\t\t-allpms ➪ очистить ВСЕ АБСОЛЮТНО ЛС(ОПАСНО)\n\t\t-prem ➪ очистить все ЛС с юзерами, обладающими Premium⭐️\n\t\t" + }, + { + "leavechats": "@chat_username1, @chat_username2, ... ➪ leave chats, with usernames in arguments | (RU) @chat_username1, @chat_username2, ... ➪ покинуть чаты, с определенными @username" + } + ], + "new_commands": [ + { + "name": "dialogsinfo", + "original_name": "dialogsinfo", + "description": { + "default": "➪ all info for your dialogs, chats, PMs...", + "ru": "➪ полная информация о ваших диалогах, чатах лс и т.д." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "dialogclear", + "original_name": "dialog_clear", + "description": { + "default": " ➪ delete dialog with user", + "ru": " ➪ удалить чат(диалог) с юзером" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "dclear", + "original_name": "dclear", + "description": { + "default": "➪ alias for command 'dialog_clear'", + "ru": "➪ алиас для команды 'dialogs_clear'" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "alldialogsclear", + "original_name": "all_dialogs_clear", + "description": { + "default": "Arguments:\n-deleted ➪ delete all dialogs PM with deleted accounts\n-fake ➪ delete all dialogs PM with fake accounts\n-scam ➪ delete all dialogs PM with scam accounts\n-bots ➪ delete all dialog with bots\n-allpms ➪ delete all dialogs PM\n-prem ➪ delete all dialogs PM with PREMIUM⭐️ users", + "ru": "Аргументы:\n\t\t-deleted ➪ очистить все ЛС с удаленными аккаунтами\n\t\t-fake ➪ очистить все ЛС с аккаунтами с пометкой \"FAKE\"\n\t\t-scam ➪ очистить все ЛС со скам аккаунтами\n\t\t-bots ➪ очистить все ЛС с ботами\n\t\t-allpms ➪ очистить ВСЕ АБСОЛЮТНО ЛС(ОПАСНО)\n\t\t-prem ➪ очистить все ЛС с юзерами, обладающими Premium⭐️\n\t\t" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "leavechats", + "original_name": "leave_chats", + "description": { + "default": "@chat_username1, @chat_username2, ... ➪ leave chats, with usernames in arguments", + "ru": "@chat_username1, @chat_username2, ... ➪ покинуть чаты, с определенными @username" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "DialogsManager", + "dialogs_info": "All Dialogs info:\n\n👤 All users PM ➪ {}\n👥 All chats ➪ {}\n📢 All channels ➪ {}\n🤖 All bots PM ➪ {}\n\n👤 Users(PM):\n\n 🚫 Fake ➪ {}\n 🚫 Scam ➪ {}\n {} Premium ➪ {}\n ✅️ Verified ➪ {}\n 🗑 Deleted ➪ {}\n\n👥 Chats:\n\n Megagroups ➪ {}\n\n ☑️ Gigagroups ➪ {}\n\n 🚫 Fake ➪ {}\n 🚫 Scam ➪ {}\n ✅️ Verified ➪ {}\n\n📢 Channels:\n 🚫 Fake ➪ {}\n 🚫 Scam ➪ {}\n ✅️ Verified ➪ {}\n\n🤖 Bots(PM):\n 🚫 Fake ➪ {}\n 🚫 Scam ➪ {}\n✅️ Verified ➪ {}", + "waiting_dinfo": "Please wait, loading information...", + "waiting_dinfo_ru": "Пожалуйста подождите, идёт загрузка данных..." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/RandomPasswordGenerator.py": { + "name": "PasswordGeneratorMod", + "description": "Random password/pincode generator", + "cls_doc": { + "ru": "Генератор рандомного пароля/пин-кода\nНастроить генератор можно через конфиг" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/PasswordGenerator.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "generatorcfg": "—>config for this module | (RU) —>конфиг этого модуля" + }, + { + "igenerator": "—>generate random password/pin | (RU) —>сгенерировать случайный пароль/пин-код" + } + ], + "new_commands": [ + { + "name": "generatorcfg", + "original_name": "generatorcfgcmd", + "description": { + "default": "—>config for this module", + "ru": "—>конфиг этого модуля" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "igenerator", + "original_name": "igeneratorcmd", + "description": { + "default": "—>generate random password/pin", + "ru": "—>сгенерировать случайный пароль/пин-код" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "RandomPasswordGenerator", + "_cfg_doc_pass_length": "set password length (in number of characters)", + "_cfg_doc_pin_code_length": "set pincode length (in number of characters)", + "_cfg_doc_simbols_in_pass": "Will there be additional characters in the generated password (+-*!&$#?=@<>)?", + "what_to_generate": "🆗What should be generated?", + "new_random_pass": "🔣 new random password 🆕", + "new_random_pincode": "🔢 new random PIN-code 🆕", + "pass": "🆕 Your new password in {} characters:\n{}", + "pincode": "🆕 Your new pincode in {} characters:\n{}", + "menu": "💻 Menu", + "close": "🚫 Close", + "_cfg_doc_pass_length_ru": "выставьте длину пароля(в кол-ве символов)", + "_cfg_doc_pin_code_length_ru": "выставьте длину Пин-Кода(в кол-ве символов)", + "_cfg_doc_simbols_in_pass_ru": "Какие символы должны быть в сгенерированном пароле?", + "what_to_generate_ru": "🆗 Что надо сгенерировать?", + "new_random_pass_ru": "🔣 Новый рандомный пароль 🆕", + "new_random_pincode_ru": "🔢 Новый рандомный PIN-код 🆕", + "pass_ru": "🆕 Ваш новый пароль в {} символов:\n{}", + "pincode_ru": "🆕 Ваш новый пин-код в {} символов:\n{}", + "menu_ru": "💻 Меню", + "close_ru": "🚫 Закрыть" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/Compliments.py": { + "name": "ComplimentsMod", + "description": "Покажи девушке какая она прекрасная (ну или им, какие они прекрасные)", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/Compliments.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "complimentscfg": "—>конфиг этого модуля" + }, + { + "ilike": "Инлайн анимация комплиментов(полная настройка в конфиге)" + } + ], + "new_commands": [ + { + "name": "complimentscfg", + "original_name": "complimentscfgcmd", + "description": { + "default": "—>конфиг этого модуля" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ilike", + "original_name": "ilikecmd", + "description": { + "default": "Инлайн анимация комплиментов(полная настройка в конфиге)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Compliments", + "_cfg_doc_for_one_or_more": "Выберите пожалуйста, комплименты будут для 1 человека женского пола, или для всех людей женского пола в чате \nЕсли для 1 человека--> one\nЕсли для всех людей женского пола в чате--> more", + "_cfg_doc_command_mode": "Выберите пожалуйста режим команды, какая будет анимация: \n Если вы хотите, чтобы печатался список комплиментов --> 1 \n Если вы хотите, чтобы каждую секунду комплимент заменялся на другой и в конце вывелся полный список --> 2" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/brawl_stats.py": { + "name": "BrawlStatsInfo", + "description": "Brawl Stars Players/Clubs information :)", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/BrawlStats.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "getmyip": "Получить свой IP-Адрес для получения API-Токен BrawlStarsAPI" + }, + { + "setbsapitoken": " - сохранить свой API-Токен в конфиге модуля." + }, + { + "bsgetplayer": "<#player_tag> <#player_tag2> -> получить информацию об игроке/игроках(теги можно через пробел указывать)" + }, + { + "bsgetclub": "<#CLUB_TAG> -> получить информацию о клубе по его #ТЕГУ" + }, + { + "bsgetclubmembers": "<#CLUB_TAG> -> получить информацию об участниках клуба по его #ТЕГУ" + }, + { + "bsgetplayerbrawlers": "<#player_tag> -> получить информацию о Бравлерах(Бойцах) игрока по его #ТЕГУ" + } + ], + "new_commands": [ + { + "name": "getmyip", + "original_name": "get_my_ip", + "description": { + "default": "Получить свой IP-Адрес для получения API-Токен BrawlStarsAPI" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "setbsapitoken", + "original_name": "set_bs_api_token", + "description": { + "default": " - сохранить свой API-Токен в конфиге модуля." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "bsgetplayer", + "original_name": "bs_get_player", + "description": { + "default": "<#player_tag> <#player_tag2> -> получить информацию об игроке/игроках(теги можно через пробел указывать)" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "bsgetclub", + "original_name": "bs_get_club", + "description": { + "default": "<#CLUB_TAG> -> получить информацию о клубе по его #ТЕГУ" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "bsgetclubmembers", + "original_name": "bs_get_club_members", + "description": { + "default": "<#CLUB_TAG> -> получить информацию об участниках клуба по его #ТЕГУ" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "bsgetplayerbrawlers", + "original_name": "bs_get_player_brawlers", + "description": { + "default": "<#player_tag> -> получить информацию о Бравлерах(Бойцах) игрока по его #ТЕГУ" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "BrawlStatsInfo" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/AntiMat.py": { + "name": "AntiMatMod", + "description": "Будьте культурным человеком, не материтесь", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/AntiMat.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "antimat": "- активировать или диактивировать АнтиМат" + }, + { + "matlist": "- открыть список матов" + }, + { + "amchat": "- запретить/разрешить чату выражаться нецензурой" + } + ], + "new_commands": [ + { + "name": "antimat", + "original_name": "antimat", + "description": { + "default": "- активировать или диактивировать АнтиМат" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "matlist", + "original_name": "matlist", + "description": { + "default": "- открыть список матов" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "amchat", + "original_name": "amchatcmd", + "description": { + "default": "- запретить/разрешить чату выражаться нецензурой" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Анти-Мат", + "am_on": "🤬 Антимат включен.", + "am_off": "🤬 Антимат отключен.", + "action_text": "Какое действие выполнять при обнаружении мата в сообщении?", + "list_txt": "Здесь вы можете добавить свои маты.\np.s.: добавляйте по одному мату", + "added": " Чат успешно добавлен в антимат систему", + "uadded": "🗑 Чат успешно удален из системы антимат" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/commands_logger.py": { + "name": "HikkaCommandsLoggerMod", + "description": "Hikka Commands Logger", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/HikkaCommandsLogger.jpg", + "developer": "@AstroModules" + }, + "commands": [], + "new_commands": [], + "inline_handlers": [], + "strings": { + "name": "HikkaCommandsLogger", + "log-groups": "#GROUP\n\n┌ Command:\n├ « {} »\n├ From --> {}\n├ Chat --> {}\n└ Message Link --> CLICK", + "log-pm": "#PM\n\n┌ Command:\n├ « {} »\n├ From --> {}\n├ Chat with --> {}\n└ Message Link --> CLICK", + "log-channels": "#CHANNEL\n\n┌ Command:\n├ « {} »\n├ From --> {}\n├ Channel --> {}\n└ Message Link --> CLICK" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/convertio.py": { + "name": "ConvertioMod", + "description": "Convert file with api from https://convertio.co", + "cls_doc": { + "ru": "Конвертирует файл с помощью https://convertio.co" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/Convertio.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "renewconvertio": "Renew convertio api key" + }, + { + "convert": " | Example: png | (RU) <выходной формат> | Пример: png" + } + ], + "new_commands": [ + { + "name": "renewconvertio", + "original_name": "renewconvertio", + "description": { + "default": "Renew convertio api key" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "convert", + "original_name": "convert", + "description": { + "default": " | Example: png", + "ru": "<выходной формат> | Пример: png" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "Convertio", + "converting": "☺️ Wait, converting...", + "getting_api_key": "#️⃣ Wait, getting random API key...", + "error": "💔 Something went wrong...\n💭 Contact @AstroModsChat", + "no_file": "⁉️ Where is the file?", + "no_args": " Where is the file format you want to convert the file to?", + "renewed": "🍀 New API-key generated and saved!", + "downloading_file": " Downloading your file...", + "converted": "⭐️ File successfully converted from {} to {}", + "api_error": "🚨 API: {}", + "uploading": "☺️ File converted. Uploading to Telegram...", + "converting_ru": "☺️ Подождите, идёт конвертация...", + "getting_api_key_ru": "#️⃣ Подождите, получаю рандомный API KEY...", + "error_ru": "💔 Что-то пошло не так...\n💭 Обратитесь в @AstroModsChat", + "no_file_ru": "⁉️ Где файл?", + "no_args_ru": " Где формат файла, в который вы хотите преобразовать файл?", + "renewed_ru": "🍀 Новый API-ключ сохранен!", + "downloading_file_ru": " Загружаю ваш файл...", + "converted_ru": "⭐️ Файл успешно конвертирован с {} в {}", + "uploading_ru": "☺️ Файл конвертирован. Загружаю в Telegram..." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/вахуи_пон.py": { + "name": "ВахуиПонMod", + "description": "пон и вахуи", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": "@AstroModules" + }, + "commands": [ + { + "пон": "--> пон" + }, + { + "вахуи": "--> вахуи" + } + ], + "new_commands": [ + { + "name": "пон", + "original_name": "понcmd", + "description": { + "default": "--> пон" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "вахуи", + "original_name": "вахуиcmd", + "description": { + "default": "--> вахуи" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "ВАХУИ-ПОН", + "pon": "пон", + "vahui": "вахуи" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/video_to_voice.py": { + "name": "VideoToVoice", + "description": "Convert Video to voice", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/VideoToVoice.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "converttovoice": " -> получить Войс звука видео, либо отправлять команду с прикрепленным видео(оно удалится после отправления сделанного войса), либо отправить команду ответом на видео." + } + ], + "new_commands": [ + { + "name": "converttovoice", + "original_name": "convert_to_voice", + "description": { + "default": " -> получить Войс звука видео, либо отправлять команду с прикрепленным видео(оно удалится после отправления сделанного войса), либо отправить команду ответом на видео." + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "VideoToVoice" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/iOSAppsForAndroid.py": { + "name": "iOSAppsForAndroid", + "description": "iOS Applications for Android, or more simply, Android applications with an interface like on iOS", + "cls_doc": { + "ru": "iOS приложения на Android, или же проще говоря Android приложения с интерфейсом, как на iOS" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/iOSAppsForAndroid.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "iappslist": "-->Inline List iOS Apps for Android | (RU) -->Inline Лист iOS приложений для Android" + } + ], + "new_commands": [ + { + "name": "iappslist", + "original_name": "iappslistcmd", + "description": { + "default": "-->Inline List iOS Apps for Android", + "ru": "-->Inline Лист iOS приложений для Android" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "iOSappsForAndroid", + "apps-list": "The list of applications in the module at the moment:\n| iCall | iLauncher | iLock |\n| iSwiftKeyboard | iWhatsApp |\n| iNotes | iMessages | iContacts |\n| iPhotos | iCalculator | iVoice |\n|iCompas |\n| All applications are taken from the channel @progi95 | @IbreymMods |", + "support_chat_btn": "🎩 Support Chat 🎓", + "more_modules_btn": "🌌 More Modules ✨", + "close_btn": "🚫 Close", + "iCalculator_text": "ICalculator Pro is an ios—style calculator for your android, with ios design, with calculator history and with other settings.\n♦️ Ibreym Mods (https://t.me/IbreymMods).\nMods: the full version is open, without ads.\nIbreym Mods. (screenshots)", + "iCall_text": "🔥iCall Proa dialer in the style of iOS on Android with a beautiful iPhone design and various functions, such as changing the contact wallpaper, etc.\n🔑Mods: PRO version.\n \n \n🔑Description of the modification:\n— Update: 2.4.4. \n— The full version has been purchased. \n— All functions are open. \n— All templates are available. \n— Advertising is disabled. \n— Removed excess. \n is a working mod.", + "iLauncher_text1": "🔥Ios 16 Launcher is an application with which you can turn your android desktop into an iPhone. Changes icons, style, screen recording on ios. \n🔑Mods: PRO version.", + "iLauncher_text2": "🚀Description of the modification: \nModder: Ibreym. \n— Removed the advertising completely. \n— The application is completely in Russian, so it will not be so difficult to configure it. ✅To activate, you need to issue all the necessary permissions, and then click on the 'make default launcher' button.", + "iLock_text": "🚀iLock — ios-style screen lock, with the coolest style, translated into Russian, removed ads, you can put your background, password and more. Throw screenshots in the comments.\n🔑Mods: PRO version. \n🔘Mods from Ibreym: \n— Removed the ad.\n— Translated into Russian to make it more understandable for you to configure.\n— And also translated the inscriptions on the lock screen to make it look better.Set up following the instructions, everything is easy and simple. The screen works cool, and it looks.", + "iSwiftKey_text1": "😍The newest iPhone keyboard for your android. And now attention: - ios themes, ios 16 emoticons, there are any languages, including Russian, there is a t9 (hint, correction of words), the ability to change the size, and most importantly - the iPhone sound when typing.\n😍Run to watch:", + "iSwiftKey_text2": "🌚iOS—style keyboard for any android with iOS 16 and emoticons: \n— Dark and white iOS theme.\n- Supports Russian and other languages. \n— there is a T9 hint, correction of words. \n— you can adjust the size of the keyboard for yourself. \n— there is an iPhone typing sound.\n🎥All this needs to be configured, and for this, watch the video review on the button above", + "iWhatsApp_text1": "♻️Ibreym-WhatsApp is the best whatsapp mod from the developer Ibreym. It's two in one! - WhatsApp+ features, and the design is apple where you can spend time with a buzz!\n🔺Developer: Stefano (Mb Mods)\n🎥Video review", + "iWhatsApp_text3": "MB WhatsApp Sticker.\nThe version is for those who need stickers in whatsapp.\nWorks great with sticker programs, for example: https://t.me/progi95/3562\n\nTo install, you need to remove the original version from the play store.\nDelete the old one, or the version that is not needed at the top, you can install both versions at once and use them from two numbers.", + "iWhatsApp_text2": "Custom double-tap emoticons for each chat (chat > call icon > custom reaction)\n🔷 Added: Showing notifications about blocked calls due to call privacy.\n🔷 Added: The ability to disable double-tapping to put a like.\n🔷 Added: Admin indicator in groups.\n🔷 Added: Custom background color for text status.\n🔷 Added: Custom text color for text status.\n🔷 Added: New way to display updates.\n🔷 New pencil drawing tool v2.\n🔷 New user interface challenge.\n🔷 New rounded font.\n🔷 Added the ability to hide the admin icon. \n🔷 Added the ability to reduce the lag in some actions.\n🔷 Added the ability to disable bold font (useful if you want to use your system font).\n🔷 Added the ability to disable notifications from those who blocked you.\netc..", + "iNotes_text": "iNotes Pro — ios-style notes for your android with ios design and themes, with the ability to set a password when logging in, and with other various functions and features!\nMods: the full version is open, without ads.\nIbreym Mods. (screenshots)", + "iPhotos_text": "iPhotos is a very beautiful iOS—style gallery for your android, with a beautiful iPhone design, two themes, with a basket, with albums and other various settings.\nMods: the full version is open, without ads.\nIbreym Mods. (screenshots)", + "iMessages_text": "iMessages — ios-style messages for your android, with built-in iOS themes in black and light colors, with a beautiful design, and various settings!\nThe full version is open for subscribers, where there are no ads and pro functions are available.\nIbreym Mods.", + "iContacts_text": "iContacts Ios — iOS-style contacts for your android with a dark and light iPhone theme, design, and other functions like an iPhone.\nMods: the full version is open, without ads.\nIbreym Mods.", + "icompas1": "iCompass Pro is an iOS—style compass for android, with a beautiful iPhone design that will help you quickly and easily determine the exact direction and position on the map.\n🔑Mods: PRO version.", + "icompas2": "🚀Description of the modification:\n— Update: 1.1.5\n— The full version is open.\n— Removed ads.\n— All functionality is available.\n— Removed unnecessary.", + "ivoice1": "iVoice Pro is an iOS—style voice recorder with full iphone design and functionality on android. A dark and light theme, a choice of quality and a full version without ads are available.\n🔑Mods: PRO version.", + "ivoice2": "🚀Description of the modification:\n— Update: 1.6.1\n— The full version is open.\n— Removed ads.\n— All functionality is available.\n— Removed unnecessary.\n— Updated to the latest version.\n— And so on.", + "itelegram": "Ibreym-Telegram is an iPhone telegram for android, with a very beautiful iPhone design, themes, fonts and with various cool settings.", + "apps-list_ru": "Список приложений в модуле на данный момент: \n| iCall | iLauncher | iLock |\n| iSwiftKeyboard | iWhatsApp |\n| iNotes | iMessages | iContacts |\n| iPhotos | iCalculator | iVoice |\n|iCompas |\n| Все приложения взяты из каналов @progi95 | @IbreymMods |", + "support_chat_btn_ru": "🎩 Чат поддержки 🎓", + "more_modules_btn_ru": "🌌 Больше Модулей ✨", + "close_btn_ru": "🚫 Закрыть", + "iCalculator_text_ru": "iCalculator Pro — калькулятор в стиле ios на ваш андроид, с ios дизайном, с историей калькулятора и с другими настройками.\n♦️ Ibreym Mods (https://t.me/IbreymMods).\nМоды: открыта полная версия, без рекламы.\nIbreym Mods. (скрины", + "iCall_text_ru": "🔥iCall Proзвонилка в стиле iOS на Android с красивым, айфоновским дизайном и различными функциями, например смена обоев контакта и т.д.\n🔑Моды: PRO версия.\n \n \n🔑Описание модификации:\n— Обнова: 2.4.4. \n— Куплена полная версия. \n— Открыты все функции. \n— Доступны все шаблоны. \n— Отключена реклама. \n— Удалено лишнее. \n— Мод рабочий.", + "iLauncher_text1_ru": "🔥Ios 16 Launcher — приложение с помощью которого вы можете превратить свой рабочий стол андроида в айфон. Меняет иконки, стиль, запись экрана на ios. \n🔑Моды: PRO версия.\n🎥Видеообзор", + "iLauncher_text2_ru": "🚀Описание модификации: \nМоддер: Ibreym. \n— Убрали полностью рекламу. \n— Приложение полностью на русском языке, поэтому настроить его будет не так сложно. \n ✅Для активации нужно выдать все необходимые разрешения, а потом нажать на кнопку «make default launcher».", + "iLock_text_ru": "🚀iLock — блокировка экрана в стиле ios, с крутейшим стилем., перевел на русским язык, удалил рекламу, можно поставить свой фон, пароль и не только. Скрины кидайте в комментариях.\n🔑Моды: PRO версия. \n🔘Моды от Ibreym: \n— Удалил рекламу.\n— Перевел на русский язык, чтобы вам было более понятно настроить.\n— А также перевел надписи на экране блокировки, чтобы выглядело лучше.\nНастраивайте следуя инструкциям, все легко и просто. Экран круто работает, и выглядит.", + "iSwiftKey_text1_ru": "😍Новейшая айфоновская клавиатура на ваш андроид. А теперь внимание: - темы ios, смайлы ios 16, есть любые языки, в том числе русский, есть т9(подсказка, исправление слов), возможность менять размер, и самое главное - айфоновский звук при печатании.\n😍Беги смотреть:🎥Видеообзор", + "iSwiftKey_text2_ru": "🌚Клавиатура в стиле ios на любой андроид со смайлами ios 16 и: \n— темная и белая тема ios.\n— поддерживает русский и другие языки. \n— есть Т9 подсказка, исправление слов. \n— можно подстроить под себя размер клавиатуры. \n— есть айфоновский звук печатания.\n🎥Всё это нужно настроить, а для этого смотрите видеообзор по кнопке выше", + "iWhatsApp_text1_ru": "♻️Ibreym-WhatsApp — самый лучший мод на whatsapp от разработчика Ibreym. Это два в одном! - функции whatsApp +, а дизайн - apple где можно с кайфом проводить время!\n🔺Developer: Stefano (Mb Mods)\n🎥Видеообзор", + "iWhatsApp_text2_ru": "Пользовательские смайлы двойного нажатия для каждого чата (чат > значок вызова > пользовательская реакция)\n🔷 Добавлено: показ уведомлений о заблокированных вызовах из-за конфиденциальности звонков.\n🔷 Добавлено: Возможность отключить двойное нажатие, чтобы поставить лайк.\n🔷 Добавлено: Индикатор администратора в группах.\n🔷 Добавлено: Пользовательский цвет фона для текстового статуса.\n🔷 Добавлено: Пользовательский цвет текста для текстового статуса.\n🔷 Добавлено: Новый способ отображения обновлений.\n🔷 Новый инструмент для рисования карандашом v2.\n🔷 Новый вызов пользовательского интерфейса.\n🔷 Новый округленный шрифт.\n🔷 Добавлена возможность скрыть значок администратора. \n🔷 Добавлена возможность уменьшить отставание в некоторых действиях.\n🔷 Добавлена возможность отключить жирный шрифт (полезно, если вы хотите использовать свой системный шрифт).\n🔷 Добавлена возможность отключения уведомлений от тех , кто Вас заблокировал.\nИ т.п..", + "iWhatsApp_text3_ru": "MBWhatsApp Sticker.\nВерсия для тех, кому нужны стикеры в ватсап.\nОтлично работает с прогами для стикеров, например: https://t.me/progi95/3562\n\nДля установки нужно удалить оригинальную версию с плай маркета.\nУдалять старую, или версию которая вверху не нужно, можно установить сразу оба версии и пользоваться с двух номеров.", + "iNotes_text_ru": "iNotes Pro — заметки в стиле ios на ваш андроид с дизайном и темами ios, с возможностью поставить пароль при входе, и с другими различными функциями и возможностями!\nМоды: открыта полная версия, без рекламы.\nIbreym Mods. (скрины)", + "iPhotos_text_ru": "iPhotos — очень красивая галерея в стиле ios на ваш андроид, с красивым айфоновским дизайном, двумя темами, с корзиной, с альбомами и другими различными настройками.\nМоды: открыта полная версия, без рекламы.\nIbreym Mods. (скрины)", + "iMessages_text_ru": "iMessages — сообщения в стиле ios на ваш андроид, со встроенными ios темами черного и светлого цвета, с красивым дизайном, и различными настройками!\nОткрыта полная версия для подписчиков, где нет рекламы и доступны pro функции.\nIbreym Mods.", + "iContacts_text_ru": "iContacts Ios — контакты в стиле ios на ваш андроид с темной и светлой айфоновской темой, дизайном, и другими функциями как у айфона.\nМоды: открыта полная версия, без рекламы.\nIbreym Mods.", + "icompas1_ru": "🔥iCompass Pro — это компасс в стиле ios на android, с красивым айфоновским дизайном, которые поможет вам быстро и легко определить точное направление и положение на карте.\n🔑Моды: PRO версия.", + "icompas2_ru": "🚀Описание модификации:\n— Обнова: 1.1.5\n— Открыта полная версия.\n— Удалена реклама.\n— Доступен весь функционал.\n— Удалено лишнее.", + "ivoice1_ru": "🚀iVoice Pro — диктофон в стиле ios в полным дизайном и функционалом iphone на android. Доступны тёмная и светлая тема, выбор качества и полная версия без рекламы.\n🔑Моды: PRO версия.", + "ivoice2_ru": "🚀Описание модификации:\n— Обнова: 1.6.1.\n— Скрин.\n— Открыта полная версия.\n— Удалена реклама.\n— Доступен весь функционал.\n— Удалено лишнее.\n— Обновлена до последней версии.\n— И прочее.", + "itelegram_ru": "Ibreym-Telegram — это айфоновский телеграм на андроид, с очень красивым, айфоновским дизайном, темами, шрифтами и с различными классными настройками." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/wordly.py": { + "name": "WordlyHelper", + "description": "Помощник для игры \"Вордли на Русском\"", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/WordlyHelper.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "wordly": "<маркировка слова> [-not <буквы подряд, которых точно нету в слове>] [-yes <буквы подряд, которые точно есть в слове] - Найти слова по маркировке:\n* - одна любая буква\n$ - любое кол-во любых букв\nпример: *т**т -yes оч -not абвгд : отчет" + } + ], + "new_commands": [ + { + "name": "wordly", + "original_name": "wordly", + "description": { + "default": "<маркировка слова> [-not <буквы подряд, которых точно нету в слове>] [-yes <буквы подряд, которые точно есть в слове] - Найти слова по маркировке:\n* - одна любая буква\n$ - любое кол-во любых букв\nпример: *т**т -yes оч -not абвгд : отчет" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "WordlyHelper", + "not_args": "🚫 | Аргументы где?", + "search": "🔸 | Поиск слов...", + "result": "🔹 Слова по вашему запросу:\n\n{}\n\n🔸 Запрос: {}", + "not_found": " Не найдено", + "not_in_db": "🚫 | В базе нету слов из {} букв 😠" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/пон_вахуи.py": { + "name": "ПонВахуиMod", + "description": "пон и вахуи", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": null, + "developer": "@AstroModules" + }, + "commands": [ + { + "пон": "--> инлайн меню со стикерами пон" + }, + { + "вахуи": "--> инлайн меню со стикерами \"вахуи\" " + } + ], + "new_commands": [ + { + "name": "пон", + "original_name": "пон", + "description": { + "default": "--> инлайн меню со стикерами пон" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "вахуи", + "original_name": "вахуи", + "description": { + "default": "--> инлайн меню со стикерами \"вахуи\" " + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "ПОН-ВАХУИ" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/RandomUser.py": { + "name": "RandomUserMod", + "description": "choose a random user in chat\nAutors: @AstroModules & @smeowcodes", + "cls_doc": { + "ru": "Выбрать случайного пользователя в чате\nАвторы: @AstroModules & @smeowcodes" + }, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/RandomUser.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "iranduser": "choose a random user in chat | inline menu with prize buttons | (RU) --> выбрать случайного пользователя в чате | inline-меню с призовыми кнопками" + } + ], + "new_commands": [ + { + "name": "iranduser", + "original_name": "irandusercmd", + "description": { + "default": "choose a random user in chat | inline menu with prize buttons", + "ru": "--> выбрать случайного пользователя в чате | inline-меню с призовыми кнопками" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "RandomUser", + "astro-modules-btn": "🌌 𝑨𝒔𝒕𝒓𝒐 𝑴𝒐𝒅𝒖𝒍𝒆𝒔 ✨", + "meow-modules-btn": "Meow Modules😽", + "inline-text": "👥 Click on the button to select a random user in the chat!", + "rand-user-btn": "🤩 Choose", + "rand-user-2-btn": "🤯 Choose again", + "user": "{} The choice fell on {}!\n{}", + "user-...": "{} The choice fell on {}!\n{}\n\n{}", + "id": "🆔 ID: {}", + "id+username": "{} Username: @{}\n🆔 ID: {}", + "give-adm-btn": "🎩 Give Admin", + "text-adm": "🎩 Was assigned to the administrator\n👨🏻‍💻 Prefix(rank): «{}»", + "mute-btn": "🤫 Mute", + "unmute-btn": "😊 UnMute", + "ban-btn": "👔 Ban", + "unban-btn": "😊 UnBan", + "kick-btn": "👞 Kick", + "invite-btn": "➕ Add back", + "prefix-1": "Cool person :)", + "prefix-2": "Lucky", + "prefix-3": "⁤ ", + "prefix-4": "King of Random", + "prefix-5": "GAY", + "not_admin": "I`m not an admin here.", + "no_rights": "I don`t have rights.", + "user-invited": "🚹 The user has been invited successfully!", + "user-kicked": "👞 Was kicked out", + "user-unmuted": "😊 The user unmuted", + "user-muted": "🤫 Muted", + "user-banned": "👔 Banned", + "user-unbanned": "😊 The user unbanned.", + "invite-error-1": "The user's privacy settings do not allow you to invite him.", + "invite-error-2": "I don't have a permission.", + "invite-error-3": "The user is kicked out of the chat, contact the administrators.", + "invite-error-4": "The bot is blocked in the chat, contact the administrators.", + "invite-error-5": "The user is blocked in the chat, contact the administrators.", + "invite-error-6": "The user's account has been deleted.", + "invite-error-7": "The user is already in the group.", + "invite-error-8": "You have blocked this user.", + "inline-text_ru": "👥 Нажмите на кнопку, чтобы выбрать случайного пользователя в чате:", + "rand-user-btn_ru": "🤩 Выбрать", + "rand-user-2-btn_ru": "🤯 Выбрать ещё раз", + "user_ru": "{} Выбор упал на {}!\n{}", + "user-..._ru": "{} Выбор упал на {}!\n{}\n\n{}", + "id+username_ru": "{} Имя пользователя: @{}\n🆔 ID: {}", + "give-adm-btn_ru": "🎩 Назначить на администратора", + "text-adm_ru": "🎩 Был назначен на администратора\n👨🏻‍💻 Префикс: «{}»", + "prefix-1_ru": "Крутой :)", + "prefix-2_ru": "Везучий", + "prefix-3_ru": "Удачливый", + "prefix-4_ru": "Король рандома", + "prefix-5_ru": "Лютейший чел", + "mute-btn_ru": "🤫 Заткнуть", + "unmute-btn_ru": "😊 Снять мут", + "ban-btn_ru": "🚫 Забанить", + "unban-btn_ru": "💥 Разбанить", + "kick-btn_ru": "👞 Выгнать", + "invite-btn_ru": "➕ Добавить обратно", + "not_admin_ru": "Я здесь не администратор.", + "no_rights_ru": "🚫 У Вас нет нужных прав", + "user-invited_ru": "🚹 Пользователь был добавлен обратно!", + "user-kicked_ru": "👞 Был выгнан", + "user-unmuted_ru": "🔔 С пользователя был снят мут", + "user-muted_ru": "🤫 Был заткнут", + "user-banned_ru": "👔 Был забанен", + "user-unbanned_ru": "😊 С пользователя был снят бан.", + "invite-error-1_ru": "Настройки приватности пользователя не позволяют пригласить его.", + "invite-error-2_ru": "🚫 У Вас нет нужных прав", + "invite-error-3_ru": "Пользователь кикнут из чата, обратитесь к администраторам.", + "invite-error-4_ru": "Бот заблокирован в чате, обратитесь к администраторам.", + "invite-error-5_ru": "Пользователь заблокирован в чате, обратитесь к администраторам.", + "invite-error-6_ru": "Аккаунт пользователя удалён.", + "invite-error-7_ru": "Пользователь уже в группе.", + "invite-error-8_ru": "Вы заблокировали этого пользователя." + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "Den4ikSuperOstryyPer4ik/Astro-modules/summer.py": { + "name": "SummerMod", + "description": "Сколько осталось дней до лета?)", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/Den4ikSuperOstryyPer4ik/Astro-modules/main/Banners/Summer.jpg", + "developer": "@AstroModules" + }, + "commands": [ + { + "st": "- вывести таймер" + } + ], + "new_commands": [ + { + "name": "st", + "original_name": "st", + "description": { + "default": "- вывести таймер" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "SummerTimer" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "AmoreForever/amoremods/animevoices.py": { + "name": "AnimeVoicesMod", + "description": "🎤 Popular Anime Voices", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/AmoreForever/assets/master/AnimeVoices.jpg", + "developer": "@hikamorumods" + }, + "commands": [ + { + "smexk": "Смех Канеки" + }, + { + "smexy": "Смех Ягами" + }, + { + "znay": "Знай свое место ничтожество" + }, + { + "madara": "Учиха Мадара" + }, + { + "sharingan": "Итачи Шаринган" + }, + { + "itachi": "Учиха Итачи" + }, + { + "imsasuke": "Учиха Саске" + }, + { + "pain": "Познайте боль" + }, + { + "ras": "Расширение территории" + }, + { + "tensei": "Shinra tensei" + }, + { + "dazai": "Dazai" + }, + { + "gay": "I'm gay" + }, + { + "bankai": "Bankai" + }, + { + "sate": "Sate sate sate" + }, + { + "yoaimo": "Yoaimo" + }, + { + "ghoul": "Я гуль" + }, + { + "welaw": "Мы закон" + }, + { + "dattebayo": "Даттебайо" + }, + { + "hardlife": "Жизнь такова" + }, + { + "hanma": "Я Ханма Шужи" + }, + { + "surprise": "Surprise MxtherFxcker" + }, + { + "equal": "Мы созданы равными" + }, + { + "beautytree": "Красота леса" + }, + { + "bankaii": "Bankai remix" + }, + { + "yamete": "Фулл ямете кудасай" + }, + { + "mafia": "Просыпается мафия" + }, + { + "sharingann": "Sharingan remix" + }, + { + "smexe": "Смех Эрен" + }, + { + "naruto": "Naruto heroes" + }, + { + "smexr": "Смех рюк" + }, + { + "ohayo": "Охаё" + }, + { + "iamhungry": "Есть хочу" + }, + { + "amaterasu": "Аматерасу remix" + }, + { + "owo": "Full OwO" + }, + { + "ghoulru": "Русский Tokyo Ghoul" + } + ], + "new_commands": [ + { + "name": "smexk", + "original_name": "smexkcmd", + "description": { + "default": "Смех Канеки" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "smexy", + "original_name": "smexycmd", + "description": { + "default": "Смех Ягами" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "znay", + "original_name": "znaycmd", + "description": { + "default": "Знай свое место ничтожество" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "madara", + "original_name": "madaracmd", + "description": { + "default": "Учиха Мадара" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "sharingan", + "original_name": "sharingancmd", + "description": { + "default": "Итачи Шаринган" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "itachi", + "original_name": "itachicmd", + "description": { + "default": "Учиха Итачи" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "imsasuke", + "original_name": "imsasukecmd", + "description": { + "default": "Учиха Саске" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "pain", + "original_name": "paincmd", + "description": { + "default": "Познайте боль" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ras", + "original_name": "rascmd", + "description": { + "default": "Расширение территории" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "tensei", + "original_name": "tenseicmd", + "description": { + "default": "Shinra tensei" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "dazai", + "original_name": "dazaicmd", + "description": { + "default": "Dazai" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "gay", + "original_name": "gaycmd", + "description": { + "default": "I'm gay" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "bankai", + "original_name": "bankaicmd", + "description": { + "default": "Bankai" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "sate", + "original_name": "satecmd", + "description": { + "default": "Sate sate sate" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "yoaimo", + "original_name": "yoaimocmd", + "description": { + "default": "Yoaimo" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ghoul", + "original_name": "ghoulcmd", + "description": { + "default": "Я гуль" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "welaw", + "original_name": "welawcmd", + "description": { + "default": "Мы закон" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "dattebayo", + "original_name": "dattebayocmd", + "description": { + "default": "Даттебайо" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "hardlife", + "original_name": "hardlifecmd", + "description": { + "default": "Жизнь такова" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "hanma", + "original_name": "hanmacmd", + "description": { + "default": "Я Ханма Шужи" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "surprise", + "original_name": "surprisecmd", + "description": { + "default": "Surprise MxtherFxcker" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "equal", + "original_name": "equalcmd", + "description": { + "default": "Мы созданы равными" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "beautytree", + "original_name": "beautytreecmd", + "description": { + "default": "Красота леса" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "bankaii", + "original_name": "bankaiicmd", + "description": { + "default": "Bankai remix" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "yamete", + "original_name": "yametecmd", + "description": { + "default": "Фулл ямете кудасай" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "mafia", + "original_name": "mafiacmd", + "description": { + "default": "Просыпается мафия" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "sharingann", + "original_name": "sharinganncmd", + "description": { + "default": "Sharingan remix" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "smexe", + "original_name": "smexecmd", + "description": { + "default": "Смех Эрен" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "naruto", + "original_name": "narutocmd", + "description": { + "default": "Naruto heroes" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "smexr", + "original_name": "smexrcmd", + "description": { + "default": "Смех рюк" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ohayo", + "original_name": "ohayocmd", + "description": { + "default": "Охаё" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "iamhungry", + "original_name": "iamhungrycmd", + "description": { + "default": "Есть хочу" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "amaterasu", + "original_name": "amaterasucmd", + "description": { + "default": "Аматерасу remix" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "owo", + "original_name": "owocmd", + "description": { + "default": "Full OwO" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "ghoulru", + "original_name": "ghoulrucmd", + "description": { + "default": "Русский Tokyo Ghoul" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "AnimeVoices" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "AmoreForever/amoremods/universaltime.py": { + "name": "UniversalTimeMod", + "description": "See the time of other countries", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/AmoreForever/assets/master/Universaltime.jpg", + "developer": "@amoremods" + }, + "commands": [ + { + "atime": "See time" + }, + { + "atimei": "See time on inline mode" + } + ], + "new_commands": [ + { + "name": "atime", + "original_name": "atimecmd", + "description": { + "default": "See time" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + }, + { + "name": "atimei", + "original_name": "atimeicmd", + "description": { + "default": "See time on inline mode" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "UnivTime" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "AmoreForever/amoremods/amoreinfo.py": { + "name": "AmoreindoMod", + "description": "Show userbot info", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/AmoreForever/assets/master/AmoreInfo.jpg", + "developer": "@hikamorumods" + }, + "commands": [ + { + "ainfo": "Send userbot info" + } + ], + "new_commands": [ + { + "name": "ainfo", + "original_name": "ainfocmd", + "description": { + "default": "Send userbot info" + }, + "cmd_names": {}, + "aliases": [], + "usage": null, + "inline": false, + "is_inline_handler": false, + "decorators": [] + } + ], + "inline_handlers": [], + "strings": { + "name": "AmoreInfo", + "owner": "Owner", + "version": "Version", + "build": "Build", + "prefix": "Prefix", + "time": "Time", + "platform": "Platform", + "uptime": "Uptime", + "up-to-date": "😌 Actual version", + "update_required": "😕 Outdated version .update", + "_cfg_cst_msg": "Custom message for info. May contain {me}, {version}, {build}, {prefix}, {platform}, {upd}, {time}, {uptime} keywords", + "_cfg_cst_btn": "Custom button for info. Leave empty to remove button", + "_cfg_cst_bnr": "Custom Banner for info.", + "_cfg_cst_frmt": "Custom fileformat for Banner info.", + "_cfg_banner": "Set `True` in order to disable an image banner", + "_cfg_time": "Use 1, -1, -3 etc.", + "_cfg_close": "Here you can change close button name", + "owner_ru": "Владелец", + "version_ru": "Версия", + "build_ru": "Сборка", + "prefix_ru": "Префикс", + "uptime_ru": "Аптайм", + "platform_ru": "Платформа", + "time_ru": "Время", + "up-to-date_ru": "😌 Актуальная версия", + "update_required_ru": "😕 Требуется обновление .update", + "owner_uz": "Egasi", + "version_uz": "Versiya", + "build_uz": "Yig'ish", + "prefix_uz": "Prefix", + "uptime_uz": "Uptime", + "platform_uz": "Platforma", + "time_uz": "Soat", + "up-to-date_uz": "😌 Joriy versiya", + "update_required_uz": "😕 Yangilanish talab qilinadi .update", + "owner_de": "Besitzer", + "version_de": "Version", + "build_de": "Zusammenbau", + "prefix_de": "Präfix", + "uptime_de": "Betriebszeit", + "platform_de": "Plattform", + "time_de": "Die Zeit", + "up-to-date_de": "😌 Aktuelle Version", + "update_required_de": "😕 Aktualisierung erforderlich .update" + }, + "has_on_load": false, + "has_on_unload": false, + "class_cmd_names": {} + }, + "AmoreForever/amoremods/alarm.py": { + "name": "AlarmMod", + "description": "Alarm module for remind you about something", + "cls_doc": {}, + "meta": { + "pic": null, + "banner": "https://raw.githubusercontent.com/AmoreForever/assets/master/Alarm.jpg", + "developer": "@hikamorumods" + }, + "commands": [ + { + "setalarm": "