mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Added and updated repositories 2026-02-05 01:22:45
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
__version__ = (3, 1, 1)
|
__version__ = (3, 1, 1)
|
||||||
# meta banner: https://raw.githubusercontent.com/kamekuro/hikka-mods/main/banners/yamusic.png
|
# meta banner: https://raw.githubusercontent.com/kamekuro/hikka-mods/main/banners/yamusic.png
|
||||||
# packurl: https://raw.githubusercontent.com/coddrago/assets/refs/heads/main/modules/yamusic.yml
|
# packurl: https://raw.githubusercontent.com/coddrago/assets/refs/heads/main/modules/yamusic.yml
|
||||||
# meta pic: https://raw.githubusercontent.com/kamekuro/hikka-mods/main/icons/yamusic.png
|
# meta banner: https://raw.githubusercontent.com/coddrago/modules/refs/heads/main/banner.png
|
||||||
# meta developer: @codrago
|
# meta developer: @codrago_m
|
||||||
# old meta dev: @kamekuro xuesos
|
# old meta dev: @kamekuro xuesos
|
||||||
# scope: heroku_only
|
# scope: heroku_only
|
||||||
# scope: heroku_min 1.7.2
|
# scope: heroku_min 1.7.2
|
||||||
@@ -41,6 +41,7 @@ class Banners:
|
|||||||
meta_info: str = "Music",
|
meta_info: str = "Music",
|
||||||
is_liked: bool = False,
|
is_liked: bool = False,
|
||||||
repeat_mode: str = "NONE",
|
repeat_mode: str = "NONE",
|
||||||
|
blur: int = 0,
|
||||||
):
|
):
|
||||||
self.title = title
|
self.title = title
|
||||||
self.artists = artists
|
self.artists = artists
|
||||||
@@ -52,6 +53,7 @@ class Banners:
|
|||||||
self.meta_info = meta_info
|
self.meta_info = meta_info
|
||||||
self.is_liked = is_liked
|
self.is_liked = is_liked
|
||||||
self.repeat_mode = repeat_mode
|
self.repeat_mode = repeat_mode
|
||||||
|
self.blur = blur
|
||||||
|
|
||||||
def ultra(self) -> io.BytesIO:
|
def ultra(self) -> io.BytesIO:
|
||||||
WIDTH, HEIGHT = 2560, 1220
|
WIDTH, HEIGHT = 2560, 1220
|
||||||
@@ -96,7 +98,9 @@ class Banners:
|
|||||||
background = background.crop((0, offset, bg_w, offset + new_h))
|
background = background.crop((0, offset, bg_w, offset + new_h))
|
||||||
|
|
||||||
background = background.resize((WIDTH, HEIGHT), Image.Resampling.LANCZOS)
|
background = background.resize((WIDTH, HEIGHT), Image.Resampling.LANCZOS)
|
||||||
background = background.filter(ImageFilter.GaussianBlur(radius=0))
|
|
||||||
|
if self.blur > 0:
|
||||||
|
background = background.filter(ImageFilter.GaussianBlur(radius=self.blur))
|
||||||
|
|
||||||
dark_overlay = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 180))
|
dark_overlay = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 180))
|
||||||
background = Image.alpha_composite(background, dark_overlay)
|
background = Image.alpha_composite(background, dark_overlay)
|
||||||
@@ -296,30 +300,6 @@ class Banners:
|
|||||||
(heart_x, icon_y_center + heart_size + 5),
|
(heart_x, icon_y_center + heart_size + 5),
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.is_liked:
|
|
||||||
draw.ellipse(c1_box, fill="red", outline="red")
|
|
||||||
draw.ellipse(c2_box, fill="red", outline="red")
|
|
||||||
draw.polygon(tri_points, fill="red", outline="red")
|
|
||||||
else:
|
|
||||||
draw.ellipse(c1_box, fill=None, outline="red", width=3)
|
|
||||||
draw.ellipse(c2_box, fill=None, outline="red", width=3)
|
|
||||||
draw.line(
|
|
||||||
[
|
|
||||||
(heart_x - c_r * 2 + 1, icon_y_center),
|
|
||||||
(heart_x, icon_y_center + heart_size + 5),
|
|
||||||
],
|
|
||||||
fill="red",
|
|
||||||
width=3,
|
|
||||||
)
|
|
||||||
draw.line(
|
|
||||||
[
|
|
||||||
(heart_x + c_r * 2 - 1, icon_y_center),
|
|
||||||
(heart_x, icon_y_center + heart_size + 5),
|
|
||||||
],
|
|
||||||
fill="red",
|
|
||||||
width=3,
|
|
||||||
)
|
|
||||||
|
|
||||||
by = io.BytesIO()
|
by = io.BytesIO()
|
||||||
background.save(by, format="PNG")
|
background.save(by, format="PNG")
|
||||||
by.seek(0)
|
by.seek(0)
|
||||||
@@ -378,8 +358,13 @@ class YaMusicMod(loader.Module):
|
|||||||
option="banner_version",
|
option="banner_version",
|
||||||
default="ultra",
|
default="ultra",
|
||||||
doc=lambda: self.strings["_cfg"]["banner_version"],
|
doc=lambda: self.strings["_cfg"]["banner_version"],
|
||||||
validator=loader.validators.Choice(["old", "new", "ultra"]),
|
validator=loader.validators.Choice(["ultra"]),
|
||||||
),)
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
option="blur",
|
||||||
|
default=0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
self.ym_client = None
|
self.ym_client = None
|
||||||
self.device_id = "".join(random.choices(string.ascii_lowercase, k=16))
|
self.device_id = "".join(random.choices(string.ascii_lowercase, k=16))
|
||||||
@@ -392,6 +377,7 @@ class YaMusicMod(loader.Module):
|
|||||||
#"now_play", self._now_play_placeholder, "placeholder for nowplay music"
|
#"now_play", self._now_play_placeholder, "placeholder for nowplay music"
|
||||||
# Heroku 2.0.0 feature
|
# Heroku 2.0.0 feature
|
||||||
#)
|
#)
|
||||||
|
#utils.register_placeholder("duration", self._duration_placeholder, "progress bar")
|
||||||
|
|
||||||
if not self.get("guide_sent", False):
|
if not self.get("guide_sent", False):
|
||||||
await self.inline.bot.send_message(self._tg_id, self.strings("iguide"))
|
await self.inline.bot.send_message(self._tg_id, self.strings("iguide"))
|
||||||
@@ -437,7 +423,7 @@ class YaMusicMod(loader.Module):
|
|||||||
me = await self._client.get_me()
|
me = await self._client.get_me()
|
||||||
self._premium = me.premium if hasattr(me, "premium") else False
|
self._premium = me.premium if hasattr(me, "premium") else False
|
||||||
|
|
||||||
@loader.loop(30)
|
@loader.loop(15)
|
||||||
async def autobio(self):
|
async def autobio(self):
|
||||||
if not self.config["token"]:
|
if not self.config["token"]:
|
||||||
self.autobio.stop()
|
self.autobio.stop()
|
||||||
@@ -547,6 +533,88 @@ class YaMusicMod(loader.Module):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _duration_placeholder(self):
|
||||||
|
"""Placeholder for {duration} with custom emoji bar"""
|
||||||
|
if not self.config["token"]:
|
||||||
|
return "No Token"
|
||||||
|
|
||||||
|
try:
|
||||||
|
now = await self.__get_now_playing()
|
||||||
|
if not now or now.get("paused"):
|
||||||
|
return "<code>Not Playing</code>"
|
||||||
|
|
||||||
|
duration = now.get("duration_ms", 0)
|
||||||
|
progress = now.get("progress_ms", 0)
|
||||||
|
|
||||||
|
if duration == 0:
|
||||||
|
return "0%"
|
||||||
|
|
||||||
|
percent = (progress / duration) * 100
|
||||||
|
|
||||||
|
s_less_10 = (
|
||||||
|
"<emoji document_id=5454137780454067986>➖</emoji>"
|
||||||
|
"<emoji document_id=6158923355173949539>⭐</emoji>"
|
||||||
|
"<emoji document_id=6159012102083188132>⭐</emoji>"
|
||||||
|
"<emoji document_id=6159012102083188132>⭐</emoji>"
|
||||||
|
"<emoji document_id=6158753257289158944>⭐</emoji>"
|
||||||
|
"<emoji document_id=6156700344526049665>⭐</emoji>"
|
||||||
|
)
|
||||||
|
|
||||||
|
s_10_to_20 = (
|
||||||
|
"<emoji document_id=5454137780454067986>➖</emoji>"
|
||||||
|
"<emoji document_id=6159095673556840262>⭐</emoji>"
|
||||||
|
"<emoji document_id=6159012102083188132>⭐</emoji>"
|
||||||
|
"<emoji document_id=6156933677214341691>⭐</emoji>"
|
||||||
|
"<emoji document_id=6158753257289158944>⭐</emoji>"
|
||||||
|
"<emoji document_id=6156700344526049665>⭐</emoji>"
|
||||||
|
)
|
||||||
|
|
||||||
|
s_30_to_40 = (
|
||||||
|
"<emoji document_id=5454137780454067986>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=6158923355173949539>⭐</emoji>"
|
||||||
|
"<emoji document_id=6159012102083188132>⭐</emoji>"
|
||||||
|
"<emoji document_id=6156700344526049665>⭐</emoji>"
|
||||||
|
)
|
||||||
|
|
||||||
|
s_over_50 = (
|
||||||
|
"<emoji document_id=5454137780454067986>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=6156933677214341691>⭐</emoji>"
|
||||||
|
"<emoji document_id=6156700344526049665>⭐</emoji>"
|
||||||
|
)
|
||||||
|
|
||||||
|
s_over_80 = (
|
||||||
|
"<emoji document_id=5454137780454067986>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=5454397458471750662>➖</emoji>"
|
||||||
|
"<emoji document_id=6156700344526049665>⭐</emoji>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if percent < 10:
|
||||||
|
return s_less_10
|
||||||
|
elif percent < 20:
|
||||||
|
return s_10_to_20
|
||||||
|
elif percent < 30:
|
||||||
|
return s_10_to_20
|
||||||
|
elif percent < 40:
|
||||||
|
return s_30_to_40
|
||||||
|
elif percent < 50:
|
||||||
|
return s_30_to_40
|
||||||
|
elif percent < 80:
|
||||||
|
return s_over_50
|
||||||
|
else:
|
||||||
|
return s_over_80
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error: {e}"
|
||||||
|
|
||||||
async def _download_bytes(self, url: str) -> typing.Optional[bytes]:
|
async def _download_bytes(self, url: str) -> typing.Optional[bytes]:
|
||||||
try:
|
try:
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
@@ -686,8 +754,10 @@ class YaMusicMod(loader.Module):
|
|||||||
meta_info=meta_info,
|
meta_info=meta_info,
|
||||||
is_liked=is_liked,
|
is_liked=is_liked,
|
||||||
repeat_mode=repeat_mode,
|
repeat_mode=repeat_mode,
|
||||||
|
blur=self.config["blur"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
file = await utils.run_sync(
|
file = await utils.run_sync(
|
||||||
getattr(banners, self.config["banner_version"], banners.ultra)
|
getattr(banners, self.config["banner_version"], banners.ultra)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,10 +17,9 @@ figlet
|
|||||||
promoclaimer
|
promoclaimer
|
||||||
passwordgen
|
passwordgen
|
||||||
send
|
send
|
||||||
lastfm
|
|
||||||
dbmod
|
dbmod
|
||||||
chatmodule
|
chatmodule
|
||||||
stats
|
stats
|
||||||
tagwatcher
|
tagwatcher
|
||||||
hardspam
|
hardspam
|
||||||
YaMusic
|
YaMusic
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
# ---------------------------------------------------------------------------------
|
|
||||||
#░█▀▄░▄▀▀▄░█▀▄░█▀▀▄░█▀▀▄░█▀▀▀░▄▀▀▄░░░█▀▄▀█
|
|
||||||
#░█░░░█░░█░█░█░█▄▄▀░█▄▄█░█░▀▄░█░░█░░░█░▀░█
|
|
||||||
#░▀▀▀░░▀▀░░▀▀░░▀░▀▀░▀░░▀░▀▀▀▀░░▀▀░░░░▀░░▒▀
|
|
||||||
# Name: LastFM
|
|
||||||
# Description: Module for music from different services
|
|
||||||
# Author: @codrago_m
|
|
||||||
# ---------------------------------------------------------------------------------
|
|
||||||
# 🔒 Licensed under the GNU AGPLv3
|
|
||||||
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
|
|
||||||
# ---------------------------------------------------------------------------------
|
|
||||||
# Author: @codrago
|
|
||||||
# Commands: nowplay
|
|
||||||
# scope: heroku_only
|
|
||||||
# meta developer: @codrago_m
|
|
||||||
# meta banner: https://raw.githubusercontent.com/coddrago/modules/refs/heads/main/banner.png
|
|
||||||
# meta pic: https://envs.sh/Hob.webp
|
|
||||||
# ---------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
from .. import loader, utils
|
|
||||||
from herokutl import events
|
|
||||||
import requests
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
|
|
||||||
@loader.tds
|
|
||||||
class lastfmmod(loader.Module):
|
|
||||||
"""Module for music from different services"""
|
|
||||||
def __init__(self):
|
|
||||||
self.config = loader.ModuleConfig(
|
|
||||||
loader.ConfigValue(
|
|
||||||
"username_lastfm",
|
|
||||||
None,
|
|
||||||
lambda: self.strings["_doc_username_lastfm"],
|
|
||||||
),
|
|
||||||
loader.ConfigValue(
|
|
||||||
"text",
|
|
||||||
"<emoji document_id=6007938409857815902>🎧</emoji> <b>now playing...</b>\n"
|
|
||||||
"<emoji document_id=5915480455603295660>🎶</emoji><b> playlist: </b><code>{song_album}</code>\n"
|
|
||||||
"<emoji document_id=5891249688933305846>🎵</emoji> <b>track:</b> <code>{song_name}</code>\n"
|
|
||||||
"<emoji document_id=5897554554894946515>🎤</emoji> <b>artist:</b> <code>{song_artist}</code>",
|
|
||||||
lambda: self.strings["_doc_text"],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
strings = {
|
|
||||||
"name": "LastFm",
|
|
||||||
"loading":"<emoji document_id=5873204392429096339>⌨️</emoji> Loading song...",
|
|
||||||
"bot_no_result": "<emoji document_id=5465665476971471368>❌</emoji> Nothing found.\nTitle: {song_name}\nAuthor: {song_artist}\nAlbum:{song_album}",
|
|
||||||
"_doc_text": "The text that will be written next to the file",
|
|
||||||
"_doc_username_lastfm": "Your username from last.fm",
|
|
||||||
"nick_error": "<emoji document_id=5465665476971471368>❌</emoji> Put your nickname from last.fm",
|
|
||||||
"tutorial": "Go to last.fm and register.\nBE SURE to remember the username and password, they will come in handy later.\nLet's look at the VK version\nAfter that, go to the @vkxci channel, download VK X and log in to your VK account, then go to settings and click «Integrations», select Last FM.\nEnter the username and password.\nThen you're almost done!\nWrite <code>{prefix}fcfg lastfm username_lastfm</code> {username}\nUse the <code>{prefix}nowplay</code> command and enjoy life!",
|
|
||||||
}
|
|
||||||
|
|
||||||
strings_ru = {
|
|
||||||
"name": "LastFm",
|
|
||||||
"loading": "<emoji document_id=5873204392429096339>⌨️</emoji> Загрузка трека...",
|
|
||||||
"bot_no_result": "<emoji document_id=5465665476971471368>❌</emoji> Ничего не найдено.\nНазвание: {song_name}\nИсполнитель: {song_artist}\nАльбом: {song_album}",
|
|
||||||
"_doc_text": "Текст, который будет написан рядом с файлом",
|
|
||||||
"_doc_username_lastfm": "Ваш username с last.fm",
|
|
||||||
"nick_error": "<emoji document_id=5465665476971471368>❌</emoji> Укажите ваш никнейм с last.fm",
|
|
||||||
"tutorial": "Зайдите на last.fm и зарегистрируйтесь.\nОБЯЗАТЕЛЬНО запомните логин и пароль, они пригодятся позже.\nРассмотрим вариант для VK\nПосле этого зайдите в канал @vkxci, скачайте VK X и авторизуйтесь в своём аккаунте VK, затем зайдите в настройки и нажмите «Интеграции», выберите Last FM.\nВведите логин и пароль.\nЗатем вы почти закончили!\nНапишите <code>{prefix}fcfg lastfm username_lastfm</code> {username}\nИспользуйте команду <code>{prefix}nowplay</code> и наслаждайтесь жизнью!",
|
|
||||||
}
|
|
||||||
|
|
||||||
@loader.command(alias="np")
|
|
||||||
async def nowplay(self, message):
|
|
||||||
"""| send playing track"""
|
|
||||||
|
|
||||||
lastfm_username = self.config["username_lastfm"]
|
|
||||||
API_KEY = "460cda35be2fbf4f28e8ea7a38580730" # Облегчение жизни школьникам
|
|
||||||
|
|
||||||
if not lastfm_username:
|
|
||||||
response_text = self.strings["nick_error"]
|
|
||||||
await self.invoke("config", "lastfm", message=message)
|
|
||||||
await utils.answer(message, response_text)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
current_track_url = f'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&nowplaying=true&user={lastfm_username}&api_key={API_KEY}&format=json'
|
|
||||||
response = requests.get(current_track_url)
|
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
if 'recenttracks' in data and 'track' in data['recenttracks'] and data['recenttracks']['track']:
|
|
||||||
nowplaying_track = None
|
|
||||||
for track in data['recenttracks']['track']:
|
|
||||||
if '@attr' in track and 'nowplaying' in track['@attr']:
|
|
||||||
nowplaying_track = track
|
|
||||||
break
|
|
||||||
|
|
||||||
if nowplaying_track:
|
|
||||||
song_name = nowplaying_track.get('name', 'Unknown song')
|
|
||||||
song_artist = nowplaying_track.get('artist', {}).get('#text', 'Unknown Artist')
|
|
||||||
if nowplaying_track.get('album', {}).get('#text') == nowplaying_track.get('name'):
|
|
||||||
song_album = "single"
|
|
||||||
else:
|
|
||||||
song_album = nowplaying_track.get('album', {}).get('#text', 'Unknown Album')
|
|
||||||
response_text = f"/search {song_name} - {song_artist}"
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with message.client.conversation("@LyaDownbot") as conv:
|
|
||||||
await conv.send_message(response_text)
|
|
||||||
while True:
|
|
||||||
response_bot = await conv.get_response()
|
|
||||||
if "Не удалось найти трек" in response_bot.text:
|
|
||||||
await utils.answer(message, self.strings["bot_no_result"])
|
|
||||||
return
|
|
||||||
|
|
||||||
if "Ищем треки..." in response_bot.text:
|
|
||||||
await utils.answer(message, self.strings["loading"])
|
|
||||||
|
|
||||||
if response_bot.media:
|
|
||||||
await message.client.send_file(message.chat_id, response_bot.media, caption = self.config["text"].format(song_artist=song_artist, song_album=song_album, song_name=song_name))
|
|
||||||
await message.delete()
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
await utils.answer(message, f"<pre><code class='language-python'>{e}</code></pre>")
|
|
||||||
except Exception as e:
|
|
||||||
await utils.answer(message, f"<pre><code class='language-python'>{e}</code></pre>")
|
|
||||||
|
|
||||||
@loader.command()
|
|
||||||
async def tutorl(self, message):
|
|
||||||
"""| tutorial"""
|
|
||||||
|
|
||||||
await utils.answer(message, self.strings['tutorial'].format(prefix = self.get_prefix(), username="{username}"))
|
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
# meta developer: @codrago_m
|
# meta developer: @codrago_m
|
||||||
# scope: heroku_min 2.0.0
|
# scope: heroku_min 2.0.0
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from .. import utils, loader, main
|
|
||||||
from telethon.tl.functions.messages import MarkDialogUnreadRequest
|
from telethon.tl.functions.messages import MarkDialogUnreadRequest
|
||||||
|
|
||||||
|
from .. import loader, main, utils
|
||||||
|
|
||||||
logger = logging.getLogger("TagWatcher")
|
logger = logging.getLogger("TagWatcher")
|
||||||
|
|
||||||
|
|
||||||
@@ -124,6 +127,7 @@ class TagWatcher(loader.Module):
|
|||||||
description="Here will be notifications about mentions in chats.",
|
description="Here will be notifications about mentions in chats.",
|
||||||
icon_emoji_id=5409025823388741707,
|
icon_emoji_id=5409025823388741707,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.xdlib = await self.import_lib(
|
self.xdlib = await self.import_lib(
|
||||||
"https://raw.githubusercontent.com/coddrago/modules/refs/heads/main/libs/xdlib.py",
|
"https://raw.githubusercontent.com/coddrago/modules/refs/heads/main/libs/xdlib.py",
|
||||||
suspend_on_error=True,
|
suspend_on_error=True,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# -- version --
|
# -- version --
|
||||||
__version__ = (1, 2, 1)
|
__version__ = (1, 2, 2)
|
||||||
# -- version --
|
# -- version --
|
||||||
|
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ __version__ = (1, 2, 1)
|
|||||||
# ██║╚██╔╝██║██╔══╝░░██╔══██║██║░░██║██║░░██║░░████╔═████║░░╚═══██╗░╚═══██╗
|
# ██║╚██╔╝██║██╔══╝░░██╔══██║██║░░██║██║░░██║░░████╔═████║░░╚═══██╗░╚═══██╗
|
||||||
# ██║░╚═╝░██║███████╗██║░░██║██████╔╝╚█████╔╝░░╚██╔╝░╚██╔╝░██████╔╝██████╔╝
|
# ██║░╚═╝░██║███████╗██║░░██║██████╔╝╚█████╔╝░░╚██╔╝░╚██╔╝░██████╔╝██████╔╝
|
||||||
# ╚═╝░░░░░╚═╝╚══════╝╚═╝░░╚═╝╚═════╝░░╚════╝░░░░╚═╝░░░╚═╝░░╚═════╝░╚═════╝░
|
# ╚═╝░░░░░╚═╝╚══════╝╚═╝░░╚═╝╚═════╝░░╚════╝░░░░╚═╝░░░╚═╝░░╚═════╝░╚═════╝░
|
||||||
# © Copyright 2025
|
# © Copyright 2026
|
||||||
# ✈ https://t.me/mead0wssMods
|
# ✈ https://t.me/mead0wssMods
|
||||||
|
|
||||||
|
|
||||||
@@ -33,14 +33,17 @@ class SenderGifts(loader.Module):
|
|||||||
"checking_user": "<emoji document_id=5206634672204829887>🔍</emoji> Проверка пользователя...",
|
"checking_user": "<emoji document_id=5206634672204829887>🔍</emoji> Проверка пользователя...",
|
||||||
"checking_balance": "<emoji document_id=5206634672204829887>🔍</emoji> Проверка баланса...",
|
"checking_balance": "<emoji document_id=5206634672204829887>🔍</emoji> Проверка баланса...",
|
||||||
"user_not_found": "<emoji document_id=4958526153955476488>❌</emoji> Пользователь не найден",
|
"user_not_found": "<emoji document_id=4958526153955476488>❌</emoji> Пользователь не найден",
|
||||||
"gift_menu": "<emoji document_id=5931696400982088015>🎁</emoji> Выберите категорию подарков.\n\n<emoji document_id=6032693626394382504>👤</emoji> Пользователь: {}\n<emoji document_id=5873153278023307367>📄</emoji> Текст: {}\n<emoji document_id=5951810621887484519>⭐</emoji> Баланс: {} звезд",
|
"gift_menu": "<tg-emoji emoji-id=5370781982886220096>🎁</tg-emoji> Выберите категорию подарков.\n\n<tg-emoji emoji-id=6048471184461271609>👤</tg-emoji> Пользователь: {}\n<tg-emoji emoji-id=6048762138430803961>📂</tg-emoji> Текст: {}\n<tg-emoji emoji-id=5321485469249198987>⭐️</tg-emoji> Баланс: {} звезд",
|
||||||
"category_menu": "<emoji document_id=5931696400982088015>🎁</emoji> Подарки за {} ⭐\n\n<emoji document_id=6032693626394382504>👤</emoji> Пользователь: {}\n<emoji document_id=5873153278023307367>📄</emoji> Текст: {}",
|
"category_menu": "<tg-emoji emoji-id=5370781982886220096>🎁</tg-emoji> Подарки за {} ⭐\n\n<tg-emoji emoji-id=6048471184461271609>👤</tg-emoji> Пользователь: {}\n<tg-emoji emoji-id=6048762138430803961>📂</tg-emoji> Текст: {}",
|
||||||
|
"privacy_menu": "<tg-emoji emoji-id=5370781982886220096>🎁</tg-emoji> Выбран подарок: {}\n\nКак отправить подарок?",
|
||||||
"sending_gift": "<emoji document_id=5201691993775818138>🛫</emoji> Отправка подарка...",
|
"sending_gift": "<emoji document_id=5201691993775818138>🛫</emoji> Отправка подарка...",
|
||||||
"gift_sent": "<emoji document_id=5021905410089550576>✅</emoji> Подарок успешно отправлен!",
|
"gift_sent": "<emoji document_id=5021905410089550576>✅</emoji> Подарок успешно отправлен!",
|
||||||
"not_enough_stars": "<emoji document_id=4958526153955476488>❌</emoji> Недостаточно звезд для отправки подарка {}!",
|
"not_enough_stars": "<emoji document_id=4958526153955476488>❌</emoji> Недостаточно звезд для отправки подарка {}!",
|
||||||
"min_stars_error": "<emoji document_id=4958526153955476488>❌</emoji> Недостаточно звезд для отправки минимального подарка!",
|
"min_stars_error": "<emoji document_id=4958526153955476488>❌</emoji> Недостаточно звезд для отправки минимального подарка!",
|
||||||
"no_available_gifts": "<emoji document_id=4958526153955476488>❌</emoji> Нет доступных подарков для вашего баланса",
|
"no_available_gifts": "<emoji document_id=4958526153955476488>❌</emoji> Нет доступных подарков для вашего баланса",
|
||||||
"balance_error": "<emoji document_id=4958526153955476488>❌</emoji> Ошибка при проверке баланса",
|
"balance_error": "<emoji document_id=4958526153955476488>❌</emoji> Ошибка при проверке баланса",
|
||||||
|
"btn_public": "📢 Публично",
|
||||||
|
"btn_anon": "🕵️ Анонимно",
|
||||||
}
|
}
|
||||||
|
|
||||||
gift_categories = {
|
gift_categories = {
|
||||||
@@ -57,6 +60,7 @@ class SenderGifts(loader.Module):
|
|||||||
{"id": 5170314324215857265, "emoji": "💐", "name": "Цветы"},
|
{"id": 5170314324215857265, "emoji": "💐", "name": "Цветы"},
|
||||||
{"id": 5170564780938756245, "emoji": "🚀", "name": "Ракета"},
|
{"id": 5170564780938756245, "emoji": "🚀", "name": "Ракета"},
|
||||||
{"id": 5922558454332916696, "emoji": "🎄", "name": "Ёлка"},
|
{"id": 5922558454332916696, "emoji": "🎄", "name": "Ёлка"},
|
||||||
|
{"id": 5956217000635139069, "emoji": "🧸", "name": "Новогодний мишка"}
|
||||||
],
|
],
|
||||||
100: [
|
100: [
|
||||||
{"id": 5168043875654172773, "emoji": "🏆", "name": "Кубок"},
|
{"id": 5168043875654172773, "emoji": "🏆", "name": "Кубок"},
|
||||||
@@ -135,9 +139,11 @@ class SenderGifts(loader.Module):
|
|||||||
|
|
||||||
if row:
|
if row:
|
||||||
buttons.append(row)
|
buttons.append(row)
|
||||||
|
|
||||||
|
helper_msg = await self.inline.form("🪐", balance_msg)
|
||||||
|
|
||||||
await utils.answer(
|
await utils.answer(
|
||||||
balance_msg,
|
helper_msg,
|
||||||
self.strings["gift_menu"].format(
|
self.strings["gift_menu"].format(
|
||||||
f"@{user.username}" if user.username else user.first_name,
|
f"@{user.username}" if user.username else user.first_name,
|
||||||
text if text else "-",
|
text if text else "-",
|
||||||
@@ -153,8 +159,8 @@ class SenderGifts(loader.Module):
|
|||||||
for gift in gifts:
|
for gift in gifts:
|
||||||
row.append({
|
row.append({
|
||||||
"text": gift["emoji"],
|
"text": gift["emoji"],
|
||||||
"callback": self._send_gift,
|
"callback": self._select_privacy,
|
||||||
"args": (user_id, gift["id"], text, gift["emoji"], msg_id, balance),
|
"args": (user_id, gift["id"], text, gift["emoji"], msg_id, balance, price),
|
||||||
})
|
})
|
||||||
if len(row) == 3:
|
if len(row) == 3:
|
||||||
buttons.append(row)
|
buttons.append(row)
|
||||||
@@ -183,6 +189,34 @@ class SenderGifts(loader.Module):
|
|||||||
reply_markup=buttons
|
reply_markup=buttons
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _select_privacy(self, call, user_id, gift_id, text, gift_emoji, msg_id, balance, price):
|
||||||
|
buttons = [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"text": self.strings["btn_public"],
|
||||||
|
"callback": self._send_gift,
|
||||||
|
"args": (user_id, gift_id, text, gift_emoji, msg_id, balance, False) # hide_name=False публично
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": self.strings["btn_anon"],
|
||||||
|
"callback": self._send_gift,
|
||||||
|
"args": (user_id, gift_id, text, gift_emoji, msg_id, balance, True) # hide_name=True анонимно
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"text": "⬅️ Назад",
|
||||||
|
"callback": self._show_category,
|
||||||
|
"args": (user_id, price, text, balance, msg_id)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
await call.edit(
|
||||||
|
self.strings["privacy_menu"].format(gift_emoji),
|
||||||
|
reply_markup=buttons
|
||||||
|
)
|
||||||
|
|
||||||
async def _back_to_categories(self, call, user_id, text, balance, msg_id):
|
async def _back_to_categories(self, call, user_id, text, balance, msg_id):
|
||||||
try:
|
try:
|
||||||
user = await self.client.get_entity(user_id)
|
user = await self.client.get_entity(user_id)
|
||||||
@@ -216,7 +250,7 @@ class SenderGifts(loader.Module):
|
|||||||
reply_markup=buttons
|
reply_markup=buttons
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _send_gift(self, call, user_id, gift_id, text, gift_emoji, msg_id, balance):
|
async def _send_gift(self, call, user_id, gift_id, text, gift_emoji, msg_id, balance, hide_name):
|
||||||
try:
|
try:
|
||||||
await call.edit(
|
await call.edit(
|
||||||
self.strings["sending_gift"],
|
self.strings["sending_gift"],
|
||||||
@@ -227,11 +261,11 @@ class SenderGifts(loader.Module):
|
|||||||
self.client.parse_mode,
|
self.client.parse_mode,
|
||||||
)
|
)
|
||||||
text, entities = parse_mode.parse(text)
|
text, entities = parse_mode.parse(text)
|
||||||
|
|
||||||
user = await self.client.get_input_entity(user_id)
|
user = await self.client.get_input_entity(user_id)
|
||||||
inv = InputInvoiceStarGift(
|
inv = InputInvoiceStarGift(
|
||||||
user,
|
user,
|
||||||
gift_id,
|
gift_id,
|
||||||
|
hide_name=hide_name,
|
||||||
message=TextWithEntities(text, entities) if text else TextWithEntities("", [])
|
message=TextWithEntities(text, entities) if text else TextWithEntities("", [])
|
||||||
)
|
)
|
||||||
form = await self.client(GetPaymentFormRequest(inv))
|
form = await self.client(GetPaymentFormRequest(inv))
|
||||||
|
|||||||
214
radiocycle/Modules/LastFm.py
Normal file
214
radiocycle/Modules/LastFm.py
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# =======================================
|
||||||
|
# _ __ __ __ _
|
||||||
|
# | |/ /___ | \/ | ___ __| |___
|
||||||
|
# | ' // _ \ | |\/| |/ _ \ / _` / __|
|
||||||
|
# | . \ __/ | | | | (_) | (_| \__ \
|
||||||
|
# |_|\_\___| |_| |_|\___/ \__,_|___/
|
||||||
|
# @ke_mods
|
||||||
|
# =======================================
|
||||||
|
#
|
||||||
|
# LICENSE: CC BY-ND 4.0 (Attribution-NoDerivatives 4.0 International)
|
||||||
|
# --------------------------------------
|
||||||
|
# https://creativecommons.org/licenses/by-nd/4.0/legalcode
|
||||||
|
# =======================================
|
||||||
|
|
||||||
|
# meta developer: @ke_mods
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import requests
|
||||||
|
import io
|
||||||
|
import textwrap
|
||||||
|
from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont, ImageOps
|
||||||
|
|
||||||
|
class Banners:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
title: str,
|
||||||
|
artists: list,
|
||||||
|
track_cover: bytes,
|
||||||
|
font
|
||||||
|
):
|
||||||
|
self.title = title
|
||||||
|
self.artists = ", ".join(artists) if isinstance(artists, list) else artists
|
||||||
|
self.track_cover = track_cover
|
||||||
|
self.font_url = font
|
||||||
|
|
||||||
|
def _get_font(self, size, font_bytes):
|
||||||
|
return ImageFont.truetype(io.BytesIO(font_bytes), size)
|
||||||
|
|
||||||
|
def _prepare_cover(self, size, radius):
|
||||||
|
cover = Image.open(io.BytesIO(self.track_cover)).convert("RGBA")
|
||||||
|
cover = cover.resize((size, size), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
|
mask = Image.new("L", (size, size), 0)
|
||||||
|
draw = ImageDraw.Draw(mask)
|
||||||
|
draw.rounded_rectangle((0, 0, size, size), radius=radius, fill=255)
|
||||||
|
|
||||||
|
output = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
||||||
|
output.paste(cover, (0, 0), mask=mask)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def _prepare_background(self, w, h):
|
||||||
|
bg = Image.open(io.BytesIO(self.track_cover)).convert("RGBA")
|
||||||
|
bg = bg.resize((w, h), Image.Resampling.BICUBIC)
|
||||||
|
bg = bg.filter(ImageFilter.GaussianBlur(radius=20))
|
||||||
|
bg = ImageEnhance.Brightness(bg).enhance(0.4)
|
||||||
|
return bg
|
||||||
|
|
||||||
|
def horizontal(self):
|
||||||
|
W, H = 1500, 600
|
||||||
|
padding = 60
|
||||||
|
cover_size = 480
|
||||||
|
|
||||||
|
font_bytes = requests.get(self.font_url).content
|
||||||
|
title_font = self._get_font(55, font_bytes)
|
||||||
|
artist_font = self._get_font(45, font_bytes)
|
||||||
|
lfm_font = self._get_font(35, font_bytes)
|
||||||
|
|
||||||
|
img = self._prepare_background(W, H)
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
cover = self._prepare_cover(cover_size, 30)
|
||||||
|
img.paste(cover, (padding, (H - cover_size) // 2), cover)
|
||||||
|
|
||||||
|
text_x = padding + cover_size + 60
|
||||||
|
text_y_start = 100
|
||||||
|
text_width_limit = W - text_x - padding
|
||||||
|
|
||||||
|
display_title = self.title
|
||||||
|
while title_font.getlength(display_title) > text_width_limit and len(display_title) > 0:
|
||||||
|
display_title = display_title[:-1]
|
||||||
|
if len(display_title) < len(self.title): display_title += "…"
|
||||||
|
|
||||||
|
display_artist = self.artists
|
||||||
|
while artist_font.getlength(display_artist) > text_width_limit and len(display_artist) > 0:
|
||||||
|
display_artist = display_artist[:-1]
|
||||||
|
if len(display_artist) < len(self.artists): display_artist += "…"
|
||||||
|
|
||||||
|
draw.text((text_x, text_y_start), display_title, font=title_font, fill="white")
|
||||||
|
draw.text((text_x, text_y_start + 70), display_artist, font=artist_font, fill="#B3B3B3")
|
||||||
|
|
||||||
|
bar_y = 480
|
||||||
|
draw.text((text_x, bar_y), "last.fm", font=lfm_font, fill="white")
|
||||||
|
|
||||||
|
by = io.BytesIO()
|
||||||
|
img.save(by, format="PNG")
|
||||||
|
by.seek(0)
|
||||||
|
by.name = "banner.png"
|
||||||
|
return by
|
||||||
|
|
||||||
|
def vertical(self):
|
||||||
|
W, H = 1000, 1500
|
||||||
|
padding = 80
|
||||||
|
cover_size = 800
|
||||||
|
|
||||||
|
font_bytes = requests.get(self.font_url).content
|
||||||
|
title_font = self._get_font(60, font_bytes)
|
||||||
|
artist_font = self._get_font(45, font_bytes)
|
||||||
|
lfm_font = self._get_font(35, font_bytes)
|
||||||
|
|
||||||
|
img = self._prepare_background(W, H)
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
cover = self._prepare_cover(cover_size, 40)
|
||||||
|
cover_x = (W - cover_size) // 2
|
||||||
|
cover_y = 120
|
||||||
|
img.paste(cover, (cover_x, cover_y), cover)
|
||||||
|
|
||||||
|
text_area_y = cover_y + cover_size + 120
|
||||||
|
text_width_limit = W - (padding * 2)
|
||||||
|
|
||||||
|
display_title = self.title
|
||||||
|
while title_font.getlength(display_title) > text_width_limit and len(display_title) > 0:
|
||||||
|
display_title = display_title[:-1]
|
||||||
|
if len(display_title) < len(self.title): display_title += "…"
|
||||||
|
|
||||||
|
display_artist = self.artists
|
||||||
|
while artist_font.getlength(display_artist) > text_width_limit and len(display_artist) > 0:
|
||||||
|
display_artist = display_artist[:-1]
|
||||||
|
if len(display_artist) < len(self.artists): display_artist += "…"
|
||||||
|
|
||||||
|
title_w = title_font.getlength(display_title)
|
||||||
|
draw.text(((W - title_w) / 2, text_area_y), display_title, font=title_font, fill="white")
|
||||||
|
|
||||||
|
artist_w = artist_font.getlength(display_artist)
|
||||||
|
draw.text(((W - artist_w) / 2, text_area_y + 75), display_artist, font=artist_font, fill="#B3B3B3")
|
||||||
|
|
||||||
|
bar_y = text_area_y + 260
|
||||||
|
|
||||||
|
lfm_w = lfm_font.getlength("last.fm")
|
||||||
|
draw.text(((W - lfm_w) / 2, bar_y), "last.fm", font=lfm_font, fill="white")
|
||||||
|
|
||||||
|
by = io.BytesIO()
|
||||||
|
img.save(by, format="PNG")
|
||||||
|
by.seek(0)
|
||||||
|
by.name = "banner.png"
|
||||||
|
return by
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class lastfmmod(loader.Module):
|
||||||
|
"""Module for music from different services"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "LastFm",
|
||||||
|
"no_track": "<emoji document_id=5465665476971471368>❌</emoji> <b>No track is currently playing</b>",
|
||||||
|
"_doc_text": "The text that will be written next to the file",
|
||||||
|
"_doc_username": "Your username from last.fm",
|
||||||
|
"nick_error": "<emoji document_id=5465665476971471368>❌</emoji> <b>Put your nickname from last.fm</b>",
|
||||||
|
"uploading": "<emoji document_id=5841359499146825803>🕔</emoji> <i>Uploading banner...</i>",
|
||||||
|
}
|
||||||
|
strings_ru = {
|
||||||
|
"name": "LastFm",
|
||||||
|
"no_track": "<emoji document_id=5465665476971471368>❌</emoji> <b>Сейчас ничего не играет</b>",
|
||||||
|
"_doc_text": "Текст, который будет написан рядом с файлом",
|
||||||
|
"_doc_username": "Ваш username с last.fm",
|
||||||
|
"nick_error": "<emoji document_id=5465665476971471368>❌</emoji> <b>Укажите ваш никнейм с last.fm</b>",
|
||||||
|
"uploading": "<emoji document_id=5841359499146825803>🕔</emoji> <i>Загрузка баннера...</i>",
|
||||||
|
}
|
||||||
|
strings_jp = {
|
||||||
|
"name": "LastFm",
|
||||||
|
"no_track": "<emoji document_id=5465665476971471368>❌</emoji> <b>現在再生中のトラックはありません</b>",
|
||||||
|
"_doc_text": "ファイルの横に表示されるテキスト",
|
||||||
|
"_doc_username": "Last.fmのユーザー名",
|
||||||
|
"nick_error": "<emoji document_id=5465665476971471368>❌</emoji> <b>Last.fmのニックネームを入力してください</b>",
|
||||||
|
"uploading": "<emoji document_id=5841359499146825803>🕔</emoji> <i>バナーをアップロード中...</i>",
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue("username", None, lambda: self.strings["_doc_username"]),
|
||||||
|
loader.ConfigValue("custom_text", "<emoji document_id=5413612466208799435>🤩</emoji> <b>{song_name}</b> — <b>{song_artist}</b>", lambda: self.strings["_doc_text"]),
|
||||||
|
loader.ConfigValue("font", "https://raw.githubusercontent.com/kamekuro/assets/master/fonts/Onest-Bold.ttf", "Custom font URL (ttf)"),
|
||||||
|
loader.ConfigValue("banner_version", "horizontal", lambda: "Banner version", validator=loader.validators.Choice(["horizontal", "vertical"])),
|
||||||
|
)
|
||||||
|
|
||||||
|
@loader.command(alias="np")
|
||||||
|
async def nowplay(self, message):
|
||||||
|
"""| send playing track info"""
|
||||||
|
user = self.config["username"]
|
||||||
|
if not user:
|
||||||
|
await self.invoke("config", "lastfm", message=message)
|
||||||
|
return await utils.answer(message, self.strings["nick_error"])
|
||||||
|
|
||||||
|
try:
|
||||||
|
url = f'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&nowplaying=true&user={user}&api_key=460cda35be2fbf4f28e8ea7a38580730&format=json'
|
||||||
|
data = requests.get(url).json()
|
||||||
|
track = next((t for t in data.get('recenttracks', {}).get('track', []) if t.get('@attr', {}).get('nowplaying')), None)
|
||||||
|
if not track:
|
||||||
|
return await utils.answer(message, self.strings["no_track"])
|
||||||
|
name = track.get('name', 'Unknown')
|
||||||
|
artist = track.get('artist', {}).get('#text', 'Unknown')
|
||||||
|
caption = self.config["custom_text"].format(song_artist=artist, song_name=name)
|
||||||
|
imgs = track.get('image', [])
|
||||||
|
cov_url = next((i['#text'] for i in imgs if i['size'] == 'extralarge'), imgs[-1]['#text'] if imgs else None)
|
||||||
|
|
||||||
|
if not cov_url:
|
||||||
|
return await utils.answer(message, caption)
|
||||||
|
msg = await utils.answer(message, self.strings["uploading"])
|
||||||
|
cov_bytes = await utils.run_sync(requests.get, cov_url)
|
||||||
|
banners = Banners(name, artist, cov_bytes.content, self.config["font"])
|
||||||
|
file = await utils.run_sync(getattr(banners, self.config["banner_version"]))
|
||||||
|
await utils.answer(msg, caption, file=file)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await utils.answer(message, f"<pre><code class='language-python'>{e}</code></pre>")
|
||||||
48
radiocycle/Modules/Neofetch.py
Normal file
48
radiocycle/Modules/Neofetch.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# =======================================
|
||||||
|
# _ __ __ __ _
|
||||||
|
# | |/ /___ | \/ | ___ __| |___
|
||||||
|
# | ' // _ \ | |\/| |/ _ \ / _` / __|
|
||||||
|
# | . \ __/ | | | | (_) | (_| \__ \
|
||||||
|
# |_|\_\___| |_| |_|\___/ \__,_|___/
|
||||||
|
# @ke_mods
|
||||||
|
# =======================================
|
||||||
|
#
|
||||||
|
# LICENSE: CC BY-ND 4.0 (Attribution-NoDerivatives 4.0 International)
|
||||||
|
# --------------------------------------
|
||||||
|
# https://creativecommons.org/licenses/by-nd/4.0/legalcode
|
||||||
|
# =======================================
|
||||||
|
|
||||||
|
# meta developer: @ke_mods
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
from .. import loader, utils
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class NeofetchMod(loader.Module):
|
||||||
|
strings = {
|
||||||
|
"name": "Neofetch",
|
||||||
|
"not_installed": "<b>Please, install</b> <i>Neofetch</i> <b>package</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"not_installed": "<b>Пожалуйста, установите пакет</b> <i>Neofetch</i>",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ua = {
|
||||||
|
"not_installed": "<b>Будь ласка, встановіть пакет<b> <i>Neofetch</i>",
|
||||||
|
}
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
ru_doc="- запустить команду neofetch",
|
||||||
|
ua_doc="- запустити команду neofetch",
|
||||||
|
)
|
||||||
|
async def neofetchcmd(self, message):
|
||||||
|
"""- run neofetch command"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(["neofetch", "--stdout"], capture_output=True, text=True)
|
||||||
|
output = result.stdout
|
||||||
|
await utils.answer(message, f"<pre>{utils.escape_html(output)}</pre>")
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
await utils.answer(message, self.strings("not_installed"))
|
||||||
|
|
||||||
211
radiocycle/Modules/PicToStories.py
Normal file
211
radiocycle/Modules/PicToStories.py
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
# =======================================
|
||||||
|
# _ __ __ __ _
|
||||||
|
# | |/ /___ | \/ | ___ __| |___
|
||||||
|
# | ' // _ \ | |\/| |/ _ \ / _` / __|
|
||||||
|
# | . \ __/ | | | | (_) | (_| \__ \
|
||||||
|
# |_|\_\___| |_| |_|\___/ \__,_|___/
|
||||||
|
# @ke_mods
|
||||||
|
# =======================================
|
||||||
|
#
|
||||||
|
# LICENSE: CC BY-ND 4.0 (Attribution-NoDerivatives 4.0 International)
|
||||||
|
# --------------------------------------
|
||||||
|
# https://creativecommons.org/licenses/by-nd/4.0/legalcode
|
||||||
|
# =======================================
|
||||||
|
|
||||||
|
# meta developer: @ke_mods
|
||||||
|
# requires: pillow
|
||||||
|
|
||||||
|
import io
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from telethon import functions, types
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class PicToStoriesMod(loader.Module):
|
||||||
|
"""Grid for stories"""
|
||||||
|
|
||||||
|
strings = {
|
||||||
|
"name": "PicToStories",
|
||||||
|
"no_rep": (
|
||||||
|
"<emoji document_id=5879813604068298387>❗️</emoji> "
|
||||||
|
"<b>Reply to photo!</b>"
|
||||||
|
),
|
||||||
|
"work": (
|
||||||
|
"<emoji document_id=5841359499146825803>🕔</emoji> "
|
||||||
|
"<b>Processing...</b>"
|
||||||
|
),
|
||||||
|
"done": (
|
||||||
|
"<emoji document_id=5776375003280838798>✅</emoji> "
|
||||||
|
"<b>Done! Check your profile.</b>"
|
||||||
|
),
|
||||||
|
"err": (
|
||||||
|
"<emoji document_id=5778527486270770928>❌</emoji> "
|
||||||
|
"<b>Error:</b> {}"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"no_rep": (
|
||||||
|
"<emoji document_id=5879813604068298387>❗️</emoji> "
|
||||||
|
"<b>Реплай на фото!</b>"
|
||||||
|
),
|
||||||
|
"work": (
|
||||||
|
"<emoji document_id=5841359499146825803>🕔</emoji> "
|
||||||
|
"<b>Обрабатываю...</b>"
|
||||||
|
),
|
||||||
|
"done": (
|
||||||
|
"<emoji document_id=5776375003280838798>✅</emoji> "
|
||||||
|
"<b>Готово! Проверяй профиль.</b>"
|
||||||
|
),
|
||||||
|
"err": (
|
||||||
|
"<emoji document_id=5778527486270770928>❌</emoji> "
|
||||||
|
"<b>Ошибка:</b> {}"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.config = loader.ModuleConfig(
|
||||||
|
loader.ConfigValue(
|
||||||
|
"period",
|
||||||
|
48,
|
||||||
|
lambda: "Visibility period in hours",
|
||||||
|
validator=loader.validators.Integer(),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"blacklist",
|
||||||
|
[],
|
||||||
|
lambda: "Blacklisted user IDs",
|
||||||
|
validator=loader.validators.Series(loader.validators.Integer()),
|
||||||
|
),
|
||||||
|
loader.ConfigValue(
|
||||||
|
"cooldown",
|
||||||
|
0,
|
||||||
|
lambda: "Cooldown between stories in seconds",
|
||||||
|
validator=loader.validators.Integer(minimum=0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@loader.command(ru_doc="<реплай на фото> [название альбома] - сделать сетку")
|
||||||
|
async def ptscmd(self, message):
|
||||||
|
"""<reply to photo> [album name] - make grid"""
|
||||||
|
args = utils.get_args_raw(message)
|
||||||
|
reply = await message.get_reply_message()
|
||||||
|
if not reply or not reply.media:
|
||||||
|
await utils.answer(message, self.strings("no_rep"))
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
image_bytes = await reply.download_media(file=bytes)
|
||||||
|
img = Image.open(io.BytesIO(image_bytes))
|
||||||
|
except Exception as e:
|
||||||
|
await utils.answer(message, self.strings("err").format(e))
|
||||||
|
return
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("work"))
|
||||||
|
|
||||||
|
w, h = img.size
|
||||||
|
curr_ratio = w / h
|
||||||
|
variants = [
|
||||||
|
(5 / 4, 2),
|
||||||
|
(4 / 5, 3),
|
||||||
|
(3 / 5, 4),
|
||||||
|
(9 / 16, 5)
|
||||||
|
]
|
||||||
|
best_ratio, rows = min(variants, key=lambda x: abs(curr_ratio - x[0]))
|
||||||
|
|
||||||
|
new_h = int(w / best_ratio)
|
||||||
|
img = img.resize((w, new_h), Image.LANCZOS)
|
||||||
|
w, h = img.size
|
||||||
|
|
||||||
|
parts = []
|
||||||
|
pw, ph = w // 3, h // rows
|
||||||
|
for r in range(rows):
|
||||||
|
for c in range(3):
|
||||||
|
x, y = c * pw, r * ph
|
||||||
|
parts.append(img.crop((x, y, x + pw, y + ph)))
|
||||||
|
|
||||||
|
parts.reverse()
|
||||||
|
|
||||||
|
privacy = [types.InputPrivacyValueAllowAll()]
|
||||||
|
if self.config["blacklist"]:
|
||||||
|
entities = []
|
||||||
|
for uid in self.config["blacklist"]:
|
||||||
|
try:
|
||||||
|
entities.append(await self.client.get_input_entity(uid))
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if entities:
|
||||||
|
privacy.append(types.InputPrivacyValueDisallowUsers(users=entities))
|
||||||
|
|
||||||
|
story_ids = []
|
||||||
|
for i, p in enumerate(parts):
|
||||||
|
out = io.BytesIO()
|
||||||
|
p.save(out, "JPEG", quality=95)
|
||||||
|
out.seek(0)
|
||||||
|
|
||||||
|
uploaded_file = await self.client.upload_file(out, file_name="s.jpg")
|
||||||
|
res = await self.client(
|
||||||
|
functions.stories.SendStoryRequest(
|
||||||
|
peer=types.InputPeerSelf(),
|
||||||
|
media=types.InputMediaUploadedPhoto(uploaded_file),
|
||||||
|
privacy_rules=privacy,
|
||||||
|
period=self.config["period"] * 3600,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
sid = next(
|
||||||
|
(
|
||||||
|
u.story_id if hasattr(u, "story_id") else u.id
|
||||||
|
for u in res.updates
|
||||||
|
if hasattr(u, "story_id") or hasattr(u, "id")
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
if sid:
|
||||||
|
story_ids.append(sid)
|
||||||
|
|
||||||
|
if self.config["cooldown"] > 0 and i < len(parts) - 1:
|
||||||
|
await asyncio.sleep(self.config["cooldown"])
|
||||||
|
|
||||||
|
if not story_ids:
|
||||||
|
return
|
||||||
|
|
||||||
|
if args:
|
||||||
|
all_albums = await self.client(
|
||||||
|
functions.stories.GetAlbumsRequest(peer=types.InputPeerSelf(), hash=0)
|
||||||
|
)
|
||||||
|
|
||||||
|
target = next(
|
||||||
|
(a for a in all_albums.albums if getattr(a, 'title', '') == args),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
if target:
|
||||||
|
await self.client(
|
||||||
|
functions.stories.UpdateAlbumRequest(
|
||||||
|
peer=types.InputPeerSelf(),
|
||||||
|
album_id=target.album_id,
|
||||||
|
add_stories=story_ids,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await self.client(
|
||||||
|
functions.stories.CreateAlbumRequest(
|
||||||
|
peer=types.InputPeerSelf(),
|
||||||
|
stories=story_ids,
|
||||||
|
title=args,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await self.client(
|
||||||
|
functions.stories.TogglePinnedRequest(
|
||||||
|
peer=types.InputPeerSelf(), id=story_ids, pinned=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("done"))
|
||||||
1436
radiocycle/Modules/SpotifyMod.py
Normal file
1436
radiocycle/Modules/SpotifyMod.py
Normal file
File diff suppressed because it is too large
Load Diff
74
radiocycle/Modules/UnbanAll.py
Normal file
74
radiocycle/Modules/UnbanAll.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# =======================================
|
||||||
|
# _ __ __ __ _
|
||||||
|
# | |/ /___ | \/ | ___ __| |___
|
||||||
|
# | ' // _ \ | |\/| |/ _ \ / _` / __|
|
||||||
|
# | . \ __/ | | | | (_) | (_| \__ \
|
||||||
|
# |_|\_\___| |_| |_|\___/ \__,_|___/
|
||||||
|
# @ke_mods
|
||||||
|
# =======================================
|
||||||
|
#
|
||||||
|
# LICENSE: CC BY-ND 4.0 (Attribution-NoDerivatives 4.0 International)
|
||||||
|
# --------------------------------------
|
||||||
|
# https://creativecommons.org/licenses/by-nd/4.0/legalcode
|
||||||
|
# =======================================
|
||||||
|
|
||||||
|
# meta developer: @ke_mods
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
from telethon.tl.types import ChatBannedRights
|
||||||
|
from telethon.tl.functions.channels import EditBannedRequest
|
||||||
|
from telethon.tl.types import ChannelParticipantsKicked
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class UnbanAllMod(loader.Module):
|
||||||
|
strings = {
|
||||||
|
"name": "UnbanAll",
|
||||||
|
"no_rights": "<b>❌ I don't have administrator rights to remove restrictions.</b>",
|
||||||
|
"success": "<b>✅ All banned chat members have been unbanned.</b>",
|
||||||
|
"unban_in_process": "<b>👀 Unbanning users...</b>",
|
||||||
|
"no_banned": "<b>ℹ️ There are no banned members in this chat.</b>",
|
||||||
|
"error_occured": "<b>💢 An error occurred while unbanning user <code>{}</code>:</b>\n<code>{}</code>",
|
||||||
|
}
|
||||||
|
strings_ru = {
|
||||||
|
"no_rights": "<b>❌ У меня нет прав администратора для снятия ограничений.</b>",
|
||||||
|
"success": "<b>✅ Все забаненные участники чата были разблокированы.</b>",
|
||||||
|
"unban_in_process": "<b>👀 Разбаниваю пользователей...</b>",
|
||||||
|
"no_banned": "<b>ℹ️ В этом чате нет забаненных участников.</b>",
|
||||||
|
"error_occured": "<b>💢 Произошла ошибка при разблокировке пользователя <code>{}</code>:</b>\n<code>{}</code>",
|
||||||
|
}
|
||||||
|
|
||||||
|
@loader.command(ru_doc="- Разбанить всех забаненных пользователей")
|
||||||
|
async def unbanallcmd(self, message):
|
||||||
|
"""- Unban all banned users"""
|
||||||
|
chat = await message.get_chat()
|
||||||
|
|
||||||
|
if not chat.admin_rights and not chat.creator:
|
||||||
|
await utils.answer(message, self.strings("no_rights"))
|
||||||
|
return
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("unban_in_process"))
|
||||||
|
|
||||||
|
no_banned = True
|
||||||
|
|
||||||
|
async for user in self.client.iter_participants(
|
||||||
|
message.chat_id, filter=ChannelParticipantsKicked
|
||||||
|
):
|
||||||
|
|
||||||
|
no_banned = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.client(EditBannedRequest(
|
||||||
|
message.chat_id,
|
||||||
|
user.id,
|
||||||
|
ChatBannedRights(until_date=0)
|
||||||
|
))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await utils.answer(message, self.strings("error_occured").format(user.id, e))
|
||||||
|
pass
|
||||||
|
|
||||||
|
if no_banned:
|
||||||
|
await utils.answer(message, self.strings("no_banned"))
|
||||||
|
return
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("success"))
|
||||||
7
radiocycle/Modules/full.txt
Normal file
7
radiocycle/Modules/full.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Neofetch
|
||||||
|
randomanimepic
|
||||||
|
SpotifyMod
|
||||||
|
UnbanAll
|
||||||
|
voicetotext
|
||||||
|
LastFm
|
||||||
|
PicToStories
|
||||||
65
radiocycle/Modules/randomanimepic.py
Normal file
65
radiocycle/Modules/randomanimepic.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# =======================================
|
||||||
|
# _ __ __ __ _
|
||||||
|
# | |/ /___ | \/ | ___ __| |___
|
||||||
|
# | ' // _ \ | |\/| |/ _ \ / _` / __|
|
||||||
|
# | . \ __/ | | | | (_) | (_| \__ \
|
||||||
|
# |_|\_\___| |_| |_|\___/ \__,_|___/
|
||||||
|
# @ke_mods
|
||||||
|
# =======================================
|
||||||
|
#
|
||||||
|
# LICENSE: CC BY-ND 4.0 (Attribution-NoDerivatives 4.0 International)
|
||||||
|
# --------------------------------------
|
||||||
|
# https://creativecommons.org/licenses/by-nd/4.0/legalcode
|
||||||
|
# =======================================
|
||||||
|
|
||||||
|
# meta developer: @ke_mods
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
from logging import basicConfig
|
||||||
|
from .. import loader, utils
|
||||||
|
|
||||||
|
basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class RandomAnimePicMod(loader.Module):
|
||||||
|
strings = {
|
||||||
|
"name": "RandomAnimePic",
|
||||||
|
"img": "<emoji document_id=4916036072560919511>✅</emoji> <b>Your anime pic</b>\n<emoji document_id=5877465816030515018>🔗</emoji> <b>URL:</b> {}",
|
||||||
|
"loading": "<emoji document_id=4911241630633165627>✨</emoji> <b>Loading image...</b>",
|
||||||
|
"error": "<emoji document_id=5116151848855667552>🚫</emoji> <b>An unexpected error occurred...</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"img": "<emoji document_id=4916036072560919511>✅</emoji> <b>Ваша аниме-картинка</b>\n<emoji document_id=5877465816030515018>🔗</emoji> <b>Ссылка:</b> {}",
|
||||||
|
"loading": "<emoji document_id=4911241630633165627>✨</emoji> <b>Загрузка изображения...</b>",
|
||||||
|
"error": "<emoji document_id=5116151848855667552>🚫</emoji> <b>Произошла непредвиденная ошибка...</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
ru_doc="- получить рандомную аниме-картинку 👀"
|
||||||
|
)
|
||||||
|
async def rapiccmd(self, message):
|
||||||
|
"""- fetch random anime-pic 👀"""
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("loading"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = requests.get("https://api.nekosia.cat/api/v1/images/cute?count=1")
|
||||||
|
res.raise_for_status()
|
||||||
|
data = res.json()
|
||||||
|
image_url = data['image']['original']['url']
|
||||||
|
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("img").format(image_url), file=image_url, reply_to=message.reply_to_msg_id)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
logger.error("Error fetching random anime pic: %s", traceback.format_exc())
|
||||||
|
|
||||||
|
await utils.answer(message, self.strings("error"))
|
||||||
|
|
||||||
|
await asyncio.sleep(5)
|
||||||
77
radiocycle/Modules/voicetotext.py
Normal file
77
radiocycle/Modules/voicetotext.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# =======================================
|
||||||
|
# _ __ __ __ _
|
||||||
|
# | |/ /___ | \/ | ___ __| |___
|
||||||
|
# | ' // _ \ | |\/| |/ _ \ / _` / __|
|
||||||
|
# | . \ __/ | | | | (_) | (_| \__ \
|
||||||
|
# |_|\_\___| |_| |_|\___/ \__,_|___/
|
||||||
|
# @ke_mods
|
||||||
|
# =======================================
|
||||||
|
#
|
||||||
|
# LICENSE: CC BY-ND 4.0 (Attribution-NoDerivatives 4.0 International)
|
||||||
|
# --------------------------------------
|
||||||
|
# https://creativecommons.org/licenses/by-nd/4.0/legalcode
|
||||||
|
# =======================================
|
||||||
|
|
||||||
|
# meta developer: @ke_mods
|
||||||
|
# scope: ffmpeg
|
||||||
|
# requires: pydub SpeechRecognition
|
||||||
|
|
||||||
|
from .. import loader, utils
|
||||||
|
import os
|
||||||
|
import speech_recognition as sr
|
||||||
|
from pydub import AudioSegment
|
||||||
|
|
||||||
|
@loader.tds
|
||||||
|
class VoiceToTextMod(loader.Module):
|
||||||
|
strings = {
|
||||||
|
"name": "VoiceToText",
|
||||||
|
"process_text": "<emoji document_id=4911241630633165627>✨</emoji> <b>Recognizing the message text...</b>",
|
||||||
|
"vtt_success": "<emoji document_id=5116110535565247270>🔥</emoji> <b>Recognized text:</b>\n<blockquote expandable>{}</blockquote>",
|
||||||
|
"vtt_failure": "<emoji document_id=5116151848855667552>🚫</emoji> <b>Failed to recognize the message.</b>",
|
||||||
|
"vtt_request_error": "<emoji document_id=5116151848855667552>🚫</emoji> <b>Error when contacting the recognition service:</b>\n<code>{}</code>",
|
||||||
|
"vtt_invalid": "<emoji document_id=5116151848855667552>🚫</emoji> <b>Please reply to a voice or video message with the command</b> <code>{}vtt</code>",
|
||||||
|
"vtt_successful": "<emoji document_id=4916036072560919511>✅</emoji> <b>Text recognized successfully</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
strings_ru = {
|
||||||
|
"process_text": "<emoji document_id=4911241630633165627>✨</emoji> <b>Распознаю текст сообщения...</b>",
|
||||||
|
"vtt_success": "<emoji document_id=5116110535565247270>🔥</emoji> <b>Распознанный текст:</b>\n<blockquote expandable>{}</blockquote>",
|
||||||
|
"vtt_failure": "<emoji document_id=5116151848855667552>🚫</emoji> <b>Не удалось распознать сообщение.</b>",
|
||||||
|
"vtt_request_error": "<emoji document_id=5116151848855667552>🚫</emoji> <b>Ошибка при обращении к сервису распознавания:</b>\n<code>{}</code>",
|
||||||
|
"vtt_invalid": "<emoji document_id=5116151848855667552>🚫</emoji> <b>Пожалуйста, ответьте на голосовое или видеосообщение командой</b> <code>{}vtt</code>",
|
||||||
|
"vtt_successful": "<emoji document_id=4916036072560919511>✅</emoji> <b>Текст успешно распознан</b>",
|
||||||
|
}
|
||||||
|
|
||||||
|
@loader.command(
|
||||||
|
ru_doc="- распознает текст из голосового или видеосообщения.",
|
||||||
|
)
|
||||||
|
async def vttcmd(self, message):
|
||||||
|
"""- recognizes text from voice or video messages."""
|
||||||
|
reply = await message.get_reply_message()
|
||||||
|
|
||||||
|
if not reply or not (reply.voice or reply.video_note):
|
||||||
|
await utils.answer(message, self.strings["vtt_invalid"].format(self.get_prefix()))
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = await utils.answer(
|
||||||
|
message, self.strings["process_text"], reply_to=message.id
|
||||||
|
)
|
||||||
|
|
||||||
|
media_file = await reply.download_media()
|
||||||
|
wav_file = media_file.replace('.mp4', '.wav') if reply.video_note else media_file.replace('.oga', '.wav')
|
||||||
|
|
||||||
|
try:
|
||||||
|
AudioSegment.from_file(media_file).export(wav_file, format='wav')
|
||||||
|
recognizer = sr.Recognizer()
|
||||||
|
with sr.AudioFile(wav_file) as source:
|
||||||
|
audio_data = recognizer.record(source)
|
||||||
|
try:
|
||||||
|
text = recognizer.recognize_google(audio_data, language='ru-RU')
|
||||||
|
await utils.answer(msg, self.strings["vtt_success"].format(text))
|
||||||
|
except sr.UnknownValueError:
|
||||||
|
await utils.answer(msg, self.strings["vtt_failure"])
|
||||||
|
except sr.RequestError as e:
|
||||||
|
await utils.answer(msg, self.strings["vtt_request_error"].format(e))
|
||||||
|
finally:
|
||||||
|
os.remove(media_file)
|
||||||
|
os.remove(wav_file)
|
||||||
@@ -1,396 +1,409 @@
|
|||||||
__version__ = (1, 1, 1, 1)
|
__version__ = (1, 2, 0, 0)
|
||||||
|
|
||||||
# This file is a part of Hikka Userbot!
|
# This file is a part of Hikka Userbot!
|
||||||
# This product includes software developed by t.me/Fl1yd and t.me/spypm.
|
# This product includes software developed by t.me/Fl1yd and t.me/spypm.
|
||||||
# Based on the "SQuotes" module.
|
# Based on the "SQuotes" module.
|
||||||
|
|
||||||
# 🌐 https://github.com/hikariatama/Hikka
|
# 🌐 https://github.com/hikariatama/Hikka
|
||||||
|
|
||||||
# You CAN edit this file without direct permission from the author.
|
# You CAN edit this file without direct permission from the author.
|
||||||
# You can redistribute this file with any modifications.
|
# You can redistribute this file with any modifications.
|
||||||
|
|
||||||
# thx to t.me/LyoSU for github.com/LyoSU/quote-api
|
# thx to t.me/LyoSU for github.com/LyoSU/quote-api
|
||||||
|
|
||||||
# meta developer: @yg_modules
|
# meta developer: @yg_modules
|
||||||
# scope: hikka_only
|
# scope: hikka_only
|
||||||
# scope: hikka_min 1.6.3
|
# scope: hikka_min 1.6.3
|
||||||
|
|
||||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
# Changelog v1.2:
|
||||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
# - Added: Proxy for users from RF
|
||||||
|
# - Fixed: Correct reply author resolving for forwarded messages
|
||||||
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
|
import base64, io, requests, telethon
|
||||||
from telethon.extensions import html
|
from time import gmtime
|
||||||
from telethon.tl.patched import Message
|
from typing import List, Optional, Tuple, Union
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
from .. import loader, utils
|
from telethon.tl import types
|
||||||
|
from telethon.extensions import html
|
||||||
class Dick:
|
from telethon.tl.patched import Message
|
||||||
@staticmethod
|
|
||||||
def ents(es: types.TypeMessageEntity) -> List[dict]:
|
from .. import loader, utils
|
||||||
out: List[dict] = []
|
|
||||||
if not es: return out
|
class Dick:
|
||||||
for e in es:
|
@staticmethod
|
||||||
try:
|
def ents(es: types.TypeMessageEntity) -> List[dict]:
|
||||||
d = e.to_dict(); t = d.pop("_","").replace("MessageEntity","").lower()
|
out: List[dict] = []
|
||||||
if not t: continue
|
if not es: return out
|
||||||
mt = {"bold": "bold","italic": "italic","underline": "underline","strikethrough": "strikethrough",
|
for e in es:
|
||||||
"code": "code","pre": "pre","texturl": "text_link","url": "url","email": "email",
|
try:
|
||||||
"phone": "phone_number","mention": "mention",
|
d = e.to_dict(); t = d.pop("_","").replace("MessageEntity","").lower()
|
||||||
"mentionname": "text_mention","hashtag": "hashtag","cashtag": "cashtag",
|
if not t: continue
|
||||||
"botcommand": "bot_command","spoiler": "spoiler","customemoji": "custom_emoji"}.get(t,t)
|
mt = {"bold": "bold","italic": "italic","underline": "underline","strikethrough": "strikethrough",
|
||||||
it = {"type": mt,"offset": d.get("offset",0),"length": d.get("length",0)}
|
"code": "code","pre": "pre","texturl": "text_link","url": "url","email": "email",
|
||||||
if t=="texturl": it["url"]=d.get("url","")
|
"phone": "phone_number","mention": "mention",
|
||||||
elif t=="mentionname": it["user"]={"id": d.get("user_id",0)}
|
"mentionname": "text_mention","hashtag": "hashtag","cashtag": "cashtag",
|
||||||
elif t=="customemoji": it["custom_emoji_id"]=str(d.get("document_id",""))
|
"botcommand": "bot_command","spoiler": "spoiler","customemoji": "custom_emoji"}.get(t,t)
|
||||||
elif t=="pre": it["language"]=d.get("language","")
|
it = {"type": mt,"offset": d.get("offset",0),"length": d.get("length",0)}
|
||||||
out.append(it)
|
if t=="texturl": it["url"]=d.get("url","")
|
||||||
except Exception: continue
|
elif t=="mentionname": it["user"]={"id": d.get("user_id",0)}
|
||||||
return out
|
elif t=="customemoji": it["custom_emoji_id"]=str(d.get("document_id",""))
|
||||||
|
elif t=="pre": it["language"]=d.get("language","")
|
||||||
@staticmethod
|
out.append(it)
|
||||||
def dur(s: Union[int,float]) -> str:
|
except Exception: continue
|
||||||
t=gmtime(s); return (f"{t.tm_hour:02d}:" if t.tm_hour>0 else "")+f"{t.tm_min:02d}:{t.tm_sec:02d}"
|
return out
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def desc(m: Message, rep: bool=False) -> str:
|
def dur(s: Union[int,float]) -> str:
|
||||||
return (
|
t=gmtime(s); return (f"{t.tm_hour:02d}:" if t.tm_hour>0 else "")+f"{t.tm_min:02d}:{t.tm_sec:02d}"
|
||||||
"📷 Фото" if m.photo and rep else
|
|
||||||
(m.file.emoji+" Стикер") if m.sticker and rep else
|
@staticmethod
|
||||||
"📹 Видеосообщение" if m.video_note and rep else
|
def desc(m: Message, rep: bool=False) -> str:
|
||||||
"📹 Видео" if m.video and rep else
|
return (
|
||||||
"🖼 GIF" if m.gif else
|
"📷 Фото" if m.photo and rep else
|
||||||
"📊 Опрос" if m.poll else
|
(m.file.emoji+" Стикер") if m.sticker and rep else
|
||||||
"📍 Местоположение" if m.geo else
|
"📹 Видеосообщение" if m.video_note and rep else
|
||||||
"👤 Контакт" if m.contact else
|
"📹 Видео" if m.video and rep else
|
||||||
(f"🎵 Голосовое сообщение: {Dick.dur(m.voice.attributes[0].duration)}" if m.voice else
|
"🖼 GIF" if m.gif else
|
||||||
(f"🎧 Музыка: {Dick.dur(m.audio.attributes[0].duration)} | {m.audio.attributes[0].performer} - {m.audio.attributes[0].title}" if m.audio else
|
"📊 Опрос" if m.poll else
|
||||||
(f"💾 Файл: {m.file.name}" if isinstance(m.media, types.MessageMediaDocument) and not Dick.pick(m) else
|
"📍 Местоположение" if m.geo else
|
||||||
(f"{m.media.emoticon} Кость: {m.media.value}" if isinstance(m.media, types.MessageMediaDice) else
|
"👤 Контакт" if m.contact else
|
||||||
(f"Сервисное сообщение: {m.action.to_dict().get('_')}" if isinstance(m, types.MessageService) 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
|
||||||
@staticmethod
|
(f"💾 Файл: {m.file.name}" if isinstance(m.media, types.MessageMediaDocument) and not Dick.pick(m) else
|
||||||
def split(name: Optional[str]) -> Tuple[str,str]:
|
(f"{m.media.emoticon} Кость: {m.media.value}" if isinstance(m.media, types.MessageMediaDice) else
|
||||||
if not name: return "",""
|
(f"Сервисное сообщение: {m.action.to_dict().get('_')}" if isinstance(m, types.MessageService) else "")))))) #)))
|
||||||
p=name.split(); return (p[0], " ".join(p[1:]) if len(p)>1 else "")
|
|
||||||
|
@staticmethod
|
||||||
@staticmethod
|
def split(name: Optional[str]) -> Tuple[str,str]:
|
||||||
def pick(m: Message):
|
if not name: return "",""
|
||||||
if m and m.media:
|
p=name.split(); return (p[0], " ".join(p[1:]) if len(p)>1 else "")
|
||||||
return m.photo or m.sticker or m.video or m.video_note or m.gif or m.web_preview
|
|
||||||
return None
|
@staticmethod
|
||||||
|
def pick(m: Message):
|
||||||
@staticmethod
|
if m and m.media:
|
||||||
def wf(b: Optional[bytes]) -> List[int]:
|
return m.photo or m.sticker or m.video or m.video_note or m.gif or m.web_preview
|
||||||
if not b: return []
|
return None
|
||||||
n=(len(b)*8)//5
|
|
||||||
if not n: return []
|
@staticmethod
|
||||||
out: List[int]=[]
|
def wf(b: Optional[bytes]) -> List[int]:
|
||||||
last=n-1
|
if not b: return []
|
||||||
for i in range(last):
|
n=(len(b)*8)//5
|
||||||
j=i*5; bi,sh=j//8,j%8
|
if not n: return []
|
||||||
v=int.from_bytes(b[bi:bi+2],"little") if bi+1<len(b) else b[bi]
|
out: List[int]=[]
|
||||||
out.append((v>>sh)&0b11111)
|
last=n-1
|
||||||
j=last*5; bi,sh=j//8,j%8
|
for i in range(last):
|
||||||
v=int.from_bytes(b[bi:bi+2],"little") if bi+1<len(b) else b[bi]
|
j=i*5; bi,sh=j//8,j%8
|
||||||
out.append((v>>sh)&0b11111)
|
v=int.from_bytes(b[bi:bi+2],"little") if bi+1<len(b) else b[bi]
|
||||||
return out
|
out.append((v>>sh)&0b11111)
|
||||||
|
j=last*5; bi,sh=j//8,j%8
|
||||||
@staticmethod
|
v=int.from_bytes(b[bi:bi+2],"little") if bi+1<len(b) else b[bi]
|
||||||
async def img(b: bytes, circle: bool=False) -> Optional[str]:
|
out.append((v>>sh)&0b11111)
|
||||||
try:
|
return out
|
||||||
im=Image.open(io.BytesIO(b))
|
|
||||||
if im.mode!="RGBA": im=im.convert("RGBA")
|
@staticmethod
|
||||||
if circle:
|
async def img(b: bytes, circle: bool=False) -> Optional[str]:
|
||||||
size=min(im.size)
|
try:
|
||||||
mask=Image.new("L",(size,size),0); ImageDraw.Draw(mask).ellipse((0,0,size,size),fill=255)
|
im=Image.open(io.BytesIO(b))
|
||||||
sq=Image.new("RGBA",(size,size),(0,0,0,0))
|
if im.mode!="RGBA": im=im.convert("RGBA")
|
||||||
off=((size-im.width)//2,(size-im.height)//2); sq.paste(im,off)
|
if circle:
|
||||||
im=Image.composite(sq,Image.new("RGBA",(size,size),(0,0,0,0)),mask)
|
size=min(im.size)
|
||||||
o=io.BytesIO(); im.save(o,format="PNG")
|
mask=Image.new("L",(size,size),0); ImageDraw.Draw(mask).ellipse((0,0,size,size),fill=255)
|
||||||
return f"data:image/png;base64,{base64.b64encode(o.getvalue()).decode()}"
|
sq=Image.new("RGBA",(size,size),(0,0,0,0))
|
||||||
except Exception:
|
off=((size-im.width)//2,(size-im.height)//2); sq.paste(im,off)
|
||||||
return None
|
im=Image.composite(sq,Image.new("RGBA",(size,size),(0,0,0,0)),mask)
|
||||||
|
o=io.BytesIO(); im.save(o,format="PNG")
|
||||||
@staticmethod
|
return f"data:image/png;base64,{base64.b64encode(o.getvalue()).decode()}"
|
||||||
async def stc(b: bytes) -> Optional[str]:
|
except Exception:
|
||||||
try:
|
return None
|
||||||
im=Image.open(io.BytesIO(b))
|
|
||||||
if im.mode not in ("RGBA","LA"): im=im.convert("RGBA")
|
@staticmethod
|
||||||
elif im.mode=="LA": im=im.convert("RGBA")
|
async def stc(b: bytes) -> Optional[str]:
|
||||||
o=io.BytesIO(); im.save(o,format="PNG")
|
try:
|
||||||
return f"data:image/png;base64,{base64.b64encode(o.getvalue()).decode()}"
|
im=Image.open(io.BytesIO(b))
|
||||||
except Exception:
|
if im.mode not in ("RGBA","LA"): im=im.convert("RGBA")
|
||||||
return None
|
elif im.mode=="LA": im=im.convert("RGBA")
|
||||||
|
o=io.BytesIO(); im.save(o,format="PNG")
|
||||||
@staticmethod
|
return f"data:image/png;base64,{base64.b64encode(o.getvalue()).decode()}"
|
||||||
async def proc(cli, obj, m: Message) -> Optional[dict]:
|
except Exception:
|
||||||
try:
|
return None
|
||||||
if m.voice:
|
|
||||||
for a in m.voice.attributes or []:
|
@staticmethod
|
||||||
if getattr(a,"voice",False) and hasattr(a,"waveform"):
|
async def proc(cli, obj, m: Message) -> Optional[dict]:
|
||||||
return {"voice":{"waveform":Dick.wf(a.waveform)}}
|
try:
|
||||||
b: bytes = await cli.download_media(obj, bytes, thumb=-1)
|
if m.voice:
|
||||||
if not b: return None
|
for a in m.voice.attributes or []:
|
||||||
if m.sticker:
|
if getattr(a,"voice",False) and hasattr(a,"waveform"):
|
||||||
u=await Dick.stc(b); return {"url": u} if u else None
|
return {"voice":{"waveform":Dick.wf(a.waveform)}}
|
||||||
u=await Dick.img(b, circle=bool(m.video_note))
|
b: bytes = await cli.download_media(obj, bytes, thumb=-1)
|
||||||
return {"url": u} if u else None
|
if not b: return None
|
||||||
except Exception:
|
if m.sticker:
|
||||||
return None
|
u=await Dick.stc(b); return {"url": u} if u else None
|
||||||
|
u=await Dick.img(b, circle=bool(m.video_note))
|
||||||
@staticmethod
|
return {"url": u} if u else None
|
||||||
async def ava(cli, uid: int) -> Optional[str]:
|
except Exception:
|
||||||
try:
|
return None
|
||||||
b=await cli.download_profile_photo(uid, bytes)
|
|
||||||
if b: return f"data:image/jpeg;base64,{base64.b64encode(b).decode()}"
|
@staticmethod
|
||||||
except Exception: pass
|
async def ava(cli, uid: int) -> Optional[str]:
|
||||||
return None
|
try:
|
||||||
|
b=await cli.download_profile_photo(uid, bytes)
|
||||||
@staticmethod
|
if b: return f"data:image/jpeg;base64,{base64.b64encode(b).decode()}"
|
||||||
async def post(url: str, data: dict):
|
except Exception: pass
|
||||||
try:
|
return None
|
||||||
return await utils.run_sync(requests.post, url, json=data, timeout=30)
|
|
||||||
except Exception:
|
@staticmethod
|
||||||
return None
|
async def post(url: str, data: dict):
|
||||||
|
try:
|
||||||
@loader.tds
|
return await utils.run_sync(requests.post, url, json=data, timeout=30)
|
||||||
class Quotes(loader.Module):
|
except Exception:
|
||||||
"""Модуль для создания цитат из сообщений"""
|
return None
|
||||||
|
|
||||||
strings = {"name": "yg_quotes",
|
@loader.tds
|
||||||
"no_reply": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Нет реплая на сообщение",
|
class Quotes(loader.Module):
|
||||||
"processing": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Обработка…",
|
"""Модуль для создания цитат из сообщений"""
|
||||||
"api_processing": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ожидание ответа API…",
|
|
||||||
"api_error": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка API: {}",
|
strings = {"name": "yg_quotes",
|
||||||
"loading_media": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Отправка…",
|
"no_reply": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Нет реплая на сообщение",
|
||||||
"no_args_or_reply": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Нет аргументов или реплая",
|
"processing": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Обработка…",
|
||||||
"args_error": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка разбора аргументов. Запрос: <code>{}</code>",
|
"api_processing": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ожидание ответа API…",
|
||||||
"too_many_messages": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Слишком много сообщений. Максимум: <code>{}</code>"}
|
"api_error": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка API: {}",
|
||||||
|
"loading_media": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Отправка…",
|
||||||
def __init__(self):
|
"no_args_or_reply": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Нет аргументов или реплая",
|
||||||
self.config=loader.ModuleConfig(
|
"args_error": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка разбора аргументов. Запрос: <code>{}</code>",
|
||||||
loader.ConfigValue("type","quote",
|
"too_many_messages": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Слишком много сообщений. Максимум: <code>{}</code>"}
|
||||||
lambda:"Тип цитаты",
|
|
||||||
validator=loader.validators.Choice(["quote", "stories"])),
|
def __init__(self):
|
||||||
loader.ConfigValue("bg_color","#162330",
|
self.config=loader.ModuleConfig(
|
||||||
lambda:"Цвет фона цитаты (например, #1a1a1a или red)"),
|
loader.ConfigValue("type","quote",
|
||||||
loader.ConfigValue("width",512,
|
lambda:"Тип цитаты",
|
||||||
lambda:"Ширина цитаты (px)",
|
validator=loader.validators.Choice(["quote", "stories"])),
|
||||||
validator=loader.validators.Integer(minimum=200,maximum=2000)),
|
loader.ConfigValue("bg_color","#162330",
|
||||||
loader.ConfigValue("height",768,
|
lambda:"Цвет фона цитаты (например, #1a1a1a или red)"),
|
||||||
lambda:"Высота цитаты (px)",
|
loader.ConfigValue("width",512,
|
||||||
validator=loader.validators.Integer(minimum=200,maximum=2000)),
|
lambda:"Ширина цитаты (px)",
|
||||||
loader.ConfigValue("scale",2,
|
validator=loader.validators.Integer(minimum=200,maximum=2000)),
|
||||||
lambda:"Масштаб рендера",
|
loader.ConfigValue("height",768,
|
||||||
validator=loader.validators.Choice([1, 2, 3])),
|
lambda:"Высота цитаты (px)",
|
||||||
loader.ConfigValue("emoji_brand","apple",
|
validator=loader.validators.Integer(minimum=200,maximum=2000)),
|
||||||
lambda:"Стиль эмодзи (apple, google, twitter и т.д.)"),
|
loader.ConfigValue("scale",2,
|
||||||
loader.ConfigValue("max_messages",15,
|
lambda:"Масштаб рендера",
|
||||||
lambda:"Максимальное число сообщений в цитате",
|
validator=loader.validators.Choice([1, 2, 3])),
|
||||||
validator=loader.validators.Integer(minimum=1,maximum=50)),
|
loader.ConfigValue("emoji_brand","apple",
|
||||||
loader.ConfigValue("endpoint","https://kok.gay/gayotes/generate",
|
lambda:"Стиль эмодзи (apple, google, twitter и т.д.)"),
|
||||||
lambda:"URL API-эндпоинта (можешь поднять локально - github.com/yummy1gay/quote-api)",
|
loader.ConfigValue("max_messages",15,
|
||||||
validator=loader.validators.Link()))
|
lambda:"Максимальное число сообщений в цитате",
|
||||||
|
validator=loader.validators.Integer(minimum=1,maximum=50)),
|
||||||
async def client_ready(self, client, db):
|
loader.ConfigValue("endpoint","https://kok.gay/gayotes/generate",
|
||||||
self.client=client; self.db=db
|
lambda:"URL API-эндпоинта (можешь поднять локально - github.com/yummy1gay/quote-api)",
|
||||||
|
validator=loader.validators.Link()),
|
||||||
async def qcmd(self, m: Message):
|
loader.ConfigValue("use_rf_proxy", False,
|
||||||
"""
|
lambda:'Включает прокси для РФ, если основной эндпоинт возвращает ошибку "Нетворк еррорь", и при этом сервер с юзерботом находится в России или ты сам сидишь в России с ограниченным доступом к зарубежным ресурсам (Termux / UserLAnd)',
|
||||||
Обычные цитаты:
|
validator=loader.validators.Boolean()),
|
||||||
• .q — процитировать одно сообщение из реплая
|
loader.ConfigValue("rf_endpoint", "https://ru.kok.gay/gayotes/generate",
|
||||||
• .q 2 — процитировать 2 сообщения
|
lambda:"URL API-эндпоинта для РФ",
|
||||||
• .q 3 #2d2d2d — 3 сообщения на тёмном фоне
|
validator=loader.validators.Link()))
|
||||||
• .q pink — фон по имени цвета
|
|
||||||
• .q !file — отправить как файл (PNG)
|
async def client_ready(self, client, db):
|
||||||
"""
|
self.client=client; self.db=db
|
||||||
try:
|
|
||||||
args=utils.get_args(m); rep=await m.get_reply_message()
|
async def qcmd(self, m: 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
|
• .q — процитировать одно сообщение из реплая
|
||||||
n=next((int(a) for a in args if a.isdigit() and int(a)>0),1)
|
• .q 2 — процитировать 2 сообщения
|
||||||
bg=next((a for a in args if a!="!file" and not a.isdigit()), self.config["bg_color"])
|
• .q 3 #2d2d2d — 3 сообщения на тёмном фоне
|
||||||
if n>self.config["max_messages"]:
|
• .q pink — фон по имени цвета
|
||||||
return await utils.answer(st,self.strings["too_many_messages"].format(self.config["max_messages"]))
|
• .q !file — отправить как файл (PNG)
|
||||||
|
"""
|
||||||
js=await self.parse(m,n)
|
try:
|
||||||
if not js: return await utils.answer(st,self.strings["api_error"].format("Не удалось собрать сообщения"))
|
args=utils.get_args(m); rep=await m.get_reply_message()
|
||||||
|
if not rep: return await utils.answer(m,self.strings["no_reply"])
|
||||||
pay={"backgroundColor":bg,"width":self.config["width"],"height":self.config["height"],
|
st=await utils.answer(m,self.strings["processing"])
|
||||||
"scale":self.config["scale"],"emojiBrand":self.config["emoji_brand"],"messages":js,
|
doc="!file" in args
|
||||||
"format": "webp" if not doc else "png", "type": self.config["type"]}
|
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"])
|
||||||
await utils.answer(st,self.strings["api_processing"])
|
if n>self.config["max_messages"]:
|
||||||
r=await Dick.post(f"{self.config['endpoint']}.webp",pay)
|
return await utils.answer(st,self.strings["too_many_messages"].format(self.config["max_messages"]))
|
||||||
if not r or r.status_code!=200:
|
|
||||||
try: err=r.json().get("error",f"HTTP {r.status_code}") if r else "Нетворк еррорь"
|
js=await self.parse(m,n)
|
||||||
except Exception: err=f"HTTP {r.status_code}" if r else "Нетворк еррорь"
|
if not js: return await utils.answer(st,self.strings["api_error"].format("Не удалось собрать сообщения"))
|
||||||
return await utils.answer(st,self.strings["api_error"].format(err))
|
|
||||||
|
pay={"backgroundColor":bg,"width":self.config["width"],"height":self.config["height"],
|
||||||
buf=io.BytesIO(r.content); buf.name="YgQuote"+(".png" if doc else ".webp")
|
"scale":self.config["scale"],"emojiBrand":self.config["emoji_brand"],"messages":js,
|
||||||
await utils.answer(st,buf,force_document=doc)
|
"format": "webp" if not doc else "png", "type": self.config["type"]}
|
||||||
except Exception as e:
|
|
||||||
return await utils.answer(m,f"<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка: {e}")
|
await utils.answer(st,self.strings["api_processing"])
|
||||||
|
endpoint=self.config['rf_endpoint'] if self.config['use_rf_proxy'] else self.config['endpoint']
|
||||||
async def fqcmd(self, m: Message):
|
r=await Dick.post(f"{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 "Нетворк еррорь (попробуй включить <code>use_rf_proxy</code> в конфиге)"
|
||||||
• .fq <@ или ID> <текст> — цитата от пользователя
|
except Exception: err=f"HTTP {r.status_code}" if r else "Нетворк еррорь (попробуй включить <code>use_rf_proxy</code> в конфиге)"
|
||||||
• .fq <reply> <текст> — цитата от автора реплая
|
return await utils.answer(st,self.strings["api_error"].format(err))
|
||||||
• .fq <@/ID> <текст> -r <@/ID> <текст> — с ответом
|
|
||||||
• .fq user1 текст; user2 текст — несколько сообщений
|
buf=io.BytesIO(r.content); buf.name="YgQuote"+(".png" if doc else ".webp")
|
||||||
"""
|
await utils.answer(st,buf,force_document=doc)
|
||||||
try:
|
except Exception as e:
|
||||||
raw=utils.get_args_html(m); rep=await m.get_reply_message()
|
return await utils.answer(m,f"<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка: {e}")
|
||||||
if not (raw or rep): return await utils.answer(m,self.strings["no_args_or_reply"])
|
|
||||||
st= await utils.answer(m,self.strings["processing"])
|
async def fqcmd(self, m: Message):
|
||||||
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"]:
|
• .fq <@ или ID> <текст> — цитата от пользователя
|
||||||
return await utils.answer(st,self.strings["too_many_messages"].format(self.config["max_messages"]))
|
• .fq <reply> <текст> — цитата от автора реплая
|
||||||
|
• .fq <@/ID> <текст> -r <@/ID> <текст> — с ответом
|
||||||
dickk={"backgroundColor":self.config["bg_color"],"width":self.config["width"],"height":self.config["height"],
|
• .fq user1 текст; user2 текст — несколько сообщений
|
||||||
"scale":self.config["scale"],"emojiBrand":self.config["emoji_brand"],"messages":js,
|
"""
|
||||||
"format": "webp","type":self.config["type"]}
|
try:
|
||||||
|
raw=utils.get_args_html(m); rep=await m.get_reply_message()
|
||||||
await utils.answer(st,self.strings["api_processing"])
|
if not (raw or rep): return await utils.answer(m,self.strings["no_args_or_reply"])
|
||||||
r=await Dick.post(f"{self.config['endpoint']}.webp",dickk)
|
st= await utils.answer(m,self.strings["processing"])
|
||||||
if not r or r.status_code!=200:
|
try: js=await self.fake(raw,rep)
|
||||||
try: err=r.json().get("error",f"HTTP {r.status_code}") if r else "Нетворк еррорь"
|
except (IndexError,ValueError): return await utils.answer(st,self.strings["args_error"].format(m.text))
|
||||||
except Exception: err=f"HTTP {r.status_code}" if r else "Нетворк еррорь"
|
if len(js)>self.config["max_messages"]:
|
||||||
return await utils.answer(st,self.strings["api_error"].format(err))
|
return await utils.answer(st,self.strings["too_many_messages"].format(self.config["max_messages"]))
|
||||||
|
|
||||||
buf=io.BytesIO(r.content); buf.name="YgQuote.webp"
|
dickk={"backgroundColor":self.config["bg_color"],"width":self.config["width"],"height":self.config["height"],
|
||||||
await utils.answer(st,buf)
|
"scale":self.config["scale"],"emojiBrand":self.config["emoji_brand"],"messages":js,
|
||||||
except Exception as e:
|
"format": "webp","type":self.config["type"]}
|
||||||
return await utils.answer(m,f"<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка: {e}")
|
|
||||||
|
await utils.answer(st,self.strings["api_processing"])
|
||||||
async def parse(self, trg: Message, n: int) -> Optional[List[dict]]:
|
endpoint=self.config['rf_endpoint'] if self.config['use_rf_proxy'] else self.config['endpoint']
|
||||||
try:
|
r=await Dick.post(f"{endpoint}.webp",dickk)
|
||||||
rep= await trg.get_reply_message()
|
if not r or r.status_code!=200:
|
||||||
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)]
|
try: err=r.json().get("error",f"HTTP {r.status_code}") if r else "Нетворк еррорь (попробуй включить <code>use_rf_proxy</code> в конфиге)"
|
||||||
except Exception:
|
except Exception: err=f"HTTP {r.status_code}" if r else "Нетворк еррорь (попробуй включить <code>use_rf_proxy</code> в конфиге)"
|
||||||
return None
|
return await utils.answer(st,self.strings["api_error"].format(err))
|
||||||
|
|
||||||
out: List[dict]=[]
|
buf=io.BytesIO(r.content); buf.name="YgQuote.webp"
|
||||||
for mm in lst:
|
await utils.answer(st,buf)
|
||||||
try:
|
except Exception as e:
|
||||||
u=await self.who(mm)
|
return await utils.answer(m,f"<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка: {e}")
|
||||||
if not u: continue
|
|
||||||
name=telethon.utils.get_display_name(u); f,l=Dick.split(name)
|
async def parse(self, trg: Message, n: int) -> Optional[List[dict]]:
|
||||||
ava=await Dick.ava(self.client,getattr(u,"id",0)) if getattr(u,"id",None) else None
|
try:
|
||||||
|
rep= await trg.get_reply_message()
|
||||||
rb=None
|
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)]
|
||||||
try:
|
except Exception:
|
||||||
r=await mm.get_reply_message()
|
return None
|
||||||
if r:
|
|
||||||
rname=telethon.utils.get_display_name(r.sender)
|
out: List[dict]=[]
|
||||||
rtxt=Dick.desc(r,True)
|
for mm in lst:
|
||||||
if r.raw_text: rtxt=(rtxt+". "+r.raw_text) if rtxt else r.raw_text
|
try:
|
||||||
rb={"name":rname,"text":rtxt or "","entities":Dick.ents(r.entities),
|
u=await self.who(mm)
|
||||||
"chatId": r.sender.id if r.sender else mm.chat_id,"from":{"name":rname}}
|
if not u: continue
|
||||||
except Exception: rb=None
|
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
|
||||||
med=None; obj=Dick.pick(mm)
|
|
||||||
if obj: med=await Dick.proc(self.client,obj,mm)
|
rb=None
|
||||||
|
try:
|
||||||
txt=mm.raw_text or ""; ad=Dick.desc(mm)
|
r=await mm.get_reply_message()
|
||||||
if ad: txt=f"{txt}\n\n{ad}" if txt else ad
|
if r:
|
||||||
|
ruser = await self.who(r)
|
||||||
item={"from":{"id":getattr(u,"id", 0),"first_name":getattr(u,"first_name","") or f,"last_name":getattr(u,"last_name","") or l,
|
rname=telethon.utils.get_display_name(ruser)
|
||||||
"username":getattr(u,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
|
rtxt=Dick.desc(r,True)
|
||||||
"text":txt,"entities":Dick.ents(mm.entities),"avatar":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),
|
||||||
try:
|
"chatId": r.sender.id if r.sender else mm.chat_id,"from":{"name":rname}}
|
||||||
if mm.voice:
|
except Exception: rb=None
|
||||||
a = next((a for a in mm.voice.attributes or []
|
|
||||||
if getattr(a, "voice", False) and hasattr(a, "waveform")), None)
|
med=None; obj=Dick.pick(mm)
|
||||||
if a: item["voice"] = {"waveform": Dick.wf(a.waveform)}
|
if obj: med=await Dick.proc(self.client,obj,mm)
|
||||||
except Exception: pass
|
|
||||||
|
txt=mm.raw_text or ""; ad=Dick.desc(mm)
|
||||||
if med: item["voice" if "voice" in med else "media"] = med.get("voice", med)
|
if ad: txt=f"{txt}\n\n{ad}" if txt else ad
|
||||||
|
|
||||||
es=getattr(u,"emoji_status",None)
|
item={"from":{"id":getattr(u,"id", 0),"first_name":getattr(u,"first_name","") or f,"last_name":getattr(u,"last_name","") or l,
|
||||||
if getattr(es,"document_id",None): item["from"]["emoji_status"]=str(es.document_id)
|
"username":getattr(u,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
|
||||||
if rb: item["replyMessage"]=rb
|
"text":txt,"entities":Dick.ents(mm.entities),"avatar":True}
|
||||||
out.append(item)
|
|
||||||
except Exception: continue
|
try:
|
||||||
return out
|
if mm.voice:
|
||||||
|
a = next((a for a in mm.voice.attributes or []
|
||||||
async def who(self, m: Message):
|
if getattr(a, "voice", False) and hasattr(a, "waveform")), None)
|
||||||
try:
|
if a: item["voice"] = {"waveform": Dick.wf(a.waveform)}
|
||||||
if m.fwd_from:
|
except Exception: pass
|
||||||
if m.fwd_from.from_id:
|
|
||||||
pid=m.fwd_from.from_id
|
if med: item["voice" if "voice" in med else "media"] = med.get("voice", med)
|
||||||
uid=pid.channel_id if isinstance(pid, types.PeerChannel) else pid.user_id
|
|
||||||
try: return await self.client.get_entity(uid)
|
es=getattr(u,"emoji_status",None)
|
||||||
except Exception: return m.sender
|
if getattr(es,"document_id",None): item["from"]["emoji_status"]=str(es.document_id)
|
||||||
if m.fwd_from.from_name:
|
if rb: item["replyMessage"]=rb
|
||||||
return types.User(
|
out.append(item)
|
||||||
id=hash(m.fwd_from.from_name)%2147483647, first_name=m.fwd_from.from_name,
|
except Exception: continue
|
||||||
username=None, phone=None, bot=False, verified=False, restricted=False,
|
return out
|
||||||
scam=False, fake=False, premium=False)
|
|
||||||
return m.sender
|
async def who(self, m: Message):
|
||||||
except Exception:
|
try:
|
||||||
return m.sender
|
if m.fwd_from:
|
||||||
|
if m.fwd_from.from_id:
|
||||||
async def fake(self, args: str, rep: Optional[Message]) -> List[dict]:
|
pid=m.fwd_from.from_id
|
||||||
async def tok(ch: str):
|
uid=pid.channel_id if isinstance(pid, types.PeerChannel) else pid.user_id
|
||||||
p=ch.split()
|
try: return await self.client.get_entity(uid)
|
||||||
if not p: return None,""
|
except Exception: return m.sender
|
||||||
who=p[0]; tx=ch.split(maxsplit=1)[1] if len(p)>1 else ""
|
if m.fwd_from.from_name:
|
||||||
try:
|
return types.User(
|
||||||
u=await self.client.get_entity(int(who) if who.isdigit() else who)
|
id=hash(m.fwd_from.from_name)%2147483647, first_name=m.fwd_from.from_name,
|
||||||
return u,tx
|
username=None, phone=None, bot=False, verified=False, restricted=False,
|
||||||
except Exception:
|
scam=False, fake=False, premium=False)
|
||||||
return None,tx
|
return m.sender
|
||||||
|
except Exception:
|
||||||
if rep and not args:
|
return m.sender
|
||||||
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
|
async def fake(self, args: str, rep: Optional[Message]) -> List[dict]:
|
||||||
msg={"from":{"id":u.id,"first_name":getattr(u,"first_name","") or f,"last_name":getattr(u,"last_name","") or l,
|
async def tok(ch: str):
|
||||||
"username":getattr(u,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
|
p=ch.split()
|
||||||
"text":"","entities":[], "avatar":True}
|
if not p: return None,""
|
||||||
es=getattr(u,"emoji_status",None)
|
who=p[0]; tx=ch.split(maxsplit=1)[1] if len(p)>1 else ""
|
||||||
if getattr(es,"document_id", None): msg["from"]["emoji_status"]=str(es.document_id)
|
try:
|
||||||
return [msg]
|
u=await self.client.get_entity(int(who) if who.isdigit() else who)
|
||||||
|
return u,tx
|
||||||
if rep and args:
|
except Exception:
|
||||||
u=rep.sender
|
return None,tx
|
||||||
return await self.fake(f"{getattr(u,'id','')} {args}", None)
|
|
||||||
|
if rep and not args:
|
||||||
out: List[dict]=[]
|
u=rep.sender; name=telethon.utils.get_display_name(u); f,l=Dick.split(name)
|
||||||
for part in args.split("; "):
|
ava=await Dick.ava(self.client,u.id) if getattr(u,"id",None) else None
|
||||||
try:
|
msg={"from":{"id":u.id,"first_name":getattr(u,"first_name","") or f,"last_name":getattr(u,"last_name","") or l,
|
||||||
rb=None
|
"username":getattr(u,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
|
||||||
if " -r " in part:
|
"text":"","entities":[], "avatar":True}
|
||||||
a,b=part.split(" -r ",1); u1,t1=await tok(a); u2,t2=await tok(b)
|
es=getattr(u,"emoji_status",None)
|
||||||
else:
|
if getattr(es,"document_id", None): msg["from"]["emoji_status"]=str(es.document_id)
|
||||||
u1,t1=await tok(part); u2,t2=None,None
|
return [msg]
|
||||||
if not u1: continue
|
|
||||||
|
if rep and args:
|
||||||
txt1, ents1 = html.parse(t1) if t1 else ("", [])
|
u=rep.sender
|
||||||
|
return await self.fake(f"{getattr(u,'id','')} {args}", None)
|
||||||
name=telethon.utils.get_display_name(u1); f,l=Dick.split(name)
|
|
||||||
ava=await Dick.ava(self.client,u1.id)
|
out: List[dict]=[]
|
||||||
|
for part in args.split("; "):
|
||||||
if u2:
|
try:
|
||||||
txt2, ents2 = html.parse(t2) if t2 else ("", [])
|
rb=None
|
||||||
name2=telethon.utils.get_display_name(u2); ava2=await Dick.ava(self.client,u2.id)
|
if " -r " in part:
|
||||||
rb={"name":name2,"text":txt2,"entities":Dick.ents(ents2),"chatId":u2.id,"from":{"name":name2,"photo":{"url":ava2} if ava2 else {}}}
|
a,b=part.split(" -r ",1); u1,t1=await tok(a); u2,t2=await tok(b)
|
||||||
|
else:
|
||||||
msg={"from":{"id":u1.id,"first_name":getattr(u1,"first_name","") or f,"last_name":getattr(u1,"last_name","") or l,
|
u1,t1=await tok(part); u2,t2=None,None
|
||||||
"username":getattr(u1,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
|
if not u1: continue
|
||||||
"text":txt1,"entities":Dick.ents(ents1), "avatar":True}
|
|
||||||
|
txt1, ents1 = html.parse(t1) if t1 else ("", [])
|
||||||
es=getattr(u1,"emoji_status",None)
|
|
||||||
if getattr(es,"document_id",None): msg["from"]["emoji_status"]=str(es.document_id)
|
name=telethon.utils.get_display_name(u1); f,l=Dick.split(name)
|
||||||
if rb: msg["replyMessage"]=rb
|
ava=await Dick.ava(self.client,u1.id)
|
||||||
out.append(msg)
|
|
||||||
except Exception: continue
|
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
|
return out
|
||||||
Reference in New Issue
Block a user