Compare commits

..

2 Commits

Author SHA1 Message Date
github-actions[bot]
b42232dd0b Updated modules.json after parse 2026-04-24 02:01:18 2026-04-24 02:01:18 +00:00
github-actions[bot]
2cff934bf7 Added and updated repositories 2026-04-24 02:00:45 2026-04-24 02:00:45 +00:00
234 changed files with 96628 additions and 77647 deletions

View File

@@ -0,0 +1,3 @@
![amoremods](https://te.legra.ph/file/8600de18766a556b2f78e.jpg)
# amoremods
My mods for userbot

View File

@@ -0,0 +1,53 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta pic: https://te.legra.ph/file/868a280910e7f61f6ab0e.png
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Abstract.jpg
from .. import utils, loader
chat = "@aeabstractbot"
class AbstractMod(loader.Module):
"""Write a beautiful summary on a notebook"""
strings = {
"name": "Abstract",
"processing": (
"<emoji document_id='6318766236746384900'>🕔</emoji> <b>Processing...</b>"
),
}
@loader.owner
@loader.command(ru_doc="<текст> - Создать конспект")
async def konspcmd(self, message):
"""<text> - Create summary"""
text = utils.get_args_raw(message)
message = await utils.answer(message, self.strings("processing"))
async with self._client.conversation(chat) as conv:
msgs = []
msgs += [await conv.send_message("/start")]
msgs += [await conv.get_response()]
msgs += [await conv.send_message(text)]
m = await conv.get_response()
await self._client.send_file(
message.peer_id,
m.media,
reply_to=message.reply_to_msg_id,
)
for msg in msgs + [m]:
await msg.delete()
if message.out:
await message.delete()
await self.client.delete_dialog(chat)

View File

@@ -0,0 +1,33 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Activity.jpg
# requires: deep_translator
import requests
import deep_translator
from .. import loader, utils
def generate_activity():
return requests.get("http://api.farkhodovme.tk/activity/en").json()['activity']
class Activity(loader.Module):
"""Generate activity if you're bored"""
strings = {"name": "Activity", "activity": "⛩ <b>Activity:</b> <code>{}</code>", "lang": "en"}
strings_ru = {"activity": "⛩ <b>Занятие:</b> <code>{}</code>", "lang": "ru"}
strings_uz = {"activity": "⛩ <b>Harakat:</b> <code>{}</code>", "lang": "uz"}
@loader.command(ru_doc="Сгенерировать занятие", uz_doc="Harakat yaratish")
async def activity(self, message):
"""Generate activity"""
res = (deep_translator.GoogleTranslator(source="auto", target=self.strings["lang"]).translate(generate_activity()) if self.strings["lang"] != "en" else generate_activity())
txt = self.strings['activity'].format(res)
await utils.answer(message, txt)

View File

@@ -0,0 +1,287 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://github.com/AmoreForever/assets/blob/master/Aeconv.jpg?raw=true
# meta pic: https://cdn-icons-png.flaticon.com/512/5670/5670084.png
import re
import logging
from bs4 import BeautifulSoup as bs
from requests import get
from asyncio import sleep
from asyncio.exceptions import TimeoutError
from hikkatl.tl.types import Message
from hikkatl.errors.common import AlreadyInConversationError
from .. import utils, loader
from ..inline.types import InlineCall
logger = logging.getLogger(__name__)
@loader.tds
class Aeconv(loader.Module):
"""Easy and fast valute converter"""
bot = "@exchange_rates_vsk_bot"
strings = {
"name": "Aeconv",
"wait": "<emoji document_id=5346192260029489215>💵</emoji> <b>Converting...</b>",
"no_args": "<emoji document_id=5343820329980535275>🖕</emoji> <b>Where are the arguments?</b>",
"unsupported": "<emoji document_id=5307761055873638139>🚫</emoji> <b>Unsupported currency!</b>",
"converted": "<emoji document_id=6037400506823871682>💸</emoji> <b>Converted <code>{}</code></b>\n\n",
"already": "<emoji document_id=5348177037431414677>⚠️</emoji> <b>Wait until the bot responds!</b>",
"wrong_currency": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji><b> Wrong currency</b>",
"choose_currency": "📉 <b> Choose currency</b>",
"processing": "🕔 <b>Processing...</b>",
"done": "✅ <b>Done!</b>",
"already_in_conv": "⚠️ <b>Already in conversation!</b>",
}
strings_ru = {
"wait": "<emoji document_id=5346192260029489215>💵</emoji> <b>Конвертирую...</b>",
"no_args": "<emoji document_id=5343820329980535275>🖕</emoji> <b>Где аргументы?</b>",
"unsupported": "<emoji document_id=5307761055873638139>🚫</emoji> <b>Валюта не поддерживается!</b>",
"converted": "<emoji document_id=6037400506823871682>💸</emoji> <b>Сконвертирован <code>{}</code></b>\n\n",
"already": "<emoji document_id=5348177037431414677>⚠️</emoji> <b>Подожди пока бот ответит!</b>",
"wrong_currency": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji><b> Неправильная валюта</b>",
"choose_currency": "📉 <b> Выберите валюту</b>",
"processing": "🕔 Обрабатываю...",
"done": "<code>[Aeconv]</code> ✅ <b> Готово!</b>",
"already_in_conv": "⚠️ <b>Жди пока закончится процесс!</b>",
}
strings_uz = {
"wait": "<emoji document_id=5346192260029489215>💵</emoji> <b>Valyuta konvertatsiyasi...</b>",
"no_args": "<emoji document_id=5343820329980535275>🖕</emoji> <b>Argumetlar qayerda?</b>",
"unsupported": "<emoji document_id=5307761055873638139>🚫</emoji> <b>Valyuta qo'llab-quvvatlanmaydi!</b>",
"converted": "<emoji document_id=6037400506823871682>💸</emoji> <b>Konvertatsiya qilindi <code>{}</code></b>\n\n",
"already": "<emoji document_id=5348177037431414677>⚠️</emoji> <b>Bot javob berishini kuting!</b>",
"wrong_currency": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji><b> Noto'g'ri valyuta</b>",
"choose_currency": "📉 <b> Valyutani tanlang</b>",
"processing": "🕔 Qayta ishlayapman...",
"done": "<code>[Aeconv]</code> ✅ <b>Tayyor!</b>",
"already_in_conv": "⚠️ <b>Protsess tugaguncha kuting!</b>",
}
strings_de = { # i'm really sorry for translations, i'm not good at it
"wait": "<emoji document_id=5346192260029489215>💵</emoji> <b>Konvertiere...</b>",
"no_args": "<emoji document_id=5343820329980535275>🖕</emoji> <b>Wo sind die Argumente?</b>",
"unsupported": "<emoji document_id=5307761055873638139>🚫</emoji> <b>Nicht unterstützte Währung!</b>",
"converted": "<emoji document_id=6037400506823871682>💸</emoji> <b>Konvertiert <code>{}</code></b>\n\n",
"already": "<emoji document_id=5348177037431414677>⚠️</emoji> <b>Warten Sie, bis der Bot antwortet!</b>",
"wrong_currency": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji><b> Falsche Währung</b>",
"choose_currency": "📉 <b> Währung auswählen</b>",
"processing": "🕔 Verarbeitung...",
"done": "<code>[Aeconv]</code> ✅ <b>Fertig!</b>",
"already_in_conv": "⚠️ <b>Warten Sie, bis der Prozess beendet ist!</b>",
}
strings_tr = { # i'm really sorry for translations, i'm not good at it
"wait": "<emoji document_id=5346192260029489215>💵</emoji> <b>Dönüştürülüyor...</b>",
"no_args": "<emoji document_id=5343820329980535275>🖕</emoji> <b>Argümanlar nerede?</b>",
"unsupported": "<emoji document_id=5307761055873638139>🚫</emoji> <b>Desteklenmeyen para birimi!</b>",
"converted": "<emoji document_id=6037400506823871682>💸</emoji> <b>Dönüştürüldü <code>{}</code></b>\n\n",
"already": "<emoji document_id=5348177037431414677>⚠️</emoji> <b>Bot cevap verene kadar bekleyin!</b>",
"wrong_currency": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji><b> Yanlış para birimi</b>",
"choose_currency": "📉 <b> Para birimini seçin</b>",
"processing": "🕔 İşleniyor...",
"done": "<code>[Aeconv]</code> ✅ <b>Tamam!</b>",
"already_in_conv": "⚠️ <b>İşlem bitene kadar bekleyin!</b>",
}
strings_kk = { # i'm really sorry for translations, i'm not good at it
"wait": "<emoji document_id=5346192260029489215>💵</emoji> <b>Валюта айырбасталуда...</b>",
"no_args": "<emoji document_id=5343820329980535275>🖕</emoji> <b>Аргументтер қайда?</b>",
"unsupported": "<emoji document_id=5307761055873638139>🚫</emoji> <b>Валюта қолдау көрсетілмейді!</b>",
"converted": "<emoji document_id=6037400506823871682>💸</emoji> <b>Айырбасталды <code>{}</code></b>\n\n",
"already": "<emoji document_id=5348177037431414677>⚠️</emoji> <b>Бот жауап бергенге дейін күтіңіз!</b>",
"wrong_currency": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji><b> Дұрыс валюта емес</b>",
"choose_currency": "📉 <b> Валютаны таңдаңыз</b>",
"processing": "🕔 Қайта өңдеу...",
"done": "<code>[Aeconv]</code> ✅ <b>Тайық!</b>",
"already_in_conv": "⚠️ <b>Процесс аяқталғанда дейін күтіңіз!</b>",
}
custom_emojis = {
"🇬🇧": "<emoji document_id=6323589145717376403>🇬🇧</emoji>",
"🇺🇿": "<emoji document_id=6323430017179059570>🇺🇿</emoji>",
"🇺🇸": "<emoji document_id=6323374027985389586>🇺🇸</emoji>",
"🇷🇺": "<emoji document_id=6323139226418284334>🇷🇺</emoji>",
"🇰🇿": "<emoji document_id=6323135275048371614>🇰🇿</emoji>",
"🇪🇺": "<emoji document_id=6323217102765295143>🇪🇺</emoji>",
"🇺🇦": "<emoji document_id=6323289850921354919>🇺🇦</emoji>",
"🇹🇷": "<emoji document_id=6321003171678259486>🇹🇷</emoji>",
"🇵🇱": "<emoji document_id=6323602387101550101>🇵🇱</emoji>",
"🇰🇬": "<emoji document_id=6323615997852910673>🇰🇬</emoji>",
"bit": "<emoji document_id=6034931909945985955>💰</emoji>",
"eth": "<emoji document_id=5280647120607521572>🔹</emoji>",
"ton": "<emoji document_id=5863980370340351884>💰</emoji>"
}
currency_mapping = {
"EU": ("🇪🇺", "EUR"),
"GB": ("🇬🇧", "GBP"),
"UZ": ("🇺🇿", "UZS"),
"US": ("🇺🇸", "USD"),
"RU": ("🇷🇺", "RUB"),
"KZ": ("🇰🇿", "KZT"),
"UA": ("🇺🇦", "UAH"),
"PL": ("🇵🇱", "PLN"),
"TR": ("🇹🇷", "TRY"),
"KG": ("🇰🇬", "KGS")
}
currencies = [
"EUR", "GBP", "UZS", "USD", "RUB", "KZT", "UAH", "PLN", "TRY", "KGS", "TON", "ETH", "BTC"
]
currency_flags = {
"EUR": "🇪🇺",
"GBP": "🇬🇧",
"UZS": "🇺🇿",
"USD": "🇺🇸",
"RUB": "🇷🇺",
"KZT": "🇰🇿",
"UAH": "🇺🇦",
"PLN": "🇵🇱",
"TRY": "🇹🇷",
"KGS": "🇰🇬"
}
letters_stashing = {
"E": "cur_df",
"G": "cur_gh",
"P": "cur_nq",
"R": "cur_rs",
"S": "cur_rs",
"T": "cur_tu",
"U": "cur_tu",
}
def currencies_markup(self, argument: str = "") -> list:
return utils.chunks(
[
{
"text": f"{self.currency_flags[cur]} {cur}",
"callback": self.callback_4_currency,
"args": (cur,),
}
for cur in [
i
for i in self.currency_flags.keys()
if i.startswith(argument.upper())
]
if cur.startswith(argument.upper())
],
5,
)
async def client_ready(self, client, db):
await utils.dnd(client, self.bot, archive=True)
async def get_ton_in_rub(self, am, what: str = "uzs", cup: bool = False) -> str:
r = (
get(f"https://coinchefs.com/{what}/ton/{am}/")
if cup
else get(f"https://coinchefs.com/ton/{what}/{am}/")
)
soup = bs(r.text, "html.parser")
if result_div := soup.find('div', class_='convert-result'):
if result_text_div := result_div.find(
'div', class_='col-xs-10 col-sm-10 text-center result-text'
):
if value_element := result_text_div.b:
return value_element.get_text(strip=True)
else:
logger.debug("Value element not found")
else:
logger.debug("Result text div not found")
else:
logger.debug("Result div not found")
return None
async def callback_4_currency(self, call: InlineCall, currency: str):
try:
first_letter = currency[0]
await call.answer(self.strings["processing"], show_alert=True)
await call.delete()
async with self.client.conversation(self.bot) as conv:
m = await conv.send_message("/settings")
r = await conv.get_response()
await r.click(data=b'cur_menu')
await r.click(data=b'cur_curmenu')
await r.click(data=self.letters_stashing[first_letter])
await r.click(data=f"cur_{currency.upper()}")
await r.delete()
await m.delete()
await self.inline.bot.send_message(self.tg_id, self.strings["done"])
except AlreadyInConversationError:
await call.answer(self.strings["already_in_conv"], show_alert=True)
@loader.command(ru_doc="<количество> [валюта] должны быть разделены пробелом")
async def conv(self, message: Message):
"""<amount> [currency] should be separated by space"""
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, self.strings["no_args"])
return
# if args.split(" ")[1].upper() not in self.currencies:
# await utils.answer(message, self.strings["wrong_currency"])
# return
await utils.answer(message, self.strings["wait"])
if "ton".lower() in args.lower():
li_args = args.split(" ")
ex_ = await self.get_ton_in_rub(li_args[0])
try:
async with message.client.conversation(self.bot) as conv:
msg = await conv.send_message(args) if "ton".lower() not in args.lower() else await conv.send_message(ex_)
r = await conv.get_response()
res = r.text
text_ = ""
text_ += (
self.strings["converted"].format(args)
if "ton".lower() not in args.lower()
else self.strings["converted"].format(f"{li_args[0]} TON")
)
for emoji, currency, *_ in self.currency_mapping.values():
if match := re.findall(f"{emoji} ?(.*) {currency}", res):
text_ += (
f"<b>{self.custom_emojis.get(emoji)} {currency}:</b> "
f"<code>{match[0]}</code>\n"
)
if match := re.findall(r"(.*) BTC", res):
text_ += f"\n<b>{self.custom_emojis['bit']} BTC:</b> <code>{match[0]}</code>\n"
if match := re.findall(r"(.*) ETH", res):
text_ += f"<b>{self.custom_emojis['eth']} ETH:</b> <code>{match[0]}</code>\n"
if ex_ := await self.get_ton_in_rub(args.split(" ")[0], args.split(" ")[1].lower(), True):
text_ += f"<b>{self.custom_emojis['ton']} TON:</b> <code>{ex_.split(' = ')[1]}</code>\n"
await utils.answer(message, text_)
await msg.delete()
await r.delete()
except AlreadyInConversationError:
await utils.answer(message, self.strings["already"])
except TimeoutError:
await utils.answer(message, self.strings["unsupported"])
except IndexError:
await utils.answer(message, self.strings["no_args"])
@loader.command(ru_doc="[валюта] | без аргументов покажет список валют для включения/выключения")
async def controlvalute(self, message: Message):
"""[currency] | without arguments will show list of currencies for enable/disable"""
if args := utils.get_args_raw(message):
await utils.answer(message, self.strings["choose_currency"], reply_markup=self.currencies_markup(args))
else:
return await utils.answer(message, self.strings["choose_currency"], reply_markup=self.currencies_markup())

View File

@@ -0,0 +1,225 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Alarm.jpg
import re
import pytz
import random
import logging
import asyncio
from datetime import datetime
from .. import utils, loader
logger = logging.getLogger(__name__)
day_to_weekday = {
"mon": 0,
"tue": 1,
"wed": 2,
"thu": 3,
"fri": 4,
"sat": 5,
"sun": 6,
"пн": 0,
"вт": 1,
"ср": 2,
"чт": 3,
"пт": 4,
"сб": 5,
"вс": 6,
}
@loader.tds
class AlarmMod(loader.Module):
"""Alarm module for remind you about something"""
strings = {
"name": "Alarm",
"set": "<emoji document_id=5870729937215819584>⏰</emoji> <b>Alarm set for <code>{}</code>!</b>",
"unset": "<emoji document_id=5213107179329953547>⏰</emoji> <b>Alarm for <code>{}</code> unset!</b>",
"unset_all": "<emoji document_id=5213107179329953547>⏰</emoji> <b>All alarms unset!</b>",
"list_item": (
"<emoji document_id=6334603778326529773>⏰</emoji> <b>Alarm for <code>{}</code>!</b> <code>#{}</code>"
"\n<emoji document_id=6334699757960693635>🕔</emoji> <b>Time:</b> <code>{}</code>"
"\n<emoji document_id=6334388660594542334>🔊</emoji> <b>Message:</b> <code>{}</code>"
),
"no_alarms": "<emoji document_id=5208549407280078951>🙅‍♂️</emoji> <b>No alarms!</b>",
"off_button": "✋ Off",
"notification": "⏰ <b>Alarm!</b>\n\n<code>{}</code>",
"turned_off": "✔️ <b>Alarm turned off!</b>",
"incorrect_time": "<emoji document_id=5371015453013450536>🖕</emoji> <b>Incorrect time!</b>",
"where_args": "<emoji document_id=5371015453013450536>🖕</emoji> <b>Where arguments?</b>",
"incorrect_args": "<emoji document_id=5371015453013450536>🖕</emoji> <b>Incorrect arguments! Write like this:</b> <code>.setalarm mon 12:00 text</code>",
"interval_doc": "Interval of sending notifications in seconds",
"time_zone_doc": "Time zone for alarms (for example, Europe/Moscow)",
}
strings_ru = {
"set": "<emoji document_id=5870729937215819584>⏰</emoji> <b>Напоминание установлено на <code>{}</code>!</b>",
"unset": "<emoji document_id=5213107179329953547>⏰</emoji> <b>Напоминание для <code>{}</code> отменено!</b>",
"unset_all": "<emoji document_id=5213107179329953547>⏰</emoji> <b>Все напоминания отменены!</b>",
"list_item": (
"<emoji document_id=6334603778326529773>⏰</emoji> <b>Напоминание для <code>{}</code>!</b> <code>#{}</code>"
"\n<emoji document_id=6334699757960693635>🕔</emoji> <b>Время:</b> <code>{}</code>"
"\n<emoji document_id=6334388660594542334>🔊</emoji> <b>Сообщение:</b> <code>{}</code>"
),
"no_alarms": "<emoji document_id=5208549407280078951>🙅‍♂️</emoji> <b>Нет напоминаний!</b>",
"off_button": "✋ Выключить",
"notification": "⏰ <b>Напоминание!</b>\n\n<code>{}</code>",
"turned_off": "✔️ <b>Напоминание выключено!</b>",
"incorrect_time": "<emoji document_id=5371015453013450536>🖕</emoji> <b>Неправильное время!</b>",
"where_args": "<emoji document_id=5371015453013450536>🖕</emoji> <b>Где аргументы?</b>",
"incorrect_args": "<emoji document_id=5371015453013450536>🖕</emoji> <b>Неправильные аргументы! Пиши так:</b> <code>.setalarm пн 12:00 текст</code>",
"interval_doc": "Интервал отправления напоминаний в секундах",
"time_zone_doc": "Часовой пояс для напоминаний (например, Europe/Moscow)",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"interval",
5,
lambda: self.strings("interval_doc"),
validator=loader.validators.Integer(minimum=1, maximum=60),
),
loader.ConfigValue(
"time_zone",
"Europe/Moscow",
lambda: self.strings("time_zone_doc"),
validator=loader.validators.RegExp(
r"^[\w/]+$",
)
),
)
@loader.command(ru_doc="<день недели> <время> <сообщение> - установить напоминание")
async def setalarm(self, message):
"""<day of the week> <time> <message> - set alarm"""
args = utils.get_args_raw(message)
if not args:
return await utils.answer(message, self.strings("where_args"))
try:
re_args = re.match(r"^(.+) (\d{1,2}):(\d{1,2}) (.*)$", args)
day = re_args.group(1).lower()
hour = int(re_args.group(2))
minute = int(re_args.group(3))
text = re_args.group(4)
except AttributeError:
return await utils.answer(message, self.strings("incorrect_args"))
if not (0 <= hour <= 23 and 0 <= minute <= 59):
return await utils.answer(message, self.strings("incorrect_time"))
if day not in day_to_weekday.keys():
text = f"<b>Wrong day of the week!</b>\n<b>Available days:</b> <code>{', '.join(day_to_weekday.keys())}</code>"
return await utils.answer(message, text)
id_ = random.randint(100, 999)
self.set(
"alarms",
{
**self.get("alarms", {}),
day: {
"hour": hour,
"minute": minute,
"text": text,
"id": id_,
"status": "on",
},
},
)
await utils.answer(message, self.strings("set").format(day))
@loader.command(ru_doc="получить список напоминаний")
async def alarms(self, message):
"""get alarms list"""
alarms = self.get("alarms", {})
if not alarms:
return await utils.answer(message, self.strings("no_alarms"))
text = ""
for day, alarm in alarms.items():
text += self.strings("list_item").format(
day,
f"{alarm['id']}",
f"{alarm['hour']}:{alarm['minute']}",
alarm["text"],
)
text += "\n\n"
await utils.answer(message, text)
@loader.command(ru_doc="<id> - отменить напоминание")
async def unsetalarm(self, message):
"""<id> - unset alarm"""
args = utils.get_args_raw(message)
if not args:
return await utils.answer(message, self.strings("where_time"))
if args.startswith("#"):
args = args[1:]
alarms = self.get("alarms", {})
for day, alarm in alarms.items():
if str(alarm["id"]) == args:
alarms.pop(day)
self.set("alarms", alarms)
return await utils.answer(
message, self.strings("unset").format(day)
)
await utils.answer(message, self.strings("unset").format(args))
@loader.command(ru_doc="отменить все напоминания")
async def unsetallalarms(self, message):
"""unset all alarms"""
self.set("alarms", {})
await utils.answer(message, self.strings("unset_all"))
@loader.loop(interval=2, autostart=True)
async def check_alarms(self):
alarms = self.get("alarms", {})
if not alarms:
return
now = datetime.now(tz=pytz.timezone(self.config["time_zone"]))
day = now.weekday()
hour = now.hour
minute = now.minute
for day_, alarm in alarms.items():
if (
day_to_weekday[day_] == day
and alarm["hour"] == hour
and alarm["minute"] == minute
):
while alarm["status"] == "on":
self._markup = self.inline.generate_markup(
{
"text": self.strings("off_button"),
"callback": self.off_alarm,
"args": (alarm["id"],),
}
)
await self.inline.bot.send_message(
self.tg_id,
self.strings("notification").format(alarm["text"]),
reply_markup=self._markup,
)
await asyncio.sleep(self.config["interval"])
break
async def off_alarm(self, call, id_):
alarms = self.get("alarms", {})
for day, alarm in alarms.items():
if alarm["id"] == id_:
alarm["status"] = "off"
self.set("alarms", alarms)
await call.edit(self.strings("turned_off"))
return False
await call.answer("Не найдено!")

View File

@@ -0,0 +1,118 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://github.com/AmoreForever/assets/blob/master/Amethyste.jpg?raw=true
from .. import utils, loader
from hikkatl.errors.common import AlreadyInConversationError
from telethon.tl.types import Message
@loader.tds
class Amethyste(loader.Module):
"""Generate memes image"""
strings = {
"name": "Amethyste",
"wait": "<emoji document_id=5328115567314346398>🫥</emoji> <b>Wait...</b>",
"already_open": "<emoji document_id=5330241494521487534>😹</emoji> <b>Conversation already opened Please wait.</b>",
"r_photo": "<emoji document_id=5298636457982826800>🖼</emoji> <b>Please reply to image.</b>",
"no_args": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Pls provide args</b>",
"not_found": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji> <b>Not found</b>",
}
strings_ru = {
"wait": "<emoji document_id=5328115567314346398>🫥</emoji> <b>Подождите...</b>",
"already_open": "<emoji document_id=5330241494521487534>😹</emoji> <b>Диалог уже открыт. Подождите.</b>",
"r_photo": "<emoji document_id=5298636457982826800>🖼</emoji> <b>Ответьте на фото.</b>",
"no_args": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Укажите аргументы</b>",
"not_found": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji> <b>Не найдено</b>",
}
strings_uz = {
"wait": "<emoji document_id=5328115567314346398>🫥</emoji> <b>Kuting...</b>",
"already_open": "<emoji document_id=5330241494521487534>😹</emoji> <b>Dialog allaqachon ochilgan. Iltimos, kuting.</b>",
"r_photo": "<emoji document_id=5298636457982826800>🖼</emoji> <b>Rasmga javob bering.</b>",
"no_args": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Argumetlarni ko'rsating</b>",
"not_found": "<emoji document_id=5345937796102104039>🤷‍♀️</emoji> <b>Topilmadi</b>",
}
_list = [
"3000years",
"approved",
"beautiful",
"brazzers",
"burn",
"challenger",
"circle",
"contrast",
"crush",
"ddungeon",
"dictator",
"distort",
"emboss",
"fire",
"frame",
"afusion",
"glitch",
"greyscale",
"instagram",
"invert",
"jail",
"magik",
"missionpassed",
"moustache",
"ps4",
"posterize",
"rejected",
"rip",
"scary",
"scrolloftruth",
"sepia",
"sharpen",
"sniper",
"thanos",
"trinity",
"triggered",
"unsharpen",
"utatoo",
"wanted",
"wasted",
]
async def amegencmd(self, message: Message):
"""Generate memes image"""
reply = await message.get_reply_message()
args = utils.get_args_raw(message)
await utils.answer(message, self.strings["wait"])
if not args:
return await utils.answer(message, self.strings["no_args"])
elif not reply.photo:
return await utils.answer(message, self.strings["r_photo"])
elif args not in self._list:
return await utils.answer(message, self.strings["not_found"])
async with self.client.conversation("@aozoram_bot") as conv:
try:
msg = await conv.send_message("/start")
s = await conv.get_response()
f = await conv.send_file(file=reply)
m = await f.reply(f"/amegen {args}")
await conv.get_response() # wait for response
response = await conv.get_response()
await utils.answer_file(message, response.media)
await s.delete()
await msg.delete()
await m.delete()
except AlreadyInConversationError:
await utils.answer(message, self.strings["already_open"])
await self.client.delete_dialog("@aozoram_bot")
async def amelistcmd(self, message: Message):
"""List of memes"""
spis = "\n".join([f"• <code>{i}</code>" for i in self._list])
await utils.answer(message, f"<b>Available memes:</b>\n\n{spis}")

View File

@@ -0,0 +1,258 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/AmoreInfo.jpg
import logging
import git
from telethon.tl.types import Message
from telethon.utils import get_display_name
from .. import loader, main, utils
import datetime
import time
logger = logging.getLogger(__name__)
@loader.tds
class AmoreindoMod(loader.Module):
"""Show userbot info"""
strings = {
"name": "AmoreInfo",
"owner": "Owner",
"version": "Version",
"build": "Build",
"prefix": "Prefix",
"time": "Time",
"platform": "Platform",
"uptime": "Uptime",
"up-to-date": "😌 Actual version",
"update_required": "😕 Outdated version </b><code>.update</code><b>",
"_cfg_cst_msg": "Custom message for info. May contain {me}, {version}, {build}, {prefix}, {platform}, {upd}, {time}, {uptime} keywords",
"_cfg_cst_btn": "Custom button for info. Leave empty to remove button",
"_cfg_cst_bnr": "Custom Banner for info.",
"_cfg_cst_frmt": "Custom fileformat for Banner info.",
"_cfg_banner": "Set `True` in order to disable an image banner",
"_cfg_time": "Use 1, -1, -3 etc.",
"_cfg_close": "Here you can change close button name",
}
strings_ru = {
"owner": "Владелец",
"version": "Версия",
"build": "Сборка",
"prefix": "Префикс",
"uptime": "Аптайм",
"platform": "Платформа",
"time": "Время",
"up-to-date": "😌 Актуальная версия",
"update_required": "😕 Требуется обновление </b><code>.update</code><b>",
}
strings_uz = {
"owner": "Egasi",
"version": "Versiya",
"build": "Yig'ish",
"prefix": "Prefix",
"uptime": "Uptime",
"platform": "Platforma",
"time": "Soat",
"up-to-date": "😌 Joriy versiya",
"update_required": "😕 Yangilanish talab qilinadi </b><code>.update</code><b>",
}
strings_de = {
"owner": "Besitzer",
"version": "Version",
"build": "Zusammenbau",
"prefix": "Präfix",
"uptime": "Betriebszeit",
"platform": "Plattform",
"time": "Die Zeit",
"up-to-date": "😌 Aktuelle Version",
"update_required": "😕 Aktualisierung erforderlich </b><code>.update</code><b>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"custom_message",
"no",
doc=lambda: self.strings("_cfg_cst_msg"),
),
loader.ConfigValue(
"custom_button1",
["🏡 Modules", "https://t.me/amoremods"],
lambda: self.strings("_cfg_cst_btn"),
validator=loader.validators.Series(min_len=0, max_len=2),
),
loader.ConfigValue(
"custom_button2",
[],
lambda: self.strings("_cfg_cst_btn"),
validator=loader.validators.Series(min_len=0, max_len=2),
),
loader.ConfigValue(
"custom_button3",
[],
lambda: self.strings("_cfg_cst_btn"),
validator=loader.validators.Series(min_len=0, max_len=2),
),
loader.ConfigValue(
"custom_banner",
"https://te.legra.ph/file/64bde7bf6b8e377521134.mp4",
lambda: self.strings("_cfg_cst_bnr"),
),
loader.ConfigValue(
"disable_banner",
False,
lambda: self.strings("_cfg_banner"),
validator=loader.validators.Boolean(),
),
loader.ConfigValue(
"custom_format",
"gif",
lambda: self.strings("_cfg_cst_frmt"),
validator=loader.validators.Choice(["photo", "video", "gif"]),
),
loader.ConfigValue(
"timezone",
"+5",
lambda: self.strings("_cfg_time"),
),
loader.ConfigValue(
"close_btn",
"🔻Close",
lambda: self.strings("_cfg_close"),
),
)
async def client_ready(self, client, db):
self._db = db
self._client = client
self._me = await client.get_me()
def _render_info(self) -> str:
ver = utils.get_git_hash() or "Unknown"
try:
repo = git.Repo()
diff = repo.git.log(["HEAD..origin/master", "--oneline"])
upd = (
self.strings("update_required") if diff else self.strings("up-to-date")
)
except Exception:
upd = ""
me = f'<b><a href="tg://user?id={self._me.id}">{utils.escape_html(get_display_name(self._me))}</a></b>'
version = f'<i>{".".join(list(map(str, list(main.__version__))))}</i>'
build = f'<a href="https://github.com/hikariatama/Hikka/commit/{ver}">#{ver[:8]}</a>' # fmt: skip
prefix = f"«<code>{utils.escape_html(self.get_prefix())}</code>»"
platform = utils.get_named_platform()
uptime = utils.formatted_uptime()
offset = datetime.timedelta(hours=self.config["timezone"])
tz = datetime.timezone(offset)
time1 = datetime.datetime.now(tz)
time = time1.strftime("%H:%M:%S")
return (
"<b> </b>\n"
+ self.config["custom_message"].format(
me=me,
version=version,
build=build,
upd=upd,
prefix=prefix,
platform=platform,
uptime=uptime,
time=time,
)
if self.config["custom_message"] != "no"
else (
"<b>🎢 AmoreInfo </b>\n"
f'<b>🤴 {self.strings("owner")}: </b>{me}\n\n'
f"<b>🕶 {self.strings('version')}: </b>{version} {build}\n"
f"<b>{upd}</b>\n"
f"<b>⏳ {self.strings('uptime')}: {uptime}</b>\n\n"
f"<b>⌚ {self.strings('time')}: {time}</b>\n"
f"<b>📼 {self.strings('prefix')}: </b>{prefix}\n"
f"{platform}\n"
)
)
def _get_mark(self, int):
if int == 1:
return (
{
"text": self.config["custom_button1"][0],
"url": self.config["custom_button1"][1],
}
if self.config["custom_button1"]
else None
)
elif int == 2:
return (
{
"text": self.config["custom_button2"][0],
"url": self.config["custom_button2"][1],
}
if self.config["custom_button2"]
else None
)
elif int == 3:
return (
{
"text": self.config["custom_button3"][0],
"url": self.config["custom_button3"][1],
}
if self.config["custom_button3"]
else None
)
elif int == 4:
return (
{
"text": self.config["close_btn"],
"action": "close",
}
if self.config["close_btn"]
else None
)
@loader.owner
async def ainfocmd(self, message: Message):
"""Send userbot info"""
m1 = self._get_mark(1)
m2 = self._get_mark(2)
m3 = self._get_mark(3)
m4 = self._get_mark(4)
await self.inline.form(
message=message,
text=self._render_info(),
reply_markup=[
[
*([m1] if m1 else []),
],
[
*([m2] if m2 else []),
*([m3] if m3 else []),
],
[
*([m4] if m4 else []),
],
],
**{}
if self.config["disable_banner"]
else {self.config["custom_format"]: self.config["custom_banner"]},
)

View File

@@ -0,0 +1,473 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/AnimeVoices.jpg
from .. import loader
@loader.tds
class AnimeVoicesMod(loader.Module):
"""🎤 Popular Anime Voices"""
strings = {"name": "AnimeVoices"}
async def smexkcmd(self, message):
"""Смех Канеки"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/9",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def smexycmd(self, message):
"""Смех Ягами"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/7",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def znaycmd(self, message):
"""Знай свое место ничтожество"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/35",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def madaracmd(self, message):
"""Учиха Мадара"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/24",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def sharingancmd(self, message):
"""Итачи Шаринган"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/VoiceAmore/29",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def itachicmd(self, message):
"""Учиха Итачи"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/26",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def imsasukecmd(self, message):
"""Учиха Саске"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/VoiceAmore/30",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def paincmd(self, message):
"""Познайте боль"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/15",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def rascmd(self, message):
"""Расширение территории"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/17",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def tenseicmd(self, message):
"""Shinra tensei"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/18",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def dazaicmd(self, message):
"""Dazai"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/3",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def gaycmd(self, message):
"""I'm gay"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/20",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def bankaicmd(self, message):
"""Bankai"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/21",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def satecmd(self, message):
"""Sate sate sate"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/5",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def yoaimocmd(self, message):
"""Yoaimo"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/11",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def ghoulcmd(self, message):
"""Я гуль"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/12",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def welawcmd(self, message):
"""Мы закон"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/13",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def dattebayocmd(self, message):
"""Даттебайо"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/14",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def hardlifecmd(self, message):
"""Жизнь такова"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/16",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def hanmacmd(self, message):
"""Я Ханма Шужи"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/25",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def surprisecmd(self, message):
"""Surprise MxtherFxcker"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/30",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def equalcmd(self, message):
"""Мы созданы равными"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/31",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def beautytreecmd(self, message):
"""Красота леса"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/32",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def bankaiicmd(self, message):
"""Bankai remix"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/33",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def yametecmd(self, message):
"""Фулл ямете кудасай"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/47",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def mafiacmd(self, message):
"""Просыпается мафия"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/48",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def sharinganncmd(self, message):
"""Sharingan remix"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/49",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def smexecmd(self, message):
"""Смех Эрен"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/50",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def narutocmd(self, message):
"""Naruto heroes"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/51",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def smexrcmd(self, message):
"""Смех рюк"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/52",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def ohayocmd(self, message):
"""Охаё"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/53",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def iamhungrycmd(self, message):
"""Есть хочу"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/54",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def amaterasucmd(self, message):
"""Аматерасу remix"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/55",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def owocmd(self, message):
"""Full OwO"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/56",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
async def ghoulrucmd(self, message):
"""Русский Tokyo Ghoul"""
reply = await message.get_reply_message()
await message.delete()
await message.client.send_file(
message.to_id,
"https://t.me/animevoicesbyamore/57",
voice_note=True,
reply_to=reply.id if reply else None,
)
return
#voices by @dziru

View File

@@ -0,0 +1,283 @@
# Friendly Telegram (telegram userbot)
# Copyright (C) 2018-2019 The Authors
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods, FTG
__version__ = (1, 1, 0)
import asyncio
import datetime
from telethon.tl import functions
from telethon.utils import get_display_name
from .. import loader, utils
@loader.tds
class AutoProfileMod(loader.Module):
"""Automatic stuff for your profile :P"""
strings = {
"name": "AutoProfile",
"invalid_args": (
"<b>Missing parameters, please read the <code>.aguide</code> <emoji document_id=5213468029597261187>✔️</emoji></b>"
),
"missing_time": (
"<b>Time was not specified in bio <emoji document_id=5215273032553078755>❎</emoji></b>"
),
"enabled_bio": (
"<b>Enabled bio clock <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"bio_not_enabled": (
"<b>Bio clock is not enabled <emoji document_id=5215273032553078755>❎</emoji></b>"
),
"disabled_bio": (
"<b>Disabled bio clock <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"enabled_name": (
"<b>Enabled name clock <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"name_not_enabled": (
"<b>Name clock is not enabled <emoji document_id=5215273032553078755>❎</emoji></b>"
),
"disabled_name": (
"<b>Name clock disabled <emoji document_id=5215273032553078755>❎</emoji></b>"
),
"_cfg_time": "Use timezone 1, -1, -3 etc.",
}
strings_uz = {
"invalid_args": (
"<b>to'g'ri argumetlar emas, <code > ni o'qing.aguide</code> <emoji document_id=5213468029597261187>✔️</emoji></b>"
),
"missing_time": (
"<b>vaqt bio-da o'rnatilmagan<emoji document_id=5215273032553078755 > ❎< / emoji></b>"
),
"enabled_bio": (
"<b>Bio soat muvaffaqiyatli o'rnatildi <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"bio_not_enabled": (
"<b>soat bio-ga o'rnatilmagan<emoji document_id=5215273032553078755 > ❎< / emoji > </b>"
),
"disabled_bio": (
"<b > Bio-dagi vaqt muvaffaqiyatli o'chirildi <emoji document_id = 5212932275376759608>✅</emoji></b>"
),
"enabled_name": (
"<b>soat taxallusga muvaffaqiyatli o'rnatildi <emoji document_id = 5212932275376759608>✅</emoji></b>"
),
"name_not_enabled": (
"<b>soat taxallusga o'rnatilmagan<emoji document_id=5215273032553078755 > ❎< / emoji > </b>"
),
"disabled_name": (
"<b>taxallusdagi vaqt muvaffaqiyatli o'chirildi <emoji document_id = 5212932275376759608>✅</emoji></b>"
),
"_cfg_time": "vaqt zonasidan foydalaning 1, -1, -3 va boshqalar.",
}
strings_ru = {
"invalid_args": (
"<b>Не правильные аргуметы, прочитай <code>.aguide</code> <emoji document_id=5213468029597261187>✔️</emoji></b>"
),
"missing_time": (
"<b>Время не было установлено в био<emoji document_id=5215273032553078755>❎</emoji></b>"
),
"enabled_bio": (
"<b>Био часы успешно установлены <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"bio_not_enabled": (
"<b>Часы не установлено в био<emoji document_id=5215273032553078755>❎</emoji></b>"
),
"disabled_bio": (
"<b>Время в био успешно отключен <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"enabled_name": (
"<b>Часы в ник успешно установлены <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"name_not_enabled": (
"<b>Часы не установлены в ник<emoji document_id=5215273032553078755>❎</emoji></b>"
),
"disabled_name": (
"<b>Время в нике успешно отключен <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"_cfg_time": "Используй таймзону 1, -1, -3 и тд.",
}
strings_de = {
"invalid_args": (
"<b>Sind nicht die richtigen Argumente, lies <code>.aguide</code> <emoji document_id=5213468029597261187>✔️</emoji></b>"
),
"missing_time": (
"<b>Die Zeit wurde nicht auf bio gesetzt<emoji document_id=5215273032553078755>❎</emoji></b>"
),
"enabled_bio": (
"<b>Bio-Uhr wurde erfolgreich installiert <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"bio_not_enabled": (
"<b>Die Uhr ist nicht auf bio eingestellt<emoji document_id=5215273032553078755>❎</emoji></b>"
),
"disabled_bio": (
"<b>Zeit in bio erfolgreich deaktiviert <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"enabled_name": (
"<b>Die Uhr wurde erfolgreich auf den Nickname gesetzt <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"name_not_enabled": (
"<b>Die Uhr ist nicht auf den Spitznamen<emoji document_id=5215273032553078755>❎</emoji></b> eingestellt"
),
"disabled_name": (
"<b>Nickzeit wurde erfolgreich deaktiviert <emoji document_id=5212932275376759608>✅</emoji></b>"
),
"_cfg_time": "Benutze die Zeitzone 1, -1, -3 usw.",
}
def __init__(self):
self.bio_enabled = False
self.name_enabled = False
self.raw_bio = None
self.raw_name = None
self.config = loader.ModuleConfig(
loader.ConfigValue(
"timezone",
"+5",
lambda: self.strings("_cfg_time"),
),
)
async def client_ready(self, client, db):
self.client = client
self._me = await client.get_me()
@loader.command(ru_doc="""Что-бы указать таймзону через конфиг""")
async def cfautoprofcmd(self, message):
"""To specify the timezone via the config"""
name = self.strings("name")
await self.allmodules.commands["config"](
await utils.answer(message,
f"{self.get_prefix()}config {name}")
)
@loader.command(ru_doc="""Автоматически изменяет биографию вашей учетной записи с учетом текущего времени, использования: .autobio 'сообщение, время как {time}'""")
async def autobiocmd(self, message):
"""Automatically changes your account's bio with current time, usage:
.autobio 'message, time as {time}'"""
msg = utils.get_args(message)
if len(msg) != 1:
return await utils.answer(message, self.strings("invalid_args", message))
raw_bio = msg[0]
if "{time}" not in raw_bio:
return await utils.answer(message, self.strings("missing_time", message))
self.bio_enabled = True
self.raw_bio = raw_bio
await self.allmodules.log("start_autobio")
await utils.answer(message, self.strings("enabled_bio", message))
while self.bio_enabled:
offset = datetime.timedelta(hours=self.config["timezone"])
tz = datetime.timezone(offset)
time1 = datetime.datetime.now(tz)
current_time = time1.strftime("%H:%M")
bio = raw_bio.format(time=current_time)
await self.client(functions.account.UpdateProfileRequest(about=bio))
await asyncio.sleep(60)
@loader.command(ru_doc="""Что-бы остановить время в био введи .stopautobio""")
async def stopautobiocmd(self, message):
"""Stop autobio cmd."""
if self.bio_enabled is False:
return await utils.answer(message, self.strings("bio_not_enabled", message))
self.bio_enabled = False
await self.allmodules.log("stop_autobio")
await utils.answer(message, self.strings("disabled_bio", message))
await self.client(
functions.account.UpdateProfileRequest(about=self.raw_bio.format(time=""))
)
@loader.command(ru_doc="""Автоматически изменяет имя вашей учетной записи с учетом текущего времени, использования: .autoname 'сообщение, время как {time}'""")
async def autonamecmd(self, message):
"""Automatically changes your Telegram name with current time, usage:
.autoname '<message, time as {time}>'"""
msg = utils.get_args(message)
if len(msg) != 1:
return await utils.answer(message, self.strings("invalid_args", message))
raw_name = msg[0]
if "{time}" not in raw_name:
return await utils.answer(message, self.strings("missing_time", message))
self.name_enabled = True
self.raw_name = raw_name
await self.allmodules.log("start_autoname")
await utils.answer(message, self.strings("enabled_name", message))
while self.name_enabled:
offset = datetime.timedelta(hours=self.config["timezone"])
tz = datetime.timezone(offset)
time1 = datetime.datetime.now(tz)
current_time = time1.strftime("%H:%M")
name = raw_name.format(time=current_time)
await self.client(functions.account.UpdateProfileRequest(first_name=name))
await asyncio.sleep(60)
@loader.command(ru_doc="""Что-бы остановить время в имени учетной записи введи .stopautoname""")
async def stopautonamecmd(self, message):
"""just write .stopautoname"""
if self.name_enabled is False:
return await utils.answer(
message, self.strings("name_not_enabled", message)
)
self.name_enabled = False
await self.allmodules.log("stop_autoname")
await utils.answer(message, self.strings("disabled_name", message))
await self.client(
functions.account.UpdateProfileRequest(
first_name=self.raw_name.format(time="")
)
)
@loader.command(ru_docs="""Доки ru/en""")
async def aguide(self, message):
"Just guide ru/en"
args = utils.get_args_raw(message)
args = args if args in {"en", "ru"} else "en"
time = "{time}"
nick = f'<a href="tg://user?id={self._me.id}">{utils.escape_html(get_display_name(self._me))}</a>'
pref = f"{utils.escape_html(self.get_prefix())}"
await utils.answer(
message,
f"<emoji document_id=5789581976176430614>💸</emoji> For example:\n\n<emoji document_id=5789667570579672963>💸</emoji> AutoName: <code>{pref}autoname '{nick} | {time}'</code>\n"
f"<emoji document_id=5789667570579672963>💸</emoji> AutoBio: <code>{pref}autobio 'smth | {time}'</code>\n"
if args == "en"
else (
f"<emoji document_id=5789581976176430614>💸</emoji> Например:\n\n<emoji document_id=5789667570579672963>💸</emoji> Авто Ник: <code>{pref}autoname '{nick} | {time}'</code>\n"
f"<emoji document_id=5789667570579672963>💸</emoji> Авто Био: <code>{pref}autobio 'что-то | {time}'</code>\n"
),
)

View File

@@ -0,0 +1,124 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# If you don't like the module don't use it
# meta developer: @hikamorumods
# meta banner: https://github.com/AmoreForever/assets/blob/master/besafe.jpg?raw=true
import logging
import requests
import ast
import re
from .. import loader, utils
logger = logging.getLogger(__name__)
__version__ = (1, 0, 0)
@loader.tds
class BeSafe(loader.Module):
"""
Check module before loading
"""
strings = {
"name": "BeSafe",
"no_args_or_reply": "<emoji document_id=5456652110143693064>🤷‍♂️</emoji> <b>[BeSafe]</b> No link or reply to file",
"safe": "<emoji document_id=5203929938024999176>🛡</emoji> <b>Module is safe</b>",
"suspicious": "<emoji document_id=5325771498718241219>🔎</emoji> Module is suspicious\n\n<emoji document_id=6334443713485342501>⛩</emoji> <b>Suspicious imports:</b>\n",
'sus_keywords': "\n<emoji document_id=6334405093139416847>🔑</emoji> <b>Suspicous keywords:</b>"
}
strings_ru = {
"no_args_or_reply": "<emoji document_id=5456652110143693064>🤷‍♂️</emoji> <b>[BeSafe]</b> Нет ссылки или реплея на модуль",
"safe": "<emoji document_id=5203929938024999176>🛡</emoji> <b>Модуль безопасен</b>",
"suspicious": "<emoji documentx_id=5325771498718241219>🔎</emoji> Модуль подозрительный\n\n<emoji document_id=6334443713485342501>⛩</emoji> <b>Подозрительные импорты:</b>\n",
'sus_keywords': "\n<emoji document_id=6334405093139416847>🔑</emoji> <b>Подозрительные ключевые слова:</b>"
}
def extract_imports(self, code):
code = code.lstrip('\ufeff') # крч удаление символа BOM, если он есть
try:
tree = ast.parse(code)
except SyntaxError as e:
if "invalid non-printable character" not in str(e):
raise
code = code.encode('utf-8-sig').decode('utf-8')
tree = ast.parse(code)
imports = []
for node in ast.walk(tree):
if isinstance(node, ast.Import):
imports.extend(name.name for name in node.names)
elif isinstance(node, ast.ImportFrom):
module_name = node.module
imports.extend(f"{module_name}.{name.name}" for name in node.names)
return imports
suspicious_imports = [
'glob',
'os',
'sys',
'telethon.tl.TLRequest',
'requests',
]
suspicious_keywords = [
r'0x418d4e0b',
r'0xf5b399ac',
r'w+z+mm+"A"+nk+u+h+lk',
r'b"\x0bN\x8dA"'
r'session',
r'TestingHikka_BOT' # временно будет тут
]
def extract_keywords(self, code):
words = []
for word in self.suspicious_keywords:
if r := re.findall(word, code):
words.append(r[0])
return words
@loader.command()
async def bs(self, message):
"""
BeSafe - <reply to module> or <link to module>
"""
args = utils.get_args_raw(message)
reply = await message.get_reply_message()
if args:
r = await utils.run_sync(requests.get, args)
string = r.text
elif reply:
code = (await self._client.download_file(reply.media, bytes)).decode("utf-8")
string = code
else:
await utils.answer(message, self.strings["no_args_or_reply"])
imports = self.extract_imports(string)
sus_imports = [f"▫️ <code>{imp}</code>" for imp in self.suspicious_imports if imp in imports]
sus_keywords = []
if sus_imports:
kw = self.extract_keywords(string)
sus_keywords = [f"▫️ <code>{k}</code>" for k in self.suspicious_keywords if k in kw]
if sus_imports or sus_keywords:
sus_list = sus_imports + [self.strings["sus_keywords"]] + sus_keywords
text = self.strings["suspicious"] + '\n'.join(sus_list)
else:
text = self.strings["safe"]
await utils.answer(message, text)

View File

@@ -0,0 +1,229 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
import re
import asyncio
import random
from aiohttp import web
from .. import utils, loader
class WebCreator:
def __init__(self, name, tg_link, preview_name):
self.url = None
self.app = web.Application()
self.app.router.add_get("/", self.index)
self.name = name
self.tg_link = tg_link
self.preview_name = preview_name
async def index(self, request):
html_content = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<title>For {self.name}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Indie+Flower&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1"></script>
</head>
<style>
body {{
margin: 0;
text-align: center;
background: #1d1d1d;
font: 22px 'Indie Flower', cursive, "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #fff;
overflow-x: hidden;
}}
#content {{
position: relative;
max-width: 470px;
margin: 0 auto;
line-height: 150%;
padding: 20px;
z-index: 1;
}}
h1 {{
font-size: 2.5rem;
color: #f7d066;
margin-top: 50px;
}}
p {{
font-size: 1.2rem;
margin-top: 20px;
}}
span {{
color: #f7467e;
}}
@media (max-width: 767px) {{
#content {{
max-width: 100%;
padding: 10px;
}}
}}
</style>
<body>
<div id="content">
<h1>Happy Birthday, {self.name}!</h1>
<p>Dear {self.name},<br>
On this special day, I wish you all the very best, all the joy you can ever have, and may you be blessed
abundantly today, tomorrow, and the days to come! May you have a fantastic birthday and many more to come...
HAPPY BIRTHDAY!!!!<br>
<span>With love, <a href="{self.tg_link}">{self.preview_name}</a></span>
</p>
</div>
<script>
function launchConfetti() {{
const duration = 2 * 1000;
const end = Date.now() + duration;
(function frame() {{
confetti({{
particleCount: 5,
angle: 60,
spread: 55,
origin: {{ x: 0 }},
colors: ['#f7d066', '#f7467e', '#fff']
}});
confetti({{
particleCount: 5,
angle: 120,
spread: 55,
origin: {{ x: 1 }},
colors: ['#f7d066', '#f7467e', '#fff']
}});
if (Date.now() < end) {{
requestAnimationFrame(frame);
}}
}})();
}}
window.onload = launchConfetti;
</script>
</body>
</html>
"""
return web.Response(text=html_content, content_type="text/html")
async def open_tunnel(self, port):
ssh_command = f"ssh -o StrictHostKeyChecking=no -R 80:localhost:{port} nokey@localhost.run"
process = await asyncio.create_subprocess_shell(
ssh_command,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
url = await self._extract_tunnel_url(process.stdout)
self.url = url or f"https://localhost:{port}"
return self.url
async def _extract_tunnel_url(self, stdout):
event = asyncio.Event()
url = None
async def read_output():
nonlocal url
while True:
line = await stdout.readline()
if not line:
break
decoded_line = line.decode()
match = re.search(r"tunneled.*?(https:\/\/.+)", decoded_line)
if match:
url = match[1]
break
event.set()
await read_output()
await event.wait()
return url
@loader.tds
class BirthdayWish(loader.Module):
"""Share warmth with your loved ones and give them this website to make their birthdays even more special and joyful."""
strings = {
"name": "BirthdayWish",
"provide_name": "<emoji document_id=5456652110143693064>🤷‍♂️</emoji> <b>Please provide a name</b>",
"web_url": "<emoji document_id=5334643333488713810>🌐</emoji> <b>URL: {} | Expires in <code>{}</code> seconds</b>",
"expired": "<emoji document_id=5981043230160981261>⏱</emoji> <b>Url Expired</b>",
}
strings_ru = {
"provide_name": "<emoji document_id=5456652110143693064>🤷‍♂️</emoji> <b>Пожалуйста, укажите имя</b>",
"web_url": "<emoji document_id=5334643333488713810>🌐</emoji> <b>URL: {} | Истекает через <code>{}</code> секунд</b>",
"expired": "<emoji document_id=5981043230160981261>⏱</emoji> <b>Url истек</b>",
}
def __init__(self):
self.wishes = {}
async def tunnel_handler(self, port):
creator = WebCreator(
name=self.name, tg_link=self.tg_link, preview_name=self.preview_name
)
runner = web.AppRunner(creator.app)
await runner.setup()
global site
site = web.TCPSite(runner, "127.0.0.1", port)
await site.start()
url = await creator.open_tunnel(port)
return url, runner
async def wishcmd(self, message):
"""Create Birthday web wishes args: <name> <time:seconds default(20)>"""
args = utils.get_args_raw(message).split(" ")
if args[0] == "":
return await utils.answer(message, self.strings("provide_name"))
text = args[0]
expiration_time = int(args[1]) if len(args) > 1 else 20
me = await message.client.get_me()
self.tg_link = f"https://t.me/{me.username}" or "https://t.me/Unknown"
self.preview_name = me.first_name
self.name = text
port = random.randint(1000, 9999)
url, runner = await self.tunnel_handler(port)
await utils.answer(
message, self.strings("web_url").format(url, expiration_time)
)
await asyncio.sleep(expiration_time)
await site.stop()
await runner.cleanup()
await utils.answer(message, self.strings("expired"))

View File

@@ -0,0 +1,100 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta pic: https://te.legra.ph/file/7772a7dae6290f0a612a6.png
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Bull.jpg
import random
from .. import loader, utils
from ..inline.types import InlineCall
from ..inline.types import InlineQuery
bullr = (
"ТЫ ПОНИМАЕШЬ ЧТО Я ПИЗДАК В ТЕОЙ МАТРЕИ НА СВОЙ УЙ КАК МАКАРОНИНУ НАМОТАЛ БЛЯДЬ И НАЧАЛ РАСКРУЧИВАТЬ ЕЁ, ПОСЛЕ ЧЕГО ВЫКИНУЛ В КОСМОС, ЧТОБ ЕЁ ТАМ ИНОПЛАНЕТЯНЫ ХУЯМИ РВАЛИ?)",
"ТЫ ПОНИМАЕШЬ ЧТО Я ТВОЮ МАТЬ ОТПРАВИЛ СО СВОЕГО ЪХУЯ В НЕБО, ЧТОБ ОНА СВОИМ ПИЗДАКОМ ПРИНИМАЛА МИТЕОРИТНУЮ АТАКУ?)",
"ЕСЛИ ТЫ СЕЙЧАС ТАК И БУДЕШЬ ПРОДОЛЖАТЬ ПРОТИВОРЕЧИТЬ МОЕМУ ХУЮ, КАК ИМ КАК БЛЯДЬ НА НЛО ЗАХВОЧУ ТВОЁ ОЧКО И НАЧНУ ОПЫТЫ ПРОВОДИТЬ",
"ТЫ ПОНИМАЕШЬЧ ТО ВТОЯ МАТЬ МОЙ ХУЙ ЗАВЕРНУЛА В ПАКЕТИК ПОТОМУ ЧТО У ЭТОЙ БОМЖИХИ НЕБЫЛО ДЕНЕГ НА ПРЕЗИКИ, И ПОКЕТИК ПОРВАЛСЯ, И РОДИЛОСЬ ТАКОЕ ХУЙЛО КАК ТЫ",
"TЫ ПОНИМАЕШЬ ЧТО Я ТВОЮ МАТЬ СЛУЧАЙНО СВОИМ ХУЁМ СМЁЛ НАХУЙ СО СВОЕГО ПУТИ, И ОНА УЛЕТЕЛА НА РАДИУС ОБСТРЕЛА МОЕЙ ЗАЛУПЫ",
"АМЕБА ЕБАНАЯ СУКА) МАМАШКУ ТВОЮ ДЫРЯВИЛ ЧЕТ ) НАХУЙ ТВОЯ МАМАША КРИЧИТ КОГДА Я НАЧИНАЮ ЕБАТЬ ЕЕ)",
"АМЕБА ИЛИ ТЫ ОЛЕНЬ?) СЛЫШЬ ЕСЛИ ТЫ ПРОЛЬЕШЬ НА МОЙ ХУЙ СЛЕЗЫ , ТО ТЫ НЕ РАССЧИТЫВАЙ НА ТО , ЧТО ПОТОМ К ТЕБЕ ПРИДЕТ ФЕЯ И ПООБЕЩАЕТ ТЕБЕ ДОЛГУЮ И СЧАСТЛИВУЮ ЖИЗНЬ)",
"ОЛЕНЬ ТЫ ЕБАНЫЙ) МАТЬ ТВОЮ ЕБУ ЧЕТ ) ДАВАЙ ТЫ ЩАС ВОЗЬМЕШЬ МОЙ ХУЙ КАК ПЕРО И СЛОВНО КАК ПИСАТЕЛЬ СЕРЕБРЯНОГО ВЕКА НАПИШЕШЬ КАКОЙ НИБУДЬ РОМАН КОТОРЫЙ БУДЕТ ПО РАЗМЕРУ ПРИМЕРНО КАК МАСТЕР И МАРГАРИТА )",
"твоя мамка блядоебская кобыла и лезби",
"у тебя мать сраная шлюха",
"Я ТОВЮ МАМАШУ СВОИМ ХУЁМ РАСПЛЮЩИЛ, И ТЕПЕРЬ ОНА КАК ХОЯЧАЮ ПРУЖИТНКА БЛЯДЬ, ОТ СЕБЯ ВСЕ ХУИ ОТТАЛКИВАЕТ КРОМЕ МОЕГО, ДЛЯ МОЕГО ХУЯ ВСЕГДА ОТКРЫТ ДОСТУП В ЕЁ ПИЗДАК",
"ТЫ ПОНИМАЕШЬ ЧТО Я ТВОЮ МАТЬ БЛЯДЬ НАТЯНУЛ ПИЗДАКОМ НА ВЫСОКОВОЛЬТНУЮ ЭЛЕКТРО ВЫШКУ, И ОНА В СЕБЯ НАХУЙ ВЕСЬ ТОК ВТЯНУЛА, ТЕПЕРЬ ОНА БЛЯДЬ КАК ЭЛЕКТРО",
"Я КОГДА ВЫЕБАЛ ТВОЮ МАТЬ Я СВОЙ ХУЙ ПОСТАВИЛ К ЕЁ УХУ, ЧТОБ ОНА СЛЫШАЛА ПРИБОЯ СПЕРМЫ, А ПОТОМ ОНА ШИРОКО РАСКРЫЛА РОТ МЫ В ЕЁ ЕБЛЯТНИКЕ УСТРОИЛИ ОКЕАН",
"ТЫ ПОНИМАЕШЬ ЧТО Я В ПИЗДАК ТВОЕЙ МАТРЕИ СВОЙ ХУЙ ЗАСУНУ КАК БЛЯДЬ ШТТЕККЕР И ЕЁ ЗАРЯД ПОВЫСИЛСЯ КАК ОТ ЭНЕРГЕТИКА)",
"ТЫ ПОНИМАЕШЬ ЧТО ТВОЯ МАТЬ НА МОЁМ ХУЮ УСТРОИЛА БЛЯДЬ ТАНЦПОЛ, И НАЧАЛА СВОИМ ПОДРУГАМ ПРОДАВАТЬ НА НЕГО БИЛЕТЫ",
" ЕСЛИ ТЫ СЕЙЧАС НЕ НАЧНЁШЬ МНЕ ОТВЕЧАТЬ, Я ТЕБЕ НАХУЙ ХЁМ ПАЛЬЦЫ ПЕРЕЛОМАЮ, ОБРАЗИНА ТЫ ЕБАНАЯ)",
"ТЫ ПОНИМАЕШЬ ЧТО ВТОЯ МАМАШКА КАШЁЛКА ЕБАНАЯ, НА МОЙ ХУЙ ВЕШАЕТСЯ СВОИМ ПИЗДАКОМ КАК МАГНИТИК НА ХОЛОДИЛЬНИК, ПИДОПР ТЫ БЛЯДЬ ЕБАНЫЙ",
"ТЫ ПОНИМАЕШЬ ЧТО Я ТВОЕЙ МАТЕРИ ГОЛОВУ ХУЁМ КАК КОПЬЁМ ПРОБИЛ БЛЯДЬ И ЕЁ КУРИНЫЙ МОЗГ УМЕР НАХУЙ)) ИЗ-ЗА ЭТОГО ОНА ТЕБЯ ДАЖЕ И НЕ ВСПОМИНАЕТ)",
"ТЫ ПОНИМАЕШЬ ЧТО Я ВЫСТАВИЛ СВОЙ ХУЙ НА АВИТО, А ТВОЯ МАТЬ ПРОШЛА БЛЯДЬ БЕЗ ОЧЕРЕДИ И ККУПИЛА ЕГО, ОТДАВ СВОЮ ГНИЛУЮ ПОЧКУ?)",
"ТЫ ПОНИМАЕШЬ ЧТО ТВОЯ МАТЬ МОЙ ХУЙ НА НОЧЬ СЕБЕ В ПИЗДАК СУЁТ КАК ОБОГРЕВАТЕЛЬ НАХУЙ?)",
"ТЫ ПОНИМАЕШЬ ЧТО Я ПОКА ЧТО ЕБАЛ ТВОЮ МАМАШУ В СРАКУ, У НЕЁ ТАМ ЗАСОР СПЕРМЫ БЛЯДЬ ОБРАЗОВАЛСЯ И ЗАСОХ, ТЕПЕРЬ ОНА СРАТЬ НОРМАЛЬНО НЕ МОЖЕТ, ИДИ НАХУЙ СПАСАЙ ЭТУ ШЛЮХУ",
"ТЫ ПОНИМАЕШЬ ЧТО КОГДА Я ЕБУ ТВОЮ МАТЬ ЧТОБЫ ОНА НЕ ОРАЛА Я ЕЙ КЛЯП В РОТ СУЮ, НО ОДИН РАЗ СЛУЧИЛОСЬ ТАКОЕ, ЧТО КОГДА Я СВОИМ ХУЁМ ДАЛ ЕЙ ПО ПИЗДЕ ОНА ЭТОТ КЛЯП ПРОГЛАТИЛА, И НАЧАЛА ЗАДЫХАТЬСЯ, СПАСИ СВОЮ ШЛЮХА МАМКУ)",
"ТЫ ПОНИМАЕШЬ ЧТО МОЙ ХУЙ ВЗЛАМЫВАЕТ ОЧКО ТВОЕЙ МАТЕРИ КАК СЕЙФ НАХУЙ, И ОТ ТУДА НАЧИНАЮТ ВАЛИТЬСЯ САМОРОДКИ СПЕРМЫ?)",
"ТЫ ПОНИМАЕШЬ ЧТО Я В ПИЗДАКЕ ТВОЕЙ МАТЕРИ УСТРОИЛ ИЗВЕРЖЕНИЕ СВОЕГО ХУЯ НАХУЙ?",
"ТЫ ПОНИМАЕШЬ ЧТО Я ХУЁМ НАЧАЛ МОТАТЬ ПЕРЕД ТВОИМ ЕБАЛОИ И ТЕБЯ СЛУЧАЙНО НАХУЙ ЗАГИПНОТЕЗИРОВАЛ, И ТЫ ОБ ХУИ СТАЛ ГОЛОВОЙ БИТЬСЯ?)",
"ТЫ ПОНИМАЕШЬ ЧТО КОГДА Я ТВОЮ МАТЬ ОНА КАК ШЛЮХА ЛОЖИТСЯ НА СПИНКУ И НАЧИНАЕТ ПОСАСЫВАТЬ МОИ ЯЙЦА",
"ТЫ ПОНИМАЕШЬ ЧТО Я В ПИЗДАКЕ ТВОЕЙ МАТЕРИ ИЗ ЕЁ КЛИТОРНЫЙ СТЕН ВЫРЕЗАЮ РАКЕТНИЦУ СВОИМ ХУЁМ?",
"Я СЕЙЧАС СВОЕЙ СПЕРМОЙ ОБОЛЬЮ ТВОЙ ПИЗДАК КАК КЕРАСИНОМ, И ПУЩУ НА НЕГО ИСКРУ, ДОБЫТАЯ КОТОРАЯ БУДЕТ О ТВОИ ГНИЛЫЕ ЗУБКИ, И ТЫ СГОРИШЬ НАХУЙ)",
"ТЫ ПОНИМАЕШЬ ЧТО ТЫ ОТ МОЕЙ ЗАЛУПЫ ПРЯЧШЬСЯ В ПИЗДАКЕ СВОЕЙ МАТЕРИ КАК НАХУЙ В БУНКЕРЕ, А Я СВОИМ ХУЁМ ЕГО НА СКВОЗЬ ПРОШИЛ И ТЕБЕ В ЕБАЛО ПОПАЛ)) ",
"ТЫ ПОНИМАЕШЬ ЧТО Я ХУЁМ СТАЛ КАТАЛИЗИРОВАТЬ ПИЗДАК ТВОЕЙ МАТЕРИ НА РАЗДВИЖЕНИЕ ЕЁ ЖИРНЫХ НОГ?)",
)
def bullme():
iwfy = random.choice(bullr)
return iwfy
@loader.tds
class BullMod(loader.Module):
"""Bull пиз#а собеседнику"""
strings = {"name": "BullMod"}
@loader.inline_everyone
async def bull_inline_handler(self, query: InlineQuery):
"""Забулить кого-то жесткими матами про мать"""
aoa = bullme()
btn_a = [{"text": "🌀 Random", "callback": self.bulls}]
return {
"title": "Пошутить про маму",
"thumb": "https://te.legra.ph/file/b2a6c8d20e0034a534ac4.jpg",
"description": "Отправить...",
"message": f"<i>{aoa}</i>",
"reply_markup": btn_a,
}
async def bullcmd(self, message):
"""Забулить кого-то жесткими матами про мать"""
aoa = bullme()
await utils.answer(message, aoa)
async def bullicmd(self, message):
"""Забулить кого-то жесткими матами про мать (inline)"""
aoa = bullme()
await self.inline.form(
message=message,
text=aoa,
reply_markup=[
[{"text": "🌀 Random", "callback": self.bulls}],
]
)
async def bulls(self, call: InlineCall):
aoa = bullme()
await call.edit(
text=aoa,
reply_markup=[
[{"text": "🌀 Random", "callback": self.bulls}],
]
)

View File

@@ -0,0 +1,158 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta pic: https://te.legra.ph/file/388e1b26a46a8c439e479.png
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Createlinks.jpg
from .. import loader, utils, security
@loader.tds
class AmorelinksMod(loader.Module):
"""Create links"""
strings = {
"name": "AmoreLinks",
"youtube": "🫂 <b>YouTube link special for you.</b>\n\n",
"google": "🫂 <b>Google link special for you.</b>\n\n",
"github": "🫂 <b>Github link special for you.</b>\n\n",
"pornhub": "🫂 <b>Pornhub link special for you.</b>\n\n",
"telegram": "🫂 <b>Telegram link special for you.</b>\n\n",
"4pda": "🫂 <b>4pda link special for you.</b>\n\n",
}
async def ytcmd(self, message):
"""<text> create YouTube link"""
text = utils.get_args_raw(message)
s = f"<b>✏ Input word: <code>{text}</code></b>"
if await self.allmodules.check_security(
message,
security.OWNER | security.SUDO,
):
try:
await self.inline.form(
self.strings("youtube", message) + s,
reply_markup=[
[{"text": "♨️ Link", "url": f"https://m.youtube.com/results?sp=mAEA&search_query={text}"}],
[{"text": "🔻 Close", "action": f"close"}],
],
message=message,
)
except Exception:
await utils.answer(message, self.strings("join", message))
async def gugcmd(self, message):
"""<text> create Google link"""
text = utils.get_args_raw(message)
s = f"<b>✏ Input word: <code>{text}</code></b>"
if await self.allmodules.check_security(
message,
security.OWNER | security.SUDO,
):
try:
await self.inline.form(
self.strings("google", message) + s,
reply_markup=[
[{"text": "🛰 Link", "url": f"https://www.google.com/search?q={text}"}],
[{"text": "🔻 Close", "action": f"close"}],
],
message=message,
)
except Exception:
await utils.answer(message, self.strings("join", message))
async def ghcmd(self, message):
"""<text> create Github link"""
text = utils.get_args_raw(message)
s = f"<b>✏ Input word: <code>{text}</code></b>"
if await self.allmodules.check_security(
message,
security.OWNER | security.SUDO,
):
try:
await self.inline.form(
self.strings("github", message) + s,
reply_markup=[
[{"text": "🛰 Link", "url": f"https://github.com/search?q={text}"}],
[{"text": "🔻 Close", "action": f"close"}],
],
message=message,
)
except Exception:
await utils.answer(message, self.strings("join", message))
async def phcmd(self, message):
"""<text> create PornHub link"""
text = utils.get_args_raw(message)
s = f"<b>✏ Input word: <code>{text}</code></b>"
if await self.allmodules.check_security(
message,
security.OWNER | security.SUDO,
):
try:
await self.inline.form(
self.strings("pornhub", message) + s,
reply_markup=[
[{"text": "🛰 Link", "url": f"https://rt.pornhub.com/video/search?search={text}"}],
[{"text": "🔻 Close", "action": f"close"}],
],
message=message,
)
except Exception:
await utils.answer(message, self.strings("join", message))
async def tgcmd(self, message):
"""<text> create Telegram link"""
text = utils.get_args_raw(message)
s = f"<b>✏ Input word: <code>{text}</code></b>"
if await self.allmodules.check_security(
message,
security.OWNER | security.SUDO,
):
try:
await self.inline.form(
self.strings("telegram", message) + s,
reply_markup=[
[{"text": "🛰 Link", "url": f"tg://search?query={text}"}],
[{"text": "🔻 Close", "action": f"close"}],
],
message=message,
)
except Exception:
await utils.answer(message, self.strings("join", message))
async def pdacmd(self, message):
"""<text> create 4pda link"""
text = utils.get_args_raw(message)
s = f"<b>✏ Input word: <code>{text}</code></b>"
if await self.allmodules.check_security(
message,
security.OWNER | security.SUDO,
):
try:
await self.inline.form(
self.strings("4pda", message) + s,
reply_markup=[
[{"text": "🛰 Link", "url": f"https://4pda.to/forum/index.php?act=search&source=all&forums=316&subforums=1&query={text}"}],
[{"text": "🔻 Close", "action": f"close"}],
],
message=message,
)
except Exception:
await utils.answer(message, self.strings("join", message))

View File

@@ -0,0 +1,73 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/DTWR.jpg
from .. import loader, utils
from telethon.tl.types import Message
@loader.tds
class DTWRMod(loader.Module):
"""Module Don't tag wihout reason"""
strings = {
"name": "DTWR",
"text": "Your custom text",
"username": "Input you username without '@'",
}
strings_ru = {
"text": "Кастомный текст",
"username": "Введи свой юзернэйм без '@'",
}
strings_uz = {
"text": "Kastom text",
"username": "Usernameingizni kiriting, '@' siz"
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"Username",
"username",
doc=lambda: self.strings("username"),
),
loader.ConfigValue(
"custom_text",
"😫 Please don't tag me without reason",
doc=lambda: self.strings("text"),
),
)
@loader.command(ru_docs="Конфиг этого модуля")
async def cfgdtwrcmd(self, message):
"""This module config"""
name = self.strings("name")
await self.allmodules.commands["config"](
await utils.answer(message, f"{self.get_prefix()}config {name}")
)
@loader.tag("only_messages", "only_groups", "in")
async def watcher(self, message: Message):
reply = await message.get_reply_message()
tag = self.config['Username']
if tag.startswith('@') is False:
tag = f"@{tag}"
if reply:
return False
if message.text.lower() == tag:
await message.reply(self.config["custom_text"])
await self._client.send_read_acknowledge(
message.chat_id,
clear_mentions=True,
)

View File

@@ -0,0 +1,48 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Facts.jpg
# channel @facti_p
from .. import loader, utils
from telethon import functions
from asyncio import sleep
import random
import datetime
chat = "@faktiru"
class FactsMod(loader.Module):
"""More Interesting Facts"""
strings = {
"name": "Facts",
"wait": "<emoji document_id=5472146462362048818>💡</emoji> Searching..."
}
strings_ru = {
"wait": "<emoji document_id=5472146462362048818>💡</emoji> Поиск..."
}
@loader.command(ru_docs="Интересные Факты")
async def afactscmd(self, message):
"""Intersting Facts"""
reply = await message.get_reply_message()
await utils.answer(message, self.strings["wait"])
result = await message.client(
functions.messages.GetHistoryRequest(
peer=chat, offset_id=0, offset_date=datetime.datetime.now(), add_offset=random.randint(0, 1000), limit=1, max_id=0, min_id=0, hash=0,
)
)
await sleep(0.30)
await message.delete()
await message.client.send_message(
message.to_id,
result.messages[0],
reply_to=reply.id if reply else None,
)

View File

@@ -0,0 +1,67 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://github.com/AmoreForever/assets/blob/master/Figlet.jpg?raw=true
import pyfiglet
import functools
from .. import loader, utils
class Figlet(loader.Module):
"""Creates Figlet Text"""
strings = {"name": "Figlet"}
style_to_font = {
"slant": "slant",
"3d": "3-d",
"5line": "5lineoblique",
"alpha": "alphabet",
"banner": "banner3-D",
"doh": "doh",
"iso": "isometric1",
"letter": "letters",
"allig": "alligator",
"dotm": "dotmatrix",
"bubble": "bubble",
"bulb": "bulbhead",
"digi": "digital"
}
@loader.command()
async def listfig(self, message):
"""List of figlet styles"""
keys_list = " , ".join(list(self.style_to_font.keys()))
await utils.answer(message, f"🚩 Available styles: {keys_list}")
@loader.command()
async def figlet(self, message):
"""Create figlet text, <style> | <args>"""
args = utils.get_args_raw(message).split(" | ")
if len(args) < 2:
await utils.answer(message, "Not enough arguments")
return
font = self.style_to_font.get(args[0], None)
if font is None:
await utils.answer(message, "There is no such style")
return
if not args[1]:
await utils.answer(message, "Text argument is empty")
return
result = await self.figlet_format_cached(args[1], font)
await utils.answer(message, f"<code>{result}<code>")
@staticmethod
@functools.lru_cache(maxsize=None)
async def figlet_format_cached(text, font):
return pyfiglet.figlet_format(text, font=font)

View File

@@ -0,0 +1,48 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/fileext.jpg
from .. import loader, utils
from telethon.tl.types import Message
from bs4 import BeautifulSoup
import requests
async def search_extention(ext):
sample_url = "https://www.fileext.com/file-extension/{}.html"
response_api = requests.get(sample_url.format(ext))
if not response_api.ok:
return (
f"Error fetching details for {ext}. Status code: {response_api.status_code}"
)
soup = BeautifulSoup(response_api.content, "html.parser")
return soup.find_all("td", {"colspan": "3"})[-1].text
@loader.tds
class FileExtMod(loader.Module):
"""Get file extention details"""
strings = {
"name": "FileExt",
"no_args": "<emoji document_id=5456652110143693064>🤷‍♂️</emoji> <b>No args passed</b>",
"response": "<emoji document_id=5467732133629926938>🔍</emoji> <b>File Extension</b>: <code>{}</code>\n<emoji document_id=5467919175160705819>🔍</emoji> <b>Description</b>: <code>{}</code>",
}
@loader.command()
async def fileext(self, message: Message):
"""Get file extention details"""
if args := utils.get_args_raw(message):
await utils.answer(
message,
self.strings("response").format(args, await search_extention(args)),
)
else:
await utils.answer(message, self.strings("no_args"))

View File

@@ -0,0 +1,37 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://github.com/AmoreForever/assets/blob/master/fragment_checker.jpg?raw=true
# requires: bs4
import requests
from bs4 import BeautifulSoup
from .. import loader, utils
class Fragment(loader.Module):
"""Show how much is the username in the Fragment.com"""
strings = {"name": "FragmentChecker"}
@loader.command()
async def fcheck(self, message):
"""check username in the Fragment.com"""
args = utils.get_args_raw(message)
response = requests.get(f"https://fragment.com/username/{args}")
if response.status_code == 200:
soup = BeautifulSoup(response.content, "html.parser")
elements = soup.select(".table-cell-value.tm-value.icon-before.icon-ton")
if elements:
text = elements[0].text.strip()
await utils.answer(message, f"<emoji document_id=5215219508670638513>💎</emoji> <b>Username Found!</b>\n<emoji document_id=5467626799556992380>✈️</emoji> <b>Username:</b> <code>{args}</code>\n<emoji document_id=5460720028288557729>🪙</emoji> <b>Cost:</b> <code>{text}</code> TON")
if not elements:
await utils.answer(message, f"<emoji document_id=5212926868012935693>❌</emoji> <b>Username <code>{args}</code> not found!</b>")

View File

@@ -0,0 +1,40 @@
autoprofile
animevoices
aeconv
amethyste
activity
phsticker
amoreinfo
abstract
dtwr
bull
meowvoices
mydiary
createlinks
imgbb
instsave
telegraphup
inlineping
poststealer
searchpic
funquotes
facts
hacker
premiuminfo
cartoonimg
trigger
recognition
universaltime
nytimer
figlet
fileext
my_usernames
fragment_checker
alarm
leta
speech
jutsu
usernamestealer
lexiwiz
birthdaywish
wakatime

View File

@@ -0,0 +1,91 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Funquotes.jpg
__version__ = (1, 0, 0)
from telethon.tl.types import Message
from .. import loader, utils
@loader.tds
class InlineFunMod(loader.Module):
"""Create Fun quotes"""
strings = {
"name": "FunQuotes",
"where_text": "<emoji document_id='6041914500272098262'>🚫</emoji> <b>Provide a text to create sticker with</b>",
"processing": (
"<emoji document_id='6318766236746384900'>🕔</emoji> <b>Processing...</b>"
),
}
strings_ru = {
"where_text": "<emoji document_id='6041914500272098262'>🚫</emoji> <b>Укажи текст для создания стикера</b>",
"processing": (
"<emoji document_id='6318766236746384900'>🕔</emoji> <b>Обработка...</b>"
),
}
async def glaxcmd(self, message: Message):
"""<text> - Create Google search quote"""
text = utils.get_args_raw(message)
if not text:
await message.edit(self.strings("where_text"))
return
await message.edit(self.strings("processing"))
try:
query = await self._client.inline_query("@googlaxbot", text)
await message.respond(file=query[0].document)
except Exception as e:
await utils.answer(message, str(e))
return
if message.out:
await message.delete()
async def twitcmd(self, message: Message):
"""<text> - Create Twitter message quote"""
text = utils.get_args_raw(message)
if not text:
await message.edit(self.strings("where_text"))
return
await message.edit(self.strings("processing"))
try:
query = await self._client.inline_query("@TwitterStatusBot", text)
await message.respond(file=query[0].document)
except Exception as e:
await utils.answer(message, str(e))
return
if message.out:
await message.delete()
async def frogcmd(self, message: Message):
"""<text> - Create Frog text quote"""
text = utils.get_args_raw(message)
if not text:
await message.edit(self.strings("where_text"))
return
await message.edit(self.strings("processing"))
try:
query = await self._client.inline_query("@honka_says_bot", text + ".")
await message.respond(file=query[0].document)
except Exception as e:
await utils.answer(message, str(e))
return
if message.out:
await message.delete()

View File

@@ -0,0 +1,69 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Hacker.jpg
__version__ = (1, 0, 0)
from .. import loader, utils
import requests
from PIL import Image,ImageFont,ImageDraw
import io
from textwrap import wrap
@loader.tds
class HackerMod(loader.Module):
"""Create hacker message stickers"""
strings = {
'name': 'Hacker',
'what': 'Reply to text or write text <emoji document_id="5467928559664242360">❗️</emoji>',
'processing': 'Processing <emoji document_id="6334710044407368265">🚀</emoji>'
}
@loader.owner
async def hackercmd(self, message):
"""Reply to text or write text"""
ufr = requests.get("https://0x0.st/opzq.ttf")
f = ufr.content
reply = await message.get_reply_message()
args = utils.get_args_raw(message)
if not args:
if not reply:
await message.edit(self.strings('what', message))
return
else:
txt = reply.raw_text
else:
txt = utils.get_args_raw(message)
await message.edit(self.strings("processing"))
pic = requests.get("https://0x0.st/opzN.jpg")
pic.raw.decode_content = True
img = Image.open(io.BytesIO(pic.content)).convert("RGB")
W, H = img.size
txt = txt.replace("\n", "𓃐")
text = "\n".join(wrap(txt, 19))
t = text + "\n"
t = t.replace("𓃐","\n")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(io.BytesIO(f), 32, encoding='UTF-8')
w, h = draw.multiline_textsize(t, font=font)
imtext = Image.new("RGBA", (w+10, h+10), (255, 250, 250, 1))
draw = ImageDraw.Draw(imtext)
draw.multiline_text((10, 10),t,(255, 255, 255),font=font, align='left')
imtext.thumbnail((339, 181))
w, h = 339, 181
img.paste(imtext, (10,10), imtext)
out = io.BytesIO()
out.name = "amore.webp"
img.save(out)
out.seek(0)
await message.client.send_file(message.to_id, out, reply_to=reply)
await message.delete()

View File

@@ -0,0 +1,129 @@
# ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
# ⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠟⠛⠛⠛⠛⠛
# ⣶⣦⣤⣤⣤⣤⣤⣤⣬⣭⣭⣍⣉⡙⠛⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⣋⣩⣭⣥⣤⣴⣶⣶⣶⣶⣶⣶⣶⣶⣶
# ⣆⠀⠀⠀⢡⠁⠀⡀⠀⢸⠟⠻⣯⠙⠛⠷⣶⣬⡙⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⢉⣥⣶⡟⠻⣙⡉⠀⢰⡆⠀⠀⣡⠀⣧⠀⠀⠀⢨
# ⠻⣦⠀⠀⠈⣇⣀⣧⣴⣿⣶⣶⣿⣷⠀⢀⡇⠉⠻⢶⣌⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣡⡶⠟⠉⠀⢣⠀⣿⠷⠀⠀⠀⠀⣿⡷⢀⠇⠀⠀⢠⣿
# ⣦⡈⢧⡀⠀⠘⢮⡙⠛⠉⠀⠄⠙⢿⣀⠞⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠈⠳⣄⠉⠓⠒⠚⠋⢀⡠⠋⠀⢀⣴⣏⣿
# ⣿⣿⣿⣛⣦⣀⠀⠙⠓⠦⠤⣤⠔⠛⠁⠀⠀⠀⠀⠀⢀⣀⣹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣤⣤⣤⣤⣤⣀⣀⣀⣀⢙⢓⣒⡒⠚⠋⢠⣤⢶⣟⣽⣿⣿
# ⣿⣿⣿⣿⣿⣿⣷⣦⠀⠀⣴⣿⣷⣶⣶⣶⣾⡖⢰⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣾⣿⣿⣶⣾⣿⣿⣿⣿⣿⣿
# ⣿⣿⣿⣿⣿⣿⣿⣿⠀⢀⣿⣿⣿⣿⣿⣿⣿⠃⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
# ⣿⣿⣿⣿⣿⣿⣿⡏⠀⢸⣿⣿⣿⣿⣿⣿⣿⠁⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
# ⣿⣿⣿⣿⣿⣿⣿⣷⠀⢸⣿⣿⣿⣿⣿⣿⣿⠀⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
#          https://t.me/amorescam
#
# █ █ ▀ █▄▀ ▄▀█ █▀█ ▀
# █▀█ █ █ █ █▀█ █▀▄ █
#             © Copyright 2022
#
#          https://t.me/hikariatama
#
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# meta developer: @amoremods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Imgbb.jpg
import imghdr
import io
import random
import re
import requests
from telethon.errors.rpcerrorlist import YouBlockedUserError
from telethon.tl.types import Message
from .. import loader, utils
@loader.tds
class ImgbbUploader(loader.Module):
"""Upload you photo/video/gif to https://ibb.co"""
strings = {
"name": "Imgbb",
"noargs": "🚫 <b>File not specified</b>",
"err": "🚫 <b>Error uploading</b>",
"ban": "🎒 @Imgbb_com_bot pls unblock this bot",
"not_an_image": "🚫 <b>This platform only supports images </b>",
"uploading": "📤 <b>Uploading...</b>",
}
async def get_media(self, message: Message):
reply = await message.get_reply_message()
m = None
if reply and reply.media:
m = reply
elif message.media:
m = message
elif not reply:
await utils.answer(message, self.strings("noargs"))
return False
if not m:
file = io.BytesIO(bytes(reply.raw_text, "utf-8"))
file.name = "file.txt"
else:
file = io.BytesIO(await self._client.download_media(m, bytes))
file.name = (
m.file.name
or (
"".join(
[
random.choice("abcdefghijklmnopqrstuvwxyz1234567890")
for _ in range(16)
]
)
)
+ m.file.ext
)
return file
async def get_image(self, message: Message):
file = await self.get_media(message)
if not file:
return False
if imghdr.what(file) not in ["gif", "png", "jpg", "jpeg", "tiff", "bmp"]:
await utils.answer(message, self.strings("not_an_image"))
return False
return file
async def imgbbcmd(self, message: Message):
"""imgbb uploader"""
message = await utils.answer(message, self.strings("uploading"))
file = await self.get_image(message)
if not file:
return
chat = "@Imgbb_com_bot"
async with self._client.conversation(chat) as conv:
try:
m = await conv.send_message(file=file)
response = await conv.get_response()
except YouBlockedUserError:
await utils.answer(message, self.strings("ban"))
return
await m.delete()
await response.delete()
try:
url = (
re.search(
r'<meta property="og:image" data-react-helmet="true"'
r' content="(.*?)"',
(await utils.run_sync(requests.get, response.raw_text)).text,
)
.group(1)
.split("?")[0]
)
except Exception:
url = response.raw_text
await utils.answer(message, f"😸 Your file uploaded: <code>{url}</code>")

View File

@@ -0,0 +1,83 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Inlineping.jpg
import logging
import time
from telethon.tl.types import Message
from .. import loader, utils
from ..inline.types import InlineCall, InlineQuery
logger = logging.getLogger(__name__)
@loader.tds
class PingerMod(loader.Module):
"""Inline Pinger For Test"""
strings = {
"name": "InlinePing",
"results_ping": "✨ <b>Telegram ping:</b> <code>{}</code> <b>ms</b>"
}
strings_ru = {"results_ping": "✨ <b>Телеграм пинг:</b> <code>{}</code> <b>ms</b>"}
strings_uz = {"results_ping": "✨ <b>Telegram ping:</b> <code>{}</code> <b>ms</b>"}
strings_de = {"results_ping": "✨ <b>Telegramm Ping:</b> <code>{}</code> <b>ms</b>"}
strings_ru = {"results_ping": "✨ <b>Скорость отклика Telegram:</b> <code>{}</code> <b>ms</b>"}
@loader.command(ru_doc="Проверить скорость отклика юзербота")
async def iping(self, message: Message):
"""Test your userbot ping"""
start = time.perf_counter_ns()
ping = self.strings("results_ping").format(
round((time.perf_counter_ns() - start) / 10**3, 3),
)
await self.inline.form(
ping,
reply_markup=[[{"text": "⏱️ PePing", "callback": self.ladno}]],
message=message,
)
async def ladno(self, call: InlineCall):
start = time.perf_counter_ns()
ping = self.strings("results_ping").format(
round((time.perf_counter_ns() - start) / 10**3, 3),
)
await call.edit(
ping,
reply_markup=[[{"text": "⏱️ PePing", "callback": self.ladno,}],]
)
async def ping_inline_handler(self, query: InlineQuery):
"""Test your userbot ping"""
start = time.perf_counter_ns()
ping = self.strings("results_ping").format(
round((time.perf_counter_ns() - start) / 10**3, 3),
)
button = [{
"text": "⏱️ PePing",
"callback": self.ladno
}]
return {
"title": "Ping",
"description": "Tap here",
"thumb": "https://te.legra.ph/file/5d8c7f1960a3e126d916a.jpg",
"message": ping,
"reply_markup": button,
}

View File

@@ -0,0 +1,57 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta pic: https://te.legra.ph/file/0251f5d602a8f32cd7368.png
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Instsave.jpg
__version__ = (1, 0, 0)
from .. import utils, loader
chat = "@SaveAsBot"
class InstagramMod(loader.Module):
"""Download video from instagram without watermark"""
strings = {
"name": "InstSave",
"processing": (
"<emoji document_id='6318766236746384900'>🕔</emoji> <b>Processing...</b>"
),
"mods": (
"<b>Successfuly downloaded</b> <emoji document_id='6320882302708614449'>🚀</emoji></b>"
),
}
@loader.group_member
@loader.command(ru_doc="<линк> - Скачать видео из инстаграм")
async def instascmd(self, message):
"""instagram video/reels/photo url"""
text = utils.get_args_raw(message)
message = await utils.answer(message, self.strings("processing"))
async with self._client.conversation(chat) as conv:
msgs = []
msgs += [await conv.send_message("/start")]
msgs += [await conv.get_response()]
msgs += [await conv.send_message(text)]
m = await conv.get_response()
await self._client.send_file(
message.peer_id,
m.media,
caption=self.strings("mods"),
reply_to=message.reply_to_msg_id,
)
for msg in msgs + [m]:
await msg.delete()
if message.out:
await message.delete()
await self.client.delete_dialog(chat)

View File

@@ -0,0 +1,355 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# requires: bs4 cloudscraper loguru tqdm lxml
# meta developer: @hikamorumods
import os
import pathlib
import shutil
import string
import random
import logging
from tqdm import tqdm
from dataclasses import dataclass
from bs4 import BeautifulSoup
from cloudscraper import create_scraper, CloudScraper
from telethon.tl.types import DocumentAttributeVideo
from aiogram.types import CallbackQuery
from .. import loader, utils
logger = logging.getLogger(__name__)
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36",
}
@dataclass
class Season:
title: str
episodes_urls: list[str]
def download_video(url: str, path, scraper: CloudScraper):
with scraper.get(url, stream=True) as r:
total_length = int(r.headers.get("Content-Length"))
with tqdm.wrapattr(r.raw, "read", total=total_length, desc="") as raw:
with open(path, "wb") as file:
shutil.copyfileobj(raw, file)
def remove_symbols(filename: str) -> str:
if not filename:
return filename
forbidden = '\\/*:?|"<>'
for symbol in forbidden:
filename.replace(symbol, "")
return filename
class JutSuD:
def loader(self, anime_url, season_from, episode_from, season_to, episode_to):
scraper = create_scraper(
delay=1,
browser={
"custom": "ScraperBot/1.0",
},
)
response = scraper.get(anime_url)
soup = BeautifulSoup(response.text, "lxml")
anime_title = soup.find("h1", {"class": "anime_padding_for_title"}).text
anime_title = (
anime_title.replace("Смотреть", "")
.replace("все серии", "")
.replace("и сезоны", "")
.strip()
)
seasons = [
Season(
title=season_title.text,
episodes_urls=[],
)
for season_title in soup.find_all("h2", class_=["the-anime-season"])
]
if not seasons:
seasons.append(
Season(
title=anime_title,
episodes_urls=[],
)
)
episodes_soup = soup.find_all(
"a",
class_=[
"short-btn black video the_hildi",
"short-btn green video the_hildi",
],
)
current_season_index = -1
current_episode_class = None
for ep in episodes_soup:
if ep["class"] != current_episode_class:
current_episode_class = ep["class"]
current_season_index += 1
url = "https://jut.su" + ep["href"]
seasons[current_season_index].episodes_urls.append(url)
for i, season in enumerate(seasons):
season_number = i + 1
if season_number < season_from or season_number > season_to:
continue
for j, episode_url in enumerate(season.episodes_urls):
episode_number = j + 1
if (season_number == season_from and episode_number < episode_from) or (
(season_number == season_to or season_number == len(seasons))
and episode_number > episode_to
):
continue
response = scraper.get(episode_url)
soup = BeautifulSoup(response.content, "lxml")
try:
episode_title = (
soup.find("div", {"class": "video_plate_title"}).find("h2").text
)
except AttributeError:
episode_title = soup.find("span", {"itemprop": "name"}).text
episode_title = (
episode_title.replace("Смотреть", "")
.replace(anime_title, "")
.strip()
)
video_url = soup.find("source")["src"]
name_video = random.choices("".join(string.ascii_letters), k=10)
video_path = pathlib.Path(f"{''.join(name_video)}.mp4")
episode_slug = f"{season.title} - {episode_title} [#{episode_number}]"
try:
download_video(url=video_url, path=video_path, scraper=scraper)
return video_path, episode_slug
except Exception as e:
logger.exception(e)
return False, False
def get_info(self, url):
scraper = create_scraper()
response = scraper.get(url, headers=HEADERS)
soup = BeautifulSoup(response.text, "lxml")
anime_title = soup.find("h1", {"class": "anime_padding_for_title"}).text
anime_title = (
anime_title.replace("Смотреть", "")
.replace("все серии", "")
.replace("и сезоны", "")
.strip()
)
seasons = [
Season(
title=season_title,
episodes_urls=[],
)
for season_title in soup.find_all("h2", class_=["the-anime-season"])
]
if not seasons:
seasons.append(
Season(
title=anime_title,
episodes_urls=[],
)
)
episodes_soup = soup.find_all(
"a",
class_=[
"short-btn black video the_hildi",
"short-btn green video the_hildi",
],
)
return anime_title, seasons, episodes_soup
@loader.tds
class Jutsu(loader.Module):
"""Download and get info about anime from jut.su"""
strings = {
"name": "Jutsu",
"info": (
"📺 <b>Anime info</b>\n\n"
"<b>Title:</b> {}\n"
"<b>Seasons:</b> {}\n"
"<b>Total episodes:</b> {}\n"
"<b>Link:</b> {}"
),
"download_button": "📥 Download",
"done": "✅ Download completed!",
"choose_season": "📺 <b>Choose season</b>",
"choose_episode": "🪶 <b>Choose episode</b>",
"wrong_url": "❌ Wrong url!",
"no_args": "❌ No args!",
"download": "📥 Downloading episode {}... (speed depends on your internet connection)",
"close": "❌ Close",
}
strings_ru = {
"info": (
"📺 <b>Информация о аниме</b>\n\n"
"<b>Название:</b> {}\n"
"<b>Сезонов:</b> {}\n"
"<b>Всего серий:</b> {}\n"
"<b>Ссылка:</b> {}"
),
"download_button": "📥 Скачать",
"done": "✅ Скачивание завершено!",
"choose_season": "📺 <b>Выберите сезон</b>",
"choose_episode": "🪶 <b>Выберите серию</b>",
"wrong_url": "❌ Неверная ссылка!",
"no_args": "❌ Нет аргументов!",
"download": "📥 Скачиваем серию {}... (скорость скачивание зависит от вашего интернета)",
"close": "❌ Закрыть",
}
async def client_ready(self, client, db):
asset_ch, _ = await utils.asset_channel(
self._client,
"JutSu downloads",
"Downloaded anime from JutSu will be sent here. (Hikamoru back?)",
avatar="https://i.pinimg.com/564x/0a/da/0b/0ada0bb575146736679f5ea7a78971b8.jpg",
)
self.chid = int(f"-100{asset_ch.id}")
async def download_(self, call, url, seasons, episodes_soup):
seasons = [season for season in range(1, len(seasons) + 1)]
kb = []
for mod_row in utils.chunks(seasons, 3):
row = [
{
"text": f"{season}",
"callback": self.season_,
"args": (season, episodes_soup, url),
}
for season in mod_row
]
kb += [row]
await call.edit(self.strings["choose_season"], reply_markup=kb)
async def season_(self, call, season, eps, url):
episodes = [episode for episode in range(1, len(eps) + 1)]
kb = []
for mod_row in utils.chunks(episodes, 3):
row = [
{
"text": f"{episode}",
"callback": self.episod_,
"args": (episode, season, url),
}
for episode in mod_row
]
kb += [row]
await call.edit(self.strings["choose_episode"], reply_markup=kb)
async def episod_(self, call: CallbackQuery, episode, episode_number, url):
await call.edit(self.strings["download"].format(episode_number))
try:
name, title = JutSuD().loader(
url, episode_number, episode, episode_number, episode
)
except TypeError:
await call.edit("There is not such a episode (This bug with button will be fixed soon)")
await self.client.send_file(
self.chid,
open(name, "rb"),
caption=self.strings["done"] + f"\n\n{title}",
filetype="video",
attributes=(DocumentAttributeVideo(0, 0, 0),),
)
await call.edit(self.strings["done"])
os.remove(name)
async def close_(self, call):
await call.delete()
@loader.command()
async def jutsud(self, message):
"""Download anime from jutsu - [url]"""
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, self.strings["no_args"])
return
if not args.startswith("https://jut.su"):
await utils.answer(message, self.strings["wrong_url"])
return
anime_title, seasons, episodes_soup = JutSuD().get_info(args)
await utils.answer(
message,
self.strings["info"].format(
anime_title, len(seasons), len(episodes_soup), args
),
reply_markup=[
[
{
"text": self.strings["download_button"],
"callback": self.download_,
"kwargs": {
"url": args,
"seasons": seasons,
"episodes_soup": episodes_soup,
},
},
{
"text": self.strings["close"],
"callback": self.close_,
},
]
],
)

View File

@@ -0,0 +1,155 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/leta.jpg
# I don't care about other people's opinions, if you don't like it, don't use it. i will update this module in the future, if i have time.
import time
import logging
from .. import loader, utils
from telethon.errors import ChatAdminRequiredError
logger = logging.getLogger(__name__)
class Leta(loader.Module):
"""Customizable nightmode [Leta] for your group"""
strings = {
"name": "Leta",
"info": (
"🐈‍⬛ Heeey! I'm <b>Leta</b>! I'm a module for nightmode in your group.\n"
"📫 You can get acquainted with my settings using the command <code>.help Leta</code>."
),
"wrong_format": "<emoji document_id=5258419835922030550>🕔</emoji> <b>Enter the time in the format HH:MM</b>",
"day": "<emoji document_id=6332496306593859160>🌅</emoji> <b>Good morning!</b>\n<b>Night mode is disabled.</b>",
"night": "<emoji document_id=6334806423473489632>🌚</emoji> <b>Good night!</b>\n<b>Night mode is enabled.</b>",
"rm": "<emoji document_id=5021905410089550576>✅</emoji> <b>Removed nightmode.</b>",
"rm_notfound": "<emoji document_id=5456652110143693064>🤷‍♂️</emoji> <b>Nightmode is not set.</b>",
"set": "<emoji document_id=5980930633298350051>✅</emoji> Time set to\n<emoji document_id=6334361735444563461>🌃</emoji>🌙</emoji> Night: <code>{}</code>\n<emoji document_id=6332496306593859160>🌅</emoji> Day: <code>{}</code>",
}
strings_ru = {
"info": (
"🐈‍⬛ Привет! Я <b>Leta</b>! Я модуль для ночного режима в вашей группе.\n"
"📫 Ознакомиться с моими настройками можно с помощью команды <code>.help Leta</code>."
),
"wrong_format": "<emoji document_id=5258419835922030550>🕔</emoji> <b>Введите время в формате HH:MM</b>",
"day": "<emoji document_id=6332496306593859160>🌅</emoji> <b>Доброе утро!</b>\n<b>Ночной режим отключен.</b>",
"night": "<emoji document_id=6334806423473489632>🌚</emoji> <b>Доброй ночи!</b>\n<b>Ночной режим включен.</b>",
"rm": "<emoji document_id=5021905410089550576>✅</emoji> <b>Удален ночной режим.</b>",
"rm_notfound": "<emoji document_id=5456652110143693064>🤷‍♂️</emoji> <b>Ночной режим не установлен.</b>",
"set": "<emoji document_id=5980930633298350051>✅</emoji> Время установлено на\n<emoji document_id=6334361735444563461>🌃</emoji>🌙</emoji> Ночь: <code>{}</code>\n<emoji document_id=6332496306593859160>🌅</emoji> День: <code>{}</code>",
}
def resolve_id(self, marked_id):
if marked_id >= 0:
return "user"
marked_id = -marked_id
marked_id -= 1000000000000
return "chat"
async def client_ready(self, client, db):
if not self.get("info", False):
await self.inline.bot.send_animation(
self._tg_id,
"https://0x0.st/Hpqm.mp4",
caption=self.strings("info"),
parse_mode="HTML",
)
self.set("info", True)
async def lettimecmd(self, message):
"""Set time - morning [HH:MM] evening [HH:MM]"""
args = utils.get_args_raw(message).split(" ")
resolving = self.resolve_id(message.chat_id)
if resolving != "chat":
return await utils.answer(message, "<b>Use this command in group</b>")
if not args:
return await utils.answer(message, self.strings("wrong_format"))
try:
dh, dm = args[0].split(":")
eh, em = args[1].split(":")
if int(dh) > 23 or int(dh) < 0 or int(dm) > 59 or int(dm) < 0 or int(eh) > 23 or int(eh) < 0 or int(em) > 59 or int(em) < 0:
return await utils.answer(message, self.strings("wrong_format"))
except Exception:
return await utils.answer(message, self.strings('wrong_format'))
day = args[0]
night = args[1]
self.set(
"ngs",
{
message.chat_id: {
"time": night,
"day": day,
"chat": message.chat_id
},
}
)
await utils.answer(message, self.strings("set").format(night, day))
async def letrmchatcmd(self, message):
"""Remove nightmode - chat-id"""
try:
args = int(utils.get_args_raw(message))
d = self.get("ngs", {})
logging.info(d)
if not args:
return await utils.answer(message, self.strings("rm_notfound"))
if args not in d:
return await utils.answer(message, self.strings("rm_notfound"))
del d[args]
self.set("ngs", d)
await utils.answer(message, self.strings("rm"))
except ValueError:
await utils.answer(message, self.strings("rm_notfound"))
@loader.loop(interval=60, autostart=True)
async def checker_loop_night(self):
"""Check time"""
ngs = self.get("ngs", {})
for i in ngs:
if ngs[i]["time"] == time.strftime("%H:%M"):
try:
await self.client.send_message(ngs[i]["chat"], self.strings("night"))
await self.client.edit_permissions(ngs[i]['chat'], send_messages=False)
except ChatAdminRequiredError:
await self.inline.bot.send_message(
self._tg_id,
f"👎 You don't have enough rights to change permissions in <code>{i['chat']}</code>",
parse_mode="HTML",
)
async def letchatscmd(self, message):
"""Get all chats with nightmode"""
ngs = self.get("ngs", {})
if not ngs:
return await utils.answer(message, "<b>There are no chats with nightmode</b>")
msg = "<b>Chats with nightmode:</b>\n"
for i in ngs:
msg += f"\n<code>{i}</code>"
await utils.answer(message, msg + "\n")
@loader.loop(interval=60, autostart=True)
async def checker_loop_day(self):
"""Check time"""
ngs = self.get("ngs", {})
for i in ngs:
if ngs[i]["day"] == time.strftime("%H:%M"):
try:
await self.client.edit_permissions(ngs[i]['chat'], send_messages=True)
await self.client.send_message(ngs[i]["chat"], self.strings("day"))
except ChatAdminRequiredError:
await self.inline.bot.send_message(
self._tg_id,
f"👎 You don't have enough rights to change permissions in <code>{i['chat']}</code>",
parse_mode="HTML",
)

View File

@@ -0,0 +1,249 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# requires: bs4 aiohttp
# meta developer: @hikamorumods
import aiohttp
from bs4 import BeautifulSoup as bs
from .. import utils, loader
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
class English:
async def definition_get(self, word: str):
async with aiohttp.ClientSession() as session:
headers = {"User-Agent": user_agent}
async with session.get(
f"https://dictionary.cambridge.org/us/dictionary/english/{word}/",
headers=headers,
) as resp:
if resp.status != 200:
return f"Failed to retrieve data. Status code: {resp.status}"
soup = bs(await resp.text(), "html.parser")
if not (div_element := soup.find("div", class_="def ddef_d db")):
return "Definition not found"
text = div_element.get_text()
example_spans = soup.find_all("span", class_="eg deg")
examples = []
for ex in example_spans:
example_text = ex.get_text()
examples.append(example_text)
return {"definition": text.replace(":", ""), "examples": examples}
async def get_word_pronunciation_uk(self, word: str):
async with aiohttp.ClientSession() as session:
async with session.get(
f"https://dictionary.cambridge.org/dictionary/english/{word}",
headers={"User-Agent": user_agent},
) as resp:
if resp.status == 200:
soup = bs(await resp.text(), "html.parser")
try:
audio_tag = soup.find_all("audio", class_="hdn")[0]
pron_tag = soup.find_all("span", class_="ipa dipa lpr-2 lpl-1")[
0
]
audio_src = audio_tag.find("source", type="audio/mpeg")["src"]
return {
"audio": f"https://dictionary.cambridge.org/us{audio_src}",
"pron": pron_tag.get_text(),
}
except IndexError:
return False
async def get_word_pronunciation_us(self, word: str):
async with aiohttp.ClientSession() as session:
async with session.get(
f"https://dictionary.cambridge.org/dictionary/english/{word}",
headers={"User-Agent": user_agent},
) as resp:
if resp.status == 200:
soup = bs(await resp.text(), "html.parser")
try:
audio_tag = soup.find_all("audio", class_="hdn")[1]
audio_src = audio_tag.find("source")["src"]
pron_tag = soup.find_all("span", class_="ipa dipa lpr-2 lpl-1")[
1
]
return {
"audio": f"https://dictionary.cambridge.org{audio_src}",
"pron": pron_tag.get_text(),
}
except IndexError:
return False
async def thesaurus_synonyms(self, word: str):
url = f"https://thesaurus.plus/thesaurus/{word}"
async with aiohttp.ClientSession() as session:
async with session.get(url, headers={"User-Agent": user_agent}) as resp:
if resp.status == 200:
soup = bs(await resp.text(), "html.parser")
synonyms_list = []
synonyms_ul = soup.find_all("ul", class_="list")[1]
list_terms = synonyms_ul.find_all("li", class_="list_term")
for term in list_terms:
synonym = term.find("div", class_="p-2").get_text(strip=True)
synonyms_list.append(synonym)
return synonyms_list
async def thesaurus_antonyms(self, word: str):
url = f"https://thesaurus.plus/thesaurus/{word}"
async with aiohttp.ClientSession() as session:
async with session.get(url, headers={"User-Agent": user_agent}) as resp:
if resp.status == 200:
soup = bs(await resp.text(), "html.parser")
antonyms_list = []
antonyms_ul = soup.find_all("ul", class_="list")[0]
list_terms = antonyms_ul.find_all("li", class_="list_term")
for term in list_terms:
antonym = term.find("div", class_="p-2").get_text(strip=True)
antonyms_list.append(antonym)
return antonyms_list
@loader.tds
class LexiwizMod(loader.Module, English):
"""Lexical wizard - your english companion"""
strings = {
"name": "Lexiwiz",
"no_word": "🤷‍♂️ <b>No word provided</b>",
"no_definition": "😖 <b>Unfortunately, I couldn't find the definition of this word.</b>",
"definition": "📝 <b>Word:</b> <code>{}</code>\n\n🔆 <b>Definition:</b> <code>{}</code>\n\n📦 <b>Examples:</b>\n{}",
"Pronunciation": "{} <b>{} Pronunciation:</b> <code>{}</code>\n🔊 <a href='{}'>Listen</a>",
"no_synonyms": "😓 <b>Sorry, I couldn't find synonyms for this word.</b>",
"synonyms": "📝 <b>Synonyms for the word:</b> <code>{}</code>\n\n🔆 <b>Synonyms:</b> <code>{}</code>",
"no_antonyms": "😓 <b>Sorry, I couldn't find antonyms for this word.</b>",
"antonyms": "📝 <b>Antonyms for the word:</b> <code>{}</code>\n\n🔆 <b>Antonyms:</b> <code>{}</code>",
}
@loader.command()
async def getdef(self, message):
"""Get definition of a word"""
word = utils.get_args_raw(message)
if not word:
await utils.answer(message, self.strings("no_word"))
return
definition = await self.definition_get(word)
if definition == "Definition not found":
await utils.answer(message, self.strings("no_definition"))
if isinstance(definition, dict):
text = ""
_definition = definition["definition"]
_examples = definition["examples"]
for index, example in enumerate(_examples):
text += f"<b>{index}</b>. <i>{example}</i>\n"
await utils.answer(
message, self.strings("definition").format(word, _definition, text)
)
else:
await utils.answer(
message, self.strings("definiotion").format(word, definition, " ")
)
@loader.command()
async def getpron(self, message):
"""Get pronunciation of a word"""
word = utils.get_args_raw(message)
reply = await message.get_reply_message()
if not word:
await utils.answer(message, self.strings("no_word"))
return
uk = await self.get_word_pronunciation_uk(word)
us = await self.get_word_pronunciation_us(word)
if uk:
await message.delete()
await message.client.send_file(
message.to_id,
uk["audio"],
caption=self.strings("Pronunciation").format(
"🇬🇧",
"UK",
uk["pron"],
uk["audio"],
),
voice_note=True,
reply_to=reply.id if reply else None,
)
if us:
await message.delete()
await message.client.send_file(
message.to_id,
us["audio"],
caption=self.strings("Pronunciation").format(
"🇺🇸",
"US",
us["pron"],
us["audio"],
),
voice_note=True,
reply_to=reply.id if reply else None,
)
@loader.command()
async def getsyn(self, message):
"""Get synonyms of a word"""
word = utils.get_args_raw(message)
if not word:
await utils.answer(message, self.strings("no_word"))
return
synonyms = await self.thesaurus_synonyms(word)
if not synonyms:
await utils.answer(message, self.strings("no_synonyms"))
return
await utils.answer(
message, self.strings("synonyms").format(word, ", ".join(synonyms))
)
@loader.command()
async def getant(self, message):
"""Get antonyms of a word"""
word = utils.get_args_raw(message)
if not word:
await utils.answer(message, self.strings("no_word"))
return
antonyms = await self.thesaurus_antonyms(word)
if not antonyms:
await utils.answer(message, self.strings("no_antonyms"))
return
await utils.answer(
message, self.strings("antonyms").format(word, ", ".join(antonyms))
)

View File

@@ -0,0 +1,27 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://github.com/AmoreForever/assets/blob/master/my_usernames.jpg?raw=true
from telethon import functions
from telethon.tl.types import Channel
from .. import loader, utils
@loader.tds
class MyUsernames(loader.Module):
"""The usernames I own"""
strings = {"name": "My Usernames"}
@loader.command()
async def myusern(self, message):
"""A list of usernames that were created by me"""
result = await self.client(functions.channels.GetAdminedPublicChannelsRequest())
output_str = ""
for channel_obj in result.chats:
if isinstance(channel_obj, Channel) and channel_obj.username is not None:
output_str += f"<code>{channel_obj.title}</code> | <b>@{channel_obj.username}</b>\n"
await utils.answer(message, f"<b>💼 List usernames reserved by me</b>\n\n{output_str[:-3]}")

View File

@@ -0,0 +1,296 @@
__version__ = (1, 0, 0)
# ▀█▀ █ █ █▀█ █▀▄▀█ ▄▀█ █▀
# █  █▀█ █▄█ █ ▀ █ █▀█ ▄█
# https://t.me/netuzb
#
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta pic: https://te.legra.ph/file/4c1b4581de961df145a70.png
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Mydiary.jpg
# meta developer: @hikamoru & @wilsonmods
# scope: hikka_min 1.4.0
from .. import loader, utils
from telethon.tl.types import Message
from ..inline.types import InlineQuery
from ..inline.types import InlineCall
emoji_close = "🔻 "
emoji_back = "↙️ "
emoji_open = "💌 "
emoji_about = "🚨 "
@loader.tds
class PagesMod(loader.Module):
"""Diary page"""
strings = {
"name": "Diary",
"_about_module": "What does this module do? - here you can write about your day and write notes",
"_cfg_inline_banner": "Set `True` in order to disable an inline media banner.",
"_cfdiary_open_text": "enter a diary name or information about it",
"_cfdiary_second_text": "here you can write on dairy «text2»",
"_cfdiary_three_text": "here you can write on dairy «text3»",
"_cfdiary_four_text": "here you can write on dairy «text4»",
"_cfdiary_first_text": "here you can write on dairy «text1»",
"_cfdiary_second_text": "here you can write on dairy «text2»",
"_cfdiary_three_text": "here you can write on dairy «text3»",
"_cfdiary_four_text": "here you can write on dairy «text4»",
"_cfg_button_1_": "here you can change button name «day1»",
"_cfg_button_2_": "here you can change button name «day2»",
"_cfg_button_3_": "here you can change button name «day3»",
"_cfg_button_4_": "here you can change button name «day4»",
"x": emoji_close + "Close",
"back": emoji_back + "Back",
}
strings_ru = {
"_about_module": "Что делает этот модуль? - Ты ты можешь писать свои заметки или что делал сегодня",
"_cfg_inline_banner": "Установите `True`, чтобы отключить встроенный медиа-баннер",
"_cfdiary_open_text": "Введите название дневника или информацию о нем",
"_cfdiary_first_text": "здесь ты можешь написать написать дневник на «text1»",
"_cfdiary_second_text": "здесь ты можешь написать написать дневник на «text2»",
"_cfdiary_three_text": "здесь ты можешь написать написать дневник на «text3»",
"_cfdiary_four_text": "здесь ты можешь написать написать дневник на «text4»",
"_cfg_button_1_": "здесь ты можешь поменять название кнопки «day1»",
"_cfg_button_2_": "здесь ты можешь поменять название кнопки «day2»",
"_cfg_button_3_": "здесь ты можешь поменять название кнопки «day3»",
"_cfg_button_4_": "здесь ты можешь поменять название кнопки «day4»",
"x": emoji_close + "Закрыть",
"back": emoji_back + "Назад",
}
strings_uz = {
"_about_module": "Modul vazifasi nima?\n- Siz modul orqali bugungi kun rejangiz yoki eslatmani saqlab qoʻyishingiz mumkin.",
"_cfg_inline_banner": "Media-bannerni yopish uchun `True` rejimini yoqing",
"_cfdiary_open_text": "Kundalik nomini yoki unga bogʻliq maʼlumotni yozing",
"_cfdiary_first_text": "Bu yerda siz «text_numb_1» sozlashingiz mumkin",
"_cfdiary_second_text": "Bu yerda siz «text_numb_2» sozlashingiz mumkin",
"_cfdiary_three_text": "Bu yerda siz «text_numb_3» sozlashingiz mumkin",
"_cfdiary_four_text": "Bu yerda siz «text_numb_4» sozlashingiz mumkin",
"_cfg_button_1_": "Bu yerda siz «button_numb_1» tugmasini sozlashingiz mumkin",
"_cfg_button_2_": "Bu yerda siz «button_numb_2» tugmasini sozlashingiz mumkin",
"_cfg_button_3_": "Bu yerda siz «button_numb_3» tugmasini sozlashingiz mumkin",
"_cfg_button_4_": "Bu yerda siz «button_numb_4» tugmasini sozlashingiz mumkin",
"x": emoji_close + "Yopish",
"back": emoji_back + "Orqaga",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"open_text",
"Here is a caption for my diary",
doc=lambda: self.strings('_cfdiary_open_text'),
),
loader.ConfigValue(
"off_inline_banner",
False,
lambda: self.strings("_cfg_inline_banner"),
validator=loader.validators.Boolean(),
),
loader.ConfigValue(
"button_numb_1",
"Day 1",
doc=lambda: self.strings('_cfg_button_1_'),
),
loader.ConfigValue(
"button_numb_2",
"Day 2",
doc=lambda: self.strings('_cfg_button_2_'),
),
loader.ConfigValue(
"button_numb_3",
"Day 3",
doc=lambda: self.strings('_cfg_button_3_'),
),
loader.ConfigValue(
"button_numb_4",
"Day 4",
doc=lambda: self.strings('_cfg_button_4_'),
),
loader.ConfigValue(
"text_numb_1",
"Today i played football with my friends then i fall,",
doc=lambda: self.strings('_cfdiary_first_text'),
),
loader.ConfigValue(
"text_numb_2",
"Today i walked with my friends and i saw my best friend who was drawer",
doc=lambda: self.strings('_cfdiary_second_text'),
),
loader.ConfigValue(
"text_numb_3",
"What are you did today?",
doc=lambda: self.strings('_cfdiary_three_text'),
),
loader.ConfigValue(
"text_numb_4",
"What are you did today?",
doc=lambda: self.strings('_cfdiary_four_text'),
),
loader.ConfigValue(
"banner_numb_1",
"https://imgur.com/NqNGNOb",
lambda: f"here you can write on dairy photo1",
),
loader.ConfigValue(
"banner_numb_2",
"https://ibb.co/ZJ9hnfL",
lambda: f"here you can write on dairy photo2",
),
loader.ConfigValue(
"banner_numb_3",
"https://imgur.com/kITkUry",
lambda: f"here you can write on dairy photo3",
),
loader.ConfigValue(
"banner_numb_4",
"https://imgur.com/TOzh9u1",
lambda: f"here you can write on dairy photo3",
),
)
async def cfdiarycmd(self, message):
"""> Set up buttons for the module"""
name = self.strings("name")
await self.allmodules.commands["config"](
await utils.answer(message,
f"{self.get_prefix()}config {name}")
)
async def mydiarycmd(self, message: Message):
"""> Main the diary section"""
await self.inline.form(
text = self.config["open_text"],
message=message,
reply_markup=[
[{
"text": f"{emoji_open}Open diary",
"callback": self.page_one
}],
[{
"text": f"{emoji_about}About modules",
"callback": self._about_us
}]],
**{"photo": "https://te.legra.ph/file/64bb29a68030e118dfa21.jpg"},
)
async def mydiary_inline_handler(self, query: InlineQuery):
"""> Main the diary section"""
btn_a = [{
"text": f"{emoji_open}Open diary",
"callback": self.page_one
}],
btn_b = [{
"text": f"{emoji_about}About modules",
"callback": self._about_us
}],
msg_type = "message" if self.config["off_inline_banner"] else "caption"
return {
"title": "open diary",
"description": "open my own diary page",
msg_type: self.config['open_text'],
"photo": "https://te.legra.ph/file/64bb29a68030e118dfa21.jpg",
"thumb": (
"https://te.legra.ph/file/4c1b4581de961df145a70.png"
),
"reply_markup": btn_a + btn_b,
}
async def _back(self, call: InlineCall):
await call.edit(
text = self.config["open_text"],
reply_markup=[
[{
"text": f"{emoji_open}Open diary",
"callback": self.page_one
}],
[{
"text": f"{emoji_about}About modules",
"callback": self._about_us
}]
],
**{"photo": "https://te.legra.ph/file/64bb29a68030e118dfa21.jpg"},
)
async def _about_us(self, call: InlineCall):
await call.edit(
text = self.strings('_about_module'),
reply_markup=[
[
{
"text": self.strings("back"),
"callback": self._back
},
{
"text": self.strings("x"),
"action": "close"
},
]
],
)
async def page_one(self, call: InlineCall):
await call.edit(
text = self.config["text_numb_1"],
reply_markup=[
[{"text": self.config["button_numb_1"], "callback": self.page_one}, {"text": self.config["button_numb_2"], "callback": self.page_two}],
[{"text": self.config["button_numb_3"], "callback": self.page_three}, {"text": self.config["button_numb_4"], "callback": self.page_four}],
[{
"text": self.strings("x"),
"action": "close"
}]],
**{"photo": self.config["banner_numb_1"]},
)
async def page_two(self, call: InlineCall):
await call.edit(
text = self.config["text_numb_2"],
reply_markup=[
[{"text": self.config["button_numb_1"], "callback": self.page_one}, {"text": self.config["button_numb_2"], "callback": self.page_two}],
[{"text": self.config["button_numb_3"], "callback": self.page_three}, {"text": self.config["button_numb_4"], "callback": self.page_four}],
[{
"text": self.strings("x"),
"action": "close"
}]],
**{"photo": self.config["banner_numb_2"]},
)
async def page_three(self, call: InlineCall):
await call.edit(
text = self.config["text_numb_3"],
reply_markup=[
[{"text": self.config["button_numb_1"], "callback": self.page_one}, {"text": self.config["button_numb_2"], "callback": self.page_two}],
[{"text": self.config["button_numb_3"], "callback": self.page_three}, {"text": self.config["button_numb_4"], "callback": self.page_four}],
[{
"text": self.strings("x"),
"action": "close"
}]],
**{"photo": self.config["banner_numb_3"]},
)
async def page_four(self, call: InlineCall):
await call.edit(
text = self.config["text_numb_4"],
reply_markup=[
[{"text": self.config["button_numb_1"], "callback": self.page_one}, {"text": self.config["button_numb_2"], "callback": self.page_two}],
[{"text": self.config["button_numb_3"], "callback": self.page_three}, {"text": self.config["button_numb_4"], "callback": self.page_four}],
[{
"text": self.strings("x"),
"action": "close"
}]],
**{"photo": self.config["banner_numb_4"]},
)

View File

@@ -0,0 +1,31 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Nytimer.jpg
from .. import loader, utils
import datetime
from time import strftime
@loader.tds
class NYMod(loader.Module):
"""Check how much is left until the new year"""
strings = {'name': 'NewYearTimer'}
async def nycmd(self, message):
"""Check date"""
now = datetime.datetime.today()
ng = datetime.datetime(int(strftime('%Y')) + 1, 1, 1)
d = ng - now
mm, ss = divmod(d.seconds, 60)
hh, mm = divmod(mm, 60)
soon = '<b><emoji document_id=6334530007968253960>☃️</emoji> Until the <u>New Year</u>: {} d. {} h. {} m. {} s.</b>\n<b><emoji document_id=5393226077520798225>🥰</emoji> Wait for the new year together <u>Family</u></b>'.format(
d.days, hh, mm, ss)
await utils.answer(message, soon)

View File

@@ -0,0 +1,62 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/phstiker.jpg
# requires: phlogo
import os
from .. import loader, utils
from phlogo import generate
@loader.tds
class PhLogo(loader.Module):
"""Make Pornhub logo sticker"""
strings = {
"name": "Phlogo",
"only_two": "Something's wrong. Try giving two words only like `Hello world`",
"none_args": "Give some text bruh, e.g.: `Hello world`"
}
strings_ru = {
"only_two": "Что-то не так. Попробуйте указать только два аргумента, например «Hello world».",
"none_args": "Дай какой-нибудь текст, например: `Hello world`."
}
strings_uz = {
"only_two": "Xatolik bor. `Hello world` kabi faqat ikkita matn keltirishga harakat qiling.",
"none_args": "Bir oz matn bering, masalan: `Salom dunyo`."
}
@loader.command()
async def phl(self, message):
"Makes PHub style logo sticker."
args = utils.get_args_raw(message).split(' ')
reply = await message.get_reply_message()
if args == " ":
await utils.answer(message, self.strings('none_args'))
return
try:
p = args[0]
h = args[1]
except:
await utils.answer(message, self.strings('only_two'))
return
result = generate(f"{p}",f"{h}")
result.save("ph.webp")
path = os.getcwd()
stc = f"{path}/ph.webp"
await message.delete()
await self._client.send_file(
message.peer_id,
stc,
caption=f"{p} {h}",
link_preview=False,
reply_to=reply.id if reply else None,
)
os.remove(stc)

View File

@@ -0,0 +1,88 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/poststeal.jpg
from .. import loader, utils
@loader.tds
class PostStealer(loader.Module):
"Steal post from another channel to your channel"
strings = {
'name': 'PostStealler',
'enable': '<b>Steal mode enabled.</b>',
'disable': '<b>Steal mode disabled.</b>',
'channel': 'channel id where ub will steal messages',
'my_channel': 'channel id where ub will send messages'
}
strings_ru = {
'enable': '<b>StealMod включен.</b>',
'disable': '<b>StealMod отключен.</b>',
'channel': 'айди канала откуда юб будет пересылать сообщения',
'my_channel': 'айди канала куда юб будет пересылать сообщения'
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"my_channel",
None,
lambda: self.strings("my_channel"),
),
loader.ConfigValue(
"channel",
None,
lambda: self.strings("channel"),
),
)
async def client_ready(self, client, db):
self.client = client
self.db = db
@loader.command()
async def smode(self, message):
"""- off/on steal mode"""
status = self.db.get(
"steal_status",
"status",
)
if status == "":
self.db.set("steal_status", "status", True)
if status == False:
self.db.set("steal_status", "status", True)
await utils.answer(message, self.strings("enable"))
else:
self.db.set("steal_status", "status", False)
await utils.answer(message, self.strings("disable"))
async def watcher(self, message):
"""Лень писать описание"""
status = self.db.get("steal_status", "status")
if status == False:
return False
if status == True:
steal = self.config['channel']
chatid = int(message.chat_id)
text = message.text
if chatid == steal:
if message.photo:
await self._client.send_file(int(self.config['my_channel']), message.photo, caption=message.text if text else None, link_preview=False)
elif message.video:
await self._client.send_file(int(self.config['my_channel']), message.video, caption=message.text if text else None, link_preview=False)
elif message.document:
await self._client.send_file(int(self.config['my_channel']), message.document, caption=message.text if text else None, link_preview=False)
elif message.text:
await message.client.send_message(int(self.config['my_channel']), message.text)
else:
return False

View File

@@ -0,0 +1,80 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/recognition.jpg
from .. import utils, loader
import imghdr
import io
import random
from telethon.tl.types import Message
@loader.tds
class RecognitionMod(loader.Module):
"""Recognition from photo"""
strings = {
'name': 'Recognition',
'args': "No args!"
}
async def get_media(self, message: Message):
reply = await message.get_reply_message()
m = None
if reply and reply.media:
m = reply
elif message.media:
m = message
elif not reply:
await utils.answer(message, self.strings('args'))
return False
if not m:
file = io.BytesIO(bytes(reply.raw_text, "utf-8"))
file.name = "file.txt"
else:
file = io.BytesIO(await self._client.download_media(m, bytes))
file.name = (
m.file.name
or (
"".join(
[
random.choice(
"abcdefghijklmnopqrstuvwxyz1234567890")
for _ in range(16)
]
)
)
+ m.file.ext
)
return file
async def get_image(self, message: Message):
file = await self.get_media(message)
if not file:
return False
if imghdr.what(file) not in ["gif", "png", "jpg", "jpeg", "tiff", "bmp"]:
return False
return file
@loader.command()
async def reco(self, message: Message):
"""recognize from photo <reply to photo>"""
file = await self.get_image(message)
if not file:
return False
async with self._client.conversation("@Rekognition_Bot") as conv:
await conv.send_message(file=file) # upload step
await conv.get_response() # ignore message
cp = await conv.get_response() # get message
await utils.answer(message, cp)
await self.client.delete_dialog("@Rekognition_Bot")

View File

@@ -0,0 +1,34 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Searchpic.jpg
# meta developer: @amoremods
from .. import loader, utils
from telethon.tl.types import Message
@loader.tds
class SearchPic(loader.Module):
strings = {"name": "SearchPic"}
@loader.unrestricted
async def spiccmd(self, message: Message):
"""Search picture"""
text = utils.get_args_raw(message)
await self.inline.form(
message=message,
text = f"🎑 Your pic found\n✍ Input argument: {text}",
reply_markup=[
[{"text": "Pic here", "url": f"https://yandex.uz/images/touch/search/?text={text}",}],
[{"text": "Close", "action": "close"}],
],
**(
{"photo": f"https://yandex.uz/images/touch/search/?text={text}"}
),
)

View File

@@ -0,0 +1,94 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# scope: ffmpeg
# requires: pydub speechrecognition python-ffmpeg
# meta developer: @hikamorumods
# meta banner: https://github.com/AmoreForever/assets/blob/master/Speech.jpg?raw=true
import os
import logging
import speech_recognition as sr
from pydub import AudioSegment
from .. import loader, utils
logger = logging.getLogger(__name__)
recognizer = sr.Recognizer()
@loader.tds
class SpeechMod(loader.Module):
"""Simple speech recognition module."""
strings = {
"name": "Speech",
"only_voice": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Reply to a voice message!</b>",
"downloading": "<emoji document_id=5213251580425414358>🔽</emoji> <b>Downloading...</b>",
"recognizing": "<emoji document_id=5472199711366584503>👂</emoji> <b>Recognizing...</b>",
"not_recognized": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Not recognized</b>",
"request_error": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Request error occured.\n{}</b>",
"recognized": "<emoji document_id=5267468588985363056>🚛</emoji> <b>Recognized:</b> <code>{}</code>",
}
strings_ru = {
"only_voice": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Ответь на голосовое сообщение!</b>",
"downloading": "<emoji document_id=5213251580425414358>🔽</emoji> <b>Загрузка...</b>",
"recognizing": "<emoji document_id=5472199711366584503>👂</emoji> <b>Распознавание...</b>",
"not_recognized": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Не распознано</b>",
"request_error": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Произошла ошибка запроса.\n{}</b>",
"recognized": "<emoji document_id=5267468588985363056>🚛</emoji> <b>Распознано:</b> <code>{}</code>",
}
strings_uz = {
"only_voice": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Ovozli xabarga javob bering!</b>",
"downloading": "<emoji document_id=5213251580425414358>🔽</emoji> <b>Yuklanmoqda...</b>",
"recognizing": "<emoji document_id=5472199711366584503>👂</emoji> <b>Eshitilmoqda...</b>",
"not_recognized": "<emoji document_id=5877477244938489129>🚫</emoji> <b>Tanilmadi</b>",
"request_error": "<emoji document_id=5877477244938489129>🚫</emoji> <b>So'rovda xatolik yuz berdi.\n{}</b>",
"recognized": "<emoji document_id=5267468588985363056>🚛</emoji> <b>Text:</b> <code>{}</code>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"language",
"ru-RU",
lambda: "Language for recognition.",
validator=loader.validators.RegExp(r"^[a-z]{2}-[A-Z]{2}$"),
),
)
@loader.command()
async def spech(self, message):
"""Recognize voice message. Usage: .spech <reply to voice message>"""
reply = await message.get_reply_message()
if not reply or not reply.voice:
await utils.answer(message, self.strings("only_voice"))
return
await utils.answer(message, self.strings("downloading"))
voice = await message.client.download_media(reply.voice)
wav_voice = voice.replace(voice.split(".")[-1], "wav")
ogg_audio = AudioSegment.from_ogg(voice)
ogg_audio.export(wav_voice, format="wav")
audio = sr.AudioFile(wav_voice)
with audio as source:
try:
audio = recognizer.record(source)
await utils.answer(message, self.strings("recognizing"))
recognized = recognizer.recognize_google(audio, language=self.config["language"])
except sr.UnknownValueError:
await utils.answer(message, self.strings("not_recognized"))
return
except sr.RequestError as e:
await utils.answer(message, self.strings("request_error").format(e))
return
await utils.answer(message, self.strings("recognized").format(recognized))
os.remove(voice)
os.remove(wav_voice)

View File

@@ -0,0 +1,70 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# meta pic: https://te.legra.ph/file/5ef64ee0466032d8a4687.png
# meta banner: hhttps://raw.githubusercontent.com/AmoreForever/assets/master/Telegraphup.jpg
from .. import loader, utils
import requests
from telethon.tl.types import DocumentAttributeFilename
@loader.tds
class Telegraphup(loader.Module):
"""Upload video and photo to telegraph"""
strings = {
"name": "Telegraph",
"pls_reply": "⚠️ Reply to photo or video/gif",
}
@loader.sudo
async def thupcmd(self, message):
"""<reply photo or video>"""
if message.is_reply:
reply_message = await message.get_reply_message()
data = await check_media(reply_message)
if isinstance(data, bool):
await message.edit(self.strings("pls_reply"))
return
else:
await message.edit(self.strings("pls_reply"))
return
file = await message.client.download_media(data, bytes)
path = requests.post(
"https://te.legra.ph/upload", files={"file": ("file", file, None)}
).json()
try:
amore = "https://te.legra.ph" + path[0]["src"]
except KeyError:
amore = path["error"]
await utils.answer(message, f"😸 Your file uploaded: <code>{amore}</code>")
async def check_media(reply_message):
if reply_message and reply_message.media:
if reply_message.photo:
data = reply_message.photo
elif reply_message.document:
if (
DocumentAttributeFilename(file_name="AnimatedSticker.tgs")
in reply_message.media.document.attributes
):
return False
if reply_message.audio or reply_message.voice:
return False
data = reply_message.media.document
else:
return False
else:
return False
if not data or data is None:
return False
else:
return data

View File

@@ -0,0 +1,98 @@
# ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
# ⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠿⠟⠛⠛⠛⠛⠛
# ⣶⣦⣤⣤⣤⣤⣤⣤⣬⣭⣭⣍⣉⡙⠛⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠛⣋⣩⣭⣥⣤⣴⣶⣶⣶⣶⣶⣶⣶⣶⣶
# ⣆⠀⠀⠀⢡⠁⠀⡀⠀⢸⠟⠻⣯⠙⠛⠷⣶⣬⡙⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⢉⣥⣶⡟⠻⣙⡉⠀⢰⡆⠀⠀⣡⠀⣧⠀⠀⠀⢨
# ⠻⣦⠀⠀⠈⣇⣀⣧⣴⣿⣶⣶⣿⣷⠀⢀⡇⠉⠻⢶⣌⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣡⡶⠟⠉⠀⢣⠀⣿⠷⠀⠀⠀⠀⣿⡷⢀⠇⠀⠀⢠⣿
# ⣦⡈⢧⡀⠀⠘⢮⡙⠛⠉⠀⠄⠙⢿⣀⠞⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠈⠳⣄⠉⠓⠒⠚⠋⢀⡠⠋⠀⢀⣴⣏⣿
# ⣿⣿⣿⣛⣦⣀⠀⠙⠓⠦⠤⣤⠔⠛⠁⠀⠀⠀⠀⠀⢀⣀⣹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣤⣤⣤⣤⣤⣀⣀⣀⣀⢙⢓⣒⡒⠚⠋⢠⣤⢶⣟⣽⣿⣿
# ⣿⣿⣿⣿⣿⣿⣷⣦⠀⠀⣴⣿⣷⣶⣶⣶⣾⡖⢰⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣾⣿⣿⣶⣾⣿⣿⣿⣿⣿⣿
# ⣿⣿⣿⣿⣿⣿⣿⣿⠀⢀⣿⣿⣿⣿⣿⣿⣿⠃⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
# ⣿⣿⣿⣿⣿⣿⣿⡏⠀⢸⣿⣿⣿⣿⣿⣿⣿⠁⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
# ⣿⣿⣿⣿⣿⣿⣿⣷⠀⢸⣿⣿⣿⣿⣿⣿⣿⠀⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
#          https://t.me/amorescam
# meta developer: @amoremods
# meta banner: https://raw.githubusercontent.com/AmoreForever/assets/master/Universaltime.jpg
# @Den4ikSuperOstryyPer4ik: I Love AmoreMods :)
from .. import loader, utils
import datetime
from ..inline.types import InlineCall
import logging
logger = logging.getLogger(__name__)
def check_time():
offsets = [3, 5, 4, 2, 1, -7, 6, 9, 5.30, 9, 8, -8, -4]
hrs = []
for x in offsets:
offset = datetime.timedelta(hours=x)
not_tz = datetime.timezone(offset)
time = datetime.datetime.now(not_tz)
format_ = time.strftime("%d.%m.%y | %H:%M")
hrs.append(format_)
return f"<emoji document_id=4920662486778119009>🌐</emoji> <b>Universal time</b>\n\n<emoji document_id=6323139226418284334>🇷🇺</emoji> Russia ➪ {hrs[0]}\n<emoji document_id=6323430017179059570>🇺🇿</emoji> Uzbekistan ➪ {hrs[1]}\n<emoji document_id=6323289850921354919>🇺🇦</emoji> Ukraine ➪ {hrs[3]}\n<emoji document_id=6323575251498174463>🇦🇿</emoji> Azerbaijan ➪ {hrs[2]}\n<emoji document_id=6320817337033295141>🇩🇪</emoji> German ➪ {hrs[3]}\n<emoji document_id=6323589145717376403>🇬🇧</emoji> UK ➪ {hrs[4]}\n<emoji document_id=6323602387101550101>🇵🇱</emoji> Poland ➪ {hrs[3]}\n<emoji document_id=6323374027985389586>🇺🇸</emoji> USA ➪ {hrs[5]}\n<emoji document_id=6323615997852910673>🇰🇬</emoji> Kyrgyzstan ➪ {hrs[6]}\n<emoji document_id=6323135275048371614>🇰🇿</emoji> Kazakhstan ➪ {hrs[6]}\n<emoji document_id=6323555846835930376>🇮🇶</emoji> Iraq ➪ {hrs[0]}\n<emoji document_id=6323356796576597627>🇯🇵</emoji> Japan ➪ {hrs[7]}\n<emoji document_id=6323152716910561397>🇰🇷</emoji> South KR ➪ {hrs[7]}\n<emoji document_id=6323181871148566277>🇮🇳</emoji> India ➪ {hrs[8]}\n<emoji document_id=6323570711717742330>🇫🇷</emoji> France ➪ {hrs[3]}\n<emoji document_id=6323453751168337485>🇨🇳</emoji> China ➪ {hrs[9]}\n<emoji document_id=6321003171678259486>🇹🇷</emoji> Turkey ➪ {hrs[0]}\n<emoji document_id=6323602322677040561>🇨🇱</emoji> Mongolia ➪ {hrs[10]}\n<emoji document_id=6323325327351219831>🇨🇦</emoji> Canada ➪ {hrs[11]}\n<emoji document_id=6323471399188957082>🇮🇹</emoji> Italia ➪ {hrs[2]}\n<emoji document_id=6323516260122363644>🇪🇬</emoji> Egypt ➪ {hrs[3]}\n<emoji document_id=6323236391463421376>🇦🇲</emoji> Armenia ➪ {hrs[12]}\n\n<emoji document_id=5188216117272780281>🍙</emoji> #whyamore"
@loader.tds
class UniversalTimeMod(loader.Module):
"""See the time of other countries"""
strings = {"name": "UnivTime"}
@loader.command(ru_docs="Смотреть мировое время")
async def atimecmd(self, message):
"""See time"""
kk = check_time()
await utils.answer(message, kk)
@loader.command(ru_docs="Смотреть мировое время в инлайн режиме")
async def atimeicmd(self, message):
"""See time on inline mode"""
kk = check_time()
await self.inline.form(
text=kk,
message=message,
gif="https://te.legra.ph/file/2ab9b131ceceb9b020583.mp4",
reply_markup=[
[
{
"text": "🍃 Refresh",
"callback": self.refresh,
}
],
[
{
"text": "🔻 Close",
"action": "close",
}
]
]
)
async def refresh(self, call: InlineCall): # thanks @Den4ikSuperOstryyPer4ik
kk = check_time()
await call.edit(
text=kk,
reply_markup=[
[
{
"text": "🍃 Refresh",
"callback": self.refresh,
}
],
[
{
"text": "🔻 Close",
"action": "close",
}
]
]
)
await call.answer("Refreshed ✨")

View File

@@ -0,0 +1,214 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# meta developer: @hikamorumods
# pusechka @saint_players thanks for the idea
import logging
import requests
from telethon.tl.types import Message
from telethon.tl.functions.channels import CreateChannelRequest, UpdateUsernameRequest
from telethon.tl.types import InputPeerChannel
from .. import loader, utils
from ..inline.types import InlineCall
logger = logging.getLogger(__name__)
@loader.tds
class UserStealer(loader.Module):
"""
Username tracking module.
"""
strings = {
"name": "UsernameChecker",
"create_channel": "📂 Create channel",
"user_free": "⚡️ Username @{} is free, quickly take it.",
"user_busy": "😥 Username @{} is busy. Do you want to enable tracking?",
"yes": "🛟 Yes",
"nope": "🧰 No",
"free_now": "🌟 Username {} is free, do you want to create a channel?",
"not_args": "🚫 Enter the user for check",
"status": "🆔 Username @{} is being tracked\n🟢 Status: free",
"status_busy": "🆔 Username @{} is being tracked\n🟡 Status: busy",
"none": "❌ No username is being tracked.",
"done": "🦉 Done, wait till username will be free",
"created": "🌟 Channel created, go to username @{}",
"tg_bug": "🐞 Telegram has bugs, if the channel is not created, try to create it manually",
}
strings_ru = {
"create_channel": "📂 Создать канал",
"user_free": "⚡️ Юзернейм @{} свободен, быстро забирай его.",
"user_busy": "😥 Юзернейм @{} занят. Хотите включить отслеживание?",
"yes": "🛟 Да",
"nope": "🧰 Нет",
"free_now": "🌟 Юзернейм {} свободен, хотите создать канал?",
"not_args": "🚫 Укажи юзернейм для проверки",
"status": "🆔 Юзернейм @{} отслеживается\n🟢 Статус: свободен",
"status_busy": "🆔 Юзернейм @{} отслеживается\n🟡 Статус: занят",
"none": "❌ Ни один юзернейм не отслеживается.",
"done": "🦉 Готово, жди пока юзернейм освободится",
"created": "🌟 Канал создан, переходи по юзернейму @{}",
"tg_bug": "🐞 У телеграма баги, если канал не создан, попробуй создать его вручную",
}
@loader.loop(interval=30, autostart=True)
async def _steal(self):
user = self.db.get("usernames_to_check", "username")
if user == "none":
return
else:
r = requests.get(url=f"https://t.me/{user}")
if (
r.text.find(
'If you have <strong>Telegram</strong>, you can contact <a class="tgme_username_link"'
)
>= 0
):
self._markup = lambda: self.inline.generate_markup(
[
{"text": self.strings("create_channel"), "callback": self.create_inline, "args": (user,),},
{"text": "Отмена", "callback": self.delete, },
]
)
await self.inline.bot.send_message(
self._client.tg_id,
self.strings("user_free").format(user),
disable_web_page_preview=True,
reply_markup=self._markup()
)
self.db.set("usernames_to_check", "username", "none")
else:
pass
@loader.command()
async def ucheckcmd(self, message: Message):
"""> Enter the user for check (without @)"""
args = utils.get_args_raw(message)
r = requests.get(url=f"https://t.me/{args}")
if args.startswith('@'):
args = args[1:]
if not args:
return await utils.answer(message, self.strings("not_args"))
if (
r.text.find(
'If you have <strong>Telegram</strong>, you can contact <a class="tgme_username_link"'
)
>= 0
):
await self.inline.form(
text=self.strings("free_now").format(args),
reply_markup=[
[
{
"text": self.strings("yes"),
"callback": self.create,
"args": (args,),
},
{"text": self.strings("nope"), "action": "close"},
],
], **{'video': 'https://te.legra.ph/file/a14a9ff4071d079272171.mp4'},
message=message,
)
else:
await self.inline.form(
text=self.strings("user_busy").format(args),
reply_markup=[
[
{
"text": self.strings("yes"),
"callback": self.owo,
"args": (args,),
},
{"text": self.strings("nope"), "action": "close"},
],
], **{"video": "https://te.legra.ph/file/90fbbd0deabfc5e740eb3.mp4"},
message=message,
)
@loader.command()
async def myus(self, message: Message):
"""> Check status of the user being tracked"""
proc = self.db.get("usernames_to_check", "username")
if proc == 'none':
await utils.answer(message, self.strings("none"))
else:
r = requests.get(url=f"https://t.me/{proc}")
if (
r.text.find(
'If you have <strong>Telegram</strong>, you can contact <a class="tgme_username_link"'
)
>= 0
):
await utils.answer(message, self.strings("status").format(proc))
else:
await utils.answer(message, self.strings("status_busy").format(proc))
async def owo(self, call: InlineCall, text: str):
self.db.set("usernames_to_check", "username", text)
await call.edit(self.strings("done"))
async def create(self, call: InlineCall, text: str):
channel = await self.client(
CreateChannelRequest(f"{text}", f"{text}", megagroup=False)
)
channel_id = channel.__dict__["chats"][0].__dict__["id"]
channel_hash = channel.__dict__["chats"][0].__dict__["access_hash"]
try:
await self.client(
UpdateUsernameRequest(
InputPeerChannel(channel_id=channel_id,
access_hash=channel_hash),
text,
)
)
await call.edit(self.strings("created").format(text))
except:
await call.edit(
self.strings("tg_bug"),
)
await self.client.delete_dialog(channel_id)
async def create_inline(self, call: InlineCall, text: str):
channel = await self.client(
CreateChannelRequest(f"{text}", f"{text}", megagroup=False)
)
channel_id = channel.__dict__["chats"][0].__dict__["id"]
channel_hash = channel.__dict__["chats"][0].__dict__["access_hash"]
try:
await self.client(
UpdateUsernameRequest(
InputPeerChannel(channel_id=channel_id,
access_hash=channel_hash),
text,
)
)
await call.answer(self.strings("created").format(text))
await call.delete()
except:
await call.answer(
self.strings("tg_bug"),
show_alert=True
)
await self.client.delete_dialog(channel_id)
await call.delete()
async def delete(self, call: InlineCall):
await call.delete()

View File

@@ -0,0 +1,134 @@
# █ █ █ █▄▀ ▄▀█ █▀▄▀█ █▀█ █▀█ █ █
# █▀█ █ █ █ █▀█ █ ▀ █ █▄█ █▀▄ █▄█
# 🔒 Licensed under the GNU GPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# 👤 https://t.me/hikamoru
# required: aiohttp
# meta banner: https://github.com/AmoreForever/shizuassets/blob/master/wakatime.jpg?raw=true
# meta developer: @hikamorumods
import asyncio
import aiohttp
from .. import utils, loader
@loader.tds
class Wakatime(loader.Module):
"""Show your Wakatime stats"""
strings = {
"name": "Wakatime",
"set_waka": "Set your Wakatime token",
"no_token": "🚫 <b>You don't have a token</b>",
}
def __init__(self):
self.config = loader.ModuleConfig(
"WAKATIME_TOKEN",
None,
lambda: self.strings["set_waka"],
)
@loader.command()
async def waka(self, message):
"""See your stat"""
token = self.config["WAKATIME_TOKEN"]
if token is None:
return await utils.answer(message, self.strings("no_token"))
async with aiohttp.ClientSession() as session:
endpoints = [
"status_bar/today",
"stats/all_time",
"stats/all_time",
"all_time_since_today",
]
tasks = [
session.get(
f"https://wakatime.com/api/v1/users/current/{endpoint}?api_key={token}"
)
for endpoint in endpoints
]
responses = await asyncio.gather(*tasks)
results = await asyncio.gather(*[response.json() for response in responses])
result_t, result, result_s, result_w = results
all_time = result_w["data"]["text"]
username = result["data"]["username"]
languages = result["data"]["languages"]
today = result_t["data"]["categories"]
os = result["data"]["operating_systems"]
editor = result["data"]["editors"]
OS = ", ".join(f"<code>{stat['name']}</code>" for stat in os if stat["text"] != "0 secs")
EDITOR = ", ".join(f"<code>{stat['name']}</code>" for stat in editor if stat["text"] != "0 secs")
LANG = "\n".join(f"▫️ <b>{stat['name']}</b>: <i>{stat['text']}</i>" for stat in languages if stat["text"] != "0 secs")
TODAY = "\n".join(stat["text"] for stat in today if stat["text"] != "0 secs")
await utils.answer(
message,
f"👤 <b>Username:</b> <code>{username}</code>\n🖥 <b>OS:</b> {OS}\n🌀 <b>Editor:</b> {EDITOR}\n⏳ <b>All time</b>: <code>{all_time}</code>\n💼 <b>Today</b>: <code>{TODAY}</code>\n\n🧬 LANGUAGES\n\n{LANG}\n",
reply_markup=[
[
{
"text": "🔄 Update",
"callback": self.update_waka,
}
]
],
)
async def update_waka(self, call):
await call.edit("🔄 <b>Updating...</b>")
token = self.config["WAKATIME_TOKEN"]
if token is None:
return await call.edit(self.strings("no_token"))
async with aiohttp.ClientSession() as session:
endpoints = [
"status_bar/today",
"stats/all_time",
"stats/all_time",
"all_time_since_today",
]
tasks = [
session.get(
f"https://wakatime.com/api/v1/users/current/{endpoint}?api_key={token}"
)
for endpoint in endpoints
]
responses = await asyncio.gather(*tasks)
results = await asyncio.gather(*[response.json() for response in responses])
result_t, result, result_s, result_w = results
all_time = result_w["data"]["text"]
username = result["data"]["username"]
languages = result["data"]["languages"]
today = result_t["data"]["categories"]
os = result["data"]["operating_systems"]
editor = result["data"]["editors"]
OS = ", ".join(f"<code>{stat['name']}</code>" for stat in os if stat["text"] != "0 secs")
EDITOR = ", ".join(f"<code>{stat['name']}</code>" for stat in editor if stat["text"] != "0 secs")
LANG = "\n".join(f"▫️ <b>{stat['name']}</b>: <i>{stat['text']}</i>" for stat in languages if stat["text"] != "0 secs")
TODAY = "\n".join(stat["text"] for stat in today if stat["text"] != "0 secs")
await call.edit(
f"👤 <b>Username:</b> <code>{username}</code>\n🖥 <b>OS:</b> {OS}\n🌀 <b>Editor:</b> {EDITOR}\n⏳ <b>All time</b>: <code>{all_time}</code>\n💼 <b>Today</b>: <code>{TODAY}</code>\n\n🧬 LANGUAGES\n\n{LANG}\n",
reply_markup=[
[
{
"text": "🔄 Update",
"callback": self.update_waka,
}
]
],
)

View File

@@ -42,7 +42,6 @@ async def to_code(n: int) -> str:
n_shifted //= 31
return "X" + "".join(reversed(res))
@loader.tds
class BSR(loader.Module):
'''Module for finding nearby game rooms in BrawlStars.'''
@@ -140,7 +139,7 @@ class BSR(loader.Module):
'''(room code/link) (previous) (next) - find rooms.'''
args = utils.get_args_raw(message).split()
if not args:
return await utils.answer(message, self.strings["invalid_args"].format(prefix=self.get_prefix()))
return await utils.answer(message, self.strings("invalid_args").format(prefix=self.get_prefix()))
raw_input = args[0]
before = 0
@@ -162,13 +161,13 @@ class BSR(loader.Module):
nxt = max(0, min(nxt, 5000))
if before == 0 and nxt == 0:
return await utils.answer(message, self.strings["at_least_one"])
return await utils.answer(message, self.strings("at_least_one"))
clean_tag = await extract_code(raw_input)
base_id = await to_id(clean_tag)
if base_id == 0:
return await utils.answer(message, self.strings["invalid_code"])
return await utils.answer(message, self.strings("invalid_code"))
text, page, total_pages = await self.get_page_content(base_id, before, nxt, 0)
kb = self.build_keyboard(base_id, before, nxt, page, total_pages, clean_tag)
@@ -206,10 +205,10 @@ class BSR(loader.Module):
blocks = []
if prev_list:
blocks.append(self.strings["prev_block"].format(prev_list="\n".join(prev_list)))
blocks.append(self.strings("prev_block").format(prev_list="\n".join(prev_list)))
if next_list:
blocks.append(self.strings["next_block"].format(next_list="\n".join(next_list)))
blocks.append(self.strings("next_block").format(next_list="\n".join(next_list)))
res = "\n\n".join(blocks)
if not res.strip():
@@ -221,7 +220,7 @@ class BSR(loader.Module):
kb = [
[
{
"text": self.strings["btn_target"],
"text": self.strings("btn_target"),
"copy": clean_tag
}
]

View File

@@ -19,19 +19,15 @@ import re
import sys
import uuid
import importlib
from contextlib import suppress
from typing import Optional, Dict, List, Union, Tuple, Any
from urllib.parse import unquote
from importlib.machinery import ModuleSpec
import telethon
from .. import loader, utils
from ..types import CoreOverwriteError
from herokutl.tl.functions.contacts import UnblockRequest
try:
from aiogram.types import InlineQueryResultArticle, InputTextMessageContent, LinkPreviewOptions
except ImportError:
InlineQueryResultArticle = InputTextMessageContent = LinkPreviewOptions = Any
from aiogram.types import InlineQueryResultArticle, InputTextMessageContent, LinkPreviewOptions, ChosenInlineResult, CallbackQuery, Message
class FHetaAPI:
@@ -81,7 +77,7 @@ class MInstaller:
try:
code = await plugin._storage.fetch(url, auth=plugin.config.get("basic_auth"))
except Exception:
return "error",[]
return "error", []
for step in range(5):
state = await self.load(plugin, code, url, step)
@@ -89,10 +85,10 @@ class MInstaller:
if state == "success":
if plugin.fully_loaded:
plugin.update_modules_in_db()
return "success",[]
return "success", []
if state == "overwrite":
return "overwrite",[]
return "overwrite", []
if isinstance(state, list):
return "dependency", state
@@ -102,37 +98,35 @@ class MInstaller:
await asyncio.sleep(0.5)
return "dependency",[]
return "dependency", []
async def load(self, plugin: 'loader.Module', code: str, origin: str, step: int) -> Union[str, List[str]]:
if step == 0:
try:
raw_pip = loader.VALID_PIP_PACKAGES.search(code)
if raw_pip:
dependencies = [
dep.strip() for dep in raw_pip[1].replace(',', ' ').split()
if dep.strip() and not dep.strip().startswith(("-", "_", "."))
]
dependencies = list(filter(
lambda requirement: not requirement.startswith(("-", "_", ".")),
map(lambda raw: raw.strip().rstrip(','), loader.VALID_PIP_PACKAGES.search(code)[1].split())
))
if dependencies:
await plugin.install_requirements(dependencies)
importlib.invalidate_caches()
return "retry"
if dependencies:
if not await plugin.install_requirements(dependencies):
return dependencies
importlib.invalidate_caches()
return "retry"
except Exception:
pass
try:
raw_apt = loader.VALID_APT_PACKAGES.search(code)
if raw_apt:
packages = [
pkg.strip() for pkg in raw_apt[1].replace(',', ' ').split()
if pkg.strip() and not pkg.strip().startswith(("-", "_", "."))
]
packages = list(filter(
lambda requirement: not requirement.startswith(("-", "_", ".")),
map(lambda raw: raw.strip().rstrip(','), loader.VALID_APT_PACKAGES.search(code)[1].split())
))
if packages:
await plugin.install_packages(packages)
importlib.invalidate_caches()
return "retry"
if packages:
if not await plugin.install_packages(packages):
return packages
importlib.invalidate_caches()
return "retry"
except Exception:
pass
@@ -179,8 +173,8 @@ class MInstaller:
finally:
if instance and sys.exc_info()[0] is not None:
await plugin.allmodules.unload_module(instance.__class__.__name__)
if instance in plugin.allmodules.modules:
with suppress(Exception):
await plugin.allmodules.unload_module(instance.__class__.__name__)
plugin.allmodules.modules.remove(instance)
@@ -232,17 +226,11 @@ class FHetaUI:
description = utils.escape_html(description).split('\n')[0] if description else ""
name = utils.escape_html(item.get("name", ""))
if item.get('inline'):
character = '@' + self.main.inline.bot_username + ' '
display_name = name
elif kind == "ph":
character = ""
display_name = f"{{{name}}}"
if kind == "cmd":
character = '@' + self.main.inline.bot_username + ' ' if item.get('inline') else self.main.get_prefix()
row = f"<code>{character}{name}</code> {description}".strip()
else:
character = self.main.get_prefix()
display_name = name
row = f"<code>{character}{display_name}</code> {description}".strip()
row = f"<code>{{{name}}}</code> {description}".strip()
extra = f"<i>{self.main.strings[more].format(remaining=len(items) - index)}</i>"
test = "\n".join(lines + [row, extra])
@@ -374,7 +362,7 @@ class FHeta(loader.Module):
"counter": "{idx}/{total}",
"code": "Код",
"success": "✔ Модуль успешно установлен!",
"error": "✘ Ошибка, возможно, модуль сломан!",
"error": "✘ Ошибка, возможно, модуль поломан!",
"overwrite": "✘ Ошибка, модуль пытался перезаписать встроенный модуль!",
"dependency": "✘ Ошибка установки зависимостей! {deps}",
"docdevs": "Использовать только модули от официальных разработчиков Heroku при поиске?",
@@ -428,7 +416,7 @@ class FHeta(loader.Module):
"search": "{query} сұрауы бойынша іздеу...",
"noquery": "Сіз іздеу сұрауын енгізбедіңіз, мысал: {prefix}fheta сіздің сұрауыңыз",
"notfound": "{query} сұрауы бойынша ештеңе табылмады.",
"toolong": "Сіздің сұрауыңыз тым үлкен, оны 168 таңбаға до қысқартыңыз.",
"toolong": "Сіздің сұрауыңыз тым үлкен, оны 168 таңбаға дейін қысқартыңыз.",
"added": "✔ Бағалау қосылды!",
"changed": "✔ Бағалау өзгертілді!",
"deleted": "✔ Бағалау жойылды!",
@@ -465,7 +453,7 @@ class FHeta(loader.Module):
"added": "✔ Reyting qo'shildi!",
"changed": "✔ Reyting o'zgartirildi!",
"deleted": "✔ Reyting o'chirildi!",
"prompt": "Qidirish o'rniga so'rov kiritish.",
"prompt": "Qidirish uchun so'rov kiriting.",
"hint": "Nomi, buyruq, tavsif, muallif.",
"retry": "Boshqa so'rovni sinab ko'ring.",
"query": "So'rov",
@@ -477,7 +465,7 @@ class FHeta(loader.Module):
"overwrite": "✘ Xatolik, modul o'rnatilgan modulni qayta yozishga harakat qildi!",
"dependency": "✘ Bog'liqliklarni o'rnatish xatosi! {deps}",
"docdevs": "Qidiruv paytida faqat rasmiy Heroku ishlab chiquvchilarining modullaridan foydalanish kerakmi?",
"doctheme": "Emojilar uchun mavзу.",
"doctheme": "Emojilar uchun mavzu.",
"channel": "Bu FHeta-dagi barcha yangilanishlari bo'lgan kanal!"
}
@@ -542,9 +530,9 @@ class FHeta(loader.Module):
"error": "✘ Fehler, vielleicht ist das Modul kaputt!",
"overwrite": "✘ Fehler, Modul hat versucht, das integrierte Modul zu überschreiben!",
"dependency": "✘ Fehler bei der Installation von Abhängigkeiten! {deps}",
"docdevs": "Nur Module von offiziellen Heroku-Entwicklern bei की खोज में उपयोग करें?",
"doctheme": "Theма для эмодзи.",
"channel": "Dies ist der Kanal with all updates in FHeta!"
"docdevs": "Nur Module von offiziellen Heroku-Entwicklern bei der Suche verwenden?",
"doctheme": "Thema für Emojis.",
"channel": "Dies ist der Kanal mit allen Updates in FHeta!"
}
strings_jp = {
@@ -572,8 +560,8 @@ class FHeta(loader.Module):
"counter": "{idx}/{total}",
"code": "コード",
"success": "✔ モジュールが正常にインストールされました!",
"error": "✘ エラー, モジュールが壊れている可能性があります!",
"overwrite": "✘ エラー, モジュールが組み込みモジュールを上書きしようとしました!",
"error": "✘ エラーモジュールが壊れている可能性があります!",
"overwrite": "✘ エラーモジュールが組み込みモジュールを上書きしようとしました!",
"dependency": "✘ 依存関係のインストールエラー! {deps}",
"docdevs": "検索時に公式Heroku開発者のモジュールのみを使用しますか",
"doctheme": "絵文字のテーマ。",
@@ -643,13 +631,13 @@ class FHeta(loader.Module):
loader.ConfigValue(
"only_official_developers",
False,
lambda: self.strings["docdevs"],
lambda: self.strings("docdevs"),
validator=loader.validators.Boolean()
),
loader.ConfigValue(
"theme",
"default",
lambda: self.strings["doctheme"],
lambda: self.strings("doctheme"),
validator=loader.validators.Choice(["default", "winter", "summer", "spring", "autumn"])
)
)
@@ -658,30 +646,12 @@ class FHeta(loader.Module):
if hasattr(self, "api") and self.api.session and not self.api.session.closed:
await self.api.session.close()
@property
def _inline_mgr(self):
if hasattr(self, "_raw_inline_cache") and self._raw_inline_cache:
return self._raw_inline_cache
am_attr = "seludomlla"[::-1]
allmodules = getattr(self, am_attr, None)
if allmodules:
for cmd in getattr(allmodules, "commands", {}).values():
mod = getattr(cmd, "__self__", None)
if mod and getattr(mod, "__origin__", "").startswith("<core"):
real_allmodules = getattr(mod, am_attr, None)
if real_allmodules:
self._raw_inline_cache = getattr(real_allmodules, "inline", None)
if self._raw_inline_cache:
return self._raw_inline_cache
return self._raw_inline_cache
async def client_ready(self, client: 'telethon.TelegramClient', database: 'loader.Database') -> None:
await client(UnblockRequest("@FHeta_robot"))
await utils.dnd(client, "@FHeta_robot", archive=True)
try:
await client(UnblockRequest("@FHeta_robot"))
await utils.dnd(client, "@FHeta_robot", archive=True)
except Exception:
pass
self.identifier = (await client.get_me()).id
self.token = database.get("FHeta", "token")
@@ -692,99 +662,93 @@ class FHeta(loader.Module):
await self.request_join(
"NFHeta_Updates",
f"{self.ui.emoji('channel')} {self.strings['channel']}"
f"{self.ui.emoji('channel')} {self.strings('channel')}"
)
self.api.token = self.token
self._is_telethon = hasattr(self._inline_mgr, "_bot_client")
if self._is_telethon:
if hasattr(self._inline_mgr, "register_bot_update_handler"):
async def telethon_chosen_handler(event: Any) -> None:
if isinstance(event, telethon.tl.types.UpdateBotInlineSend):
if event.id.startswith("fh_"):
class MockCallback:
result_id = event.id
inline_message_id = event.msg_id
await self.click(MockCallback())
router = None
try:
frame = sys._getframe()
while frame:
if 'self' in frame.f_locals and type(frame.f_locals['self']).__name__ == "Modules":
router = getattr(frame.f_locals['self'], "inline", None)
if router:
break
frame = frame.f_back
except Exception:
pass
self._inline_mgr.register_bot_update_handler("fheta_chosen", "chosen_inline_result", telethon_chosen_handler)
else:
bot_client = self._inline_mgr._bot_client
if not hasattr(bot_client, "_fpatched"):
@bot_client.on(telethon.events.Raw)
async def telethon_raw_handler(event: Any) -> None:
if isinstance(event, telethon.tl.types.UpdateBotInlineSend):
if event.id.startswith("fh_"):
class MockCallback:
result_id = event.id
inline_message_id = event.msg_id
await self.lookup("FHeta").click(MockCallback())
bot_client._fpatched = True
router = router or self.inline
dispatcher = getattr(router, "_dp", getattr(router, "dp", getattr(router, "router", None)))
self.bot = getattr(router, "_bot", getattr(router, "bot", getattr(self.inline, "bot", None)))
if dispatcher:
if not getattr(dispatcher, "_fpatched", False):
elif hasattr(self._inline_mgr, "_dp"):
dispatcher = self._inline_mgr._dp
if not hasattr(dispatcher, "_fpatched"):
async def fmiddleware(handler: Any, event: Any, data: Any) -> Any:
module = self.lookup("FHeta")
if module and event.result_id.startswith("fh_"):
await module.click(event)
return None
try:
module = self.lookup("FHeta")
if module and getattr(event, "result_id", "").startswith("fh_"):
await module.click(event)
return None
except Exception:
pass
return await handler(event, data)
dispatcher.chosen_inline_result.middleware(fmiddleware)
dispatcher._fpatched = True
try:
dispatcher.chosen_inline_result.middleware(fmiddleware)
dispatcher._fpatched = True
except Exception:
pass
if self.token and not await self.api.fetch("validatetkn", user_id=str(self.identifier)):
self.token = None
self.api.token = None
if not self.token:
async with client.conversation("@FHeta_robot") as conversation:
await conversation.send_message('/token')
self.token = (await conversation.get_response(timeout=5)).text.strip()
database.set("FHeta", "token", self.token)
self.api.token = self.token
try:
async with client.conversation("@FHeta_robot") as conversation:
await conversation.send_message('/token')
self.token = (await conversation.get_response(timeout=5)).text.strip()
database.set("FHeta", "token", self.token)
self.api.token = self.token
except Exception:
pass
asyncio.create_task(self.sync())
async def sync(self):
ll = None
while True:
cl = self.strings["lang"]
if cl != ll:
await self.api.send("dataset", user_id=self.identifier, lang=cl)
ll = cl
try:
cl = self.strings["lang"]
if cl != ll:
await self.api.send("dataset", user_id=self.identifier, lang=cl)
ll = cl
except Exception:
pass
await asyncio.sleep(1)
async def answer(self, callback: Any, text: Optional[str] = None, alert: bool = False) -> None:
if not hasattr(callback, "answer"):
return
await callback.answer(text=text or "", show_alert=alert)
async def answer(self, callback: Union[CallbackQuery, ChosenInlineResult], text: Optional[str] = None, alert: bool = False) -> None:
try:
if text:
await callback.answer(text, show_alert=alert)
else:
await callback.answer()
except Exception:
pass
async def edit(self, target: Any, text: str, buttons: List[List[Dict[str, Any]]], banner: Optional[str] = None) -> None:
markup = self._inline_mgr.generate_markup(buttons)
if self._is_telethon:
if banner and banner not in text:
text = f'<a href="{banner}">&#8204;</a>' + text
bot_client = self._inline_mgr._bot_client
inline_msg_id = target.inline_message_id if hasattr(target, "inline_message_id") else None
await bot_client.edit_message(
inline_msg_id or target.chat_id,
None if inline_msg_id else target.message_id,
text,
parse_mode="HTML",
buttons=markup,
link_preview=banner is not None,
invert_media=True
)
elif InlineQueryResultArticle is not Any:
async def edit(self, target: Union[str, ChosenInlineResult, CallbackQuery, Message, 'telethon.types.Message'], text: str, buttons: List[List[Dict[str, Any]]], banner: Optional[str] = None) -> None:
try:
options = LinkPreviewOptions(url=banner, show_above_text=True, prefer_large_media=True) if banner else LinkPreviewOptions(is_disabled=True)
markup = self.inline.generate_markup(buttons)
if not self.bot:
return
arguments = {
"text": text,
"reply_markup": markup,
@@ -792,53 +756,64 @@ class FHeta(loader.Module):
"parse_mode": "HTML"
}
if hasattr(target, "inline_message_id") and target.inline_message_id:
arguments["inline_message_id"] = target.inline_message_id
inline = target if isinstance(target, str) else getattr(target, "inline_message_id", None)
if inline:
arguments["inline_message_id"] = inline
else:
arguments["chat_id"] = target.message.chat.id
arguments["message_id"] = target.message.message_id
message = getattr(target, "message", target)
chat = getattr(getattr(message, "chat", message), "id", getattr(message, "chat_id", None))
identifier = getattr(message, "message_id", getattr(message, "id", None))
await self._inline_mgr.bot.edit_message_text(**arguments)
if chat and identifier:
arguments["chat_id"] = chat
arguments["message_id"] = identifier
else:
return
async def click(self, callback: Any) -> None:
result_id = callback.result_id
if not result_id.startswith("fh_"):
return
await self.bot.edit_message_text(**arguments)
except Exception:
pass
parts = result_id.split("_")
if len(parts) != 3:
return
async def click(self, callback: ChosenInlineResult) -> None:
try:
if not getattr(callback, "result_id", "").startswith("fh_"):
return
queryid = parts[1]
index = int(parts[2])
parts = callback.result_id.split("_")
if len(parts) != 3:
return
if not hasattr(self._inline_mgr, "fheta_cache"):
return
queryid = parts[1]
index = int(parts[2])
saved = self._inline_mgr.fheta_cache.get(queryid, {})
query = saved.get("query", "")
modules = saved.get("mods", [])
cache = getattr(self.inline, "fheta_cache", {})
saved = cache.get(queryid, {})
query = saved.get("query", "")
modules = saved.get("mods", [])
if not modules or index >= len(modules):
return
if not modules or index >= len(modules):
return
data = modules[index]
text = self.ui.format(data, query, index+1, len(modules), True)
buttons = self.ui.buttons(data.get("install", ""), data, index, None, query)
data = modules[index]
text = self.ui.format(data, query, index+1, len(modules), True)
buttons = self.ui.buttons(data.get("install", ""), data, index, None, query)
await self.edit(callback, text, buttons, data.get("banner"))
await self.edit(callback, text, buttons, data.get("banner"))
except Exception:
pass
async def show(self, callback: Any, index: int, modules: List[Dict[str, Any]], query: str) -> None:
async def show(self, callback: Union[CallbackQuery, ChosenInlineResult], index: int, modules: List[Dict[str, Any]], query: str) -> None:
await self.answer(callback)
text = f"{self.ui.emoji('modules_list')} <b>{self.strings['list']}</b>"
await self.edit(callback, text, self.ui.pagination(modules, query, 0, index))
async def page(self, callback: Any, current: int, modules: List[Dict[str, Any]], query: str, index: int) -> None:
async def page(self, callback: Union[CallbackQuery, ChosenInlineResult], current: int, modules: List[Dict[str, Any]], query: str, index: int) -> None:
await self.answer(callback)
text = f"{self.ui.emoji('modules_list')} <b>{self.strings['list']}</b>"
await self.edit(callback, text, self.ui.pagination(modules, query, current, index))
async def navigate(self, callback: Any, index: int, modules: List[Dict[str, Any]], query: str = "") -> None:
async def navigate(self, callback: Union[CallbackQuery, ChosenInlineResult], index: int, modules: List[Dict[str, Any]], query: str = "") -> None:
await self.answer(callback)
if 0 <= index < len(modules):
data = modules[index]
@@ -846,7 +821,7 @@ class FHeta(loader.Module):
buttons = self.ui.buttons(data.get('install', ''), data, index, modules, query)
await self.edit(callback, text, buttons, data.get("banner"))
async def rate(self, callback: Any, link: str, action: str, index: int, modules: Optional[List[Dict[str, Any]]], query: str = "") -> None:
async def rate(self, callback: Union[CallbackQuery, ChosenInlineResult, Message, 'telethon.types.Message'], link: str, action: str, index: int, modules: Optional[List[Dict[str, Any]]], query: str = "") -> None:
response = await self.api.send(f"rate/{self.identifier}/{link}/{action}")
request = await self.api.send("get", payload=[unquote(link)])
@@ -855,7 +830,10 @@ class FHeta(loader.Module):
if modules and index < len(modules):
modules[index].update(stats)
await self.edit(callback, self.ui.format(modules[index], query, index + 1, len(modules)), self.ui.buttons(link, stats, index, modules, query), modules[index].get("banner"))
try:
await callback.edit(reply_markup=self.ui.buttons(link, stats, index, modules, query))
except Exception:
pass
if response and response.get("status"):
status = response.get("status")
@@ -869,18 +847,21 @@ class FHeta(loader.Module):
text = ""
await self.answer(callback, text, True)
async def install(self, callback: Any, link: str, index: int, modules: Optional[List[Dict[str, Any]]], query: str = "") -> None:
async def install(self, callback: Union[CallbackQuery, ChosenInlineResult], link: str, index: int, modules: Optional[List[Dict[str, Any]]], query: str = "") -> None:
state, dependencies = await self.installer.execute(self.lookup("loader"), link)
if state == "success":
await self.answer(callback, self.strings["success"], True)
elif state == "dependency":
formatted = f"({','.join(dependencies[:5])})" if dependencies else ""
await self.answer(callback, self.strings["dependency"].format(deps=formatted), True)
elif state == "overwrite":
await self.answer(callback, self.strings["overwrite"], True)
else:
await self.answer(callback, self.strings["error"], True)
try:
if state == "success":
await self.answer(callback, self.strings["success"], True)
elif state == "dependency":
formatted = f"({','.join(dependencies[:5])})" if dependencies else ""
await self.answer(callback, self.strings["dependency"].format(deps=formatted), True)
elif state == "overwrite":
await self.answer(callback, self.strings["overwrite"], True)
else:
await self.answer(callback, self.strings["error"], True)
except Exception:
pass
@loader.inline_handler(
ru_doc="(запрос) - поиск модулей.",
@@ -899,7 +880,7 @@ class FHeta(loader.Module):
return {
"title": self.strings["prompt"],
"description": self.strings["hint"],
"message": f"{self.ui.emoji('error')} <b>{self.strings['noquery'].format(prefix=f'<code>@{self._inline_mgr.bot_username} ')}</code></b>",
"message": f"{self.ui.emoji('error')} <b>{self.strings['noquery'].format(prefix=f'<code>@{self.inline.bot_username} ')}</code></b>",
"thumb": "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/magnifying_glass.png"
}
@@ -922,13 +903,13 @@ class FHeta(loader.Module):
}
queryid = str(uuid.uuid4())[:8]
if not hasattr(self._inline_mgr, "fheta_cache"):
self._inline_mgr.fheta_cache = {}
if not hasattr(self.inline, "fheta_cache"):
self.inline.fheta_cache = {}
if len(self._inline_mgr.fheta_cache) >= 50:
self._inline_mgr.fheta_cache.pop(next(iter(self._inline_mgr.fheta_cache)))
if len(self.inline.fheta_cache) >= 50:
self.inline.fheta_cache.pop(next(iter(self.inline.fheta_cache)))
self._inline_mgr.fheta_cache[queryid] = {"query": query, "mods": modules}
self.inline.fheta_cache[queryid] = {"query": query, "mods": modules}
results = []
@@ -937,38 +918,22 @@ class FHeta(loader.Module):
if isinstance(description, dict):
description = description.get(self.strings["lang"]) or description.get("doc") or next(iter(description.values()), "")
markup = self._inline_mgr.generate_markup(self.ui.buttons(data.get("install", ""), data, index, None, query))
markup = None
try:
markup = self.inline.generate_markup(self.ui.buttons(data.get("install", ""), data, index, None, query))
except Exception:
pass
if self._is_telethon:
thumb_url = data.get("pic") or "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/empty_pic.png"
thumb = self._inline_mgr._web_document(thumb_url)
results.append(InlineQueryResultArticle(
id=f"fh_{queryid}_{index}",
title=utils.escape_html(data.get("name", "")),
description=utils.escape_html(str(description)[:250] + ("..." if len(str(description)) > 250 else "")),
thumbnail_url=data.get("pic") or "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/empty_pic.png",
input_message_content=InputTextMessageContent(message_text="", parse_mode="HTML"),
reply_markup=markup
))
results.append(
await event.builder.article(
id=f"fh_{queryid}_{index}",
title=utils.escape_html(data.get("name", "")),
description=utils.escape_html(str(description)[:250] + ("..." if len(str(description)) > 250 else "")),
thumb=thumb,
text="",
parse_mode="HTML",
buttons=markup,
link_preview=False
)
)
elif InlineQueryResultArticle is not Any:
results.append(InlineQueryResultArticle(
id=f"fh_{queryid}_{index}",
title=utils.escape_html(data.get("name", "")),
description=utils.escape_html(str(description)[:250] + ("..." if len(str(description)) > 250 else "")),
thumbnail_url=data.get("pic") or "https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/FHeta/empty_pic.png",
input_message_content=InputTextMessageContent(message_text="", parse_mode="HTML"),
reply_markup=markup
))
if self._is_telethon:
await event.answer(results, cache_time=0)
elif InlineQueryResultArticle is not Any:
await event.inline_query.answer(results, cache_time=0)
await event.inline_query.answer(results, cache_time=0)
@loader.command(
ru_doc="(запрос) - поиск модулей.",
@@ -998,7 +963,7 @@ class FHeta(loader.Module):
data = modules[0]
buttons = self.ui.buttons(data.get("install", ""), data, 0, modules, query)
form = await self._inline_mgr.form("", message, reply_markup=buttons, silent=True)
form = await self.inline.form("", message, reply_markup=buttons, silent=True)
text = self.ui.format(data, query, 1, len(modules))
await self.edit(form, text, buttons, data.get("banner"))
@@ -1010,17 +975,20 @@ class FHeta(loader.Module):
if not url.startswith("https://api.fixyres.com/module/"):
return
state, dependencies = await self.installer.execute(self.lookup("loader"), url)
try:
state, dependencies = await self.installer.execute(self.lookup("loader"), url)
if state == "success":
reply = await message.respond("")
elif state == "dependency":
reply = await message.respond(f"📋{','.join(dependencies[:5])}" if dependencies else "📋")
elif state == "overwrite":
reply = await message.respond("😨")
else:
reply = await message.respond("")
if state == "success":
reply = await message.respond("")
elif state == "dependency":
reply = await message.respond(f"📋{','.join(dependencies[:5])}" if dependencies else "📋")
elif state == "overwrite":
reply = await message.respond("😨")
else:
reply = await message.respond("")
await asyncio.sleep(1)
await reply.delete()
await message.delete()
await asyncio.sleep(1)
await reply.delete()
await message.delete()
except Exception:
pass

View File

@@ -112,13 +112,13 @@ class FSecurity(loader.Module):
loader.ConfigValue(
"strict_mode",
False,
lambda: self.strings["strict_mode_doc"],
lambda: self.strings("strict_mode_doc"),
validator=loader.validators.Boolean(),
),
loader.ConfigValue(
"nvidia_api_key",
"",
lambda: self.strings["nvidia_api_key_doc"],
lambda: self.strings("nvidia_api_key_doc"),
validator=loader.validators.Hidden(),
)
)
@@ -386,10 +386,10 @@ class FSecurity(loader.Module):
def format(self, state, reason="", link=""):
link_part = f' (<code>{utils.escape_html(link)}</code>)' if link else ""
if state == "unavailable":
return f'<b>{self.strings["unavailable"].format(link_part)}</b>\n<b>{self.strings["continue"]}</b>'
return f'<b>{self.strings("unavailable").format(link_part)}</b>\n<b>{self.strings("continue")}</b>'
if state == "suspicious":
return f'<b>{self.strings["suspicious"].format(link_part)}</b>\n<blockquote expandable><b>{reason}</b></blockquote>\n<b>{self.strings["continue"]}</b>'
return f'<b>{self.strings["blocked"].format(link_part)}</b>\n<blockquote expandable><b>{reason}</b></blockquote>'
return f'<b>{self.strings("suspicious").format(link_part)}</b>\n<blockquote expandable><b>{reason}</b></blockquote>\n<b>{self.strings("continue")}</b>'
return f'<b>{self.strings("blocked").format(link_part)}</b>\n<blockquote expandable><b>{reason}</b></blockquote>'
def buttons(self, task):
return [[

View File

@@ -8,7 +8,6 @@ __version__ = (1, 0, 0)
# meta banner: https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/SCD/banner.png
# meta developer: @NFModules
# meta fhsdesc: SoundCloud, Music, Music downloader, Downloader
# requires: curl_cffi
@@ -106,15 +105,15 @@ class SCD(loader.Module):
'''(link) - download a song from SoundCloud.'''
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, self.strings["no_args"].format(prefix=self.get_prefix()))
await utils.answer(message, self.strings("no_args").format(prefix=self.get_prefix()))
return
m = re.search(r"(https?://(?:[a-zA-Z0-9-]+\.)?soundcloud\.com/[^\s]+)", args)
if not m:
await utils.answer(message, self.strings["not_found"])
await utils.answer(message, self.strings("not_found"))
return
msg = await utils.answer(message, self.strings["downloading"])
msg = await utils.answer(message, self.strings("downloading"))
try:
async with requests.AsyncSession(impersonate="chrome120") as ses:
@@ -195,4 +194,4 @@ class SCD(loader.Module):
await msg.delete()
except:
await utils.answer(msg, self.strings["not_found"])
await utils.answer(msg, self.strings("not_found"))

View File

@@ -308,7 +308,7 @@ class Akinator(loader.Module):
loader.ConfigValue(
"child_mode",
False,
lambda: self.strings["child_mode"],
lambda: self.strings("child_mode"),
validator=loader.validators.Boolean()
)
)
@@ -339,17 +339,17 @@ class Akinator(loader.Module):
async def akinator(self, message):
'''- start the game.'''
try:
aki = AsyncAki(self.strings["lang"], self.config["child_mode"])
aki = AsyncAki(self.strings("lang"), self.config["child_mode"])
await aki.start()
self.games.setdefault(message.chat_id, {})[message.id] = aki
await self.inline.form(
text=self.strings["text"],
text=self.strings("text"),
message=message,
photo="https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/akinator/banner.png",
reply_markup={
"text": self.strings["start"],
"text": self.strings("start"),
"callback": self._cb,
"args": (message,)
}
@@ -369,12 +369,12 @@ class Akinator(loader.Module):
question = aki.q
markup = [[
{"text": self.strings["yes"], "callback": self._ans, "args": (0, message)},
{"text": self.strings["no"], "callback": self._ans, "args": (1, message)},
{"text": self.strings["idk"], "callback": self._ans, "args": (2, message)}
{"text": self.strings("yes"), "callback": self._ans, "args": (0, message)},
{"text": self.strings("no"), "callback": self._ans, "args": (1, message)},
{"text": self.strings("idk"), "callback": self._ans, "args": (2, message)}
],[
{"text": self.strings["probably"], "callback": self._ans, "args": (3, message)},
{"text": self.strings["probably_not"], "callback": self._ans, "args": (4, message)}
{"text": self.strings("probably"), "callback": self._ans, "args": (3, message)},
{"text": self.strings("probably_not"), "callback": self._ans, "args": (4, message)}
]
]
@@ -393,13 +393,13 @@ class Akinator(loader.Module):
desc = aki.desc
if desc:
text = self.strings["this_is"].format(name=name, description=desc)
text = self.strings("this_is").format(name=name, description=desc)
else:
text = self.strings["this_is_no_desc"].format(name=name)
text = self.strings("this_is_no_desc").format(name=name)
markup = [[
{"text": self.strings["yes"], "callback": self._fin, "args": (True, message, text, aki.photo)},
{"text": self.strings["not_right"], "callback": self._rej, "args": (message,)}
{"text": self.strings("yes"), "callback": self._fin, "args": (True, message, text, aki.photo)},
{"text": self.strings("not_right"), "callback": self._rej, "args": (message,)}
]
]
@@ -450,7 +450,7 @@ class Akinator(loader.Module):
await call.edit(text, photo=photo, reply_markup=[])
else:
await call.edit(
self.strings["failed"],
self.strings("failed"),
photo="https://raw.githubusercontent.com/Fixyres/FModules/refs/heads/main/assets/akinator/idk.png",
reply_markup=[]
)
)

View File

@@ -2,4 +2,3 @@ akinator
FHeta
BSR
SCD
LFSecurity

141
Limoka.py
View File

@@ -1,5 +1,5 @@
# meta developer: @limokanews
# requires: whoosh cryptography filetype
# requires: whoosh cryptography
from collections import Counter, defaultdict
@@ -24,20 +24,21 @@ from telethon.errors.rpcerrorlist import WebpageMediaEmptyError
from telethon import TelegramClient
from telethon.errors.rpcerrorlist import YouBlockedUserError
from telethon import functions
import filetype
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
import ast
from aiogram.exceptions import TelegramBadRequest as BadRequest
try:
from aiogram.utils.exceptions import BadRequest
except ImportError:
from aiogram.exceptions import TelegramBadRequest as BadRequest
from .. import utils, loader
from ..types import BotInlineCall, InlineCall
logger = logging.getLogger("Limoka")
__version__ = (1, 5, 6)
__version__ = (1, 5, 1)
def _parse_version_from_source(source: str):
@@ -488,12 +489,8 @@ class Limoka(loader.Module):
"New Limoka Version {version} already available. Please update for better performance, bug fixes, and new features.\n"
"Press the button below to update the module."
),
"no_updates_available": "<blockquote>❌ No updates available. You are using the latest version of Limoka.</blockquote>",
"module_update_available": "<blockquote>🔔 Notification about module update has been sent, check @{bot}.</blockquote>",
"index_update_started": "<blockquote>🔄 Limoka module index update has started. This may take a few minutes. Please wait...</blockquote>",
"index_update_failed": "<blockquote>❌ Failed to update Limoka module index. Please try again later. If the error persists, report to developers</blockquote>",
"index_update_success": "<blockquote>✅ Limoka module index updated successfully!</blockquote>",
"update_check_started": "<blockquote>🔍 Checking for Limoka updates...</blockquote>",
"no_updates_available": "No updates available. You are using the latest version of Limoka.",
"module_update_available": "Notification about module update has been sent, check @{bot}.",
}
strings_ru = {
"name": "Limoka",
@@ -615,12 +612,8 @@ class Limoka(loader.Module):
"Новая версия Limoka {version} уже доступна. Пожалуйста, обновитесь для лучшей производительности, исправления багов и новых функций.\n"
"Нажмите кнопку ниже, чтобы обновить модуль."
),
"no_updates_available": "<blockquote>❌ Нет доступных обновлений. У вас установлена последняя версия Limoka.</blockquote>",
"module_update_available": "<blockquote>🔔 Уведомление об обновлении модуля было отправлено, проверьте @{bot}.</blockquote>",
"index_update_started": "<blockquote>🔄 Обновление индекса модулей Limoka началось. Это может занять несколько минут. Пожалуйста, подождите...</blockquote>",
"index_update_failed": "<blockquote>❌ Не удалось обновить индекс модулей Limoka. Пожалуйста, попробуйте снова позже. Если ошибка сохраняется, сообщите разработчикам</blockquote>",
"index_update_success": "<blockquote>✅ Индекс модулей Limoka успешно обновлен!</blockquote>",
"update_check_started": "<blockquote>🔍 Проверка обновлений Limoka...</blockquote>",
"no_updates_available": "Нет доступных обновлений. У вас установлена последняя версия Limoka.",
"module_update_available": "Уведомление об обновлении модуля было отправлено, проверьте @{bot}.",
"_cls_doc": "Модули теперь в одном месте с простым и удобным поиском!",
}
@@ -805,7 +798,7 @@ class Limoka(loader.Module):
@loader.loop(interval=3600)
async def _update_modules_loop(self):
"""Periodically update modules list and rebuild index."""
await self.api.fetch_json(self._base_url, "modules.json")
raw_modules = await self.api.fetch_json(self._base_url, "modules.json")
self.modules = self.repository.apply_newbie_filter(
self.config.get("filter_newbies_modules", False)
)
@@ -846,85 +839,21 @@ class Limoka(loader.Module):
logger.error(f"Skipping unsafe rmtree for {folder}")
async def _validate_url(self, url: str) -> Optional[str]:
logger.debug(f"_validate_url called with: {url}")
if not url:
logger.warning("_validate_url: URL is empty, returning None")
if not url or url in self._invalid_banners:
return None
if url in self._invalid_banners:
logger.debug(f"_validate_url: URL already in invalid_banners: {url}, returning None")
return None
# Headers to mimic a browser request
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
try:
logger.debug(f"_validate_url: Starting validation for {url}")
async with aiohttp.ClientSession() as session:
ct = None
response_status = None
# Try HEAD first (more efficient)
try:
logger.debug(f"_validate_url: Attempting HEAD request for {url}")
async with session.head(
url, timeout=5, allow_redirects=True, headers=headers
) as response:
response_status = response.status
logger.debug(f"_validate_url: HEAD request returned status {response.status} for {url}")
if response.status == 200:
ct = response.headers.get("Content-Type", "").lower()
logger.debug(f"_validate_url: Content-Type from HEAD: '{ct}' for {url}")
except (aiohttp.ClientError, asyncio.TimeoutError) as head_error:
logger.debug(f"_validate_url: HEAD failed ({type(head_error).__name__}), will try GET for {url}")
def _is_supported_media(content_type: str) -> bool:
return content_type.startswith("image/") or content_type.startswith("video/mp4")
# If HEAD didn't work or returned non-200, try GET
if ct is None:
max_retries = 2
for attempt in range(max_retries):
try:
async with session.get(
url, timeout=10, headers=headers, allow_redirects=True
) as response:
if response.status != 200:
self._invalid_banners.add(url)
return None
ct = response.headers.get("Content-Type", "").lower()
# Try to get MIME if Content-Type is missing
if not ct:
try:
data = await response.content.read(2048)
mime = filetype.guess_mime(data, mime=True)
if mime and _is_supported_media(mime):
return url
else:
self._invalid_banners.add(url)
return None
except Exception as mime_error:
logger.error(f"_validate_url: Error reading content for MIME detection: {mime_error}")
break # Success, exit retry loop
except (aiohttp.ClientError, asyncio.TimeoutError) as get_error:
if attempt < max_retries - 1:
await asyncio.sleep(1) # Wait before retry
else:
self._invalid_banners.add(url)
return None
# Check Content-Type from successful request
if ct and _is_supported_media(ct):
async with session.head(
url, timeout=5, allow_redirects=True
) as response:
if response.status != 200:
self._invalid_banners.add(url)
return None
ct = response.headers.get("Content-Type", "").lower()
if not ct.startswith("image/"):
self._invalid_banners.add(url)
return None
return url
elif ct:
self._invalid_banners.add(url)
return None
else:
self._invalid_banners.add(url)
return None
except Exception as e:
if url:
self._invalid_banners.add(url)
@@ -989,6 +918,8 @@ class Limoka(loader.Module):
def _build_navigation_markup(self, session: Dict[str, Any]) -> list:
result = session["results"]
index = session["current_index"]
query = session["query"]
filters = session["filters"]
page = index + 1
markup = [
@@ -1042,6 +973,8 @@ class Limoka(loader.Module):
) -> list:
result = session["results"]
index = session["current_index"]
query = session["query"]
filters = session["filters"]
markup = []
if len(body_pages) > 1:
@@ -1419,7 +1352,6 @@ class Limoka(loader.Module):
result = searcher.search()
except Exception:
await call.edit(self.strings["?"], reply_markup=[])
return
if not result:
markup = (
@@ -1773,8 +1705,7 @@ class Limoka(loader.Module):
)
try:
result = SearchIndex(args.lower(), self.ix).search()
except Exception as e:
logger.exception(f"Error occurred while searching: {e}")
except Exception:
return await utils.answer(message, self.strings["?"])
if not result:
return await utils.answer(message, self.strings["404"].format(query=args))
@@ -1793,23 +1724,9 @@ class Limoka(loader.Module):
message, module_info, module_path, display_session, 0
)
@loader.command(ru_doc="— Обновить индекс ")
async def updateindex(self, message: Message):
"""— Update search index"""
await utils.answer(message, self.strings["index_update_started"])
try:
await self._update_index()
except Exception as e:
logger.exception(f"Error updating index: {e}")
await utils.answer(message, self.strings["index_update_failed"])
else:
await utils.answer(message, self.strings["index_update_success"])
@loader.command(ru_doc="— Проверить наличие обновлений модуля")
@loader.command(ru_doc="Проверить наличие обновлений модуля")
async def limokaupdatecmd(self, message: Message):
"""Check for module updates"""
await utils.answer(message, self.strings["checking_for_updates"])
"""Check for module updates"""
is_update_available = await self.check_for_module_update()
if is_update_available:
await utils.answer(message, self.strings["module_update_available"].format(bot=self._self_bot_username))

View File

@@ -523,10 +523,6 @@ class Limoka(loader.Module):
async def _validate_url(self, url: str) -> Optional[str]:
if not url or url in self._invalid_banners:
return None
def _is_supported_media(content_type: str) -> bool:
return content_type.startswith("image/") or content_type.startswith("video/mp4")
try:
async with aiohttp.ClientSession() as session:
async with session.head(
@@ -536,7 +532,7 @@ class Limoka(loader.Module):
self._invalid_banners.add(url)
return None
ct = response.headers.get("Content-Type", "").lower()
if not _is_supported_media(ct):
if not ct.startswith("image/"):
self._invalid_banners.add(url)
return None
return url
@@ -764,7 +760,7 @@ class Limoka(loader.Module):
),
},
{
"text": f"{self.strings['body_page']} {page_body + 1}/{len(body_pages)}",
"text": f"{self.strings["body_page"]} {page_body + 1}/{len(body_pages)}",
"callback": self._inline_void,
},
{

View File

@@ -1,157 +0,0 @@
# Midga3
# I AM NOT AFFICIATED WITH WORDLE
# meta developer: @midga3_modules
import requests
import random
import logging
from .. import loader, utils
from herokutl.tl.types import Message
__verison__ = (0, 1, 1)
logger = logging.getLogger(__name__)
@loader.tds
class wordle(loader.Module):
"""Wordle!"""
strings = {
"name": "Wordle",
"loading": "Loading...",
"language": "Language of the wordle",
"have_a_good_game": "Have a good game! Try to guess the 5 letter word in {}",
"attempts_left": "WRONG! Attempts left: {}",
"gg": "GG! YOU DIDN'T GUESS THE WORD {}",
"win": "GG! YOU WON! THE WORD WAS {}",
"already_playing": "ALREADY PLAYING! type .stopwordle to stop the current game",
"length": "Must be 5 letters",
"no_game": "No game is currently running",
"ok": "Game stopped",
"ad": "I tried to Guess word {}. Check out my result:\n{}",
"real_word": "This word is not in the word list"
}
strings_ru ={
"name": "Wordle",
"loading": "Загрузка...",
"language": "Язык вордла",
"have_a_good_game": "Хорошей игры! Попытайтесь угадать слово из 5 букв на {}",
"attempts_left": "НВЕВЕРНО! Осталось попыток: {}",
"gg": "ГГ! ВЫ НЕ УГАДАЛИ СЛОВО {}",
"win": "ГГ! ВЫ ВЫИГРАЛИ! СЛОВО БЫЛО {}",
"already_playing": "УЖЕ ИГРАЕТЕ! напишите .stopwordle чтобы остановить текущую игру",
"length": "Должно содержать 5 букв",
"no_game": "Сейчас нет активной игры",
"ok": "Игра остановлена",
"ad": "Я попытался угадать слово {}. Чекайте мой результат:\n{}",
"real_word": "Такого слова нет в списке слов"
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"language",
"en",
self.strings["language"],
validator=loader.validators.Choice(["en", "ru"])
),
)
async def handler(self, call, data):
guess = data.upper()
word = self._db.get("wordle", "word", "")
attempts = self._db.get("wordle", "attempts", 0)
buttons = self._db.get("wordle", "buttons", [])
markup = buttons + [[{"text":"Введите слово","input":self.strings("length"),"handler": self.handler}]]
if len(guess) != 5:
await call.edit(self.strings("length"), reply_markup=markup)
return
if guess not in self._db.get("wordle", "words", []):
await call.edit(self.strings("real_word"), reply_markup=markup)
return
buttons2 = []
for i in range(5):
if guess[i] == word[i]:
buttons2.append({"text": guess[i], "data": "custom/data", "style": "success"})
elif guess[i] in word:
buttons2.append({"text": guess[i], "data": "custom/data", "style": "primary"})
else:
buttons2.append({"text": guess[i], "data": "custom/data", "style": "danger"})
buttons.append(buttons2)
if guess == word:
self._db.set("wordle", "buttons", buttons)
self._db.set("wordle", "now_playing", False)
result = ""
for btn in buttons:
for b in btn:
if b["style"] == "success":
result += "🟩"
elif b["style"] == "primary":
result += "🟨"
else:
result += ""
result += "\n"
buttons.append([{"text":"Поделится резултатом","copy":self.strings("ad").format(word, result)}])
await call.edit(f"{self.strings('win').format(word)}", reply_markup=buttons)
return
self._db.set("wordle", "buttons", buttons)
attempts -= 1
self._db.set("wordle", "attempts", attempts)
if attempts == 0:
result = ""
for btn in buttons:
for b in btn:
if b["style"] == "success":
result += "🟩"
elif b["style"] == "primary":
result += "🟨"
else:
result += ""
result += "\n"
buttons.append([{"text":"Поделится резултатом","copy":self.strings("ad").format(word, result)}])
await call.edit(f"{self.strings('gg').format(word)}", reply_markup=buttons)
self._db.set("wordle", "now_playing", False)
else:
await call.edit(f"{self.strings('attempts_left').format(attempts)}", reply_markup=markup)
@loader.command()
async def wordle(self, message: Message):
"""Play wordle!"""
await utils.answer(message, self.strings("loading"))
if self._db.get("wordle", "now_playing", False):
await utils.answer(message, self.strings("already_playing"))
return
args = utils.get_args(message)
if args and args[0].lower():
if args[0].lower() == "--no-sec":
self._db.set("wordle", "nosec", True)
return
else:
self._db.set("wordle", "nosec", False)
try:
response = requests.get(f"https://raw.githubusercontent.com/mimimishka449/Worlde/refs/heads/main/words_{self.config['language']}.txt")
if response.status_code == 200:
words = response.text.splitlines()
word = random.choice(words).upper()
self._db.set("wordle", "now_playing", True)
self._db.set("wordle", "attempts", 6)
self._db.set("wordle", "word", word)
self._db.set("wordle", "words", words)
self._db.set("wordle", "buttons", [])
await self.inline.form(self.strings("have_a_good_game").format("английском" if self.config['language'] == "en" else "русском"), message, reply_markup=[[{"text":"Введите слово","input":self.strings("length"),"handler": self.handler}]])
else:
await utils.answer(message, "Error fetching wordle data.")
except Exception as e:
logger.exception(f"Error: {e}")
await utils.answer(message, "An error occurred while fetching wordle data.")
@loader.command()
async def stopwordle(self, message: Message):
"""Stop the wordle game."""
if not self._db.get("wordle", "now_playing", False):
await utils.answer(message, self.strings("no_game"))
return
self._db.set("wordle", "now_playing", False)
await utils.answer(message, self.strings("ok"))

View File

@@ -1,122 +1,150 @@
# meta developer: @matubuntu
import time
#meta developer: @matubuntu
import requests, bs4
from datetime import datetime
import aiohttp
from .. import loader, utils
import lxml
# requires: lxml requests bs4
_FLAGS = {
"AUD": "🇦🇺", "AZN": "🇦🇿", "GBP": "🇬🇧", "AMD": "🇦🇲",
"BYN": "🇧🇾", "BGN": "🇧🇬", "BRL": "🇧🇷", "HUF": "🇭🇺",
"VND": "🇻🇳", "HKD": "🇭🇰", "GEL": "🇬🇪", "DKK": "🇩🇰",
"AED": "🇦🇪", "USD": "🇺🇸", "EUR": "🇪🇺", "EGP": "🇪🇬",
"INR": "🇮🇳", "IDR": "🇮🇩", "KZT": "🇰🇿", "CAD": "🇨🇦",
"QAR": "🇶🇦", "KGS": "🇰🇬", "CNY": "🇨🇳", "MDL": "🇲🇩",
"NZD": "🇳🇿", "NOK": "🇳🇴", "PLN": "🇵🇱", "RON": "🇷🇴",
"SGD": "🇸🇬", "TJS": "🇹🇯", "THB": "🇹🇭", "TRY": "🇹🇷",
"TMT": "🇹🇲", "UZS": "🇺🇿", "UAH": "🇺🇦", "CZK": "🇨🇿",
"SEK": "🇸🇪", "CHF": "🇨🇭", "RSD": "🇷🇸", "ZAR": "🇿🇦",
"KRW": "🇰🇷", "JPY": "🇯🇵",
"AUD": "🇦🇺",
"AZN": "🇦🇿",
"GBP": "🇬🇧",
"AMD": "🇦🇲",
"BYN": "🇧🇾",
"BGN": "🇧🇬",
"BRL": "🇧🇷",
"HUF": "🇭🇺",
"VND": "🇻🇳",
"HKD": "🇭🇰",
"GEL": "🇬🇪",
"DKK": "🇩🇰",
"AED": "🇦🇪",
"USD": "🇺🇸",
"EUR": "🇪🇺",
"EGP": "🇪🇬",
"INR": "🇮🇳",
"IDR": "🇮🇩",
"KZT": "🇰🇿",
"CAD": "🇨🇦",
"QAR": "🇶🇦",
"KGS": "🇰🇬",
"CNY": "🇨🇳",
"MDL": "🇲🇩",
"NZD": "🇳🇿",
"NOK": "🇳🇴",
"PLN": "🇵🇱",
"RON": "🇷🇴",
"SGD": "🇸🇬",
"TJS": "🇹🇯",
"THB": "🇹🇭",
"TRY": "🇹🇷",
"TMT": "🇹🇲",
"UZS": "🇺🇿",
"UAH": "🇺🇦",
"CZK": "🇨🇿",
"SEK": "🇸🇪",
"CHF": "🇨🇭",
"RSD": "🇷🇸",
"ZAR": "🇿🇦",
"KRW": "🇰🇷",
"JPY": "🇯🇵",
}
_CRYPTO_EMOJIS = {
"BTC": "<emoji document_id=5289519973285257969>💰</emoji>",
"ETH": "<emoji document_id=5287735049301550386>💰</emoji>",
"SOL": "<emoji document_id=5251712673258697260>💰</emoji>",
"TON": "<emoji document_id=5289648693455119919>💰</emoji>",
"USDT": "<emoji document_id=5289904548951911168>💰</emoji>",
"XRP": "<emoji document_id=5373312921214401986>💰</emoji>",
"USDC": "<emoji document_id=5372958453268497353>💰</emoji>",
"ADA": "<emoji document_id=5373076801092338046>💰</emoji>",
"DOGE": "<emoji document_id=5375192042420842380>💰</emoji>",
"TRX": "<emoji document_id=5375187081733616165>💰</emoji>",
"AVAX": "<emoji document_id=5375311275007947936>💰</emoji>",
"LTC": "<emoji document_id=5373035462032113888>💰</emoji>",
"BCH": "<emoji document_id=5375596920397903962>💰</emoji>",
"ATOM": "<emoji document_id=5375468745688889977>💰</emoji>",
"XLM": "<emoji document_id=5372823290647690288>💰</emoji>",
"SHIB": "<emoji document_id=5375231036428924778>💰</emoji>",
"UNI": "<emoji document_id=5372953110329180525>💰</emoji>",
"XMR": "<emoji document_id=5375507073977038661>💰</emoji>",
"LINK": "<emoji document_id=5375149651093633217>💰</emoji>",
"ETC": "<emoji document_id=5375543306321146693>💰</emoji>",
"SUI": "<emoji document_id=5391002164929772708>💰</emoji>",
"NEAR": "<emoji document_id=5391181990915487346>💰</emoji>",
"VET": "<emoji document_id=5391091302681033446>💰</emoji>",
"FIL": "<emoji document_id=5373117173784919811>💰</emoji>",
"XTZ": "<emoji document_id=5390985478981829698>💰</emoji>",
"ALGO": "<emoji document_id=5391337713544738420>💰</emoji>",
"BTC": "<emoji document_id=5289519973285257969>💰</emoji>",
"ETH": "<emoji document_id=5287735049301550386>💰</emoji>",
"SOL": "<emoji document_id=5251712673258697260>💰</emoji>",
"TON": "<emoji document_id=5289648693455119919>💰</emoji>",
"USDT": "<emoji document_id=5289904548951911168>💰</emoji>",
"XRP": "<emoji document_id=5373312921214401986>💰</emoji>",
"USDC": "<emoji document_id=5372958453268497353>💰</emoji>",
"ADA": "<emoji document_id=5373076801092338046>💰</emoji>",
"DOGE": "<emoji document_id=5375192042420842380>💰</emoji>",
"TRX": "<emoji document_id=5375187081733616165>💰</emoji>",
"AVAX": "<emoji document_id=5375311275007947936>💰</emoji>",
"LTC": "<emoji document_id=5373035462032113888>💰</emoji>",
"BCH": "<emoji document_id=5375596920397903962>💰</emoji>",
"ATOM": "<emoji document_id=5375468745688889977>💰</emoji>",
"XLM": "<emoji document_id=5372823290647690288>💰</emoji>",
"SHIB": "<emoji document_id=5375231036428924778>💰</emoji>",
"UNI": "<emoji document_id=5372953110329180525>💰</emoji>",
"XMR": "<emoji document_id=5375507073977038661>💰</emoji>",
"LINK": "<emoji document_id=5375149651093633217>💰</emoji>",
"ETC": "<emoji document_id=5375543306321146693>💰</emoji>",
"SUI": "<emoji document_id=5391002164929772708>💰</emoji>",
"NEAR": "<emoji document_id=5391181990915487346>💰</emoji>",
"VET": "<emoji document_id=5391091302681033446>💰</emoji>",
"FIL": "<emoji document_id=5373117173784919811>💰</emoji>",
"XTZ": "<emoji document_id=5390985478981829698>💰</emoji>",
"ALGO": "<emoji document_id=5391337713544738420>💰</emoji>",
"THETA": "<emoji document_id=5391256014676833736>💰</emoji>",
"FTM": "<emoji document_id=5393179395521263785>💰</emoji>",
"XDAI": "<emoji document_id=5391325992578988886>💰</emoji>",
"RUNE": "<emoji document_id=5391347570494684983>💰</emoji>",
"DOT": "<emoji document_id=5375224568208177973>💰</emoji>",
"FTM": "<emoji document_id=5393179395521263785>💰</emoji>",
"XDAI": "<emoji document_id=5391325992578988886>💰</emoji>",
"RUNE": "<emoji document_id=5391347570494684983>💰</emoji>",
"DOT": "<emoji document_id=5375224568208177973>💰</emoji>",
}
_CRYPTO_NAMES = {
"BTC": "Bitcoin", "ETH": "Ethereum", "XMR": "Monero",
"LTC": "Litecoin", "XRP": "XRP", "ADA": "Cardano",
"DOGE": "Dogecoin", "SOL": "Solana", "DOT": "Polkadot",
"USDT": "Tether", "TON": "Toncoin", "USDC": "USD Coin",
"TRX": "TRON", "AVAX": "Avalanche", "BCH": "Bitcoin Cash",
"ATOM": "Cosmos", "XLM": "Stellar", "SHIB": "Shiba Inu",
"UNI": "Uniswap", "LINK": "Chainlink", "ETC": "Ethereum Classic",
"SUI": "Sui", "NEAR": "NEAR Protocol", "VET": "VeChain",
"FIL": "Filecoin", "XTZ": "Tezos", "ALGO": "Algorand",
"THETA": "Theta Network", "FTM": "Fantom", "XDAI": "xDai",
_CRYPTO_LIST = {
"BTC": "Bitcoin",
"ETH": "Ethereum",
"XMR": "Monero",
"LTC": "Litecoin",
"XRP": "XRP",
"ADA": "Cardano",
"DOGE": "Dogecoin",
"SOL": "Solana",
"DOT": "Polkadot",
"USDT": "Tether",
"TON": "Toncoin",
"USDC": "USD Coin",
"TRX": "TRON",
"AVAX": "Avalanche",
"BCH": "Bitcoin Cash",
"ATOM": "Cosmos",
"XLM": "Stellar",
"SHIB": "Shiba Inu",
"UNI": "Uniswap",
"LINK": "Chainlink",
"ETC": "Ethereum Classic",
"SUI": "Sui",
"NEAR": "NEAR Protocol",
"VET": "VeChain",
"FIL": "Filecoin",
"XTZ": "Tezos",
"ALGO": "Algorand",
"THETA": "Theta Network",
"FTM": "Fantom",
"XDAI": "xDai",
"RUNE": "THORChain",
}
_CBR_URL = "https://www.cbr.ru/scripts/XML_daily.asp"
_CRYPTO_URL = "https://api.coinlore.net/api/tickers/?limit=100"
CACHE_TTL = 300 # seconds
def _fmt_num(value: float, decimals: int = 3) -> str:
if decimals == 0:
return f"{int(value):,}".replace(",", " ")
rounded = round(value, decimals)
int_part = int(rounded)
dec_part = str(rounded - int_part)[2:2 + decimals].rstrip("0")
int_str = f"{int_part:,}".replace(",", " ")
return f"{int_str},{dec_part}" if dec_part else int_str
def _parse_cbr_xml(xml_bytes: bytes) -> tuple[str | None, dict]:
"""Parse CBR XML without bs4/lxml — pure stdlib ElementTree."""
import xml.etree.ElementTree as ET
root = ET.fromstring(xml_bytes)
date_str = root.attrib.get("Date", "")
try:
date = datetime.strptime(date_str, "%d.%m.%Y").strftime("%d.%m.%Y")
except ValueError:
date = date_str
rates: dict[str, dict] = {}
for valute in root.findall("Valute"):
code = valute.findtext("CharCode", "").strip()
if not code or code == "XDR":
continue
try:
nominal = float(valute.findtext("Nominal", "1").replace(",", "."))
value = float(valute.findtext("Value", "0").replace(",", "."))
except ValueError:
continue
rates[code] = {
"name": valute.findtext("Name", code).strip(),
"nominal": nominal,
"rub": value / nominal,
}
return date, rates
def _fmt_num(v, d=3):
p = f"{v:,.{d}f}".replace(",", " ").split(".")
i = p[0]
d = p[1].rstrip("0") if len(p) > 1 else ""
return f"{i},{d}" if d else i
@loader.tds
class FinanceMod(loader.Module):
"""Курсы валют (ЦБ РФ) и криптовалют (CoinLore)"""
strings = {"name": "FinanceMod"}
strings = {
"name": "FinanceMod",
"valute_description": "<кол-во> <код> - курс валюты\n<кол-во> - список",
"valute_no_args": (
"💵 <b>Курс валюты с сайта </b><a href='https://www.cbr.ru/'>ЦБ(РФ)</a>\n"
"<b>Актуально на</b> <i>{}</i>\n\n<blockquote expandable>{}</blockquote>"
),
"valute_specific": (
"💵 <b>Курс валюты с сайта </b><a href='https://www.cbr.ru/'>ЦБ(РФ)</a>\n"
"<b>Актуально на</b> <i>{}</i>\n\n{}"
),
"valute_not_found": "🚫 Валюта {} не найдена",
"crypto_description": "<кол-во> <код> - курс крипты\n<кол-во> - список",
"crypto_no_args": "💎 <b>Курсы криптовалют</b>\n\n<blockquote expandable>{}</blockquote>",
"crypto_specific": "💎 <b>Курс криптовалюты</b>\n\n{}",
"crypto_not_found": "🚫 Криптовалюта {} не найдена",
"error": "🚫 Ошибка получения данных",
}
def __init__(self):
self.config = loader.ModuleConfig(
@@ -124,194 +152,149 @@ class FinanceMod(loader.Module):
"crypto_currency",
"USD",
lambda: "Валюта для отображения крипты (USD, RUB, EUR)",
validator=loader.validators.Choice(["USD", "RUB", "EUR"]),
validator=loader.validators.Choice(["USD", "RUB", "EUR"])
)
)
# Simple in-process cache
self._cbr_cache: tuple[float, str, dict] | None = None # (ts, date, rates)
self._crypto_cache: tuple[float, list] | None = None # (ts, data)
# ──────────────────────────── HTTP helpers ────────────────────────────
async def _fetch(self, url: str, *, as_json: bool = False):
async with aiohttp.ClientSession() as session:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp:
resp.raise_for_status()
return await resp.json() if as_json else await resp.read()
# ──────────────────────────── CBR data ────────────────────────────────
async def _cbr_data(self) -> tuple[str | None, dict]:
now = time.monotonic()
if self._cbr_cache and now - self._cbr_cache[0] < CACHE_TTL:
return self._cbr_cache[1], self._cbr_cache[2]
async def _get_curr_data(self):
try:
raw = await self._fetch(_CBR_URL)
date, rates = _parse_cbr_xml(raw)
self._cbr_cache = (now, date, rates)
return date, rates
except Exception:
if self._cbr_cache:
return self._cbr_cache[1], self._cbr_cache[2]
return None, {}
r = requests.get("https://www.cbr.ru/scripts/XML_daily.asp")
s = bs4.BeautifulSoup(r.content, 'xml')
d = datetime.strptime(s.ValCurs['Date'], "%d.%m.%Y").strftime("%d.%m.%Y")
return d, s.find_all('Valute')
except:
return None, None
# ──────────────────────────── Crypto data ─────────────────────────────
async def _crypto_data(self) -> list:
now = time.monotonic()
if self._crypto_cache and now - self._crypto_cache[0] < CACHE_TTL:
return self._crypto_cache[1]
async def _get_rates(self):
try:
js = await self._fetch(_CRYPTO_URL, as_json=True)
data = js.get("data", [])
self._crypto_cache = (now, data)
return data
except Exception:
return self._crypto_cache[1] if self._crypto_cache else []
r = requests.get("https://www.cbr.ru/scripts/XML_daily.asp")
s = bs4.BeautifulSoup(r.content, 'xml')
rt = {'USD': None, 'EUR': None}
for v in s.find_all('Valute'):
if v.CharCode.text in ['USD', 'EUR']:
n = float(v.Nominal.text.replace(',', '.'))
vl = float(v.Value.text.replace(',', '.'))
rt[v.CharCode.text] = vl / n
if rt['USD'] and rt['EUR']:
rt['EUR_USD'] = rt['USD'] / rt['EUR']
else:
rt['EUR_USD'] = None
return rt
except:
return None
# ──────────────────────────── Formatters ──────────────────────────────
async def _fmt_curr(self, v, a=1):
if v.CharCode.text == "XDR":
return None
c = v.CharCode.text
n = v.Name.text
v = float(v.Value.text.replace(',', '.')) / float(v.Nominal.text.replace(',', '.'))
t = v * a
ts = _fmt_num(t, 3)
return f"{_FLAGS.get(c, '🏳')} [{a}] {n} ({c}) - {ts} руб."
def _fmt_valute(self, code: str, info: dict, amount: float = 1.0) -> str:
total = info["rub"] * amount
flag = _FLAGS.get(code, "🏳")
return f"{flag} [{_fmt_num(amount, 0)}] {info['name']} ({code}) — {_fmt_num(total, 3)}"
def _fmt_crypto(self, coin: dict, rates: dict, amount: float = 1.0) -> str:
symbol = coin["symbol"].upper()
async def _get_crypto(self):
try:
price_usd = float(coin["price_usd"])
except (KeyError, ValueError, TypeError):
return ""
return requests.get("https://api.coinlore.net/api/tickers/").json().get('data', [])
except:
return None
currency = self.config["crypto_currency"]
if currency == "RUB":
usd_rate = rates.get("USD", {}).get("rub")
if not usd_rate:
return ""
price = price_usd * usd_rate
sign = ""
elif currency == "EUR":
usd_rate = rates.get("USD", {}).get("rub")
eur_rate = rates.get("EUR", {}).get("rub")
if not usd_rate or not eur_rate:
return ""
price = price_usd * (usd_rate / eur_rate)
sign = ""
else:
price = price_usd
sign = "$"
async def _fmt_crypto(self, c, a=1):
r = await self._get_rates()
if not r:
return "🚫 Ошибка получения курсов валют"
cr = self.config["crypto_currency"]
try:
p = float(c['price_usd'])
except:
return "🚫 Ошибка данных криптовалюты"
if cr == "RUB":
if not r['USD']:
return "🚫 Курс USD не найден"
p *= r['USD']
elif cr == "EUR":
if not r['EUR_USD']:
return "🚫 Курс EUR/USD не рассчитан"
p *= r['EUR_USD']
t = p * a
ts = _fmt_num(t)
s = c['symbol'].upper()
e = _CRYPTO_EMOJIS.get(s, "💠")
n = _CRYPTO_LIST.get(s, c['name'])
cs = {"USD": "$", "RUB": "", "EUR": ""}.get(cr, "$")
return f"{e} [{a}] {n} ({s}) - {ts}{cs}"
total = price * amount
emoji = _CRYPTO_EMOJIS.get(symbol, "💠")
name = _CRYPTO_NAMES.get(symbol, coin.get("name", symbol))
return f"{emoji} [{_fmt_num(amount, 0)}] {name} ({symbol}) — {_fmt_num(total, 3)}{sign}"
# ──────────────────────────── Commands ────────────────────────────────
@loader.command(ru_doc="[кол-во] [код] — курс валюты по ЦБ РФ")
async def valutecmd(self, message):
"""[amount] [code] — exchange rates from CBR"""
args = utils.get_args(message)
date, rates = await self._cbr_data()
if not rates:
return await utils.answer(message, "🚫 Не удалось получить данные ЦБ РФ")
header = (
f"💵 <b>Курс валюты</b> · <a href='https://www.cbr.ru/'>ЦБ РФ</a>\n"
f"<b>Актуально на</b> <i>{date}</i>\n\n"
)
# .valute — список всех, кол-во = 1
if not args:
lines = [self._fmt_valute(c, i) for c, i in rates.items()]
return await utils.answer(
message,
header + f"<blockquote expandable>{chr(10).join(lines)}</blockquote>",
)
# Первый аргумент: число или код валюты?
amount = 1.0
code = None
arg0 = args[0].upper()
if len(args) >= 2:
# .valute 100 USD
@loader.command()
async def valutecmd(self, m):
"""[count] [usd, eur, ...]"""
a = utils.get_args(m)
d, v = await self._get_curr_data()
if not d or not v:
return await utils.answer(m, self.strings["error"])
if len(a) == 0:
l = []
for x in v:
if (n := await self._fmt_curr(x)):
l.append(n)
await utils.answer(m, self.strings["valute_no_args"].format(d, "\n".join(l)))
elif len(a) == 1:
try:
amount = float(args[0].replace(",", "."))
except ValueError:
return await utils.answer(message, "🚫 Некорректное число")
code = args[1].upper()
else:
# .valute USD или .valute 100
am = float(a[0])
l = []
for x in v:
if (n := await self._fmt_curr(x, am)):
l.append(n)
await utils.answer(m, self.strings["valute_no_args"].format(d, "\n".join(l)))
except:
await utils.answer(m, "🚫 Некорректное число")
elif len(a) == 2:
try:
amount = float(arg0.replace(",", "."))
# число без кода — список с умножением
except ValueError:
code = arg0
am = float(a[0])
c = a[1].upper()
for x in v:
if x.CharCode.text == c:
if (n := await self._fmt_curr(x, am)):
return await utils.answer(m, self.strings["valute_specific"].format(d, n))
await utils.answer(m, self.strings["valute_not_found"].format(c))
except:
await utils.answer(m, "🚫 Некорректное число")
if code:
if code not in rates:
return await utils.answer(message, f"🚫 Валюта <b>{code}</b> не найдена")
line = self._fmt_valute(code, rates[code], amount)
return await utils.answer(message, header + line)
# список с кол-вом
lines = [self._fmt_valute(c, i, amount) for c, i in rates.items()]
await utils.answer(
message,
header + f"<blockquote expandable>{chr(10).join(lines)}</blockquote>",
)
@loader.command(ru_doc="[кол-во] [код] — курс крипты")
async def cryptocmd(self, message):
"""[amount] [symbol] — crypto rates from CoinLore"""
args = utils.get_args(message)
coins = await self._crypto_data()
_, rates = await self._cbr_data()
if not coins:
return await utils.answer(message, "🚫 Не удалось получить данные крипты")
header = f"💎 <b>Курсы криптовалют</b> · <i>{self.config['crypto_currency']}</i>\n\n"
amount = 1.0
symbol = None
if not args:
pass # список, amount=1
elif len(args) == 1:
try:
amount = float(args[0].replace(",", "."))
except ValueError:
symbol = args[0].upper()
else:
try:
amount = float(args[0].replace(",", "."))
except ValueError:
return await utils.answer(message, "🚫 Некорректное число")
symbol = args[1].upper()
if symbol:
coin = next((c for c in coins if c["symbol"].upper() == symbol), None)
if not coin:
return await utils.answer(message, f"🚫 Крипта <b>{symbol}</b> не найдена")
line = self._fmt_crypto(coin, rates, amount)
if not line:
return await utils.answer(message, "🚫 Ошибка форматирования")
return await utils.answer(message, header + line)
# список только известных монет
known = {c["symbol"].upper(): c for c in coins if c["symbol"].upper() in _CRYPTO_NAMES}
# сортируем по порядку _CRYPTO_NAMES
lines = []
for sym in _CRYPTO_NAMES:
if sym in known:
line = self._fmt_crypto(known[sym], rates, amount)
if line:
lines.append(line)
await utils.answer(
message,
header + f"<blockquote expandable>{chr(10).join(lines)}</blockquote>",
)
@loader.command()
async def cryptocmd(self, m):
"""[count] [ton, btc, ...]"""
a = utils.get_args(m)
c = await self._get_crypto()
if not c:
return await utils.answer(m, self.strings["error"])
try:
if len(a) == 0:
f = [x for x in c if x['symbol'].upper() in _CRYPTO_LIST]
l = []
for x in f:
if (n := await self._fmt_crypto(x)):
l.append(n)
await utils.answer(m, self.strings["crypto_no_args"].format("\n".join(l)))
elif len(a) == 1:
am = float(a[0])
f = [x for x in c if x['symbol'].upper() in _CRYPTO_LIST]
l = []
for x in f:
if (n := await self._fmt_crypto(x, am)):
l.append(n)
await utils.answer(m, self.strings["crypto_no_args"].format("\n".join(l)))
elif len(a) == 2:
am = float(a[0])
t = a[1].upper()
f = False
for x in c:
if x['symbol'].upper() == t:
if (n := await self._fmt_crypto(x, am)):
f = True
await utils.answer(m, self.strings["crypto_specific"].format(n))
break
if not f:
await utils.answer(m, self.strings["crypto_not_found"].format(t))
except ValueError:
await utils.answer(m, "🚫 Некорректное число")
except Exception as e:
await utils.answer(m, f"🚫 Ошибка: {str(e)}")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +0,0 @@
ChatCopy.py
Gemini.py
GiftFinder.py
MaillingChatGT99.py
NekoEditorMod.py

View File

@@ -1,97 +0,0 @@
# requires: Pillow numpy
# meta developer: @SunnexGB
# meta banner: https://i.pinimg.com/control1/1200x/24/8d/40/248d40b6afa5bd3c3764556b50635691.jpg
__version__ = (1, 0, 0)
import io
import logging
from herokutl.types import Message
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class ASCII(loader.Module):
"""Convert images to braille ASCII"""
strings = {
"name": "ASCII",
"no_lib": "<tg-emoji emoji-id=5447385112612208213>🚫</tg-emoji> | <b>Library not loaded</b>",
"no_image": "<tg-emoji emoji-id=5447381715293074599>⚠️</tg-emoji> | <b>Reply to image</b>",
"processing": "<tg-emoji emoji-id=5445373981290952548>®️</tg-emoji> | <b>Processing...</b>",
"empty": "<tg-emoji emoji-id=5287613115180006030>🤬</tg-emoji> | <b>Empty result</b>",
"result": "<pre>{art}</pre>",
"Failed_to_load_library": "Failed to load library",
"Conversion_error": "Conversion error",
}
strings_ru = {
"_cls_doc": "Конвертирует картинку в braille ASCII",
"no_lib": "<tg-emoji emoji-id=5447385112612208213>🚫</tg-emoji> | <b>Библиотека не была загружена</b>",
"no_image": "<tg-emoji emoji-id=5447381715293074599>⚠️</tg-emoji> | <b>Ответьте на картинку</b>",
"processing": "<tg-emoji emoji-id=5445373981290952548>®️</tg-emoji> | <b>Обработка...</b>",
"empty": "<tg-emoji emoji-id=5287613115180006030>🤬</tg-emoji> | <b>Пустой результат</b>",
"result": "<pre>{art}</pre>",
"Failed_to_load_library": "Не удалось загрузить библиотеку",
"Conversion_error": "Ошибка конвертации",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue("width", 50),
loader.ConfigValue("threshold", 0.65),
loader.ConfigValue("contrast", 2.0),
loader.ConfigValue("chars", 464),
loader.ConfigValue("invert", False),
)
self.lib = None
async def client_ready(self):
try:
self.lib = await self.import_lib("https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/refs/heads/main/Assets/ASCII/ascii-lib.py", suspend_on_error=True)
except Exception:
logger.exception(self.strings["Failed_to_load_library"])
self.lib = None
@loader.command(ru_doc="- Отрисовать ASCII-ART (аргумент -f, отправляет файлом)")
async def dotcmd(self, message: Message):
"""- Draw ASCII-ART (argument -f, sends as a file)"""
if not self.lib:
return await utils.answer(message, self.strings["no_lib"])
args = utils.get_args_raw(message)
force_file = "-f" in args.lower()
reply = await message.get_reply_message() or message
if not reply or not (
reply.photo
or (
reply.document
and str(getattr(reply.document, "mime_type", "")).startswith("image/")
)
):
return await utils.answer(message, self.strings["no_image"])
msg = await utils.answer(message, self.strings["processing"])
try:
image_bytes = await reply.download_media(bytes)
art = self.lib.convert(
image_bytes,
width=self.config["width"],
threshold=self.config["threshold"],
contrast_boost=self.config["contrast"],
invert=self.config["invert"],
target_chars=self.config["chars"],
)
except Exception as e:
logger.exception(self.strings["Conversion_error"])
return await utils.answer(msg, f"<pre>{e}</pre>")
if not art or not art.strip():
return await utils.answer(msg, self.strings["empty"])
formatted_art = self.strings("result").format(art=art)
if force_file or len(formatted_art) > 4096:
file = io.BytesIO(art.encode("utf-8"))
file.name = "ascii.txt"
await message.client.send_file(message.peer_id, file)
await msg.delete()
else:
await utils.answer(msg, formatted_art)

View File

@@ -1,110 +0,0 @@
# requires: Pillow numpy
# Дикие оправдания по поводу именно этого ассета а точнее кода в нем,честно я не знаю что сказать была попытка переписать JS на Py и как бы особых проблем не было,
# до момента пост-обработки на помощь я позвал Claude и он не решил мою проблему от слова совсем,так как в целом я своего рода призираю пилоу,а модуль мне хотелось
# написать я примерно вайб-кодил около 50 минут и я уверен из за этого будет возможно много проблем,в итоге благодаря немного копанию в коде,я нашел проблему и уже
# начал ее решать,НО я опять же вообще не понимал как сделать то что мне нужно,в интернете были сюрсы но будто бы тот или иной мне не подходили? Я не знаю почему я
# дропнул эту идею. Потом я стал искать в JS-е что там вообще можно сделать,в итоге я там импортировал модель какую то блядскую не нужную и опять впустую время
# потратил,думал что тут определено есть решение и снова пошел к ии,вывод опятьь 0 помощи,я не знаю почему я так вцепился лишь в 1 идею.Как бы я мог упростить все,
# даже наверное просто попросив какую то флагмен ии написать модуль и переписать его,но я уже на тот момент по моему мнению сделал много и не хотел ни каким образом
# оставлять это,поэтому через время я нашел сайты которые в целом давали возможность настраивать фильтр,была переделана логика(в целом ее переделал на 60 процентов
# клод,я просто убирал мусор который он испражнял.И вот дальше точно бред я убил более дня на решение проблем которые были решены мной,но результат мне не нравился
# И ОПЯТЬ я пошел просить помощи у гугла,потом понял что возможно даже будет легко(по факту легко,но я ленивый) пока искал,мне перехотелось и я уже потом пытался
# сделать режимы в модуле,что оказалось ужасом ведь они работали,но при возможности гармонично вписать их в код были конфликты И Я В ОЧЕРЕДНОЙ РАЗ ПОШЕЛ К ИИ,спойлер
# он не смог написать лучше чем я,в итоге я отбросил эту идею и думаю в целом никак больше не апдейтать модуль по крупному.
# Да это были оправдания,но зато какие!
import io
import numpy as np
from PIL import Image, ImageFilter, ImageEnhance, ImageOps
from .. import loader
BASE = 0x2800
INVERT_MAP = {chr(BASE + c): chr(BASE + (c ^ 0xFF)) for c in range(256)}
class AsciiLib(loader.Library):
developer = "@SunnexGB"
def resize(self, img):
if img.width > 768:
img = img.resize((768, int(img.height * 768 / img.width)), Image.LANCZOS)
w = img.width - img.width % 4
h = img.height - img.height % 4
if w != img.width or h != img.height:
img = img.resize((w, h), Image.LANCZOS)
return img
def mode(self, img, threshold, contrast):
gray = img.convert("L")
edges = ImageOps.invert(gray.filter(ImageFilter.FIND_EDGES))
contrast_img = ImageEnhance.Contrast(img).enhance(contrast).convert("L")
e = np.array(edges, dtype=np.float32) / 255.0
c = np.array(contrast_img, dtype=np.float32) / 255.0
blended = Image.fromarray((e * c * 255).astype(np.uint8), "L")
t = int(threshold * 255)
processed = blended.point(lambda p: 255 if p > t else 0, "L")
return processed, t
def braille(self, img, threshold, width):
cw = width * 2
o = -(-round(cw * img.height / img.width) // 4)
ch = 4 * o
px = np.array(img.resize((cw, ch), Image.LANCZOS).convert("L"))
order = [(0,0),(1,0),(2,0),(0,1),(1,1),(2,1),(3,0),(3,1)]
rows = []
for rs in range(0, ch, 4):
line = []
for cs in range(0, cw, 2):
grays = [
int(px[rs+dy, cs+dx]) if (rs+dy < ch and cs+dx < cw) else 255
for dy, dx in order
]
bits = list(reversed([1 if g < threshold else 0 for g in grays]))
code = int("".join(str(b) for b in bits), 2)
line.append(chr(BASE + code))
rows.append("".join(line))
return rows
def trim(self, lines):
blank = "\u2800"
while lines and all(c == blank for c in lines[0]):
lines = lines[1:]
while lines and all(c == blank for c in lines[-1]):
lines = lines[:-1]
if not lines:
return lines
left = min(next((i for i,c in enumerate(r) if c!=blank), len(r)) for r in lines)
right = min(next((i for i,c in enumerate(reversed(r)) if c!=blank), len(r)) for r in lines)
return [r[left: len(r)-right if right else len(r)] for r in lines]
def invert(self, lines):
return ["".join(INVERT_MAP.get(c,c) for c in l) for l in lines]
def fit(self, img, threshold, chars, width):
lo, hi = 5, 200
best = ""
for _ in range(14):
mid = (lo + hi)//2
lines = self.trim(self.braille(img, threshold, mid))
art = "\n".join(lines)
if len(art) <= chars:
best = art
lo = mid + 1
else:
hi = mid - 1
return best
def convert(self, data, width=50, threshold=0.65, contrast_boost=2.0, invert=False, target_chars=0):
buf = io.BytesIO(data)
img = Image.open(buf)
img.load()
buf.close()
img = img.convert("RGB")
img = self.resize(img)
processed, t = self.mode(img, threshold, contrast_boost)
if target_chars > 0:
art = self.fit(processed, t, target_chars, width)
else:
art = "\n".join(self.trim(self.braille(processed, t, width)))
if invert and art:
art = "\n".join(self.invert(art.split("\n")))
return art

View File

@@ -1,832 +0,0 @@
{
"prologue": [
{
"type": "label",
"name": "prologue"
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_1",
"action": "load_asset",
"location": "anim/prolog_1",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_1.jpg?raw=true"
},
{
"type": "narration",
"text": "Мне опять снился сон."
},
{
"type": "narration",
"text": "<i>Этот</i> сон..."
},
{
"type": "narration",
"text": "Каждую ночь одно и то же."
},
{
"type": "narration",
"text": "Но наутро, как обычно, всё забудется."
},
{
"type": "narration",
"text": "Может быть, оно и к лучшему..."
},
{
"type": "narration",
"text": "Останутся только туманные воспоминания о приоткрытых, словно приглашающих куда-то воротах, рядом с которыми в камне застыли два пионера."
},
{
"type": "narration",
"text": "А ещё странная девочка...{w} которая постоянно спрашивает:"
},
{
"type": "scene",
"kind": "bg",
"name": "anim_prolog1_off",
"action": "load_asset",
"location": "anim/anim_prolog1_off",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/anim_prolog1_off.gif?raw=true"
},
{
"type": "dialogue",
"char_id": "dreamgirl",
"character": "...",
"text": "Ты пойдёшь со мной?"
},
{
"type": "narration",
"text": "Пойду?.."
},
{
"type": "narration",
"text": "Но куда?"
},
{
"type": "narration",
"text": "И зачем?.."
},
{
"type": "narration",
"text": "Да и где я вообще нахожусь?"
},
{
"type": "narration",
"text": "Конечно, случись всё на самом деле, наяву, стоило бы непременно испугаться."
},
{
"type": "narration",
"text": "Как же иначе!"
},
{
"type": "narration",
"text": "Но это всего лишь сон.{w} Тот самый, который я вижу каждую ночь."
},
{
"type": "narration",
"text": "А ведь всё это неспроста!"
},
{
"type": "narration",
"text": "Необязательно знать <i>где</i> и <i>почему</i>, чтобы понять что-то происходит."
},
{
"type": "narration",
"text": "Нечто, отчаянно требующее моего внимания."
},
{
"type": "narration",
"text": "Ведь всё окружающее меня здесь реально!"
},
{
"type": "narration",
"text": "Реально настолько, насколько реальны вещи в моей квартире; я бы мог открыть ворота, услышать скрип петель, смахнуть рукой осыпающуюся ржавчину, потянуть носом свежий прохладный воздух и поёжиться от холода."
},
{
"type": "narration",
"text": "Мог бы, но для этого надо сдвинуться с места, сделать шаг, пошевелить рукой..."
},
{
"type": "narration",
"text": "А ведь это сон я понимаю, но что дальше, что изменит моё <i>понимание</i>?"
},
{
"type": "narration",
"text": "Ведь здесь словно по ту сторону потрескавшегося экрана старого телевизора, который из последних сил борется с помехами и силится показать зрителям всё, не упустив ни малейшей детали."
},
{
"type": "narration",
"text": "Но вот картинка теряет чёткость...{w} Наверное, скоро просыпаться."
},
{
"type": "narration",
"text": "..."
},
{
"type": "narration",
"text": "Может быть, спросить у неё что-то?{w} У девочки."
},
{
"type": "narration",
"text": "Как же её зовут..."
},
{
"type": "narration",
"text": "Например про звёзды..."
},
{
"type": "narration",
"text": "Хотя почему про звёзды?"
},
{
"type": "narration",
"text": "Можно же спросить про ворота!{w} Да, про ворота!"
},
{
"type": "narration",
"text": "Вот она удивится."
},
{
"type": "narration",
"text": "Или лучше про букву <i>ё</i>."
},
{
"type": "narration",
"text": "Хорошая была буква..."
},
{
"type": "narration",
"text": "Как будто её больше нет!"
},
{
"type": "narration",
"text": "И какое отношение буквы, ворота и звёзды имеют к этому месту?"
},
{
"type": "narration",
"text": "Ведь если мне каждую ночь снится <i>этот</i> сон, который потом всё равно забудется, надо искать разгадку здесь и сейчас!"
},
{
"type": "narration",
"text": "А вот, если присмотреться, можно увидеть Магелланово Облако..."
},
{
"type": "narration",
"text": "Словно попал в южное полушарие!"
},
{
"type": "narration",
"text": "..."
},
{
"type": "narration",
"text": "Во сне всегда больше волнуют мелочи: неестественный цвет травы, невозможная кривизна прямых или своё перекошенное отражение а реальная опасность, готовая оборвать всё здесь и сейчас, кажется пустяком."
},
{
"type": "narration",
"text": "Естественно, ведь <i>здесь</i> нельзя умереть."
},
{
"type": "narration",
"text": "Я точно знаю я делал это сотни раз."
},
{
"type": "narration",
"text": "Но если нельзя умереть, нет смысла жить?"
},
{
"type": "narration",
"text": "Надо будет спросить у девочки: она местная должна знать!"
},
{
"type": "narration",
"text": "Да, именно!{w} Спросить, например, про сову."
},
{
"type": "narration",
"text": "Больно уж птица странная..."
},
{
"type": "narration",
"text": "А впрочем, неважно..."
},
{
"type": "narration",
"text": "..."
},
{
"type": "dialogue",
"char_id": "dreamgirl",
"character": "...",
"text": "Ты пойдёшь со мной?"
},
{
"type": "narration",
"text": "И каждый раз надо отвечать."
},
{
"type": "narration",
"text": "Иначе никак, иначе сон не закончится, а я не проснусь."
},
{
"type": "route",
"id": "prologue_choice_1"
},
{
"type": "narration",
"text": "Каждый раз так сложно решить, что же ответить."
},
{
"type": "narration",
"text": "Где я, что я здесь делаю, кто она такая?"
},
{
"type": "narration",
"text": "И почему от ответа на этот вопрос зависит так много в моей жизни?"
},
{
"type": "narration",
"text": "Или не зависит?.."
},
{
"type": "narration",
"text": "Ведь это просто сон..."
},
{
"type": "narration",
"text": "Просто сон..."
},
{
"type": "scene",
"kind": "bg",
"name": "black",
"action": "load_asset",
"location": "bg/black",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/black.png?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "1_prologue",
"action": "load_asset",
"location": "cg/p_kb_1",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_1.png?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "2_prologue",
"action": "load_asset",
"location": "cg/p_kb_2",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_2.png?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "3_prologue",
"action": "load_asset",
"location": "cg/p_kb_3",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_3.png?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "4_prologue",
"action": "load_asset",
"location": "cg/p_kb_4",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_4.png?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "5_prologue",
"action": "load_asset",
"location": "cg/p_kb_5",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/cg/p_kb_5.png?raw=true"
},
{
"type": "narration",
"text": "Экран монитора смотрел на меня словно живой."
},
{
"type": "narration",
"text": "Иногда мне правда казалось, что он обладает сознанием, своими мыслями и желаниями, стремлениями; умеет чувствовать, любить и страдать."
},
{
"type": "narration",
"text": "Словно в наших отношениях инструмент не он неодушевлённый кусок пластика и текстолита, а я."
},
{
"type": "narration",
"text": "Наверное, в этом есть доля правды, ведь компьютер на 90% обеспечивает моё общение с внешним миром."
},
{
"type": "narration",
"text": "Анонимные имиджборды, иногда какие-то чаты, редко аська или джаббер, ещё реже форумы."
},
{
"type": "narration",
"text": "А людей, сидящих по ту сторону сетевого кабеля, попросту не существует!"
},
{
"type": "narration",
"text": "Все они всего лишь плод его больной фантазии, ошибка в программном коде или баг ядра, зажившего собственной жизнью."
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_15",
"action": "load_asset",
"location": "anim/prolog_15",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_15.png?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_3",
"action": "load_asset",
"location": "anim/prolog_3",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_3.png?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_4",
"action": "load_asset",
"location": "anim/prolog_4",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_4.png?raw=true"
},
{
"type": "narration",
"text": "Если посмотреть со стороны на моё существование, то такие мысли покажутся не столь уж бредовыми, а какой-нибудь психолог наверняка поставит мне кучу заумных диагнозов и, возможно, выпишет направление в жёлтый дом."
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_5",
"action": "load_asset",
"location": "anim/prolog_5",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_5.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_14",
"action": "load_asset",
"location": "anim/prolog_14",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_14.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_11",
"action": "load_asset",
"location": "anim/prolog_11",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_11.jpg?raw=true"
},
{
"type": "narration",
"text": "Маленькая квартирка без следов какого бы то ни было ремонта или даже подобия порядка, и вечно одинаковый вид из окна на серый, день и ночь куда-то бегущий мегаполис, вот условия моей жизни."
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_2",
"action": "load_asset",
"location": "anim/prolog_2",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_2.jpg?raw=true"
},
{
"type": "narration",
"text": "Конечно, всё начиналось не так..."
},
{
"type": "narration",
"text": "Я родился, пошёл в школу, закончил её всё как у людей."
},
{
"type": "narration",
"text": "Поступил в институт, где кое-как промучился полтора курса."
},
{
"type": "narration",
"text": "Работал на паре-тройке разных работ.{w} Иногда даже и неплохо, иногда даже получая за это достойные деньги."
},
{
"type": "narration",
"text": "Однако всё это казалось чужим, словно списанным с биографии другого человека."
},
{
"type": "narration",
"text": "Я не ощущал полноту жизни она словно зациклилась и продолжала идти по кругу.{w} Как в фильме «День сурка»."
},
{
"type": "narration",
"text": "Только у меня не было выбора, как именно провести этот день, и каждый раз всё повторялось по одной и той же схеме.{w} Схеме пустоты, уныния и отчаяния."
},
{
"type": "narration",
"text": "Последние несколько лет я просто целыми днями сидел за компьютером."
},
{
"type": "narration",
"text": "Иногда подворачивались какие-то халтурки, иногда помогали родители."
},
{
"type": "narration",
"text": "В общем, на жизнь хватало."
},
{
"type": "narration",
"text": "Это и немудрено, ведь потребности у меня небольшие."
},
{
"type": "narration",
"text": "На улицу я практически не выхожу, а всё моё общение с людьми сводится к интернет-переписке с <i>анонимами</i>, у которых нет ни реального имени, ни пола, ни возраста."
},
{
"type": "narration",
"text": "Короче говоря, достаточно типичная жизнь достаточно типичного асоциального человека своего времени.{w} Этакий Обломов XXI века."
},
{
"type": "narration",
"text": "Может быть, маститый писатель напишет обо мне роман, который станет классикой современной литературы.{w} Или напишу я сам…"
},
{
"type": "narration",
"text": "Впрочем нет, что себя обманывать уже не раз пытался, но меня не хватало даже на короткий рассказ."
},
{
"type": "narration",
"text": "Изучал я и множество других вещей."
},
{
"type": "narration",
"text": "Рисовать не дано от природы.{w} Программирование надоело.{w} Иностранные языки долго и скучно…"
},
{
"type": "narration",
"text": "Любил я разве что читать, но даже при этом никогда бы не назвал себя эрудированным человеком."
},
{
"type": "narration",
"text": "Возможно, я был асом в просмотре аниме и гроссмейстером неумелых шуточек в интернете."
},
{
"type": "narration",
"text": "Плати мне за это деньги, я бы обрадовался (да и заработал неплохо), но вряд ли так просто можно заполнить пустоту в душе."
},
{
"type": "scene",
"kind": "bg",
"name": "semen_room_window",
"action": "load_asset",
"location": "bg/semen_room_window",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/semen_room_window.jpg?raw=true"
},
{
"type": "narration",
"text": "Сегодня очередной типичный день моей типичной жизни типичного неудачника."
},
{
"type": "narration",
"text": "И именно сегодня мне нужно ехать на встречу институтских товарищей."
},
{
"type": "narration",
"text": "По правде говоря, совершенно не хотелось."
},
{
"type": "narration",
"text": "Да и какой смысл, если вместе с ними я отучился всего ничего?"
},
{
"type": "narration",
"text": "Однако меня всё же уговорил друг, бывший одногруппник, один из немногих, с кем я поддерживал контакт не только в интернете."
},
{
"type": "scene",
"kind": "anim",
"name": "intro_1",
"action": "load_asset",
"location": "anim/intro_1",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_1.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_2",
"action": "load_asset",
"location": "anim/intro_2",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_2.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_3",
"action": "load_asset",
"location": "anim/intro_3",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_3.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_4",
"action": "load_asset",
"location": "anim/intro_4",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_4.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_5",
"action": "load_asset",
"location": "anim/intro_5",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_5.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_6",
"action": "load_asset",
"location": "anim/intro_6",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_6.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_8",
"action": "load_asset",
"location": "anim/intro_8",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_8.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_7",
"action": "load_asset",
"location": "anim/intro_7",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_7.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "bg",
"name": "bus_stop",
"action": "load_asset",
"location": "bg/bus_stop",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/bus_stop.jpg?raw=true"
},
{
"type": "narration",
"text": "Вечер. Мороз.{w} Остановка и ожидание автобуса."
},
{
"type": "narration",
"text": "Я никогда не любил зиму.{w} Впрочем, и жаркое лето тоже не моя стихия."
},
{
"type": "narration",
"text": "Просто не вижу смысла выделять какое-то одно время года не столь важно, какая погода на улице, если ты целыми днями сидишь дома."
},
{
"type": "narration",
"text": "Автобус сегодня задерживался так сильно, что я уже был готов плюнуть на всё и потратить последнюю пару сотен на такси (совсем не ехать мне почему-то в голову не пришло)."
},
{
"type": "narration",
"text": "В мозгу, как всегда, роились миллионы мыслей, из которых совершенно невозможно выудить хотя бы одну стоящую."
},
{
"type": "narration",
"text": "Такую, которую можно закончить, привести в порядок, облечь в форму идеи и претворить в жизнь."
},
{
"type": "narration",
"text": "Может быть, заняться бизнесом?{w} Но откуда я возьму деньги?"
},
{
"type": "narration",
"text": "Или пойти опять работать в офис?{w} Нет уж!"
},
{
"type": "narration",
"text": "Может, стоит попробовать фриланс?{w} Да что я умею, и кому я нужен…"
},
{
"type": "scene",
"kind": "anim",
"name": "prolog_2",
"action": "load_asset",
"location": "anim/prolog_2",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/prolog_2.jpg?raw=true"
},
{
"type": "narration",
"text": "Вдруг мне вспомнилось детство…{w} Или скорее юношество 15-17 лет."
},
{
"type": "narration",
"text": "Почему именно это время?{w} Не знаю."
},
{
"type": "narration",
"text": "Наверное, потому что тогда всё было проще."
},
{
"type": "narration",
"text": "Было проще принимать такие сложные сейчас и такие простые тогда решения."
},
{
"type": "narration",
"text": "Проснувшись с утра, я чётко знал, как пройдёт мой день, а выходных ждал с нетерпением смогу отдохнуть, заняться любимыми делами: компьютер, футбол, встречи с друзьями."
},
{
"type": "narration",
"text": "А потом, когда наступит новая неделя, вновь примусь за учёбу."
},
{
"type": "narration",
"text": "Ведь раньше не возникало этих мучительных вопросов «зачем», «кому это надо», «что изменится, если я это сделаю» или «что не изменится»."
},
{
"type": "narration",
"text": "Простой поток жизни, такой привычный для любого нормального человека и такой чуждый для меня теперешнего."
},
{
"type": "narration",
"text": "Время беззаботного детства…{w} Тогда же я и встретил свою первую любовь."
},
{
"type": "narration",
"text": "Стёрлись из памяти её внешность, характер."
},
{
"type": "narration",
"text": "Как строчка из профиля в социальной сети осталось лишь имя, да те чувства, которые захлёстывали меня, когда я был с ней.{w} Теплота, нежность, желание заботиться, защитить…"
},
{
"type": "narration",
"text": "Жаль, что это продолжалось так недолго."
},
{
"type": "narration",
"text": "Сейчас я уже с трудом могу себе представить что-то подобное."
},
{
"type": "narration",
"text": "Наверное, и хочется познакомиться с девушкой, только не знаю, как начать диалог, о чём вообще с ней говорить, чем её заинтересовать."
},
{
"type": "narration",
"text": "Да и подходящих девушек я давно не встречал.{w} Хотя где мне их встретить…"
},
{
"type": "scene",
"kind": "anim",
"name": "intro_9",
"action": "load_asset",
"location": "anim/intro_9",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_9.jpg"
},
{
"type": "narration",
"text": "Звук работающего двигателя вернул меня к реальности."
},
{
"type": "narration",
"text": "Подъехал автобус."
},
{
"type": "narration",
"text": "«Какой-то он не такой» мелькнула мысль."
},
{
"type": "narration",
"text": "Впрочем, какая разница по этому маршруту ходит только 410-ый."
},
{
"type": "scene",
"kind": "anim",
"name": "intro_10",
"action": "load_asset",
"location": "anim/intro_10",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_10.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_11",
"action": "load_asset",
"location": "anim/intro_11",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_11.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "anim",
"name": "intro_13",
"action": "load_asset",
"location": "anim/intro_13",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/anim/intro_13.jpg?raw=true",
"duration": null
},
{
"type": "scene",
"kind": "bg",
"name": "intro_xx",
"action": "load_asset",
"location": "bg/intro_xx",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/intro_xx.jpg?raw=true"
},
{
"type": "narration",
"text": "Огни пролетают мимо, их холодный свет словно зажигает внутри давно погасшие чувства."
},
{
"type": "narration",
"text": "Или не зажигает, а просто пробуждает…"
},
{
"type": "narration",
"text": "Ведь «они» уже давно живут во мне, то затихая, то просыпаясь вновь."
},
{
"type": "narration",
"text": "Какая-то очень известная мелодия играла в радиоприёмнике у водителя.{w} Но я её не слушал."
},
{
"type": "narration",
"text": "Я смотрел в запотевшее окно автобуса на проезжающие мимо машины."
},
{
"type": "narration",
"text": "Ведь люди куда-то спешат, ведь им что-то нужно, и, погружённые в свои дела, они не задумываются о вопросах, мучающих меня."
},
{
"type": "narration",
"text": "Наверное, у них тоже есть свои серьёзные проблемы, а может, им живётся куда легче."
},
{
"type": "narration",
"text": "Знать наверняка нельзя, так как все люди разные.{w} Или не разные?"
},
{
"type": "narration",
"text": "Бывает, поступки человека легко предсказуемы, но, пытаясь заглянуть к нему в душу, видишь лишь непроглядную тьму."
},
{
"type": "narration",
"text": "..."
},
{
"type": "narration",
"text": "Автобус приближался к центру, и мои мысли прервал яркий свет огней большого города."
},
{
"type": "narration",
"text": "Сотни рекламных вывесок, тысячи машин, миллионы людей."
},
{
"type": "narration",
"text": "Я смотрел на это светопреставление, и мне почему-то безумно захотелось спать."
},
{
"type": "narration",
"text": "Глаза закрылись всего на полсекунды и…"
},
{
"type": "scene",
"kind": "bg",
"name": "black",
"action": "load_asset",
"location": "bg/black",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/images/1920/bg/int_bus_black.jpg?raw=true"
},
{
"type": "opening",
"kind": "opening",
"name": "opening",
"action": "load_asset",
"location": "opening/opening",
"raw_url": "https://raw.githubusercontent.com/SunnexGB/Heroku-Modules/main/Assets/Everlasting_Summer/opening/opening.mp4?raw=true"
}
]
}

View File

@@ -1,62 +0,0 @@
{
"prologue_choice_1": {
"question": "Иначе никак, иначе сон не закончится, а я не проснусь. — что выбрать?",
"chapter": "prologue",
"options": {
"Да, я пойду с тобой": {
"effects": {}
},
"Нет, я останусь здесь": {
"effects": {}
}
}
},
"endings": {
"labels": [
"main_good_ending",
"main_bad_ending",
"sl_good_ending",
"sl_bad_ending",
"dv_good_ending",
"dv_bad_ending",
"un_good_ending",
"un_bad_ending",
"us_good_ending",
"us_bad_ending",
"mi_ending",
"uv_ending",
"harem_ending"
],
"routes": {
"sl": {
"good": "sl_good_ending",
"bad": "sl_bad_ending",
"point_key": "sl_points"
},
"dv": {
"good": "dv_good_ending",
"bad": "dv_bad_ending",
"point_key": "dv_points"
},
"un": {
"good": "un_good_ending",
"bad": "un_bad_ending",
"point_key": "un_points"
},
"us": {
"good": "us_good_ending",
"bad": "us_bad_ending",
"point_key": "us_points"
},
"mi": {
"single": "mi_ending",
"point_key": "mi_points"
},
"uv": {
"single": "uv_ending",
"point_key": "uv_points"
}
},
"fallback": "main_bad_ending"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Some files were not shown because too many files have changed in this diff Show More