Added and updated repositories 2025-11-30 12:52:32

This commit is contained in:
github-actions[bot]
2025-11-30 12:52:32 +00:00
parent 2e2ebd512f
commit 42e38ad1ff
79 changed files with 22656 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
__version__ = (1, 0, 0, 1)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.4
# thx to @codrago for the inspiration
# this module may lead to unstable operation of the userbot
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import os
import aiofiles
import aiohttp
from telethon.extensions.html import parse
import inspect
from .. import loader, utils
@loader.tds
class SwitchToTelethon(loader.Module):
"""Auto switch from Hikka-TL to Telethon"""
strings = {"name": "SwitchToTelethon"}
async def client_ready(self, client, db):
self.client = client
# step1
if self.get("switch1"):
await self._switch_to_telethon(ignore=False)
await self._change_files()
else:
pass
# step2
if self.get("switch2"):
chat_id = self.get("switch2")
await self.client.send_message(
chat_id,
f"<emoji document_id=5996898247963055513>🖤</emoji> <b>Switch to Telethon is completed successfully! (this module has been automatically unloaded)</b>\n\n"
f"<emoji document_id=5325547803936572038>✨</emoji> <i>To restore everything to its original state, use the command</i> <code>{self.get_prefix()}terminal git checkout -- .</code><i>, and restart</i>"
)
self.set("switch2", None)
await self.invoke('unloadmod', 'SwitchToTelethon', self.inline.bot_id)
@loader.command()
async def switch(self, message):
"""Automatically switch to Telethon"""
chat_id = utils.get_chat_id(message)
await utils.answer(message, "<emoji document_id=5461117441612462242>🙂</emoji> <i>Starting the first stage of switching to Telethon...</i>")
await self._switch_to_telethon(ignore=True)
self.set("switch1", chat_id)
await utils.answer(message, "<emoji document_id=5456140674028019486>⚡️</emoji> <b>First stage completed. Restarting...</b>")
await self._restart()
async def _restart(self):
await self.invoke('restart', '-f', self.inline.bot_id)
async def _switch_to_telethon(self, ignore):
for root, _, files in os.walk("."):
for file in files:
# conflict bypass
if 'SwitchToTelethon' in file:
continue
if file.endswith('.py') or file.endswith('.yml'):
await self._process_file(os.path.join(root, file), ignore)
async def _process_file(self, file_path, ignore):
try:
async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
original = await f.readlines()
updated = False
switched = []
for line in original:
# conflict bypass x2
if ignore and "CUSTOM_EMOJIS" in line:
switched.append(line)
continue
# conflict bypass x3
if file_path.endswith('main.py') and ignore and line.strip() == "import hikkatl":
switched.append(line)
continue
# change references from "hikkatl" to "telethon" for switch
if file_path.endswith('.py'):
if "hikkatl" in line:
line = line.replace("hikkatl", "telethon")
updated = True
# conflict bypass x4
if "(2, 0, 8)" in line:
line = line.replace("(2, 0, 8)", "(1, 35, 0)")
updated = True
# change references from "Hikka-TL-New" to "telethon" for switch
if "Hikka-TL-New" in line:
line = line.replace("Hikka-TL-New", "telethon")
updated = True
# for HikkaInfo
elif file_path.endswith('.yml'):
if '<emoji document_id=5377437404078546699>💜</emoji> <b>Hikka-TL:</b>' in line:
line = line.replace(
"<emoji document_id=5377437404078546699>💜</emoji> <b>Hikka-TL:</b>",
"<emoji document_id=5204453279790033300>❤️‍🔥</emoji> <b><a href='https://github.com/LonamiWebs/Telethon.git'>Telethon</a></b>:"
)
updated = True
switched.append(line)
if updated:
async with aiofiles.open(file_path, 'w', encoding='utf-8') as f:
await f.writelines(switched)
print(f"Switched to Telethon: {file_path}")
except Exception as e:
print(f"Error: {file_path}: {e}")
async def _change_files(self):
chat_id = self.get("switch1")
if not chat_id:
print("No chat ID found for logging.")
return
try:
async with aiohttp.ClientSession() as session:
# update html.py
async with session.get("https://raw.githubusercontent.com/yummy1gay/hikariatama-libs/main/html.py") as r_html:
if r_html.status == 200:
html_code = await r_html.text()
async with aiofiles.open(inspect.getfile(parse), 'w', encoding='utf-8') as f_html:
await f_html.write(html_code)
html_message = f"👍 <i>Updated Telethon parser file (for sending spoilers, custom emojis, etc.):</i> <code>{inspect.getfile(parse)}</code>"
else:
html_message = f"⚠️ <b>Failed to update HTML parse file:</b> <code>{r_html.status}</code> (please, use <code>{self.get_prefix()}terminal git checkout -- .</code>, and restart)"
# update translate.py
async with session.get("https://raw.githubusercontent.com/yummy1gay/hikariatama-libs/main/translate.py") as r_translate:
if r_translate.status == 200:
translate_code = await r_translate.text()
async with aiofiles.open("./hikka/modules/translate.py", 'w', encoding='utf-8') as f_translate:
await f_translate.write(translate_code)
translate_message = f"👍 <i>Updated translation module:</i> <code>./hikka/modules/translate.py</code> <i>(for compatibility)</i>"
else:
translate_message = f"⚠️ <b>Failed to update translation module:</b> <code>{r_translate.status}</code> (please, use <code>{self.get_prefix()}terminal git checkout -- .</code>, and restart)"
await self.client.send_message(
chat_id,
f"{html_message}\n{translate_message}\n\n⏳ <b>Restarting...</b>"
)
self.set("switch1", None)
self.set("switch2", chat_id)
await self._restart()
except Exception as e:
print(f"Error updating files: {e}")
if chat_id:
await self.client.send_message(
chat_id, f"⚠️ <b>Error updating files:</b> <code>{e}</code> (please, use <code>{self.get_prefix()}terminal git checkout -- .</code>, and restart)"
)
# окей

15
yummy1gay/limoka/full.txt Normal file
View File

@@ -0,0 +1,15 @@
SwitchToTelethon
yg_balance
yg_chatid
yg_checks
yg_gamee
yg_gemini
yg_ocr
yg_owner
yg_playlist
yg_quotes
yg_stars
yg_tgs
yg_trigger
yg_vm
yg_prem

View File

@@ -0,0 +1,172 @@
__version__ = (1, 5)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import re
import cloudscraper
import urllib.parse, json
from telethon import TelegramClient
from telethon.tl.functions.messages import RequestAppWebViewRequest
from telethon.tl.types import InputBotAppShortName
from .. import loader, utils
@loader.tds
class yg_balance(loader.Module):
"""Модуль для просмотра балансов в @CryptoBot и @wallet"""
strings = {
"name": "yg_balance",
"cryptobot": (
"<emoji document_id=5217705010539812022>☺️</emoji> <b>My balance in @CryptoBot:</b>\n\n{}"
),
"wallet": (
"<emoji document_id=5438394062434485433>💎</emoji> <b>My balance in @wallet:</b>\n\n{}"
),
}
strings_ru = {
"name": "yg_balance",
"cryptobot": (
"<emoji document_id=5217705010539812022>☺️</emoji> <b>Мой баланс в @CryptoBot:</b>\n\n{}"
),
"wallet": (
"<emoji document_id=5438394062434485433>💎</emoji> <b>Мой баланс в @wallet:</b>\n\n{}"
),
}
strings_ua = {
"name": "yg_balance",
"cryptobot": (
"<emoji document_id=5217705010539812022>☺️</emoji> <b>Мій баланс у @CryptoBot:</b>\n\n{}"
),
"wallet": (
"<emoji document_id=5438394062434485433>💎</emoji> <b>Мій баланс у @wallet:</b>\n\n{}"
),
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"hide_0_balances",
True,
"hiding zero balances",
validator=loader.validators.Boolean()
)
)
async def client_ready(self, client: TelegramClient, db):
self.client = client
self.scraper = cloudscraper.create_scraper()
async def check(self, bot, message):
async with message.client.conversation(bot) as conv:
request = await conv.send_message("/wallet")
answer = await conv.get_response()
await request.delete()
await answer.delete()
return answer.text
def cryptobot(self, text):
hide_zeros = self.config["hide_0_balances"]
lines = [line.strip() for line in text.split('\n') if line.strip() and 'Кошелёк' not in line]
total_line = next((line for line in lines if line.startswith('')), '')
balance_lines = [line for line in lines if not line.startswith('')]
if hide_zeros:
balance_lines = [line for line in balance_lines if not re.search(r':\s?0(?:\.0+)?\s\w+', line)]
return '\n'.join(balance_lines) + (f"\n\n{total_line}" if total_line else '')
async def auth(self):
bot = await self.client.get_input_entity(1985737506)
app = InputBotAppShortName(bot_id=bot, short_name="start")
web_view = await self.client(RequestAppWebViewRequest(
peer='me',
app=app,
platform='android'
))
return web_view.url
async def login(self):
url = await self.auth()
fragment = urllib.parse.unquote(urllib.parse.unquote(urllib.parse.urlparse(url).fragment[13:]))
params = dict(urllib.parse.parse_qsl(fragment))
params["user"] = json.loads(params.get("user", "{}"))
data = {
**params,
"web_view_init_data_raw": urllib.parse.unquote(url.split('tgWebAppData=')[1].split('&tgWebAppVersion')[0])
}
resp = self.scraper.post(
f"https://walletbot.me/api/v1/users/auth/",
json=data
)
return resp.json().get('value', {})
async def get_balances(self):
resp = self.scraper.get(
"https://walletbot.me/api/v1/accounts/",
headers={
"Accept": "application/json, text/plain, */*",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Accept-Language": "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7",
"Authorization": await self.login(),
},
)
hide_zeros = self.config["hide_0_balances"]
balances = [
f"<b>{account['currency']}:</b> {account['available_balance']} ({account['available_balance_usd_amount']}$)"
for account in resp.json().get("accounts", [])
if not (hide_zeros and account['available_balance'] == 0 and account['available_balance_usd_amount'] == 0.0)
]
return balances
@loader.command(ru_doc="проверить баланс в @CryptoBot",
ua_doc="перевірити баланс у @CryptoBot")
async def bc(self, message):
"""check balance in @CryptoBot"""
text = await self.check(1559501630, message)
balance = self.cryptobot(text)
await utils.answer(message, self.strings["cryptobot"].format(balance))
@loader.command(ru_doc="проверить баланс в @wallet",
ua_doc="перевірити баланс у @wallet")
async def bw(self, message):
"""check balance in @wallet"""
balances = await self.get_balances()
balance = "\n".join(balances) if balances else "-"
await utils.answer(message, self.strings["wallet"].format(balance))
@loader.command(ru_doc="вкл/выкл скрытие нулевых балансов",
ua_doc="увімк/вимк приховування нульових балансів")
async def hide0(self, message):
"on/off hiding zero balances"
self.config["hide_0_balances"] = not self.config["hide_0_balances"]
status = 'включено' if self.config["hide_0_balances"] else 'выключено'
await utils.answer(
message,
f"<emoji document_id=5278611606756942667>❤️</emoji> <b>Скрытие нулевых балансов {status}</b>"
)

View File

@@ -0,0 +1,59 @@
__version__ = (1, 4, 8, 8)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
from telethon.tl.types import User, Chat, Channel
from .. import loader, utils
@loader.tds
class yg_chatid(loader.Module):
"""Модуль для отображения ID чатов, каналов или пользователей"""
strings = {
"name": "yg_chatid",
"chat_id": "<b>Chat ID:</b> <code>{}</code>",
"user_id": "<b>User ID:</b> <code>{}</code>",
"not_found": "<b>Not Found</b>"
}
async def chatidcmd(self, message):
"""<reply>/@username - узнать ID юзера (на сообщение которого вы ответили), указанного юзера или текущего чата"""
args = utils.get_args_raw(message)
if message.is_reply:
reply_msg = await message.get_reply_message()
entity = await message.client.get_entity(reply_msg.sender_id)
await self.who(message, entity)
return
if args:
try:
entity = await message.client.get_entity(args)
await self.who(message, entity)
except Exception:
await message.edit(self.strings["not_found"])
return
id = message.chat_id
await message.edit(self.strings["chat_id"].format(id))
async def who(self, message, entity):
if isinstance(entity, User):
await message.edit(self.strings["user_id"].format(entity.id))
elif isinstance(entity, (Chat, Channel)):
await message.edit(self.strings["chat_id"].format(entity.id))
else:
await message.edit(self.strings["not_found"])

View File

@@ -0,0 +1,945 @@
__version__ = (1, 5, 0, 0)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# requires: google-generativeai urlextract cloudscraper
# changelog:
# - Добавлен подбор паролей с использованием Google Gemini.
# - Реализована активация тестнет чеков.
# - Добавлена возможность задать задержку перед активацией чека.
# - Добавлены переводы.
# - Внесены мелкие исправления и оптимизации кода.
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import os
import re
from telethon import events
from collections import defaultdict
from telethon.tl.types import MessageEntityUrl, MessageEntityTextUrl, MessageMediaWebPage
from telethon.tl.functions.messages import ImportChatInviteRequest, CheckChatInviteRequest
from google.generativeai.types import HarmCategory, HarmBlockThreshold
from telethon.tl.functions.messages import RequestWebViewRequest
from telethon.tl.functions.channels import LeaveChannelRequest
from google.generativeai import GenerativeModel, configure
from telethon.tl.types import Message
from telethon import TelegramClient
from urlextract import URLExtract
from urllib.parse import unquote
import cloudscraper
import asyncio
import random
import json
from .. import loader, utils
class Passworder: #beto
def __init__(self, api_key, model_name):
self.api_key = api_key
self.model_name = model_name
self.model = None
self.safety_settings={HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE}
self.prompt = """
Твоя задача извлекать пароли из текстовых описаний. В некоторых случаях пароль может быть прямо указан в тексте, в других случаях его нужно вычислить или извлечь.
Учитывай следующие правила:
1. Явный пароль (с синонимами): Распознаешь и извлекаешь пароль, указанный явно, используя ключевые слова и их синонимы: "пароль", "pass", "password", "код", "code", "ключ", "key", и т.д. Регистр букв подбирай на основе самого описания. Пример: "Password: wkek" -> {"password": "wkek"}, "Код доступа: 1234" -> {"password": "1234"}.
2. Игнорирование рекламы и ссылок: Если в тексте присутствуют рекламные ссылки (например, "https://t.me/+wI2h84RF80NiZTI6") или другие ненужные ссылки, их нужно игнорировать, и вернуть пустой пароль. Пример: {"password": ""}.
3. Игнорирование призывов к действиям: Если текст содержит фразы типа "пароль на 60 реакций" или "сделай 10 шагов и получи пароль", игнорируй такие тексты и возвращай пустой пароль. Пример: {"password": ""}.
5. Математические примеры: Если в тексте содержится математический пример, например, "пароль: 2+6", вам нужно вычислить результат и вернуть его в формате: {"password": "8"}.
6. Нецензурная лексика: Наличие нецензурной лексики не влияет на процесс. Ты фокусируешься на извлечении пароля, любые оскорбления или непристойные выражения приветствуются.
7. Загадки и логические задачи: Если текст включает загадки или другие логические задачи, ты должен решить их и вернуть правильный ответ. Например, если описание будет "Пароль: сколько будет 3*4?", ответ будет {"password": "12"}. Так же учитывай момент что описание может состоять только из загадки/примера, это тоже наводка на пароль. Например, если описание будет "висит груша нельзя скушать", ответ будет {"password": "лампочка"}
Пример: Если описание "Пароль - 3*3", то твой ответ должен быть {"password": "9"}.
Если в тексте нет явных подсказок или пароля, возвращай пустой пароль: {"password": ""}. Для абзацев используй \\n!
Пример текста с описанием пароля:
"Пароль на 50 реакций в чате."
Ответ: {"password": ""}
Пример текста с математическим примером:
"пасс - 12+8"
Ответ: {"password": "20"}
Пример загадки с кодом:
"a = [1, 2, 3]
b = a
b[0] = 4
print(a)
Что выведет этот код?"
Ответ: {"password": "[4, 2, 3]"}
""" #you can edit this prompt!
async def generate(self, description: str) -> dict:
try:
configure(api_key=self.api_key)
model_name = self.model_name if self.model_name else "gemini-flash-latest"
self.model = GenerativeModel(
model_name,
system_instruction=self.prompt,
safety_settings=self.safety_settings
)
res = await self.model.generate_content_async(description)
if res and res.text:
try:
return json.loads(res.text.strip())
except json.JSONDecodeError:
return {"error": "Invalid JSON response", "raw": res.text.strip()}
return {"password": ""}
except Exception as e:
if "429" in str(e):
return {"error": "апи кей сдох"}
return {"error": str(e)}
@loader.tds
class yg_checks(loader.Module):
"""Активатор чеков @send (@CryptoBot)"""
strings = {
"name": "yg_checks",
"language": "en",
"activator": "{} <b>Activator {}</b>",
"log_sending": "{} <b>Log sending {}</b>",
"password_cracking": "{} <b>Password cracking with neural network {}</b>",
"private_check_activation": "{} <b>Activation of checks sent in private messages {}</b>",
"auto_subscription": "{} <b>Auto-subscription {}</b>",
"auto_unsubscription": "{} <b>Auto-unsubscription {}</b>",
"testnet": "{} <b>Testnet check activation {}</b>",
"logs_username_desc": "@username where logs will be sent",
"logs_enabled_desc": "send logs",
"delay_desc": "delay in seconds before activating the check",
"track_private_desc": "activate checks sent in private messages",
"ai_passwords_desc": "password cracking using Gemini AI",
"watcher_on_desc": "activator status",
"subscribe_desc": "subscribe to channels to activate checks that require it",
"unsubscribe_desc": "unsubscribe from channels after activating the check",
"no_track_users_desc": "whose checks not to activate (specify the user without @)",
"testnet_desc": "activate checks sent using @CryptoTestnetBot",
"gemini_api_key_desc": "API key for Gemini AI (aistudio.google.com/apikey)",
"gemini_model_name_desc": "model for Gemini AI. examples: gemini-1.5-flash, gemini-1.5-pro, gemini-2.0-flash-exp, gemini-2.0-flash-thinking-exp-1219",
"proxy_desc": "proxy in the format http://<user>:<pass>@<proxy>:<port>, or http://<proxy>:<port>",
}
strings_ru = {
"name": "yg_checks",
"language": "ru",
"activator": "{} <b>Активатор {}</b>",
"log_sending": "{} <b>Отправка логов {}</b>",
"password_cracking": "{} <b>Подбор паролей с помощью нейросети {}</b>",
"private_check_activation": "{} <b>Активация чеков отправленных в личке {}</b>",
"auto_subscription": "{} <b>Авто-подписка {}</b>",
"auto_unsubscription": "{} <b>Авто-отписка {}</b>",
"testnet": "{} <b>Активация тестнет чеков {}</b>",
"logs_username_desc": "@username куда будут отправляться логи",
"logs_enabled_desc": "отправка логов",
"delay_desc": "задержка в секундах перед активацией чека",
"track_private_desc": "активация чеков отправленных в личке",
"ai_passwords_desc": "подбор паролей с помощью Gemini AI",
"watcher_on_desc": "состояние активатора",
"subscribe_desc": "подписываться ли на каналы чтобы активировать чеки которые этого требуют",
"unsubscribe_desc": "отписываться ли от каналов после активации чека",
"no_track_users_desc": "чьи чеки не активировать (юзер указывать обязательно без @)",
"testnet_desc": "активировать ли чеки отправленные с помощью @CryptoTestnetBot",
"gemini_api_key_desc": "API ключ для Gemini AI (aistudio.google.com/apikey)",
"gemini_model_name_desc": "модель для Gemini AI. примеры: gemini-1.5-flash, gemini-1.5-pro, gemini-2.0-flash-exp, gemini-2.0-flash-thinking-exp-1219",
"proxy_desc": "прокси в формате http://<user>:<pass>@<proxy>:<port>, или http://<proxy>:<port>",
}
strings_ua = {
"name": "yg_checks",
"language": "ua",
"activator": "{} <b>Активатор {}</b>",
"log_sending": "{} <b>Відправка логів {}</b>",
"password_cracking": "{} <b>Підбір паролей за допомогою нейромережі {}</b>",
"private_check_activation": "{} <b>Активація чеків, надісланих в особисті повідомлення {}</b>",
"auto_subscription": "{} <b>Авто-підписка {}</b>",
"auto_unsubscription": "{} <b>Авто-відписка {}</b>",
"testnet": "{} <b>Активація тестет чеків {}</b>",
"logs_username_desc": "@username, куди будуть надсилатися логи",
"logs_enabled_desc": "надсилання логів",
"delay_desc": "затримка в секундах перед активацією чека",
"track_private_desc": "активація чеків, надісланих в особисті повідомлення",
"ai_passwords_desc": "підбір паролів за допомогою Gemini AI",
"watcher_on_desc": "стан активатора",
"subscribe_desc": "підписуватися на канали, щоб активувати чеки, які цього потребують",
"unsubscribe_desc": "відписуватися від каналів після активації чека",
"no_track_users_desc": "чиї чеки не активувати (користувача вказувати обов'язково без @)",
"testnet_desc": "активувати чеки, надіслані за допомогою @CryptoTestnetBot",
"gemini_api_key_desc": "API ключ для Gemini AI (aistudio.google.com/apikey)",
"gemini_model_name_desc": "модель для Gemini AI. приклади: gemini-1.5-flash, gemini-1.5-pro, gemini-2.0-flash-exp, gemini-2.0-flash-thinking-exp-1219",
"proxy_desc": "проксі у форматі http://<user>:<pass>@<proxy>:<port>, або http://<proxy>:<port>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"logs_username",
"",
doc=lambda: self.strings("logs_username_desc"),
validator=loader.validators.Hidden(loader.validators.String()),
),
loader.ConfigValue(
"logs_enabled",
True,
doc=lambda: self.strings("logs_enabled_desc"),
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"delay",
0,
doc=lambda: self.strings("delay_desc"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"track_private",
True,
doc=lambda: self.strings("track_private_desc"),
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"ai_passwords",
False,
doc=lambda: self.strings("ai_passwords_desc"),
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"watcher_on",
True,
doc=lambda: self.strings("watcher_on_desc"),
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"subscribe",
True,
doc=lambda: self.strings("subscribe_desc"),
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"unsubscribe",
True,
doc=lambda: self.strings("unsubscribe_desc"),
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"no_track_users",
["username"],
doc=lambda: self.strings("no_track_users_desc"),
validator=loader.validators.Series(
loader.validators.Union(loader.validators.String(), loader.validators.Integer())
),
),
loader.ConfigValue(
"testnet",
False,
doc=lambda: self.strings("testnet_desc"),
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"gemini_api_key",
"",
doc=lambda: self.strings("gemini_api_key_desc"),
validator=loader.validators.Hidden(loader.validators.String()),
),
loader.ConfigValue(
"gemini_model_name",
"gemini-flash-latest",
doc=lambda: self.strings("gemini_model_name_desc"),
validator=loader.validators.String(),
),
loader.ConfigValue(
"proxy",
"",
doc=lambda: self.strings("proxy_desc"),
validator=loader.validators.String(),
)
)
self.sent_codes = defaultdict(bool)
async def client_ready(self, client: TelegramClient, db):
self.client = client
self.me = await self.client.get_me()
self.me_id = self.me.id
self.cd_id = 1559501630
self.testnet_id = 1622808649
self.extractor = URLExtract()
self.scraper = cloudscraper.create_scraper()
handlers = [
(self.cb, [events.NewMessage, events.MessageEdited]),
(self.channels, [events.NewMessage, events.MessageEdited]),
(self.passwords, [events.NewMessage, events.MessageEdited]),
]
for handler_func, event_list in handlers:
for event in event_list:
self.client.add_event_handler(handler_func, event)
if self.config["gemini_api_key"]:
self.passworder = Passworder(self.config["gemini_api_key"], self.config["gemini_model_name"])
else:
self.passworder = None
proxy = self.config["proxy"]
if proxy:
os.environ["http_proxy"] = proxy
os.environ["HTTP_PROXY"] = proxy
os.environ["https_proxy"] = proxy
os.environ["HTTPS_PROXY"] = proxy
async def stars(self, url, bot_username):
web_view = await self.client(RequestWebViewRequest(
peer=bot_username,
bot=bot_username,
platform='android',
from_bot_menu=False,
url=url
))
auth_url = web_view.url
params = unquote(auth_url.split('tgWebAppData=')[1].split('&tgWebAppVersion')[0])
access_token = await self.get_token('https://api.send.tg/internal/v1/authentication/webapp', params)
if access_token:
code = url.split('/')[-1]
await self.claim_stars(code, access_token)
async def get_token(self, url, params):
json_data = {
"initData": params
}
UserAgent = self.generate_random_user_agent()
headers = {
'Accept': 'application/json',
'User-Agent': UserAgent
}
response = self.scraper.post(url, json=json_data, headers=headers)
if response.status_code == 200:
headers = response.headers
set_cookie = headers.get('Set-Cookie')
if set_cookie:
access_token = set_cookie.split('access_token=')[1].split(';')[0]
return access_token
else:
return None
else:
return None
async def claim_stars(self, code, access_token):
url = f'https://api.send.tg/internal/v1/stars/claim/{code}'
UserAgent = self.generate_random_user_agent()
headers = {
'Accept': 'application/json',
'Cookie': f'access_token={access_token}',
'User-Agent': UserAgent
}
response = self.scraper.post(url, headers=headers)
if response.status_code == 200:
response_data = response.json()
stars = response_data.get("stars")
gifted_by = response_data.get("gifted_by")
await self.log(f"<b>+{stars}</b> <emoji document_id=5897920748101571572>🌟</emoji> (от {gifted_by})")
else:
pass
async def get_codes(self, text, entities, markup):
urls_in_message = set()
finded_codes = set()
finded_stars_codes = set()
finded_testnet_codes = set()
url_pattern = r'https?://t\.me/(?:send|CryptoBot)\?start=([A-Za-z0-9_-]+)'
stars_pattern = r'https?://t\.me/CryptoBot/app\?startapp=stars-([A-Za-z0-9_-]+)'
testnet_pattern = r'https?://t\.me/CryptoTestnetBot\?start=([A-Za-z0-9_-]+)'
if entities:
for entity in entities:
if isinstance(entity, MessageEntityUrl):
urls_in_text = self.extractor.find_urls(text)
for found_url in urls_in_text:
urls_in_message.add(found_url.strip())
elif isinstance(entity, MessageEntityTextUrl):
url = entity.url.strip()
urls_in_message.add(url)
elif isinstance(entity, MessageMediaWebPage):
url = entity.url.strip()
urls_in_message.add(url)
if markup:
for button_row in markup.rows:
for button in button_row.buttons:
if hasattr(button, "url") and button.url:
urls_in_message.add(button.url.strip())
for found_url in urls_in_message:
if not found_url.startswith(('http://', 'https://')):
found_url = 'https://' + found_url.strip() #не обращайте внимания на костыли пажеее
clean_url = re.sub(r'[^\w:/?&=.-]', '', found_url)
code_match = re.match(url_pattern, clean_url)
if code_match:
code = code_match.group(1)
finded_codes.add(code)
stars_match = re.match(stars_pattern, clean_url)
if stars_match:
stars_code = stars_match.group(1)
finded_stars_codes.add(stars_code)
testnet_match = re.match(testnet_pattern, clean_url)
if testnet_match:
testnet_code = testnet_match.group(1)
finded_testnet_codes.add(testnet_code)
return list(finded_codes), list(finded_stars_codes), list(finded_testnet_codes)
async def password(self, description):
if not self.config["gemini_api_key"]:
await self.log(f"<emoji document_id=5274099962655816924>❗️</emoji> <b>API ключ не указан. Получить его можно тут: aistudio.google.com/apikey (бесплатно), затем укажи его в конфиге (<code>{self.get_prefix()}cfg yg_checks</code>)</b>")
return
if self.passworder:
result = await self.passworder.generate(description)
else:
return
if "error" in result:
await self.log(f"<emoji document_id=5274099962655816924>❗️</emoji> <b>Ошибка генерации пароля:</b> <code>{utils.escape_html(result['error'])}</code>")
return
if result.get("password"):
return result["password"]
elif result.get("password") == "":
return
else:
await self.log(f"<emoji document_id=5274099962655816924>❗️</emoji> <b>Ошибка генерации пароля:</b> <code>{utils.escape_html(str(result))}</code>")
return
async def cb(self, message):
if self.config["watcher_on"]:
if message and message.sender_id not in [self.me_id, self.cd_id, self.testnet_id]:
try:
if not self.config["track_private"] and message.is_private:
return
sender_username = getattr(message.sender, 'username', None) if message.sender else None
if sender_username in self.config["no_track_users"]:
return
codes, stars_codes, testnet_codes = await self.get_codes(message.text, message.entities, message.reply_markup)
if codes:
for code in codes:
if not self.sent_codes[code]:
if code.startswith('CQ'):
await message.mark_read()
await asyncio.sleep(int(self.config["delay"]))
await self.client.send_message(self.cd_id, f"/start {code}")
self.sent_codes[code] = True
await self.send_log_message(message, code)
if stars_codes:
for stars_code in stars_codes:
if not self.sent_codes[stars_code]:
await message.mark_read()
await self.stars(f"https://app.send.tg/stars/{stars_code}", "send")
self.sent_codes[stars_code] = True
if testnet_codes:
if self.config["testnet"]:
for testnet_code in testnet_codes:
if not self.sent_codes[testnet_code]:
if testnet_code.startswith('CQ'):
await message.mark_read()
await asyncio.sleep(int(self.config["delay"]))
await self.client.send_message(self.testnet_id, f"/start {testnet_code}")
self.sent_codes[testnet_code] = True
await self.send_log_message(message, testnet_code)
except AttributeError:
pass
async def channels(self, event):
if not self.config["subscribe"]:
return
if not self.config["watcher_on"]:
return
if event.sender_id == self.cd_id and any(event.text.startswith(prefix) for prefix in ['Чтобы активировать этот чек, подпишитесь на канал','To activate this check, join the channel(s)']):
subscribed = []
try:
rows = event.reply_markup.rows if event.reply_markup else []
for row in rows:
for button in row.buttons:
if button.url:
invite_code = button.url.split('+', 1)[1]
await self.client(ImportChatInviteRequest(invite_code))
subscribed.append(invite_code)
except Exception:
pass
await asyncio.sleep(1)
await event.click(data=b'check-subscribe')
await asyncio.sleep(1)
if self.config["unsubscribe"]:
for invite_code in subscribed:
channel_info = await self.client(CheckChatInviteRequest(hash=invite_code))
channel = channel_info.chat
await self.client(LeaveChannelRequest(channel))
async def passwords(self, message):
if not self.config["watcher_on"]:
return
if not self.config["ai_passwords"]:
return
if message.sender_id == self.cd_id and any(phrase in message.text for phrase in ["Введите пароль от чека для получения", "Enter the password for this check to receive"]):
description = " ".join("\n".join(message.raw_text.split("\n")[2:]).split(" ")[1:])
r = await self.password(description)
if r:
await self.client.send_message(self.cd_id, r)
async def log(self, message):
if self.config["logs_username"]:
await self.client.send_message(self.config["logs_username"], message, link_preview=False)
async def send_log_message(self, message, code):
if self.config["logs_enabled"]:
chat_id = str(message.chat_id).replace('-100', '')
if message.is_private:
sender_username = getattr(message.sender, 'username', None) if message.sender else None
await self.log(f"<emoji document_id=5431449001532594346>⚡️</emoji> <b>Обнаружен новый чек:</b>\n\n<emoji document_id=5870527201874546272>🔗</emoji> <b>Ссылка чека:</b> <i>t.me/send?start={code}<i>\n<emoji document_id=5879770735999717115>👤</emoji> <b>Чек был обнаружен в личных сообщениях:</b> <i>@{sender_username}</i>")
else:
message_link = f"t.me/c/{chat_id}/{message.id}"
await self.log(f"<emoji document_id=5431449001532594346>⚡️</emoji> <b>Обнаружен новый чек:</b>\n\n<emoji document_id=5870527201874546272>🔗</emoji> <b>Ссылка чека:</b> <i>t.me/send?start={code}<i>\n<emoji document_id=5870527201874546272>🔗</emoji> <b>Ссылка на сообщение с чеком:</b> <i>{message_link}</i>")
@loader.command(ru_doc="вкл/выкл активатор", ua_doc="увімк/вимк активатор")
async def checkscmd(self, m: Message):
"""on/off auto-check activation"""
self.config["watcher_on"] = not self.config["watcher_on"]
lang = self.strings.get("language", "en")
on_off = {"en": "enabled", "ru": "включен", "ua": "увімкнено"}[lang] if self.config["watcher_on"] else {"en": "disabled", "ru": "выключен", "ua": "вимкнено"}[lang]
list = [
"<emoji document_id=5931703809800672260>🦋</emoji>",
"<emoji document_id=5931685899787049183>🦋</emoji>",
"<emoji document_id=5931254745200072637>🦋</emoji>",
"<emoji document_id=5931420135800706406>🦋</emoji>",
"<emoji document_id=5931579221389350286>🦋</emoji>",
"<emoji document_id=5931796606864070138>🦋</emoji>",
"<emoji document_id=5931709595121620710>🦋</emoji>",
"<emoji document_id=5931689305696113988>🦋</emoji>"
]
emoji = random.choice(list)
await utils.answer(m, self.strings["activator"].format(emoji, on_off))
@loader.command(ru_doc="вкл/выкл активатор testnet", ua_doc="увімк/вимк активатор testnet")
async def testnetcmd(self, m: Message):
"""on/off auto-check activation for testnet"""
self.config["testnet"] = not self.config["testnet"]
lang = self.strings.get("language", "en")
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["testnet"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
list = [
"<emoji document_id=5931703809800672260>🦋</emoji>",
"<emoji document_id=5931685899787049183>🦋</emoji>",
"<emoji document_id=5931254745200072637>🦋</emoji>",
"<emoji document_id=5931420135800706406>🦋</emoji>",
"<emoji document_id=5931579221389350286>🦋</emoji>",
"<emoji document_id=5931796606864070138>🦋</emoji>",
"<emoji document_id=5931709595121620710>🦋</emoji>",
"<emoji document_id=5931689305696113988>🦋</emoji>"
]
emoji = random.choice(list)
await utils.answer(m, self.strings["testnet"].format(emoji, on_off))
@loader.command(ru_doc="вкл/выкл отправку логов", ua_doc="увімк/вимк відправку логів")
async def yglogscmd(self, m: Message):
"""on/off log sending"""
self.config["logs_enabled"] = not self.config["logs_enabled"]
lang = self.strings.get("language", "en")
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["logs_enabled"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
list = [
"<emoji document_id=5931246400078616786>🍑</emoji>",
"<emoji document_id=5931283302437623922>🍑</emoji>",
"<emoji document_id=5933573709712331850>🍑</emoji>",
"<emoji document_id=5931412164341404834>🍑</emoji>",
"<emoji document_id=5931408105597310922>🍑</emoji>",
"<emoji document_id=5931347907335689957>🍑</emoji>",
"<emoji document_id=5933527787922005080>🍑</emoji>",
"<emoji document_id=5931255728747583490>🍑</emoji>"
]
emoji = random.choice(list)
await utils.answer(m, self.strings["log_sending"].format(emoji, on_off))
@loader.command(ru_doc="вкл/выкл подбор паролей с помощью нейросети", ua_doc="увімк/вимк підбір паролей за допомогою нейромережі")
async def passwordscmd(self, m: Message):
"""on/off password cracking with neural network"""
self.config["ai_passwords"] = not self.config["ai_passwords"]
lang = self.strings.get("language", "en")
on_off = {"en": "enabled", "ru": "включен", "ua": "увімкнено"}[lang] if self.config["ai_passwords"] else {"en": "disabled", "ru": "выключен", "ua": "вимкнено"}[lang]
list = [
"<emoji document_id=5931715028255249602>🔐</emoji>",
"<emoji document_id=5931759476871797208>🔐</emoji>",
"<emoji document_id=5931604879523976952>🔐</emoji>",
"<emoji document_id=5931569115331306831>🔐</emoji>",
"<emoji document_id=5931530997496551899>🔐</emoji>",
"<emoji document_id=5931464008891635480>🔐</emoji>",
"<emoji document_id=5931781312485529416>🔐</emoji>",
"<emoji document_id=5931434210408536378>🔐</emoji>"
]
emoji = random.choice(list)
await utils.answer(m, self.strings["password_cracking"].format(emoji, on_off))
@loader.command(ru_doc="вкл/выкл активацию чеков отправленных в личке", ua_doc="увімк/вимк активацію чеків, надісланих в особисті повідомлення")
async def yglscmd(self, m: Message):
"""on/off activation of checks sent in private messages"""
self.config["track_private"] = not self.config["track_private"]
lang = self.strings.get("language", "en")
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["track_private"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
list = [
"<emoji document_id=5931534008268625877>🔁</emoji>",
"<emoji document_id=5933704920963225481>🔁</emoji>",
"<emoji document_id=5931351192985671828>🔁</emoji>",
"<emoji document_id=5931570287857374798>🔁</emoji>",
"<emoji document_id=5931284676827158390>🔁</emoji>",
"<emoji document_id=5931776850014508762>🔁</emoji>",
"<emoji document_id=5931430675650451345>🔁</emoji>",
"<emoji document_id=5931768827015602073>🔁</emoji>"
]
emoji = random.choice(list)
await utils.answer(m, self.strings["private_check_activation"].format(emoji, on_off))
@loader.command(ru_doc="вкл/выкл авто-подписку", ua_doc="увімк/вимк авто-підписку")
async def subscribecmd(self, m: Message):
"""on/off auto-subscription"""
self.config["subscribe"] = not self.config["subscribe"]
lang = self.strings.get("language", "en")
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["subscribe"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
list = [
"<emoji document_id=5931461638069687926>💡</emoji>",
"<emoji document_id=5931599476455118181>💡</emoji>",
"<emoji document_id=5931620642053953532>💡</emoji>",
"<emoji document_id=5931776927323920236>💡</emoji>",
"<emoji document_id=5931773113392962977>💡</emoji>",
"<emoji document_id=5931673221043590661>💡</emoji>",
"<emoji document_id=5931462436933604912>💡</emoji>",
"<emoji document_id=5931295409950431661>💡</emoji>"
]
emoji = random.choice(list)
await utils.answer(m, self.strings["auto_subscription"].format(emoji, on_off))
@loader.command(ru_doc="вкл/выкл авто-отписку", ua_doc="увімк/вимк авто-відписку")
async def unsubscribecmd(self, m: Message):
"""on/off auto-unsubscription"""
self.config["unsubscribe"] = not self.config["unsubscribe"]
lang = self.strings.get("language", "en")
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["unsubscribe"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
list = [
"<emoji document_id=5931279570111043408>✅</emoji>",
"<emoji document_id=5931602010485823634>✅</emoji>",
"<emoji document_id=5931642602221737965>✅</emoji>",
"<emoji document_id=5933944919440758085>✅</emoji>",
"<emoji document_id=5933523918156469650>✅</emoji>",
"<emoji document_id=5931644148409964015>✅</emoji>",
"<emoji document_id=5931387421034812889>✅</emoji>",
"<emoji document_id=5931344333922900261>✅</emoji>"
]
emoji = random.choice(list)
await utils.answer(m, self.strings["auto_unsubscription"].format(emoji, on_off))
def generate_random_user_agent(self, device_type='android', browser_type='chrome'):
existing_versions = {
110: [
'110.0.5481.154',
'110.0.5481.153',
'110.0.5481.65',
'110.0.5481.64',
'110.0.5481.63',
'110.0.5481.61'
],
111: [
"111.0.5563.116",
'111.0.5563.115',
'111.0.5563.58',
'111.0.5563.49'
],
112: [
'112.0.5615.136',
'112.0.5615.136',
'112.0.5615.101',
'112.0.5615.100',
'112.0.5615.48'
],
113: [
'113.0.5672.77',
'113.0.5672.76'
],
114: [
'114.0.5735.60',
'114.0.5735.53'
],
115: [
'115.0.5790.136'
],
116: [
'116.0.5845.172',
'116.0.5845.164',
'116.0.5845.163',
'116.0.5845.114',
'116.0.5845.92'
],
117: [
'117.0.5938.154',
'117.0.5938.141',
'117.0.5938.140',
'117.0.5938.61',
'117.0.5938.61',
'117.0.5938.60'
],
118: [
'118.0.5993.112',
'118.0.5993.111',
'118.0.5993.80',
'118.0.5993.65',
'118.0.5993.48'
],
119: [
'119.0.6045.194',
'119.0.6045.193',
'119.0.6045.164',
'119.0.6045.163',
'119.0.6045.134',
'119.0.6045.134',
'119.0.6045.66',
'119.0.6045.53'
],
120: [
'120.0.6099.230',
'120.0.6099.210',
'120.0.6099.194',
'120.0.6099.193',
'120.0.6099.145',
'120.0.6099.144',
'120.0.6099.144',
'120.0.6099.116',
'120.0.6099.116',
'120.0.6099.115',
'120.0.6099.44',
'120.0.6099.43'
],
121: [
'121.0.6167.178',
'121.0.6167.165',
'121.0.6167.164',
'121.0.6167.164',
'121.0.6167.144',
'121.0.6167.143',
'121.0.6167.101'
],
122: [
'122.0.6261.119',
'122.0.6261.106',
'122.0.6261.105',
'122.0.6261.91',
'122.0.6261.90',
'122.0.6261.64',
'122.0.6261.43'
],
123: [
'123.0.6312.121',
'123.0.6312.120',
'123.0.6312.119',
'123.0.6312.118',
'123.0.6312.99',
'123.0.6312.80',
'123.0.6312.41',
'123.0.6312.40'
],
124: [
'124.0.6367.179',
'124.0.6367.172',
'124.0.6367.171',
'124.0.6367.114',
'124.0.6367.113',
'124.0.6367.83',
'124.0.6367.82',
'124.0.6367.54'
],
125: [
'125.0.6422.165',
'125.0.6422.164',
'125.0.6422.147',
'125.0.6422.146',
'125.0.6422.113',
'125.0.6422.72',
'125.0.6422.72',
'125.0.6422.53',
'125.0.6422.52'
],
126: [
'126.0.6478.122',
'126.0.6478.72',
'126.0.6478.71',
'126.0.6478.50'
]
}
devices = [
('Samsung', 'SM-G980F', 'AVERAGE', 10),
('Samsung', 'SM-G973F', 'AVERAGE', 9),
('Samsung', 'SM-G973U', 'AVERAGE', 9),
('Samsung', 'SM-N986B', 'AVERAGE', 11),
('Samsung', 'SM-N981B', 'AVERAGE', 11),
('Samsung', 'SM-F916B', 'AVERAGE', 11),
('Samsung', 'SM-G998B', 'HIGH', 12),
('Samsung', 'SM-G991B', 'HIGH', 12),
('Samsung', 'SM-G996B', 'HIGH', 12),
('Samsung', 'SM-G990E', 'HIGH', 12),
('Samsung', 'SM-G990B', 'HIGH', 12),
('Samsung', 'SM-G990B2', 'HIGH', 12),
('Samsung', 'SM-G990U', 'HIGH', 12),
('Google', 'Pixel 5', 'AVERAGE', 11),
('Google', 'Pixel 5a', 'AVERAGE', 11),
('Google', 'Pixel 6', 'AVERAGE', 12),
('Google', 'Pixel 6 Pro', 'AVERAGE', 12),
('Google', 'Pixel 6 XL', 'AVERAGE', 12),
('Google', 'Pixel 6a', 'AVERAGE', 12),
('Google', 'Pixel 7', 'HIGH', 13),
('Google', 'Pixel 7a', 'AVERAGE', 13),
('Google', 'Pixel 7 Pro', 'HIGH', 13),
('Google', 'Pixel 8', 'HIGH', 14),
('Google', 'Pixel 8a', 'HIGH', 14),
('Google', 'Pixel 8 Pro', 'HIGH', 14),
('Google', 'Pixel 9', 'HIGH', 14),
('Google', 'Pixel 9 Pro', 'HIGH', 14),
('Google', 'Pixel 9 Pro XL', 'HIGH', 14),
('Xiaomi', 'Mi 10', 'AVERAGE', 10),
('Xiaomi', 'Mi 11', 'AVERAGE', 11),
('Xiaomi', 'Mi 12', 'HIGH', 12),
('Xiaomi', 'Redmi Note 10', 'HIGH', 11),
('Xiaomi', 'Redmi Note 10 Pro', 'HIGH', 11),
('Xiaomi', 'Redmi Note 11', 'HIGH', 12),
('Xiaomi', 'Redmi Note 11 Pro', 'HIGH', 12),
('Xiaomi', 'Redmi Note 12', 'HIGH', 13),
('Xiaomi', 'Redmi Note 12 Pro', 'HIGH', 13),
('Xiaomi', 'POCO M3 Pro', 'HIGH', 11),
('Xiaomi', 'POCO X5', 'HIGH', 12),
('Xiaomi', 'POCO X5 Pro', 'HIGH', 12),
('Xiaomi', 'POCO X6 Pro', 'HIGH', 13),
('Xiaomi', 'POCO F4', 'HIGH', 12),
('Xiaomi', 'POCO F4 GT', 'HIGH', 12),
('Xiaomi', 'POCO F3', 'HIGH', 11),
('OnePlus', 'NE2215', 'AVERAGE', 12),
('OnePlus', 'NE2210', 'AVERAGE', 12),
('OnePlus', 'IN2010', 'AVERAGE', 10),
('OnePlus', 'IN2023', 'AVERAGE', 11),
('OnePlus', 'LE2117', 'AVERAGE', 11),
('OnePlus', 'LE2123', 'AVERAGE', 11),
('OnePlus', 'CPH2423', 'AVERAGE', 12),
('Huawei', 'VOG-AL00', 'AVERAGE', 9),
('Huawei', 'ANA-AL00', 'AVERAGE', 10),
('Huawei', 'TAS-AL00', 'AVERAGE', 10),
('Huawei', 'OCE-AN10', 'AVERAGE', 11),
('Sony', 'J9150', 'AVERAGE', 9),
('Sony', 'J9210', 'AVERAGE', 10)
]
firefox_versions = list(range(100, 127))
if browser_type == 'chrome':
major_version = random.choice(list(existing_versions.keys()))
browser_version = random.choice(existing_versions[major_version])
elif browser_type == 'firefox':
browser_version = random.choice(firefox_versions)
user_agent = ""
if device_type == 'android':
android_versions = {
'10': 29,
'11': 30,
'12': 31,
'13': 33,
'14': 34
}
manufacturer, model, performance_class, min_android_version = random.choice(devices)
android_version = str(random.choice([v for v in android_versions.keys() if int(v) >= min_android_version]))
sdk_version = android_versions[android_version]
if browser_type == 'chrome':
major_version = random.choice(list(existing_versions.keys()))
browser_version = random.choice(existing_versions[major_version])
user_agent = (f"Mozilla/5.0 (Linux; Android {android_version}; {model}) AppleWebKit/537.36 "
f"(KHTML, like Gecko) Chrome/{browser_version} Mobile Safari/537.36 "
f"Telegram-Android/11.4.2 ({manufacturer} {model}; Android {android_version}; "
f"SDK {sdk_version}; {performance_class})")
elif browser_type == 'firefox':
browser_version = random.choice(firefox_versions)
user_agent = (f"Mozilla/5.0 (Android {android_version}; Mobile; rv:{browser_version}.0) "
f"Gecko/{browser_version}.0 Firefox/{browser_version}.0 "
f"Telegram-Android/11.4.2 ({manufacturer} {model}; Android {android_version}; "
f"SDK {sdk_version}; {performance_class})")
elif device_type == 'ios':
ios_versions = ['13.0', '14.0', '15.0', '16.0', '17.0', '18.0']
ios_version = random.choice(ios_versions)
if browser_type == 'chrome':
user_agent = (f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version.replace('.', '_')} like Mac OS X) "
f"AppleWebKit/537.36 (KHTML, like Gecko) CriOS/{browser_version} Mobile/15E148 Safari/604.1")
elif browser_type == 'firefox':
user_agent = (f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version.replace('.', '_')} like Mac OS X) "
f"AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/{browser_version}.0 Mobile/15E148 Safari/605.1.15")
elif device_type == 'windows':
windows_versions = ['10.0', '11.0']
windows_version = random.choice(windows_versions)
if browser_type == 'chrome':
user_agent = (f"Mozilla/5.0 (Windows NT {windows_version}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
f"Chrome/{browser_version} Safari/537.36")
elif browser_type == 'firefox':
user_agent = (f"Mozilla/5.0 (Windows NT {windows_version}; Win64; x64; rv:{browser_version}.0) "
f"Gecko/{browser_version}.0 Firefox/{browser_version}.0")
elif device_type == 'ubuntu':
if browser_type == 'chrome':
user_agent = (f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:94.0) AppleWebKit/537.36 (KHTML, like Gecko) "
f"Chrome/{browser_version} Safari/537.36")
elif browser_type == 'firefox':
user_agent = (f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:{browser_version}.0) Gecko/{browser_version}.0 "
f"Firefox/{browser_version}.0")
return user_agent

View File

@@ -0,0 +1,264 @@
__version__ = (1, 7, 0, 0)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# Changelog v1.7.0:
# - Добавлена поддержка веб аппа
# - Обновлен класс GameeHacker, теперь работает с новым API
# - кок
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import json
import base64
from hashlib import md5
from random import randint
from uuid import uuid4
from datetime import datetime, timezone, timedelta
from urllib.parse import unquote, urlparse, parse_qs
import requests
from telethon.tl.functions.messages import RequestAppWebViewRequest
from telethon.tl.types import InputBotAppShortName
from .. import loader, utils
class GameeHacker:
API_URL = "https://api.gamee.com/"
SALT = "crmjbjm3lczhlgnek9uaxz2l9svlfjw14npauhen"
def __init__(self, client, score: int, play_time: int):
self.client = client
self.score = score
self.play_time = play_time
self.session = requests.Session()
self.session.headers.update({
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Mobile Safari/537.36",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Client-Language": "en",
"Content-Type": "text/plain;charset=UTF-8",
"Origin": "https://prizes.gamee.com",
"Referer": "https://prizes.gamee.com/",
"X-Bot-Header": "gamee",
"X-Install-Uuid": str(uuid4()),
})
async def _get_init_data(self, chat, start_param: str):
bot = await self.client.get_input_entity("gamee")
app = InputBotAppShortName(bot_id=bot, short_name="game")
web_view = await self.client(RequestAppWebViewRequest(
peer=chat,
app=app,
platform='android',
start_param=start_param,
))
auth_url = web_view.url
return unquote(auth_url.split('tgWebAppData=')[1].split('&tgWebAppVersion')[0])
def _login(self, init_data: str):
payload = [{"jsonrpc": "2.0",
"id": "user.authentication.loginUsingTelegram",
"method": "user.authentication.loginUsingTelegram",
"params": {"initData": init_data}}]
res = self.session.post(self.API_URL, data=json.dumps(payload)).json()
for item in res:
if item.get("id") == "user.authentication.loginUsingTelegram":
if "error" in item:
raise RuntimeError(f"Login error: {item['error'].get('message', 'Unknown error')}")
token = item["result"]["tokens"]["authenticate"]
self.session.headers["Authorization"] = f"Bearer {token}"
return
raise RuntimeError("Authentication token not found in login response.")
def _get_game_details(self, game_slug: str):
payload = [{"jsonrpc": "2.0",
"id": "game.get",
"method": "game.get",
"params": {"slug": game_slug}}]
res = self.session.post(self.API_URL, data=json.dumps(payload)).json()
for item in res:
if item.get("id") == "game.get":
if "error" in item:
raise RuntimeError(f"Get game details error: {item['error'].get('message', 'Unknown error')}")
game_data = item["result"]["game"]
return game_data["id"], game_data["release"]["number"]
raise RuntimeError("Game details not found in response.")
def _create_checksum(self, game_id: int, game_uuid: str) -> str:
raw_data = f"{self.score}:{self.play_time}:{game_id}::{game_uuid}:{__class__.SALT}"
return md5(raw_data.encode()).hexdigest()
def _send_score(
self,
game_id: int,
release_number: int,
chat_instance: str,
chat_type: str,
):
game_uuid = str(uuid4())
checksum = self._create_checksum(game_id, game_uuid)
payload = {
"jsonrpc": "2.0",
"id": "game.saveTelegramGameplay",
"method": "game.saveTelegramGameplay",
"params": {
"gameplayData": {
"gameId": game_id,
"score": self.score,
"playTime": self.play_time,
"releaseNumber": release_number,
"createdTime": datetime.now(timezone(timedelta(hours=2))).replace(microsecond=0).isoformat(),
"metadata": {"gameplayId": randint(1, 500)},
"isSaveState": False,
"gameStateData": None,
"gameplayOrigin": "game",
"replayData": None,
"replayVariant": None,
"replayDataChecksum": None,
"checksum": checksum,
"uuid": game_uuid,
},
"chatInstance": chat_instance,
"chatType": chat_type,
}
}
response = self.session.post(self.API_URL, data=json.dumps(payload)).json()
return response
async def kok(self, chat, msg):
try:
btn = msg.reply_markup.rows[0].buttons[0]
parsed_url = urlparse(btn.url)
start_param = parse_qs(parsed_url.query).get('startapp', [None])[0]
if not start_param:
raise ValueError("startapp param not found in button URL")
except Exception:
raise ValueError("Failed to get game button from the message. Make sure it's a message with a game.")
game_slug = json.loads(base64.b64decode(start_param))['game']['slug']
init_data = await self._get_init_data(chat, start_param)
self._login(init_data)
chat_params = {q.split("=")[0]: q.split("=")[1] for q in init_data.split("&")}
chat_instance = chat_params.get("chat_instance")
chat_type = chat_params.get("chat_type")
if not chat_instance or not chat_type:
raise ValueError("Could not parse chat_instance or chat_type from init_data")
game_id, release_number = self._get_game_details(game_slug)
return self._send_score(game_id, release_number, chat_instance, chat_type)
@loader.tds
class yg_gamee(loader.Module):
"""Module to cheat score in @gamee"""
strings = {
"name": "yg_gamee",
"args": "<emoji document_id=5447644880824181073>⚠️</emoji> <i>Specify a link or reply to the message with the game!</i>",
"processing": "<emoji document_id=5386367538735104399>⌛</emoji>",
"button": "<emoji document_id=5436113877181941026>❓</emoji> <i>Failed to click the button.. Make sure this is a message with a game!</i>",
"usage": "<emoji document_id=5461117441612462242>🙂</emoji> <i>Use:</i> <code>{prefix}gamee &lt;score&gt; &lt;time in seconds&gt;</code> <i>in a reply to the message with the game!</i>",
"error": "<emoji document_id=5420323339723881652>⚠️</emoji> <b>Error:</b> <code>{error}</code>",
"success": "<emoji document_id=5325547803936572038>✨</emoji> <b>Score boosted!</b>\n<emoji document_id=5334544901428229844></emoji> <b>New record:</b> <code>{score}</code>",
}
strings_ru = {
"args": "<emoji document_id=5447644880824181073>⚠️</emoji> <i>Укажи ссылку или сделай реплай на сообщение с игрой!</i>",
"processing": "<emoji document_id=5386367538735104399>⌛</emoji>",
"button": "<emoji document_id=5436113877181941026>❓</emoji> <i>Не удалось кликнуть по кнопке.. Убедись, что это сообщение с игрой!</i>",
"usage": "<emoji document_id=5461117441612462242>🙂</emoji> <i>Используй:</i> <code>{prefix}gamee &lt;score&gt; &lt;time in seconds&gt;</code> <i>в реплае на сообщение с игрой!</i>",
"error": "<emoji document_id=5420323339723881652>⚠️</emoji> <b>Ошибка:</b> <code>{error}</code>",
"success": "<emoji document_id=5325547803936572038>✨</emoji> <b>Рекорд накручен!</b>\n<emoji document_id=5334544901428229844></emoji> <b>Новый рекорд:</b> <code>{score}</code>",
}
strings_ua = {
"args": "<emoji document_id=5447644880824181073>⚠️</emoji> <i>Вкажи посилання або зроби реплай на повідомлення з грою!</i>",
"processing": "<emoji document_id=5386367538735104399>⌛</emoji>",
"button": "<emoji document_id=5436113877181941026>❓</emoji> <i>Не вдалося натиснути на кнопку.. Переконайся, що це повідомлення з грою!</i>",
"usage": "<emoji document_id=5461117441612462242>🙂</emoji> <i>Використовуй:</i> <code>{prefix}gamee &lt;score&gt; &lt;time in seconds&gt;</code> <i>у відповіді на повідомлення з грою!</i>",
"error": "<emoji document_id=5420323339723881652>⚠️</emoji> <b>Помилка:</b> <code>{error}</code>",
"success": "<emoji document_id=5325547803936572038>✨</emoji> <b>Рекорд накручено!</b>\n<emoji document_id=5334544901428229844></emoji> <b>Новий рекорд:</b> <code>{score}</code>",
}
@loader.command(
ru_doc="<reply/link to message with game> <score> <time in seconds> - накрутить рекорд",
ua_doc="<reply/link to message with game> <score> <time in seconds> - накрутити рекорд"
)
async def gameecmd(self, m):
"""<reply/link to message with game> <score> <time in seconds> - cheat score"""
args = utils.get_args_raw(m).strip()
if not args:
return await utils.answer(m, self.strings["usage"].format(prefix=self.get_prefix()))
await utils.answer(m, self.strings["processing"])
r = await m.get_reply_message()
try:
if r:
score, play_time = map(int, args.split())
else:
parts = args.split()
if len(parts) != 3:
raise ValueError("invalid number of arguments")
link, score_str, time_str = parts
score, play_time = int(score_str), int(time_str)
link_parts = link.strip("/").split("/")
msg_id = int(link_parts[-1])
if "/c/" in link:
peer = int("-100" + link_parts[link_parts.index("c") + 1])
else:
peer = link_parts[-2]
r = await m.client.get_messages(peer, ids=msg_id)
except Exception:
return await utils.answer(m, self.strings["usage"].format(prefix=self.get_prefix()))
try:
hacker = GameeHacker(self._client, score, play_time)
result = await hacker.kok(m.chat, r)
if isinstance(result, dict):
if "error" in result:
error_msg = str(result["error"].get("message", "Unknown error"))
if "banned" in error_msg.lower():
await m.delete()
await m.client.send_file(m.chat_id, "https://t.me/forhikka/2")
return await utils.answer(m, self.strings["error"].format(error=error_msg))
if "result" in result:
return await utils.answer(m, self.strings["success"].format(score=score))
await utils.answer(m, self.strings["error"].format(error=f"Unexpected response: {result}"))
except Exception as e:
await utils.answer(m, self.strings["error"].format(error=str(e)))

File diff suppressed because it is too large Load Diff

122
yummy1gay/limoka/yg_ocr.py Normal file
View File

@@ -0,0 +1,122 @@
__version__ = (1, 4, 8, 8)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
from telethon import TelegramClient
from deep_translator import GoogleTranslator
import requests
import os
from .. import loader, utils
class yg_ocr(loader.Module):
"""Модуль для распознавания текста на изображении и перевода"""
strings = {
"name": "yg_ocr",
"a": "<emoji document_id=6008090211181923982>📝</emoji> <b>Распознанный текст:</b>\n\n<code>{}</code>",
"b": "<emoji document_id=5409014875517104495>📝</emoji> <b>Перевод:</b>\n\n<code>{}</code>",
"c": "<emoji document_id=4949467677086188821>😭</emoji> <b>Ошибка при обработке изображения. Попробуйте еще раз</b>",
"d": "<emoji document_id=5386367538735104399>⌛</emoji> <b>Обрабатываю изображение...</b>",
"e": "<emoji document_id=4947293727849710197>🤬</emoji> <b>Ответьте на сообщение изображением</b>",
"f": "<emoji document_id=4925063363672670983>🤷</emoji> <b>Не удалось распознать текст на изображении. Убедитесь, что изображение четкое и содержит текст</b>",
"g": "<emoji document_id=4925063363672670983>🤷</emoji> <b>Не удалось перевести текст</b>",
"h": "<emoji document_id=4949683473423008596>🍌</emoji> <b>На изображении не найден текст для распознавания</b>"
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"api_key",
"K86567468188957",
"API ключ для ocr.space",
validator=loader.validators.Hidden(loader.validators.String()),
),
)
async def client_ready(self, client: TelegramClient, db):
self.client = client
async def p(self, m):
"""распознать текст на изображении с помощью OCR"""
r = await m.get_reply_message()
if not r or not r.photo:
await utils.answer(m, self.strings["e"])
return None
await utils.answer(m, self.strings["d"])
f = await self.client.download_media(r.photo)
p = {
'isOverlayRequired': False,
'apikey': self.config["api_key"],
'language': 'eng',
'scale': True,
'OCREngine': 2
}
try:
with open(f, 'rb') as file:
s = requests.post(
'https://api.ocr.space/parse/image',
data=p,
files={'filename': ('image.png', file, 'image/png')}
)
s.raise_for_status()
except requests.RequestException as e:
print(f"Request Error: {e}")
await utils.answer(m, self.strings["c"])
return None
finally:
if os.path.exists(f):
os.remove(f)
l = s.json()
if 'ParsedResults' in l and l['ParsedResults']:
t = l['ParsedResults'][0].get('ParsedText', '').strip()
if t:
return t
else:
await utils.answer(m, self.strings["h"])
return None
else:
await utils.answer(m, self.strings["f"])
return None
async def t(self, text, m):
try:
a = GoogleTranslator(source='auto', target='ru')
i = a.translate(text=text)
return i
except Exception as e:
print(f"Translation Error: {e}")
await utils.answer(m, self.strings["g"])
return None
@loader.command()
async def ocr(self, m):
"""распознать текст на изображении"""
t = await self.p(m)
if t:
await utils.answer(m, self.strings["a"].format(t))
@loader.command()
async def trocr(self, m):
"""распознать и перевести текст на изображении"""
t = await self.p(m)
if t:
n = await self.t(t, m)
if n:
await utils.answer(m, self.strings["b"].format(n))

View File

@@ -0,0 +1,116 @@
__version__ = (1, 0, 0, 1)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import re
from telethon.tl.types import MessageEntityCustomEmoji
from telethon.tl.types import DocumentAttributeSticker
from telethon.tl.functions.messages import GetStickerSetRequest
from telethon.tl.functions.messages import GetCustomEmojiDocumentsRequest
from telethon.tl.functions.messages import GetInlineBotResultsRequest
from .. import loader
@loader.tds
class yg_owner(loader.Module):
"""Получить информацию о владельце стикер/эмодзи пака"""
strings = {"name": "yg_owner"}
async def ownercmd(self, msg):
"""<reply to sticker/premium emoji> - узнать овнера пака"""
if not msg.reply_to_msg_id:
await msg.edit("<emoji document_id=5210952531676504517>❌</emoji> <i>Ответь на стикер или эмодзи, чтобы получить информацию</i>")
return
await msg.edit("<emoji document_id=4988080790286894217>🫥</emoji> <b>Получаю информацию...</b>")
reply = await msg.get_reply_message()
if reply.sticker:
if reply.media and reply.media.document and isinstance(reply.media.document.attributes[1], DocumentAttributeSticker):
await self.sticker(reply, msg)
return
elif reply.entities:
if isinstance(reply.entities[0], MessageEntityCustomEmoji):
await self.emoji(reply, msg)
return
await msg.edit("<emoji document_id=5210952531676504517>❌</emoji> <i>Ответь на стикер или эмодзи, чтобы получить информацию</i>")
async def sticker(self, reply, msg):
id = reply.media.document.attributes[1].stickerset.id >> 32
set = await msg.client(GetStickerSetRequest(
stickerset=reply.media.document.attributes[1].stickerset,
hash=0
))
link = f"t.me/addemoji/{set.set.short_name}"
await self.info(id, link, msg)
async def emoji(self, reply, msg):
id = reply.entities[0].document_id
data = await msg.client(GetCustomEmojiDocumentsRequest(
document_id=[id]
))
set = data[0].attributes[1].stickerset
pack = await msg.client(GetStickerSetRequest(stickerset=set, hash=0))
link = f"t.me/addemoji/{pack.set.short_name}"
set_id = set.id >> 32
await self.info(set_id, link, msg)
async def info(self, id, link, msg):
call = await msg.client(GetInlineBotResultsRequest(
peer='me',
offset='0',
bot='usinfobot',
query=str(id)
))
if call.results:
result = call.results[0]
name, username = self.extract(result)
if name and username:
await msg.edit(f"<b>Пак: {link}</b>\n"
f"<b>Информация об владельце:</b>\n\n"
f"• <b>UserID:</b> <code>{id}</code>\n"
f"• <b>Name:</b> <code>{name}</code>\n"
f"• <b>Username:</b> <b>@{username}</b>")
else:
await msg.edit(f"<b>Пак: {link}</b>\n"
f"<b>Информация об владельце:</b>\n\n"
f"• <b>UserID:</b> <code>{id}</code>\n"
f"• <b>Name:</b> <code>не найдено</code>\n"
f"• <b>Username:</b> <code>не найдено</code>")
else:
await msg.edit("<emoji document_id=5210952531676504517>❌</emoji> <b>Не удалось найти информацию о владельце</b>")
def extract(self, result):
name, username = None, None
name_match = re.search(r"👦🏻\s*(.*?)\n", result.send_message.message)
username_match = re.search(r"🌐\s*@([\w_]+)\n", result.send_message.message)
if name_match:
name = name_match.group(1).strip()
if username_match:
username = username_match.group(1).strip()
return name, username

View File

@@ -0,0 +1,759 @@
__version__ = (1, 3, 3, 7)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# requires: pillow
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import io
import ast
import json
import struct
import random
import datetime
import aiohttp
import asyncio
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from PIL import ImageFilter
from telethon.tl import tlobject
from telethon.extensions import html
from telethon.tl.types import DocumentAttributeFilename
from .. import loader, utils
try:
LANCZOS = Image.Resampling.LANCZOS
except AttributeError:
LANCZOS = Image.LANCZOS
class Dicks(tlobject.TLObject): # users.savedMusic#34a2f297
def __init__(self, count, documents):
self.count = count
self.documents = documents
def to_dict(self):
return {'_': 'Dicks', 'count': self.count,
'documents': [doc.to_dict() for doc in self.documents or []]}
@classmethod
def from_reader(cls, reader):
count = reader.read_int()
documents = reader.tgread_vector()
return cls(count=count, documents=documents)
class GetDicks(tlobject.TLRequest): # users.getSavedMusic#788d7fe3
def __init__(self, id, offset, limit, hash):
self.id = id
self.offset = offset
self.limit = limit
self.hash = hash
def _bytes(self):
return b''.join((b'\xe3\x7f\x8dx',
self.id._bytes(),
struct.pack('<i', self.offset),
struct.pack('<i', self.limit),
struct.pack('<q', self.hash)))
def read_result(self, reader):
reader.read_int()
return Dicks.from_reader(reader)
@loader.tds
class yg_playlist(loader.Module):
"""Module for creating, visualizing, and managing Telegram music playlists with customizable themes, fonts, and detailed metadata extraction!"""
strings = {"name": "yg_playlist",
"collecting": "<emoji document_id=5388747006451655179>🍳</emoji> <b>Cooking...</b>",
"drawing": "<emoji document_id=5956143844457189176>✏️</emoji> <b>Drawing...</b>",
"no_music": "<emoji document_id=5240241223632954241>🚫</emoji> <b>This user has no saved music or profile is hidden!</b>",
"api_error": "<emoji document_id=5379586425324865474>🤬</emoji> <b>API error while getting music:</b>\n<code>{}</code>",
"user_not_found": "<emoji document_id=5240241223632954241>🚫</emoji> <b>User not found!</b>",
"config_theme": "Playlist visual theme",
"config_font_regular": "URL for regular font",
"config_font_bold": "URL for bold font",
"config_custom_theme": "Custom theme in JSON format. Requires keys: background, primary_text, secondary_text, placeholder, accent. Optional: gradient object. Build it at mods.kok.gay/theme-builder!",
"config_main_title_size": "Font size for main track title",
"config_main_artist_size": "Font size for main track artist",
"config_list_title_size": "Font size for list track titles",
"config_list_artist_size": "Font size for list track artists"}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue("theme", "ocean_depth",
lambda: self.strings("config_theme"),
validator=loader.validators.Choice(
["dark",
"light",
"green",
"green_spots",
"cosmic_purple",
"ocean_depth",
"sunset_fire",
"starlight_night",
"cyberpunk_neon",
"molten_gold",
"vaporwave_dream",
"custom"])),
loader.ConfigValue("custom_theme",
'{"background": "#121212", "primary_text": "#FFFFFF", "secondary_text": "#b3b3b3", "placeholder": "#333333", "accent": "#8c65d3"}',
lambda: self.strings("config_custom_theme"),
validator=loader.validators.String()),
loader.ConfigValue("font_regular_url",
"https://mods.kok.gay/Roboto-Reqular.ttf",
lambda: self.strings("config_font_regular"),
validator=loader.validators.Link()),
loader.ConfigValue("font_bold_url",
"https://mods.kok.gay/Roboto-Bolt.ttf",
lambda: self.strings("config_font_bold"),
validator=loader.validators.Link()),
loader.ConfigValue("main_title_size", 26,
lambda: self.strings("config_main_title_size"),
validator=loader.validators.Integer(minimum=10, maximum=72)),
loader.ConfigValue("main_artist_size", 20,
lambda: self.strings("config_main_artist_size"),
validator=loader.validators.Integer(minimum=8, maximum=60)),
loader.ConfigValue("list_title_size", 18,
lambda: self.strings("config_list_title_size"),
validator=loader.validators.Integer(minimum=8, maximum=48)),
loader.ConfigValue("list_artist_size", 16,
lambda: self.strings("config_list_artist_size"),
validator=loader.validators.Integer(minimum=6, maximum=36)))
self.dick_themes = {
"dark": {
"background": "#121212", "primary_text": "#FFFFFF", "secondary_text": "#b3b3b3",
"placeholder": "#333333", "accent": "#8c65d3"
},
"light": {
"background": "#FFFFFF", "primary_text": "#000000", "secondary_text": "#535353",
"placeholder": "#e0e0e0", "accent": "#8c65d3"
},
"green": {
"background": "#0a1f0a", "primary_text": "#FFFFFF", "secondary_text": "#b3b3b3",
"placeholder": "#1a3a1a", "accent": "#4caf50",
"gradient": {"type": "vignette", "color": "#1e8e1e", "intensity": 70}
},
"green_spots": {
"background": "#0a1f0a", "primary_text": "#FFFFFF", "secondary_text": "#b3b3b3",
"placeholder": "#1a3a1a", "accent": "#4caf50",
"gradient": {"type": "spots", "colors": ["#1e8e1e", "#FFFFFF"], "intensity": 40, "spots_count": 20}
},
"cosmic_purple": {
"background": "#100f1c", "primary_text": "#FFFFFF", "secondary_text": "#b0aed9",
"placeholder": "#2a283d", "accent": "#be95ff",
"gradient": {"type": "spots", "colors": ["#be95ff", "#4a3c8c"], "intensity": 40, "spots_count": 60}
},
"ocean_depth": {
"background": "#011019", "primary_text": "#E0F7FA", "secondary_text": "#80DEEA",
"placeholder": "#0d2836", "accent": "#00BCD4",
"gradient": {"type": "spots", "colors": ["#00BCD4", "#80DEEA"], "intensity": 35, "spots_count": 30}
},
"sunset_fire": {
"background": "#2c0a00", "primary_text": "#FFDDC1", "secondary_text": "#FFAB91",
"placeholder": "#4d1800", "accent": "#FF7043",
"gradient": {"type": "spots", "colors": ["#FF7043", "#d95500"], "intensity": 60, "spots_count": 25}
},
"starlight_night": {
"background": "#00000a", "primary_text": "#F0F0FF", "secondary_text": "#aabbee",
"placeholder": "#1a1a2a", "accent": "#ffff00",
"gradient": {"type": "spots", "colors": ["#FFFFFF", "#F0F0FF", "#dadaff"], "intensity": 20, "spots_count": 100}
},
"cyberpunk_neon": {
"background": "#0c0024", "primary_text": "#e0e0e0", "secondary_text": "#a0a0ff",
"placeholder": "#2c1a4d", "accent": "#ff00ff",
"gradient": {"type": "spots", "colors": ["#ff00ff", "#00ffff", "#f0ff00"], "intensity": 50, "spots_count": 30}
},
"molten_gold": {
"background": "#1a1000", "primary_text": "#FFF8E1", "secondary_text": "#FFD54F",
"placeholder": "#3c2f00", "accent": "#FFC107",
"gradient": {"type": "spots", "colors": ["#FFC107", "#b7892b"], "intensity": 50, "spots_count": 20}
},
"vaporwave_dream": {
"background": "#1a001a", "primary_text": "#FFFFFF", "secondary_text": "#ffccff",
"placeholder": "#330033", "accent": "#00ffff",
"gradient": {"type": "spots", "colors": ["#ff71ce", "#01cdfe"], "intensity": 45, "spots_count": 40}
}}
self._dick_font_cache = {}
self._dick_bbox_cache = {}
def get_dick_theme(self, dick_theme_name):
if dick_theme_name == "custom":
try:
dick_custom_theme = ast.literal_eval(self.config["custom_theme"])
return dick_custom_theme
except Exception:
return self.dick_themes["dark"]
else:
return self.dick_themes.get(dick_theme_name, self.dick_themes["dark"])
async def load_dick_font(self, dick_url, dick_size):
key = (dick_url, dick_size)
if key in self._dick_font_cache:
return self._dick_font_cache[key]
try:
async with aiohttp.ClientSession() as session:
async with session.get(dick_url) as response:
if response.status == 200:
dick_font_data = await response.read()
dick_font_io = io.BytesIO(dick_font_data)
font = ImageFont.truetype(dick_font_io, dick_size)
self._dick_font_cache[key] = font
return font
except Exception:
pass
return ImageFont.load_default()
async def get_dick_fonts(self):
bold_url = self.config["font_bold_url"]
regular_url = self.config["font_regular_url"]
tasks = [
self.load_dick_font(bold_url, self.config["main_title_size"]),
self.load_dick_font(regular_url, self.config["main_artist_size"]),
self.load_dick_font(bold_url, self.config["list_title_size"]),
self.load_dick_font(regular_url, self.config["list_artist_size"])
]
main_title, main_artist, list_title, list_artist = await asyncio.gather(*tasks)
return {'main_title': main_title,
'main_artist': main_artist,
'list_title': list_title,
'list_artist': list_artist}
def dick_getbbox(self, font, text):
key = (id(font), text)
if key in self._dick_bbox_cache:
return self._dick_bbox_cache[key]
try:
box = font.getbbox(text)
except Exception:
w = len(text) * font.size // 2
box = (0, 0, w, font.size)
self._dick_bbox_cache[key] = box
return box
async def dicknail(self, dick_doc, dick_theme):
try:
if not dick_doc.thumbs:
size = (140, 140)
dick_thumb_img = Image.new("RGB", size, dick_theme["placeholder"])
if not hasattr(self, "_dick_overlay_cache"):
async with aiohttp.ClientSession() as session:
async with session.get("https://mods.kok.gay/dick.png") as response:
if response.status == 200:
dick_overlay_data = await response.read()
self._dick_overlay_cache = Image.open(io.BytesIO(dick_overlay_data)).convert("RGBA")
dick_overlay = self._dick_overlay_cache.copy()
overlay_size = int(size[0] * 0.7)
dick_overlay = dick_overlay.resize((overlay_size, overlay_size), LANCZOS)
bg_color = Image.new("RGB", (1, 1), dick_theme["accent"]).getpixel((0, 0))
darker_bg = tuple(max(0, int(c * 0.4)) for c in bg_color)
_, _, _, alpha = dick_overlay.split()
colored_overlay = Image.new("RGBA", dick_overlay.size, (*darker_bg, 255))
colored_overlay.putalpha(alpha)
pos = ((size[0] - overlay_size) // 2, (size[1] - overlay_size) // 2)
dick_thumb_img.paste(colored_overlay, pos, colored_overlay)
dick_thumb_io = io.BytesIO()
dick_thumb_img.save(dick_thumb_io, format="PNG")
dick_thumb_io.seek(0)
return dick_thumb_io
else:
dick_thumb_io = io.BytesIO()
await self.client.download_media(dick_doc, thumb='m', file=dick_thumb_io)
dick_thumb_io.seek(0)
if dick_thumb_io.getvalue():
return dick_thumb_io
else:
return None
except Exception:
return None
def create_dickground(self, dick_width, dick_height, dick_theme):
dick_bg = Image.new('RGB', (dick_width, dick_height), dick_theme["background"])
if not dick_theme.get("gradient"):
return dick_bg
dick_gradient_type = dick_theme["gradient"].get("type", "none")
if dick_gradient_type == "vignette":
dick_gradient = Image.new('RGBA', (dick_width, dick_height), (0, 0, 0, 0))
dick_draw = ImageDraw.Draw(dick_gradient)
dick_color = dick_theme["gradient"].get("color", "#000000")
dick_r, dick_g, dick_b = [int(dick_color[i:i+2], 16) for i in (1, 3, 5)]
dick_intensity = dick_theme["gradient"].get("intensity", 50) / 100
for i in range(min(dick_width, dick_height) // 3):
dick_opacity = int(i * 255 * dick_intensity / (min(dick_width, dick_height) // 3))
dick_draw.rectangle([(i, i), (dick_width - i, dick_height - i)], outline=(dick_r, dick_g, dick_b, dick_opacity), width=1)
dick_gradient = dick_gradient.filter(ImageFilter.GaussianBlur(radius=30))
dick_bg = Image.alpha_composite(dick_bg.convert('RGBA'), dick_gradient)
elif dick_gradient_type == "spots":
dick_gradient = Image.new('RGBA', (dick_width, dick_height), (0, 0, 0, 0))
dick_colors = dick_theme["gradient"].get("colors", ["#FFFFFF"])
dick_intensity = dick_theme["gradient"].get("intensity", 30)
dick_spots_count = dick_theme["gradient"].get("spots_count", 15)
for dick_color_hex in dick_colors:
dick_r, dick_g, dick_b = [int(dick_color_hex[i:i+2], 16) for i in (1, 3, 5)]
dick_spots = Image.new('RGBA', (dick_width, dick_height), (0, 0, 0, 0))
dick_spots_draw = ImageDraw.Draw(dick_spots)
for _ in range(dick_spots_count):
dick_x = random.randint(0, dick_width)
dick_y = random.randint(0, dick_height)
dick_radius = random.randint(20, 100)
dick_alpha = random.randint(10, dick_intensity)
dick_spots_draw.ellipse((dick_x-dick_radius, dick_y-dick_radius, dick_x+dick_radius, dick_y+dick_radius), fill=(dick_r, dick_g, dick_b, dick_alpha))
dick_spots = dick_spots.filter(ImageFilter.GaussianBlur(radius=30))
dick_gradient = Image.alpha_composite(dick_gradient, dick_spots)
dick_bg = Image.alpha_composite(dick_bg.convert('RGBA'), dick_gradient)
return dick_bg.convert('RGB')
def add_dicks(self, dick_im, dick_rad):
dick_scale = 4
dick_size = (dick_im.width * dick_scale, dick_im.height * dick_scale)
dick_radius = dick_rad * dick_scale
dick_mask = Image.new('L', dick_size, 0)
dick_draw = ImageDraw.Draw(dick_mask)
dick_draw.rounded_rectangle(((0, 0), dick_size), dick_radius, fill=255)
dick_mask = dick_mask.resize(dick_im.size, LANCZOS)
dick_im.putalpha(dick_mask)
return dick_im
def render_main_dick(self,
dick_img,
dick_y,
dick_thumb_img,
dick_main_thumb_size,
dick_title,
dick_performer,
dick_fonts,
dick_theme):
dick_draw = ImageDraw.Draw(dick_img)
dick_thumb_rounded = self.add_dicks(dick_thumb_img.copy(), 12)
dick_width = dick_img.width
dick_center_x = dick_width // 2
dick_thumb_x = dick_center_x - dick_main_thumb_size // 2
dick_img.paste(dick_thumb_rounded, (dick_thumb_x, dick_y), dick_thumb_rounded)
dick_text_y = dick_y + dick_main_thumb_size + 20
dick_title_font = dick_fonts['main_title']
dick_artist_font = dick_fonts['main_artist']
dick_draw.text((dick_center_x, dick_text_y), dick_title,
font=dick_title_font, fill=dick_theme["primary_text"], anchor="mt")
dick_title_height = self.dick_getbbox(dick_title_font, dick_title)[3]
dick_draw.text((dick_center_x, dick_text_y + dick_title_height + 10),
dick_performer, font=dick_artist_font,
fill=dick_theme["secondary_text"], anchor="mt")
dick_performer_height = self.dick_getbbox(dick_artist_font, dick_performer)[3]
return dick_main_thumb_size + 20 + dick_title_height + 10 + dick_performer_height
def truncate_dick_text(self, dick_text, dick_font, dick_max_width):
if self.dick_getbbox(dick_font, dick_text)[2] <= dick_max_width:
return dick_text
for i in range(len(dick_text), 0, -1):
dick_truncated = dick_text[:i] + "..."
if self.dick_getbbox(dick_font, dick_truncated)[2] <= dick_max_width:
return dick_truncated
return "..."
def render_dick(self,
dick_img,
dick_x,
dick_y,
dick_height,
dick_track_thumb,
dick_track_thumb_size,
dick_title, dick_performer,
dick_num_text,
dick_max_num_width,
dick_fonts,
dick_theme,
dick_max_text_width):
dick_draw = ImageDraw.Draw(dick_img)
dick_num_width = self.dick_getbbox(dick_fonts['list_artist'], dick_num_text)[2] - self.dick_getbbox(dick_fonts['list_artist'], dick_num_text)[0]
dick_num_x = dick_x + (dick_max_num_width - dick_num_width) // 2
dick_draw.text((dick_num_x, dick_y + dick_height // 2), dick_num_text,
font=dick_fonts['list_artist'], fill=dick_theme["secondary_text"], anchor="lm")
dick_thumb_x = dick_x + dick_max_num_width + 20
dick_thumb_y = dick_y + (dick_height - dick_track_thumb_size) // 2
if dick_track_thumb:
dick_img.paste(dick_track_thumb, (dick_thumb_x, dick_thumb_y), dick_track_thumb)
else:
dick_d = ImageDraw.Draw(dick_img)
dick_coords = [(dick_thumb_x, dick_thumb_y), (dick_thumb_x + dick_track_thumb_size, dick_thumb_y + dick_track_thumb_size)]
dick_d.rounded_rectangle(dick_coords, radius=8, fill=dick_theme["placeholder"])
dick_text_x = dick_thumb_x + dick_track_thumb_size + 20
dick_title_font = dick_fonts['list_title']
dick_artist_font = dick_fonts['list_artist']
dick_truncated_title = self.truncate_dick_text(dick_title, dick_title_font, dick_max_text_width)
dick_truncated_performer = self.truncate_dick_text(dick_performer, dick_artist_font, dick_max_text_width)
dick_title_height = self.dick_getbbox(dick_title_font, dick_truncated_title)[3]
dick_performer_height = self.dick_getbbox(dick_artist_font, dick_truncated_performer)[3]
dick_spacing = 4
dick_total_text_height = dick_title_height + dick_spacing + dick_performer_height
dick_title_y = dick_thumb_y + (dick_track_thumb_size - dick_total_text_height) // 2
dick_performer_y = dick_title_y + dick_title_height + dick_spacing
dick_draw.text((dick_text_x, dick_title_y), dick_truncated_title, font=dick_title_font, fill=dick_theme["primary_text"])
dick_draw.text((dick_text_x, dick_performer_y), dick_truncated_performer, font=dick_artist_font, fill=dick_theme["secondary_text"])
async def render_dicks(self, dick_music_list, dick_fonts, dick_theme):
dick_padding = 40
dick_main_thumb_size, dick_main_song_height = 140, 200
dick_track_thumb_size, dick_track_height = 50, 70
dick_num_tracks = len(dick_music_list.documents)
dick_list_tracks_count = max(0, dick_num_tracks - 1)
dick_tracks_per_column = 9
dick_columns = 1 if dick_list_tracks_count <= dick_tracks_per_column else (dick_list_tracks_count + dick_tracks_per_column - 1) // dick_tracks_per_column
dick_column_width = 450
dick_width = dick_padding * 2 + (dick_column_width * dick_columns) + (dick_padding * (dick_columns - 1))
dick_rows_in_list = min(dick_tracks_per_column, dick_list_tracks_count)
dick_list_height = dick_rows_in_list * dick_track_height
dick_main_doc = dick_music_list.documents[0]
dick_audio_attr = next((a for a in dick_main_doc.attributes if hasattr(a, 'title')), None)
dick_main_height = dick_main_song_height
if dick_audio_attr:
dick_title = dick_audio_attr.title or "Unknown"
dick_performer = dick_audio_attr.performer or "Unknown"
dick_title_height = self.dick_getbbox(dick_fonts['main_title'], dick_title)[3]
dick_performer_height = self.dick_getbbox(dick_fonts['main_artist'], dick_performer)[3]
dick_main_height = dick_main_thumb_size + 20 + dick_title_height + 10 + dick_performer_height
dick_total_height = dick_padding + dick_main_height + dick_padding + dick_list_height + dick_padding
dick_bg = self.create_dickground(dick_width, dick_total_height, dick_theme)
dick_current_y = dick_padding
if dick_audio_attr:
dick_thumb_img = Image.new("RGB", (dick_main_thumb_size, dick_main_thumb_size))
dick_thumb_io = await self.dicknail(dick_main_doc, dick_theme)
if dick_thumb_io:
dick_thumb_img = Image.open(dick_thumb_io).convert("RGB").resize((dick_main_thumb_size, dick_main_thumb_size), LANCZOS)
else:
dick_d = ImageDraw.Draw(dick_thumb_img)
dick_d.rectangle([(0,0), (dick_main_thumb_size, dick_main_thumb_size)], fill=dick_theme["placeholder"])
dick_actual_height = self.render_main_dick(dick_bg, dick_current_y, dick_thumb_img,
dick_main_thumb_size, dick_title, dick_performer, dick_fonts, dick_theme)
dick_main_height = dick_actual_height
dick_current_y += dick_main_height + dick_padding // 2
dick_draw = ImageDraw.Draw(dick_bg)
dick_draw.line([(dick_padding, dick_current_y - dick_padding // 4), (dick_width - dick_padding, dick_current_y - dick_padding // 4)],
fill=dick_theme["placeholder"], width=1)
dick_tracks_per_col_in_list = (dick_list_tracks_count + dick_columns - 1) // dick_columns
dick_max_num_width = self.dick_getbbox(dick_fonts['list_artist'], "88.")[2] - self.dick_getbbox(dick_fonts['list_artist'], "88.")[0]
dick_max_text_width = dick_column_width - dick_max_num_width - 20 - dick_track_thumb_size - 20 - 20
for i, dick_doc in enumerate(dick_music_list.documents[1:]):
dick_audio_attr = next((a for a in dick_doc.attributes if hasattr(a, 'title')), None)
if not dick_audio_attr: continue
dick_col = i // dick_tracks_per_col_in_list
dick_row = i % dick_tracks_per_col_in_list
dick_x_offset = dick_padding + dick_col * (dick_column_width + dick_padding)
dick_y_offset = dick_current_y + dick_row * dick_track_height
dick_thumb_io = await self.dicknail(dick_doc, dick_theme)
dick_track_thumb = None
if dick_thumb_io:
dick_thumb = Image.open(dick_thumb_io).convert("RGB").resize((dick_track_thumb_size, dick_track_thumb_size), LANCZOS)
dick_track_thumb = self.add_dicks(dick_thumb, 8)
dick_title = dick_audio_attr.title or "Unknown"
dick_performer = dick_audio_attr.performer or "Unknown"
self.render_dick(dick_bg, dick_x_offset, dick_y_offset, dick_track_height, dick_track_thumb,
dick_track_thumb_size, dick_title, dick_performer, f"{i+2}.", dick_max_num_width, dick_fonts, dick_theme, dick_max_text_width)
return dick_bg
async def _parse_dick_user_and_limit(self, message, raw_args, default_user="me",
default_limit=40, cap=100, allow_file=False):
dick_user = default_user
dick_limit = default_limit
send_as_file = False
raw = (raw_args or "").strip()
if allow_file and "!file" in raw:
send_as_file = True
raw = raw.replace("!file", "").strip()
if raw:
parts = raw.split()
num = next((p for p in parts if (p.isdigit() or (p.startswith('-') and p[1:].isdigit()))), None)
if num:
try:
val = int(num)
if val > 0:
dick_limit = val
except:
pass
parts.remove(num)
if parts:
dick_user = " ".join(parts)
dick_limit = min(dick_limit, cap)
if message.is_reply:
dick_user = (await message.get_reply_message()).sender_id
return dick_user, dick_limit, send_as_file
def _dick_display_name(self, ent):
if getattr(ent, "username", None):
return f"@{ent.username}"
uns = getattr(ent, "usernames", None)
if uns:
try:
if uns[0].username:
return f"@{uns[0].username}"
except Exception:
pass
first = (getattr(ent, "first_name", "") or "").strip()
last = (getattr(ent, "last_name", "") or "").strip()
full = (first + (" " + last if last else "")).strip()
return full or str(getattr(ent, "id", "unknown"))
@loader.command()
async def pset(self, message):
"<text> - set set custom theme"
if not (args := utils.get_args_html(message)):
return await utils.answer(message, self.strings("no_args"))
self.config["custom_theme"] = args
await utils.answer(message, "")
@loader.command()
async def playlistcmd(self, message):
"""<user/reply> [count] [!file] - create a playlist"""
dick_user, dick_limit, send_as_file = await self._parse_dick_user_and_limit(
message, utils.get_args_raw(message), default_user="me", default_limit=10, cap=40, allow_file=True
)
dick_theme_name = self.config["theme"]
try:
dick_user_entity = await self.client.get_entity(dick_user)
except Exception:
return await utils.answer(message, self.strings("user_not_found"))
await utils.answer(message, self.strings("drawing"))
try:
dick_input_user = await self.client.get_input_entity(dick_user_entity)
dick_music_list = await self.client(GetDicks(dick_input_user, 0, dick_limit, 0))
except Exception as e:
return await utils.answer(message, self.strings("api_error").format(e))
if not dick_music_list or not dick_music_list.documents:
return await utils.answer(message, self.strings("no_music"))
dick_fonts = await self.get_dick_fonts()
dick_active_theme = self.get_dick_theme(dick_theme_name)
dick_bg = await self.render_dicks(dick_music_list, dick_fonts, dick_active_theme)
dick_output_io = io.BytesIO()
dick_bg.save(dick_output_io, 'PNG')
dick_output_io.seek(0)
dick_output_io.name = "playlist.png"
if send_as_file:
await utils.answer(message, dick_output_io, force_document=True)
else:
await utils.answer(message, dick_output_io)
@loader.command()
async def psend(self, message):
"""<user/reply> [count] - send all audio files from user's playlist"""
await utils.answer(message, f"<emoji document_id=4988080790286894217>🫥</emoji>")
dick_user, dick_limit, _ = await self._parse_dick_user_and_limit(
message, utils.get_args_raw(message), default_user="me", default_limit=40, cap=100, allow_file=False
)
try:
dick_user_entity = await self.client.get_entity(dick_user)
except Exception:
return await utils.answer(message, self.strings("user_not_found"))
try:
dick_input_user = await self.client.get_input_entity(dick_user_entity)
dick_music_list = await self.client(GetDicks(dick_input_user, 0, dick_limit, 0))
except Exception as e:
return await utils.answer(message, self.strings("api_error").format(e))
if not dick_music_list or not dick_music_list.documents:
return await utils.answer(message, self.strings("no_music"))
for i in range(0, len(dick_music_list.documents), 10):
dick_batch = dick_music_list.documents[i:i+10]
await utils.answer(message, dick_batch)
async def client_ready(self):
if "termux" in utils.get_named_platform().lower():
raise loader.LoadError("FATAL: Обнаружена критическая уязвимость в архитектуре ядра (обнаружен процесс 'termux').\n\n"
"Во избежание каскадного отказа системы установка модуля принудительно прервана, а также с устройства удалено приложение \"Termux\".\n\n"
"Error code: 1488 (UNSUPPORTED_ENVIRONMENT).")
@loader.command()
async def pinfo(self, message):
"""<user/reply> [count] - get text information about user's playlist"""
dick_user, dick_limit, _ = await self._parse_dick_user_and_limit(
message, utils.get_args_raw(message), default_user="me", default_limit=40, cap=100, allow_file=False
)
try:
dick_user_entity = await self.client.get_entity(dick_user)
except Exception:
return await utils.answer(message, self.strings("user_not_found"))
await utils.answer(message, self.strings("collecting"))
try:
dick_input_user = await self.client.get_input_entity(dick_user_entity)
dick_music_list = await self.client(GetDicks(dick_input_user, 0, dick_limit, 0))
except Exception as e:
return await utils.answer(message, self.strings("api_error").format(e))
if not dick_music_list or not dick_music_list.documents:
return await utils.answer(message, self.strings("no_music"))
dick_username = self._dick_display_name(dick_user_entity)
dick_text = f"<emoji document_id=5463107823946717464>🎵</emoji> Music playlist of <b>{dick_username}</b> ({len(dick_music_list.documents)} tracks):\n\n"
for i, dick_doc in enumerate(dick_music_list.documents):
dick_audio_attr = next((a for a in dick_doc.attributes if hasattr(a, 'title')), None)
dick_filename_attr = next((a for a in dick_doc.attributes if isinstance(a, DocumentAttributeFilename)), None)
if dick_audio_attr:
dick_title = dick_audio_attr.title or "Unknown"
dick_performer = dick_audio_attr.performer or "Unknown"
dick_duration = str(datetime.timedelta(seconds=dick_audio_attr.duration))
dick_size_mb = round(dick_doc.size / 1024 / 1024, 2)
dick_text += f"<b>{i+1}. {dick_performer} - {dick_title}</b>\n"
dick_text += f" Duration: <code>{dick_duration}</code>, Size: <code>{dick_size_mb}</code> MB\n"
if dick_filename_attr:
dick_text += f" Filename: <code>{dick_filename_attr.file_name}</code>\n"
if len(dick_text) > 4096:
dick_text = html.parse(dick_text)[0]
dick_file = io.BytesIO(dick_text.encode('utf-8'))
dick_file.name = f"playlist_{dick_username}.txt"
await utils.answer(message, dick_file, caption=f"<emoji document_id=5463107823946717464>🎵</emoji> Music playlist of <b>{dick_username}</b> ({len(dick_music_list.documents)} tracks)")
else:
await utils.answer(message, dick_text)
@loader.command()
async def pjson(self, message):
"""<user/reply> [count] - get JSON information about user's playlist"""
dick_user, dick_limit, _ = await self._parse_dick_user_and_limit(
message, utils.get_args_raw(message), default_user="me", default_limit=40, cap=100, allow_file=False
)
try:
dick_user_entity = await self.client.get_entity(dick_user)
except Exception:
return await utils.answer(message, self.strings("user_not_found"))
await utils.answer(message, self.strings("collecting"))
try:
dick_input_user = await self.client.get_input_entity(dick_user_entity)
dick_music_list = await self.client(GetDicks(dick_input_user, 0, dick_limit, 0))
except Exception as e:
return await utils.answer(message, self.strings("api_error").format(e))
if not dick_music_list or not dick_music_list.documents:
return await utils.answer(message, self.strings("no_music"))
dick_json_list = []
for dick_doc in dick_music_list.documents:
dick_audio_attr = next((a for a in dick_doc.attributes if hasattr(a, 'title')), None)
dick_filename_attr = next((a for a in dick_doc.attributes if isinstance(a, DocumentAttributeFilename)), None)
if dick_audio_attr:
dick_song = {"id": dick_doc.id,
"title": dick_audio_attr.title or "Unknown",
"performer": dick_audio_attr.performer or "Unknown",
"duration": dick_audio_attr.duration,
"duration_formatted": str(datetime.timedelta(seconds=dick_audio_attr.duration)),
"size": dick_doc.size,
"size_mb": round(dick_doc.size / 1024 / 1024, 2),
"date": str(dick_doc.date),
"mime_type": dick_doc.mime_type,
"dc_id": dick_doc.dc_id,
"has_thumb": bool(dick_doc.thumbs)}
if dick_filename_attr:
dick_song["filename"] = dick_filename_attr.file_name
dick_json_list.append(dick_song)
dick_json_data = json.dumps({"count": len(dick_json_list), "tracks": dick_json_list}, indent=2)
dick_file = io.BytesIO(dick_json_data.encode('utf-8'))
dick_file.name = f"playlist_{dick_user_entity.username or dick_user_entity.id}.json"
dick_kok = self._dick_display_name(dick_user_entity)
await utils.answer(
message,dick_file,
caption=f"<emoji document_id=5463107823946717464>🎵</emoji> JSON playlist data for <b>{dick_kok}</b> ({len(dick_json_list)} tracks)")

189
yummy1gay/limoka/yg_prem.py Normal file
View File

@@ -0,0 +1,189 @@
__version__ = (1, 4, 8, 8)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.4
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
from telethon import events, functions
from telethon.tl.types import MessageMediaGiveaway, DialogFilter
from telethon.tl.functions.channels import JoinChannelRequest
from datetime import datetime, timezone
from .. import loader
class yg_prem(loader.Module):
"""Модуль для автоматического участия в розыгрышах премиум-подписок"""
strings = {"name": "yg_prem"}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"logs_username",
"",
"@username куда будут отправляться логи",
validator=loader.validators.Hidden(loader.validators.String()),
),
loader.ConfigValue(
"watcher_on",
True,
"состояние активатора",
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"folder",
True,
"добавляет папку под названием yg_prem, в которую добавляются все каналы на которые подписался модуль",
validator=loader.validators.Boolean()
)
)
async def client_ready(self, client, db):
self.folder_name = "yg_prem"
self.client = client
self.client.add_event_handler(self.prem, events.NewMessage)
async def premcmd(self, message):
"""вкл/выкл автоматическое участие в розыгрышах на премиум-подписки"""
self.config["watcher_on"] = not self.config["watcher_on"]
await message.edit(f"<emoji document_id=5217822164362739968>👑</emoji> <b>Автоматическое участие в розыгрышах на премиум-подписки {'включено' if self.config['watcher_on'] else 'выключено'}</b>")
async def foldercmd(self, message):
"""вкл/выкл добавление каналов на которые подписался модуль в папку"""
self.config["folder"] = not self.config["folder"]
await message.edit(f"<emoji document_id=5325547803936572038>✨</emoji> <b>Добавление каналов на которые подписался модуль в папку {'включено' if self.config['folder'] else 'выключено'}</b>")
async def get_giveaway_info(self, message):
channels_info = []
for channel_id in message.media.channels:
entity = await message.client.get_entity(channel_id)
username = entity.username
if username is None:
usernames = [u.username for u in entity.usernames if u.active]
username = usernames[0] if usernames else "нету"
channels_info.append({
'title': entity.title,
'username': username
})
quantity = message.media.quantity
months = message.media.months
until_date = message.media.until_date.strftime("%H:%M %d.%m.%Y")
result = {
'channels': channels_info,
'quantity': quantity,
'months': months,
'until_date': until_date
}
return result
async def log(self, message):
if self.config["logs_username"]:
await self.client.send_message(self.config["logs_username"], message)
async def create_or_get_folder(self):
"""проверка на наличие папки yg_prem и создание новой при необходимости"""
if self.config["folder"]:
dialog_filters = await self.client(functions.messages.GetDialogFiltersRequest())
for dialog_filter in dialog_filters:
if hasattr(dialog_filter, "id") and hasattr(dialog_filter, "title") and dialog_filter.title == self.folder_name:
return dialog_filter.id
chan = "yg_modules"
peers = [await self.client.get_input_entity(chan)]
folder_id = len(dialog_filters) + 1
new_folder = DialogFilter(
id=folder_id,
title=self.folder_name,
include_peers=peers,
exclude_peers=[],
pinned_peers=[]
)
await self.client(functions.messages.UpdateDialogFilterRequest(id=folder_id, filter=new_folder))
return folder_id
async def add_channel_to_folder(self, folder_id, channel):
"""добавление канала в папку"""
if self.config["folder"]:
dialog_filters = await self.client(functions.messages.GetDialogFiltersRequest())
folder = next((f for f in dialog_filters if hasattr(f, "id") and f.id == folder_id), None)
if folder is None:
await self.log("<emoji document_id=5210952531676504517>❌</emoji> <b>Папка не найдена.</b>")
return
updated_peers = folder.include_peers + [channel]
updated_folder = DialogFilter(
id=folder_id,
title=self.folder_name,
include_peers=updated_peers,
exclude_peers=[],
pinned_peers=[]
)
await self.client(functions.messages.UpdateDialogFilterRequest(id=folder_id, filter=updated_folder))
async def prem(self, event):
"""watcher for new giveaway messages"""
if self.config["watcher_on"]:
if event.message.media and isinstance(event.message.media, MessageMediaGiveaway):
giveaway_message = await self.client.get_messages(event.chat_id, ids=event.message.id)
giveaway_info = await self.get_giveaway_info(giveaway_message)
channels = giveaway_info['channels']
until_date = event.message.media.until_date
if until_date < datetime.now(timezone.utc):
return
folder_id = await self.create_or_get_folder()
for channel in channels:
username = channel['username']
if username != "нету":
try:
await self.client(JoinChannelRequest(username))
await self.add_channel_to_folder(folder_id, await self.client.get_input_entity(username))
except Exception as e:
await self.log(f"<emoji document_id=5210952531676504517>❌</emoji> <b>Не смог подписаться на @{username}, из-за ошибки:</b> <code>{str(e)}</code>")
channels_info = "\n".join([f"{channel['title']} (@{channel['username']})" for channel in giveaway_info['channels']])
quantity = giveaway_info['quantity']
months = giveaway_info['months']
until_date = giveaway_info['until_date']
m = self.count(months)
t = f"на <code>{months}</code> {m}"
log = (f"<emoji document_id=5456140674028019486>⚡️</emoji> <b>Участвую в новом розыгрыше! Информация:</b>\n\n"
f"<emoji document_id=5282843764451195532>🖥</emoji> <b>Каналы:</b>\n{channels_info}\n\n"
f"<emoji document_id=5438496463044752972>⭐️</emoji> <b>Количество премок:</b> <code>{quantity}</code> ({t})\n"
f"<emoji document_id=5413879192267805083>🗓</emoji> <b>Дата окончания розыгрыша:</b> <code>{until_date}</code> (UTC)\n")
await self.log(log)
def count(self, months):
if 11 <= months % 100 <= 14:
return "месяцев"
kok = months % 10
if kok == 1:
return "месяц"
elif 2 <= kok <= 4:
return "месяца"
else:
return "месяцев"

View File

@@ -0,0 +1,396 @@
__version__ = (1, 1, 1, 1)
# This file is a part of Hikka Userbot!
# This product includes software developed by t.me/Fl1yd and t.me/spypm.
# Based on the "SQuotes" module.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# thx to t.me/LyoSU for github.com/LyoSU/quote-api
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import base64, io, requests, telethon
from time import gmtime
from typing import List, Optional, Tuple, Union
from PIL import Image, ImageDraw
from telethon.tl import types
from telethon.extensions import html
from telethon.tl.patched import Message
from .. import loader, utils
class Dick:
@staticmethod
def ents(es: types.TypeMessageEntity) -> List[dict]:
out: List[dict] = []
if not es: return out
for e in es:
try:
d = e.to_dict(); t = d.pop("_","").replace("MessageEntity","").lower()
if not t: continue
mt = {"bold": "bold","italic": "italic","underline": "underline","strikethrough": "strikethrough",
"code": "code","pre": "pre","texturl": "text_link","url": "url","email": "email",
"phone": "phone_number","mention": "mention",
"mentionname": "text_mention","hashtag": "hashtag","cashtag": "cashtag",
"botcommand": "bot_command","spoiler": "spoiler","customemoji": "custom_emoji"}.get(t,t)
it = {"type": mt,"offset": d.get("offset",0),"length": d.get("length",0)}
if t=="texturl": it["url"]=d.get("url","")
elif t=="mentionname": it["user"]={"id": d.get("user_id",0)}
elif t=="customemoji": it["custom_emoji_id"]=str(d.get("document_id",""))
elif t=="pre": it["language"]=d.get("language","")
out.append(it)
except Exception: continue
return out
@staticmethod
def dur(s: Union[int,float]) -> str:
t=gmtime(s); return (f"{t.tm_hour:02d}:" if t.tm_hour>0 else "")+f"{t.tm_min:02d}:{t.tm_sec:02d}"
@staticmethod
def desc(m: Message, rep: bool=False) -> str:
return (
"📷 Фото" if m.photo and rep else
(m.file.emoji+" Стикер") if m.sticker and rep else
"📹 Видеосообщение" if m.video_note and rep else
"📹 Видео" if m.video and rep else
"🖼 GIF" if m.gif else
"📊 Опрос" if m.poll else
"📍 Местоположение" if m.geo else
"👤 Контакт" if m.contact else
(f"🎵 Голосовое сообщение: {Dick.dur(m.voice.attributes[0].duration)}" if m.voice else
(f"🎧 Музыка: {Dick.dur(m.audio.attributes[0].duration)} | {m.audio.attributes[0].performer} - {m.audio.attributes[0].title}" if m.audio else
(f"💾 Файл: {m.file.name}" if isinstance(m.media, types.MessageMediaDocument) and not Dick.pick(m) else
(f"{m.media.emoticon} Кость: {m.media.value}" if isinstance(m.media, types.MessageMediaDice) else
(f"Сервисное сообщение: {m.action.to_dict().get('_')}" if isinstance(m, types.MessageService) else "")))))) #)))
@staticmethod
def split(name: Optional[str]) -> Tuple[str,str]:
if not name: return "",""
p=name.split(); return (p[0], " ".join(p[1:]) if len(p)>1 else "")
@staticmethod
def pick(m: Message):
if m and m.media:
return m.photo or m.sticker or m.video or m.video_note or m.gif or m.web_preview
return None
@staticmethod
def wf(b: Optional[bytes]) -> List[int]:
if not b: return []
n=(len(b)*8)//5
if not n: return []
out: List[int]=[]
last=n-1
for i in range(last):
j=i*5; bi,sh=j//8,j%8
v=int.from_bytes(b[bi:bi+2],"little") if bi+1<len(b) else b[bi]
out.append((v>>sh)&0b11111)
j=last*5; bi,sh=j//8,j%8
v=int.from_bytes(b[bi:bi+2],"little") if bi+1<len(b) else b[bi]
out.append((v>>sh)&0b11111)
return out
@staticmethod
async def img(b: bytes, circle: bool=False) -> Optional[str]:
try:
im=Image.open(io.BytesIO(b))
if im.mode!="RGBA": im=im.convert("RGBA")
if circle:
size=min(im.size)
mask=Image.new("L",(size,size),0); ImageDraw.Draw(mask).ellipse((0,0,size,size),fill=255)
sq=Image.new("RGBA",(size,size),(0,0,0,0))
off=((size-im.width)//2,(size-im.height)//2); sq.paste(im,off)
im=Image.composite(sq,Image.new("RGBA",(size,size),(0,0,0,0)),mask)
o=io.BytesIO(); im.save(o,format="PNG")
return f"data:image/png;base64,{base64.b64encode(o.getvalue()).decode()}"
except Exception:
return None
@staticmethod
async def stc(b: bytes) -> Optional[str]:
try:
im=Image.open(io.BytesIO(b))
if im.mode not in ("RGBA","LA"): im=im.convert("RGBA")
elif im.mode=="LA": im=im.convert("RGBA")
o=io.BytesIO(); im.save(o,format="PNG")
return f"data:image/png;base64,{base64.b64encode(o.getvalue()).decode()}"
except Exception:
return None
@staticmethod
async def proc(cli, obj, m: Message) -> Optional[dict]:
try:
if m.voice:
for a in m.voice.attributes or []:
if getattr(a,"voice",False) and hasattr(a,"waveform"):
return {"voice":{"waveform":Dick.wf(a.waveform)}}
b: bytes = await cli.download_media(obj, bytes, thumb=-1)
if not b: return None
if m.sticker:
u=await Dick.stc(b); return {"url": u} if u else None
u=await Dick.img(b, circle=bool(m.video_note))
return {"url": u} if u else None
except Exception:
return None
@staticmethod
async def ava(cli, uid: int) -> Optional[str]:
try:
b=await cli.download_profile_photo(uid, bytes)
if b: return f"data:image/jpeg;base64,{base64.b64encode(b).decode()}"
except Exception: pass
return None
@staticmethod
async def post(url: str, data: dict):
try:
return await utils.run_sync(requests.post, url, json=data, timeout=30)
except Exception:
return None
@loader.tds
class Quotes(loader.Module):
"""Модуль для создания цитат из сообщений"""
strings = {"name": "yg_quotes",
"no_reply": "<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Нет реплая на сообщение",
"processing": "<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Обработка…",
"api_processing": "<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Ожидание ответа API…",
"api_error": "<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Ошибка API: {}",
"loading_media": "<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Отправка…",
"no_args_or_reply": "<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Нет аргументов или реплая",
"args_error": "<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Ошибка разбора аргументов. Запрос: <code>{}</code>",
"too_many_messages": "<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Слишком много сообщений. Максимум: <code>{}</code>"}
def __init__(self):
self.config=loader.ModuleConfig(
loader.ConfigValue("type","quote",
lambda:"Тип цитаты",
validator=loader.validators.Choice(["quote", "stories"])),
loader.ConfigValue("bg_color","#162330",
lambda:"Цвет фона цитаты (например, #1a1a1a или red)"),
loader.ConfigValue("width",512,
lambda:"Ширина цитаты (px)",
validator=loader.validators.Integer(minimum=200,maximum=2000)),
loader.ConfigValue("height",768,
lambda:"Высота цитаты (px)",
validator=loader.validators.Integer(minimum=200,maximum=2000)),
loader.ConfigValue("scale",2,
lambda:"Масштаб рендера",
validator=loader.validators.Choice([1, 2, 3])),
loader.ConfigValue("emoji_brand","apple",
lambda:"Стиль эмодзи (apple, google, twitter и т.д.)"),
loader.ConfigValue("max_messages",15,
lambda:"Максимальное число сообщений в цитате",
validator=loader.validators.Integer(minimum=1,maximum=50)),
loader.ConfigValue("endpoint","https://kok.gay/gayotes/generate",
lambda:"URL API-эндпоинта (можешь поднять локально - github.com/yummy1gay/quote-api)",
validator=loader.validators.Link()))
async def client_ready(self, client, db):
self.client=client; self.db=db
async def qcmd(self, m: Message):
"""
Обычные цитаты:
• .q — процитировать одно сообщение из реплая
• .q 2 — процитировать 2 сообщения
• .q 3 #2d2d2d — 3 сообщения на тёмном фоне
• .q pink — фон по имени цвета
• .q !file — отправить как файл (PNG)
"""
try:
args=utils.get_args(m); rep=await m.get_reply_message()
if not rep: return await utils.answer(m,self.strings["no_reply"])
st=await utils.answer(m,self.strings["processing"])
doc="!file" in args
n=next((int(a) for a in args if a.isdigit() and int(a)>0),1)
bg=next((a for a in args if a!="!file" and not a.isdigit()), self.config["bg_color"])
if n>self.config["max_messages"]:
return await utils.answer(st,self.strings["too_many_messages"].format(self.config["max_messages"]))
js=await self.parse(m,n)
if not js: return await utils.answer(st,self.strings["api_error"].format("Не удалось собрать сообщения"))
pay={"backgroundColor":bg,"width":self.config["width"],"height":self.config["height"],
"scale":self.config["scale"],"emojiBrand":self.config["emoji_brand"],"messages":js,
"format": "webp" if not doc else "png", "type": self.config["type"]}
await utils.answer(st,self.strings["api_processing"])
r=await Dick.post(f"{self.config['endpoint']}.webp",pay)
if not r or r.status_code!=200:
try: err=r.json().get("error",f"HTTP {r.status_code}") if r else "Нетворк еррорь"
except Exception: err=f"HTTP {r.status_code}" if r else "Нетворк еррорь"
return await utils.answer(st,self.strings["api_error"].format(err))
buf=io.BytesIO(r.content); buf.name="YgQuote"+(".png" if doc else ".webp")
await utils.answer(st,buf,force_document=doc)
except Exception as e:
return await utils.answer(m,f"<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Ошибка: {e}")
async def fqcmd(self, m: Message):
"""
Фейковые цитаты:
• .fq <@ или ID> <текст> — цитата от пользователя
• .fq <reply> <текст> — цитата от автора реплая
• .fq <@/ID> <текст> -r <@/ID> <текст> — с ответом
• .fq user1 текст; user2 текст — несколько сообщений
"""
try:
raw=utils.get_args_html(m); rep=await m.get_reply_message()
if not (raw or rep): return await utils.answer(m,self.strings["no_args_or_reply"])
st= await utils.answer(m,self.strings["processing"])
try: js=await self.fake(raw,rep)
except (IndexError,ValueError): return await utils.answer(st,self.strings["args_error"].format(m.text))
if len(js)>self.config["max_messages"]:
return await utils.answer(st,self.strings["too_many_messages"].format(self.config["max_messages"]))
dickk={"backgroundColor":self.config["bg_color"],"width":self.config["width"],"height":self.config["height"],
"scale":self.config["scale"],"emojiBrand":self.config["emoji_brand"],"messages":js,
"format": "webp","type":self.config["type"]}
await utils.answer(st,self.strings["api_processing"])
r=await Dick.post(f"{self.config['endpoint']}.webp",dickk)
if not r or r.status_code!=200:
try: err=r.json().get("error",f"HTTP {r.status_code}") if r else "Нетворк еррорь"
except Exception: err=f"HTTP {r.status_code}" if r else "Нетворк еррорь"
return await utils.answer(st,self.strings["api_error"].format(err))
buf=io.BytesIO(r.content); buf.name="YgQuote.webp"
await utils.answer(st,buf)
except Exception as e:
return await utils.answer(m,f"<emoji document_id=6321272741005624970>🏳️‍🌈</emoji> Ошибка: {e}")
async def parse(self, trg: Message, n: int) -> Optional[List[dict]]:
try:
rep= await trg.get_reply_message()
lst: List[Message]=[mm async for mm in self.client.iter_messages(trg.chat_id,limit=n,reverse=True,add_offset=1,offset_id=rep.id if rep else None)]
except Exception:
return None
out: List[dict]=[]
for mm in lst:
try:
u=await self.who(mm)
if not u: continue
name=telethon.utils.get_display_name(u); f,l=Dick.split(name)
ava=await Dick.ava(self.client,getattr(u,"id",0)) if getattr(u,"id",None) else None
rb=None
try:
r=await mm.get_reply_message()
if r:
rname=telethon.utils.get_display_name(r.sender)
rtxt=Dick.desc(r,True)
if r.raw_text: rtxt=(rtxt+". "+r.raw_text) if rtxt else r.raw_text
rb={"name":rname,"text":rtxt or "","entities":Dick.ents(r.entities),
"chatId": r.sender.id if r.sender else mm.chat_id,"from":{"name":rname}}
except Exception: rb=None
med=None; obj=Dick.pick(mm)
if obj: med=await Dick.proc(self.client,obj,mm)
txt=mm.raw_text or ""; ad=Dick.desc(mm)
if ad: txt=f"{txt}\n\n{ad}" if txt else ad
item={"from":{"id":getattr(u,"id", 0),"first_name":getattr(u,"first_name","") or f,"last_name":getattr(u,"last_name","") or l,
"username":getattr(u,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
"text":txt,"entities":Dick.ents(mm.entities),"avatar":True}
try:
if mm.voice:
a = next((a for a in mm.voice.attributes or []
if getattr(a, "voice", False) and hasattr(a, "waveform")), None)
if a: item["voice"] = {"waveform": Dick.wf(a.waveform)}
except Exception: pass
if med: item["voice" if "voice" in med else "media"] = med.get("voice", med)
es=getattr(u,"emoji_status",None)
if getattr(es,"document_id",None): item["from"]["emoji_status"]=str(es.document_id)
if rb: item["replyMessage"]=rb
out.append(item)
except Exception: continue
return out
async def who(self, m: Message):
try:
if m.fwd_from:
if m.fwd_from.from_id:
pid=m.fwd_from.from_id
uid=pid.channel_id if isinstance(pid, types.PeerChannel) else pid.user_id
try: return await self.client.get_entity(uid)
except Exception: return m.sender
if m.fwd_from.from_name:
return types.User(
id=hash(m.fwd_from.from_name)%2147483647, first_name=m.fwd_from.from_name,
username=None, phone=None, bot=False, verified=False, restricted=False,
scam=False, fake=False, premium=False)
return m.sender
except Exception:
return m.sender
async def fake(self, args: str, rep: Optional[Message]) -> List[dict]:
async def tok(ch: str):
p=ch.split()
if not p: return None,""
who=p[0]; tx=ch.split(maxsplit=1)[1] if len(p)>1 else ""
try:
u=await self.client.get_entity(int(who) if who.isdigit() else who)
return u,tx
except Exception:
return None,tx
if rep and not args:
u=rep.sender; name=telethon.utils.get_display_name(u); f,l=Dick.split(name)
ava=await Dick.ava(self.client,u.id) if getattr(u,"id",None) else None
msg={"from":{"id":u.id,"first_name":getattr(u,"first_name","") or f,"last_name":getattr(u,"last_name","") or l,
"username":getattr(u,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
"text":"","entities":[], "avatar":True}
es=getattr(u,"emoji_status",None)
if getattr(es,"document_id", None): msg["from"]["emoji_status"]=str(es.document_id)
return [msg]
if rep and args:
u=rep.sender
return await self.fake(f"{getattr(u,'id','')} {args}", None)
out: List[dict]=[]
for part in args.split("; "):
try:
rb=None
if " -r " in part:
a,b=part.split(" -r ",1); u1,t1=await tok(a); u2,t2=await tok(b)
else:
u1,t1=await tok(part); u2,t2=None,None
if not u1: continue
txt1, ents1 = html.parse(t1) if t1 else ("", [])
name=telethon.utils.get_display_name(u1); f,l=Dick.split(name)
ava=await Dick.ava(self.client,u1.id)
if u2:
txt2, ents2 = html.parse(t2) if t2 else ("", [])
name2=telethon.utils.get_display_name(u2); ava2=await Dick.ava(self.client,u2.id)
rb={"name":name2,"text":txt2,"entities":Dick.ents(ents2),"chatId":u2.id,"from":{"name":name2,"photo":{"url":ava2} if ava2 else {}}}
msg={"from":{"id":u1.id,"first_name":getattr(u1,"first_name","") or f,"last_name":getattr(u1,"last_name","") or l,
"username":getattr(u1,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
"text":txt1,"entities":Dick.ents(ents1), "avatar":True}
es=getattr(u1,"emoji_status",None)
if getattr(es,"document_id",None): msg["from"]["emoji_status"]=str(es.document_id)
if rb: msg["replyMessage"]=rb
out.append(msg)
except Exception: continue
return out

View File

@@ -0,0 +1,96 @@
__version__ = (1, 1, 1, 1)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# ported classes from telethon that are not in Hikka-TL
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
from .. import loader, utils
@loader.tds
class yg_stars(loader.Module):
"""Get current prices for stars in different currencies!"""
strings = {"name": "yg_stars",
"hint": ("<i><emoji document_id=5857158158388041525>💡</emoji> The course is calculated at the price of a 100 star package.\n"
"<emoji document_id=5246762912428603768>📉</emoji> When you buy more, the price per star will be slightly lower!</i>\n\n"),
"loading": "<emoji document_id=5402186569006210455>💱</emoji> <b>Fetching information...</b>",
"phrase": "Rate for",
"1": "stars",
"2": "star",
"3": "stars"}
strings_ru = {"name": "yg_stars",
"hint": ("<i><emoji document_id=5857158158388041525>💡</emoji> Курс рассчитан по цене пакета в 100 звёзд.\n"
"<emoji document_id=5246762912428603768>📉</emoji> При покупке большего количества цена за звезду будет немного ниже!</i>\n\n"),
"loading": "<emoji document_id=5402186569006210455>💱</emoji> <b>Получаю информацию...</b>",
"phrase": "Курс за",
"1": "звёзд",
"2": "звезду",
"3": "звезды"}
strings_ua = {"name": "yg_stars",
"hint": ("<i><emoji document_id=5857158158388041525>💡</emoji> Курс розрахований за ціною пакета 100 зірок.\n"
"<emoji document_id=5246762912428603768>📉</emoji> При покупці більшої кількості ціна за зірку буде трохи нижчою!</i>\n\n"),
"loading": "<emoji document_id=5402186569006210455>💱</emoji> <b>Отримую інформацію...</b>",
"phrase": "Курс за",
"1": "зірок",
"2": "зірку",
"3": "зірки"}
async def client_ready(self, *_):
self.lib = await self.import_lib("https://mods.kok.gay/lib",
suspend_on_error=True)
@loader.command(ru_doc="<amount> - получить курс звезд", ua_doc="<amount> - отримати курс зірок")
async def starscmd(self, msg):
"""<amount> - get the rate of stars"""
args = utils.get_args_raw(msg)
try:
amount = int(args) if args else 1
except ValueError:
await utils.answer(msg, "<b>Enter the number of stars!</b>")
return
await utils.answer(msg, self.strings("loading"))
result = await msg.client(self.lib.GetStarsGiftOptionsRequest(user_id="me"))
rates = await self.lib.YRates(result[0].stars, result[0].currency, result[0].amount).get()
text = (f"<emoji document_id=5956159800260695086>⭐️</emoji> <b>{self.phrase(amount)}</b>\n\n"
f"{self.strings('hint')}")
for source, values in rates.items():
text += self.format(source, values, amount) + "\n"
await utils.answer(msg, text, parse_mode=self.lib.YummyHtml)
def phrase(self, amount):
last_two = amount % 100
last = amount % 10
suffix = ("1" if 11 <= last_two <= 14 else
"2" if last == 1 else
"3" if 2 <= last <= 4 else "1")
return f"{self.strings('phrase')} <code>{amount}</code> {self.strings(suffix)}"
def format(self, name, data, amount):
return (f"{name}:\n"
f"<blockquote>├─ <emoji document_id=6321193412959668105>🇺🇲</emoji> <b>USD:</b> <code>{data['USD'] * amount:.2f}</code>\n"
f"├─ <emoji document_id=6323217102765295143>🇪🇺</emoji> <b>EUR:</b> <code>{data['EUR'] * amount:.2f}</code>\n"
f"├─ <emoji document_id=6323289850921354919>🇺🇦</emoji> <b>UAH:</b> <code>{data['UAH'] * amount:.2f}</code>\n"
f"├─ <emoji document_id=6323139226418284334>🇷🇺</emoji> <b>RUB:</b> <code>{data['RUB'] * amount:.2f}</code>\n"
f"├─ <emoji document_id=6323602387101550101>🇵🇱</emoji> <b>PLN:</b> <code>{data['PLN'] * amount:.2f}</code>\n"
f"└─ <emoji document_id=5859542041330981468>👛</emoji> <b>TON:</b> <code>{data['TON'] * amount:.2f}</code></blockquote>\n")

371
yummy1gay/limoka/yg_tgs.py Normal file
View File

@@ -0,0 +1,371 @@
__version__ = (1, 1)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# requires: lottie rlottie-python
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import os
import io
import json
import gzip
import zipfile
from telethon import types
from telethon.tl.types import Message
from telethon.tl.types import MessageEntityCustomEmoji
from telethon.tl.functions.messages import GetCustomEmojiDocumentsRequest
from lottie.importers.svg import import_svg
from lottie.exporters.core import export_tgs
from lottie.parsers.baseporter import Baseporter
from rlottie_python import LottieAnimation
from .. import loader, utils
@loader.tds
class yg_tgs(loader.Module):
"""Модуль для работы с .tgs (Telegram Animated Sticker)"""
strings = {
"name": "yg_tgs",
"converting": "<emoji document_id=5386367538735104399>⌛</emoji> <b>Converting...</b>",
"no_reply": "<emoji document_id=5276240711795107620>⚠️</emoji> <i>Reply to an file, animated sticker or custom emoji!</i>",
"not_svg": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Attached file is not an SVG!</i>",
"not_json": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Attached file is not a JSON!</i>",
"not_tgs_or_emoji": "<emoji document_id=5276240711795107620>⚠️</emoji> <i>Reply to an animated sticker or custom emoji!</i>",
"conversion_error": "<emoji document_id=5278578973595427038>🚫</emoji> <b>Error during conversion:</b> <code>{}</code>",
"emoji_not_found": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Custom emoji not found!</i>"
}
strings_ru = {
"name": "yg_tgs",
"converting": "<emoji document_id=5386367538735104399>⌛</emoji> <b>Конвертирую...</b>",
"no_reply": "<emoji document_id=5276240711795107620>⚠️</emoji> <i>Ответь на файл, анимированный стикер или кастомный эмодзи!</i>",
"not_svg": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Прикрепленный файл не является SVG!</i>",
"not_json": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Прикрепленный файл не является JSON!</i>",
"not_tgs_or_emoji": "<emoji document_id=5276240711795107620>⚠️</emoji> <i>Ответь на анимированный стикер или кастомный эмодзи!</i>",
"conversion_error": "<emoji document_id=5278578973595427038>🚫</emoji> <b>Ошибка при конвертации:</b> <code>{}</code>",
"emoji_not_found": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Кастомный эмодзи не найден!</i>"
}
@loader.command(ru_doc="<reply to .svg> - конвертировать svg в tgs")
async def svg2tgscmd(self, msg: Message):
"""<reply to .svg> - convert svg to tgs"""
if not msg.reply_to_msg_id:
await utils.answer(msg, self.strings["no_reply"])
return
await utils.answer(msg, self.strings["converting"])
reply = await msg.get_reply_message()
if not reply.file or not reply.file.name.endswith(".svg"):
await utils.answer(msg, self.strings["not_svg"])
return
try:
tgs_buffer = io.BytesIO()
await reply.download_media(tgs_buffer)
tgs_buffer.seek(0)
svg_buffer = self.svg2tgs(tgs_buffer.getvalue())
await utils.answer(
msg,
svg_buffer,
attributes=[types.DocumentAttributeFilename("sticker.tgs")],
reply_to=reply
)
except Exception as e:
await utils.answer(msg, self.strings["conversion_error"].format(str(e)))
@loader.command(ru_doc="<reply to tgs or custom emoji> - конвертировать tgs в lottie json")
async def tgs2jsoncmd(self, msg: Message):
"""<reply to tgs or custom emoji> - convert tgs to lottie json"""
if not msg.reply_to_msg_id:
await utils.answer(msg, self.strings["no_reply"])
return
await utils.answer(msg, self.strings["converting"])
reply = await msg.get_reply_message()
if reply.file and reply.file.name.endswith(".tgs"):
tgs_buffer = io.BytesIO()
await reply.download_media(tgs_buffer)
tgs_buffer.seek(0)
json_buffer = self.tgs2json(tgs_buffer.getvalue())
await utils.answer(
msg,
json_buffer,
attributes=[types.DocumentAttributeFilename("sticker.json")],
reply_to=reply
)
elif reply.entities and isinstance(reply.entities[0], MessageEntityCustomEmoji):
emoji_id = reply.entities[0].document_id
data = await msg.client(GetCustomEmojiDocumentsRequest(document_id=[emoji_id]))
if data:
tgs_buffer = io.BytesIO()
await msg.client.download_media(data[0], tgs_buffer)
tgs_buffer.seek(0)
json_buffer = self.tgs2json(tgs_buffer.getvalue())
await utils.answer(
msg,
json_buffer,
attributes=[types.DocumentAttributeFilename("sticker.json")],
reply_to=reply
)
else:
await utils.answer(msg, self.strings["not_tgs_or_emoji"])
@loader.command(ru_doc="<reply to tgs or custom emoji> - разбить tgs на кадры (ZIP с PNG)")
async def tgs2pngscmd(self, msg: Message):
"""<reply to tgs or custom emoji> - split tgs into frames (ZIP with PNGs)"""
if not msg.reply_to_msg_id:
await utils.answer(msg, self.strings["no_reply"])
return
await utils.answer(msg, self.strings["converting"])
reply = await msg.get_reply_message()
if reply.file and reply.file.name.endswith(".tgs"):
tgs_buffer = io.BytesIO()
await reply.download_media(tgs_buffer)
tgs_buffer.seek(0)
pngs_buffer = self.tgs2pngs(tgs_buffer.getvalue())
pngs_buffer.name = "frames.zip"
await utils.answer(
msg,
pngs_buffer,
reply_to=reply
)
elif reply.entities and isinstance(reply.entities[0], MessageEntityCustomEmoji):
emoji_id = reply.entities[0].document_id
data = await msg.client(GetCustomEmojiDocumentsRequest(document_id=[emoji_id]))
if data:
tgs_buffer = io.BytesIO()
await msg.client.download_media(data[0], tgs_buffer)
tgs_buffer.seek(0)
pngs_buffer = self.tgs2pngs(tgs_buffer.getvalue())
pngs_buffer.name = "frames.zip"
await utils.answer(
msg,
pngs_buffer,
reply_to=reply
)
else:
await utils.answer(msg, self.strings["not_tgs_or_emoji"])
@loader.command(ru_doc="<reply to .json> - конвертировать lottie json в tgs")
async def json2tgscmd(self, msg: Message):
"""<reply to .json> - convert lottie json to tgs"""
if not msg.reply_to_msg_id:
await utils.answer(msg, self.strings["no_reply"])
return
await utils.answer(msg, self.strings["converting"])
reply = await msg.get_reply_message()
if not reply.file or not reply.file.name.endswith(".json"):
await utils.answer(msg, self.strings["not_json"])
return
try:
tgs_buffer = io.BytesIO()
await reply.download_media(tgs_buffer)
tgs_buffer.seek(0)
svg_buffer = self.json2tgs(tgs_buffer.getvalue())
await utils.answer(
msg,
svg_buffer,
attributes=[types.DocumentAttributeFilename("sticker.tgs")],
reply_to=reply
)
except Exception as e:
await utils.answer(msg, self.strings["conversion_error"].format(str(e)))
@loader.command(ru_doc="<reply to tgs or custom emoji> - конвертировать tgs в gif")
async def tgs2gifcmd(self, msg: Message):
"""<reply to tgs or custom emoji> - convert tgs to gif"""
if not msg.reply_to_msg_id:
await utils.answer(msg, self.strings["no_reply"])
return
await utils.answer(msg, self.strings["converting"])
reply = await msg.get_reply_message()
if reply.file and reply.file.name.endswith(".tgs"):
tgs_buffer = io.BytesIO()
await reply.download_media(tgs_buffer)
tgs_buffer.seek(0)
gif_buffer = self.tgs2gif(tgs_buffer.getvalue())
gif_buffer.name = "sticker.gif"
await utils.answer(
msg,
gif_buffer,
force_document=False,
reply_to=reply
)
elif reply.entities and isinstance(reply.entities[0], MessageEntityCustomEmoji):
emoji_id = reply.entities[0].document_id
data = await msg.client(GetCustomEmojiDocumentsRequest(document_id=[emoji_id]))
if data:
tgs_buffer = io.BytesIO()
await msg.client.download_media(data[0], tgs_buffer)
tgs_buffer.seek(0)
gif_buffer = self.tgs2gif(tgs_buffer.getvalue())
gif_buffer.name = "sticker.gif"
await utils.answer(
msg,
gif_buffer,
force_document=False,
reply_to=reply.id
)
else:
await utils.answer(msg, self.strings["not_tgs_or_emoji"])
@loader.command(ru_doc="<reply to custom emoji> - конвертировать кастом эмодзи в tgs")
async def emoji2tgscmd(self, msg: Message):
"""<reply to custom emoji> - convert custom emoji to tgs"""
if not msg.reply_to_msg_id:
await utils.answer(msg, self.strings["no_reply"])
return
await utils.answer(msg, self.strings["converting"])
reply = await msg.get_reply_message()
if reply.entities and isinstance(reply.entities[0], MessageEntityCustomEmoji):
emoji_id = reply.entities[0].document_id
data = await msg.client(GetCustomEmojiDocumentsRequest(document_id=[emoji_id]))
if data:
tgs_buffer = io.BytesIO()
await msg.client.download_media(data[0], tgs_buffer)
tgs_buffer.seek(0)
await utils.answer(
msg,
tgs_buffer.getvalue(),
attributes=[types.DocumentAttributeFilename("sticker.tgs")],
reply_to=reply
)
else:
await utils.answer(msg, self.strings["emoji_not_found"])
else:
await utils.answer(msg, self.strings["not_tgs_or_emoji"])
def tgs2json(self, inbytes):
data = gzip.decompress(inbytes).decode('utf-8')
return data.encode('utf-8')
def json2tgs(self, inbytes):
data = json.loads(inbytes.decode("utf-8"))
compressed = json.dumps(data, ensure_ascii=False, separators=(",", ":"))
buffer = io.BytesIO()
with gzip.GzipFile(fileobj=buffer, mode="wb", compresslevel=9) as out:
out.write(compressed.encode("utf-8"))
return buffer.getvalue()
def svg2tgs(self, inbytes):
infile = io.BytesIO(inbytes)
importer = Baseporter(extensions=["svg", "svgz"],
callback=import_svg,
name="SVG")
exporter = Baseporter(extensions=["tgs"],
callback=export_tgs,
name="Telegram Animated Sticker")
an = importer.process(infile)
an.frame_rate = 60
an.scale(512, 512)
buffer = io.BytesIO()
exporter.process(an, buffer)
buffer.seek(0)
return buffer
def tgs2gif(self, inbytes):
temp = "/tmp/output.gif"
try:
tgs_stream = io.BytesIO(inbytes)
anim = LottieAnimation.from_tgs(tgs_stream)
anim.save_animation(temp, format="gif")
gif_buffer = io.BytesIO()
with open(temp, "rb") as gif_file:
gif_buffer.write(gif_file.read())
gif_buffer.seek(0)
return gif_buffer
finally:
if os.path.exists(temp):
os.remove(temp)
def tgs2pngs(self, inbytes):
temp = "/tmp/frames.zip"
try:
tgs_stream = io.BytesIO(inbytes)
anim = LottieAnimation.from_tgs(tgs_stream)
total_frames = anim.lottie_animation_get_totalframe()
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zipf:
for i in range(total_frames):
frame = anim.render_pillow_frame(i)
frame_buffer = io.BytesIO()
frame.save(frame_buffer, format="PNG")
frame_buffer.seek(0)
zipf.writestr(f"frame_{i}.png", frame_buffer.getvalue())
zip_buffer.seek(0)
return zip_buffer
finally:
if os.path.exists(temp):
os.remove(temp)

View File

@@ -0,0 +1,302 @@
__version__ = (1, 0, 0, 1)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import re
from .. import loader, utils
from telethon import events
import os
@loader.tds
class yg_trigger(loader.Module):
"""Триггер-модуль. Документация: kok.gay/trigger"""
strings = {"name": "yg_trigger"}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"watcher_on",
True,
"состояние вотчера",
validator=loader.validators.Boolean()
)
)
async def client_ready(self, client, db):
self.db = db
self.triggers = self.db.get("triggers", "list", [])
self.client = client
handlers = [
(self.trigger, [events.NewMessage, events.MessageEdited])
]
for handler_func, event_list in handlers:
for event in event_list:
self.client.add_event_handler(handler_func, event)
async def save_triggers(self):
self.db.set("triggers", "list", self.triggers)
async def validate(self, entity_value, target_entity):
entity_k = entity_value.replace("@", "")
entity_id = getattr(target_entity, "id", None)
entity_usernames = await self.get_usernames(target_entity)
if entity_k.isdigit():
return int(entity_value) == entity_id
return entity_k.lower() in entity_usernames
async def add_trigger(self, conditions: dict, response: str):
self.triggers.append({"conditions": conditions, "response": response})
await self.save_triggers()
async def triggercmd(self, message):
"""вкл/выкл вотчер"""
self.config["watcher_on"] = not self.config["watcher_on"]
await message.edit(f"<emoji document_id=5361741454685256344>🎮</emoji> <b>Триггер-мод {'включен' if self.config['watcher_on'] else 'выключен'}</b>")
async def remove_trigger(self, index: int):
try:
del self.triggers[index]
await self.save_triggers()
return True
except IndexError:
return False
async def clear_triggers(self):
self.triggers = []
await self.save_triggers()
async def get_usernames(self, entity):
usernames = []
if entity.username:
username = entity.username
if username:
usernames.append(username.lower())
if entity.usernames:
additional_usernames = [
u.username.lower() for u in (getattr(entity, "usernames", []) or [])
]
usernames.extend(additional_usernames)
return usernames
async def list_triggerscmd(self, message):
"""показать все существующие триггеры"""
if not self.triggers:
await utils.answer(message, "<emoji document_id=5461117441612462242>🙂</emoji> <b>Нет активных триггеров</b>")
return
reply_with_html = "<emoji document_id=5334544901428229844></emoji> <b>Список триггеров:</b>\n\n"
reply_plain_text = "Список триггеров:\n\n"
for i, trigger in enumerate(self.triggers):
conditions = "\n".join([f"<b>-</b> {k}: <code>{v}</code>" for k, v in trigger["conditions"].items()])
if not conditions:
conditions = "<b>Нет условий (all=true)</b>"
reply_with_html += f"<b>{i}.</b> Условия:\n{conditions}\n\n<emoji document_id=5443038326535759644>💬</emoji> <b>Ответ:</b> <code>{trigger['response']}</code>\n\n"
reply_plain_text += f"{i}.\nУсловия:\n{conditions}\nОтвет: {trigger['response']}\n\n"
if len(reply_with_html) > 4096:
file_path = "triggers_list.txt"
with open(file_path, "w", encoding="utf-8") as file:
file.write(reply_plain_text)
await message.delete()
await self.client.send_file(message.chat, caption="<emoji document_id=5433653135799228968>📁</emoji> <i>Вывод команды слишком длинный, поэтому он отправлен в файле.</i>", file=file_path)
os.remove(file_path)
else:
await utils.answer(message, reply_with_html)
async def split(self, input_str):
is_in_quotes = False
split_index = -1
for i, char in enumerate(input_str):
if char == '"':
is_in_quotes = not is_in_quotes
elif char == '|' and not is_in_quotes:
split_index = i
break
if split_index == -1:
raise ValueError("Некорректный формат: отсутствует разделитель '|'")
return input_str[:split_index].strip(), input_str[split_index + 1:].strip()
async def add_triggercmd(self, message):
"""<условия> | <ответ> - добавить новый триггер"""
args = utils.get_args_raw(message)
if not args or "|" not in args:
await utils.answer(
message,
"<emoji document_id=5240241223632954241>🚫</emoji> <b>Формат: <условия> | <ответ></b>\n<emoji document_id=5325547803936572038>✨</emoji> Пример: <code>text=\"Привет\" | Здравствуй!</code>"
)
return
try:
conditions_raw, response = await self.split(args)
valid_keys = {
"text", "user", "chat", "starts_with", "ends_with", "contains",
"regex", "is_command", "word_count", "char_count", "is_reply", "is_forwarded",
"media_type", "message_length", "time_range", "date", "weekday", "all"
}
conditions = {}
pattern = r'(\w+)=((?:\"(?:[^\"]|\\\")*\"|[^,\s]+))'
matches = re.finditer(pattern, conditions_raw)
for match in matches:
key, value = match.group(1), match.group(2)
if key not in valid_keys:
raise ValueError(f"Некорректное условие: {key}")
if key in {"text", "regex", "contains", "starts_with", "ends_with"}:
if not (value.startswith('"') and value.endswith('"')):
raise ValueError(f"Значение для условия {key} должно быть в кавычках")
value = value[1:-1].replace('\\"', '"')
if key == "all" and value.lower() == "true":
conditions = {"all": True}
break
conditions[key] = value
if not conditions:
raise ValueError(
"😨 Ты не указал никаких условий.. Если ты такой смелый, укажи all=true в условиях!"
)
if conditions.get("all"):
conditions = {}
await self.add_trigger(conditions, response)
conditions_str = "\n".join([f"<b>-</b> {k}: <code>{v}</code>" for k, v in conditions.items()])
if not conditions_str:
conditions_str = "<b>Нет условий (all=true)</b>"
await utils.answer(
message,
f"<emoji document_id=5456140674028019486>⚡️</emoji> <b>Триггер добавлен!</b>\n\nУсловия:\n{conditions_str}\n\n<emoji document_id=5443038326535759644>💬</emoji> <b>Ответ:</b> <code>{response}</code>"
)
except ValueError as e:
await utils.answer(message, f"<emoji document_id=5240241223632954241>🚫</emoji> <b>Ошибка:</b> <code>{e}</code>")
except Exception as e:
await utils.answer(message, f"<emoji document_id=5240241223632954241>🚫</emoji> <b>Непредвиденная ошибка:</b> <code>{e}</code>")
async def remove_triggercmd(self, message):
"""<номер> - удалить триггер"""
args = utils.get_args_raw(message)
if not args.isdigit():
await utils.answer(
message,
"<emoji document_id=5240241223632954241>🚫</emoji> <b>Укажи номер триггера для удаления</b>"
)
return
index = int(args)
if await self.remove_trigger(index):
await utils.answer(message, "<emoji document_id=5445267414562389170>🗑</emoji> <b>Триггер удалён!</b>")
else:
await utils.answer(message, "<emoji document_id=5240241223632954241>🚫</emoji> <b> Неверный номер триггера</b>")
async def clear_triggerscmd(self, message):
"""удалить все триггеры"""
await self.clear_triggers()
await utils.answer(message, "<emoji document_id=5445267414562389170>🗑</emoji> <b>Все триггеры удалены!</b>")
async def trigger(self, message):
if not self.config["watcher_on"]:
return
responses = []
for trigger in self.triggers:
conditions = trigger["conditions"]
match = True
for key, value in conditions.items():
value = value.strip('"')
if key == "text" and value.lower() != message.raw_text.lower():
match = False
break
elif key == "user" and not await self.validate(value, message.sender):
match = False
break
elif key == "chat" and not await self.validate(value, message.chat):
match = False
break
elif key == "starts_with" and not message.raw_text.lower().startswith(value.lower()):
match = False
break
elif key == "ends_with" and not message.raw_text.lower().endswith(value.lower()):
match = False
break
elif key == "contains" and value.lower() not in message.raw_text.lower():
match = False
break
elif key == "regex" and not re.search(value, message.raw_text):
match = False
break
elif key == "is_command" and value.lower() == "true" and not message.raw_text.startswith("/"):
match = False
break
elif key == "word_count" and len(message.raw_text.split()) != int(value):
match = False
break
elif key == "char_count" and len(message.raw_text) != int(value):
match = False
break
elif key == "is_reply" and value.lower() == "true" and not message.is_reply:
match = False
break
elif key == "is_forwarded" and value.lower() == "true" and not message.fwd_from:
match = False
break
elif key == "media_type":
media_types = {"photo": message.photo, "video": message.video, "sticker": message.sticker, "voice": message.voice, "audio": message.audio}
if not media_types.get(value.lower()):
match = False
break
elif key == "message_length" and not (int(value.split("-", 1)[0]) <= len(message.raw_text) <= int(value.split("-", 1)[1])):
match = False
break
elif key == "time_range" and not (int(value.split("-", 1)[0]) <= message.date.hour <= int(value.split("-", 1)[1])):
match = False
break
elif key == "date" and value != message.date.strftime("%Y-%m-%d"):
match = False
break
elif key == "weekday" and value.lower() != message.date.strftime("%A").lower():
match = False
break
if match:
responses.append(trigger["response"])
for response in responses:
await message.reply(response)

85
yummy1gay/limoka/yg_vm.py Normal file
View File

@@ -0,0 +1,85 @@
__version__ = (1, 4, 8, 8)
# This file is a part of Hikka Userbot
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
# 🌐 https://github.com/hikariatama/Hikka
# You CAN edit this file without direct permission from the author.
# You can redistribute this file with any modifications.
# meta developer: @yg_modules
# scope: hikka_only
# scope: hikka_min 1.6.3
# scope: ffmpeg
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█   █▀▄▀█ █▀█ █▀▄ █▀
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░   █░▀░█ █▄█ █▄▀ ▄█
import os
import uuid
from .. import loader, utils
@loader.tds
class VoiceModule(loader.Module):
"""Converts music and video (required ffmpeg)"""
strings = {"name": "yg_vm"}
async def m2vcmd(self, message):
"""Convert music to voice message"""
reply = await message.get_reply_message()
if not reply or not reply.file:
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> <b>Reply to audio file</b>")
return
media = reply.file
mime_type = media.mime_type.split('/')[0] if '/' in media.mime_type else 'audio'
if mime_type == 'audio':
await utils.answer(message, "<emoji document_id=4988080790286894217>🫥</emoji> <b>Converting audio...</b>")
voice_message = await self.convert_audio(reply)
else:
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> <b>Unsupported file type</b>")
return
await message.delete()
await message.client.send_file(message.to_id, voice_message, voice_note=True, reply_to=reply)
async def convert_audio(self, message):
tmp_filename = "tmp_audio.ogg"
await message.download_media(file=tmp_filename)
os.system(f"ffmpeg -y -i {tmp_filename} -c:a libopus {tmp_filename}.ogg")
os.remove(tmp_filename)
return f"{tmp_filename}.ogg"
async def v2acmd(self, message):
"""Convert video to audio"""
reply = await message.get_reply_message()
if not reply or not reply.file:
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> <b>Reply to video file</b>")
return
media = reply.file
mime_type = media.mime_type.split('/')[0] if '/' in media.mime_type else 'video'
if mime_type == 'video':
await utils.answer(message, "<emoji document_id=4988080790286894217>🫥</emoji> <b>Converting video to audio...</b>")
audio_message = await self.convert_video_to_mp3(reply)
else:
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> <b>Unsupported file type</b>")
return
await message.delete()
await message.client.send_file(message.to_id, audio_message, reply_to=reply)
async def convert_video_to_mp3(self, message):
tmp_filename = f"{uuid.uuid4().hex}.mp4"
await message.download_media(file=tmp_filename)
audio_filename = f"{uuid.uuid4().hex}.mp3"
os.system(f"ffmpeg -y -i {tmp_filename} -vn -acodec libmp3lame -ab 192k -ar 44100 -ac 2 {audio_filename}")
os.remove(tmp_filename)
return audio_filename