mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-17 23:04:17 +02:00
Added and updated repositories 2025-11-30 12:52:32
This commit is contained in:
176
yummy1gay/limoka/SwitchToTelethon.py
Normal file
176
yummy1gay/limoka/SwitchToTelethon.py
Normal 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
15
yummy1gay/limoka/full.txt
Normal 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
|
||||
172
yummy1gay/limoka/yg_balance.py
Normal file
172
yummy1gay/limoka/yg_balance.py
Normal 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>"
|
||||
)
|
||||
59
yummy1gay/limoka/yg_chatid.py
Normal file
59
yummy1gay/limoka/yg_chatid.py
Normal 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"])
|
||||
945
yummy1gay/limoka/yg_checks.py
Normal file
945
yummy1gay/limoka/yg_checks.py
Normal 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
|
||||
|
||||
264
yummy1gay/limoka/yg_gamee.py
Normal file
264
yummy1gay/limoka/yg_gamee.py
Normal 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 <score> <time in seconds></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 <score> <time in seconds></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 <score> <time in seconds></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)))
|
||||
1151
yummy1gay/limoka/yg_gemini.py
Normal file
1151
yummy1gay/limoka/yg_gemini.py
Normal file
File diff suppressed because it is too large
Load Diff
122
yummy1gay/limoka/yg_ocr.py
Normal file
122
yummy1gay/limoka/yg_ocr.py
Normal 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))
|
||||
116
yummy1gay/limoka/yg_owner.py
Normal file
116
yummy1gay/limoka/yg_owner.py
Normal 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
|
||||
759
yummy1gay/limoka/yg_playlist.py
Normal file
759
yummy1gay/limoka/yg_playlist.py
Normal 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
189
yummy1gay/limoka/yg_prem.py
Normal 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 "месяцев"
|
||||
396
yummy1gay/limoka/yg_quotes.py
Normal file
396
yummy1gay/limoka/yg_quotes.py
Normal 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
|
||||
96
yummy1gay/limoka/yg_stars.py
Normal file
96
yummy1gay/limoka/yg_stars.py
Normal 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
371
yummy1gay/limoka/yg_tgs.py
Normal 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)
|
||||
302
yummy1gay/limoka/yg_trigger.py
Normal file
302
yummy1gay/limoka/yg_trigger.py
Normal 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
85
yummy1gay/limoka/yg_vm.py
Normal 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
|
||||
Reference in New Issue
Block a user