Commited backup

This commit is contained in:
2025-07-10 21:02:34 +03:00
parent 952c1001e3
commit da0b80823e
1310 changed files with 254133 additions and 41 deletions

View File

@@ -0,0 +1,8 @@
version = 1
[[analyzers]]
name = "python"
enabled = true
[analyzers.meta]
runtime_version = "3.x.x"

8
vsecoder/hikka_modules/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
test.py
assets/chromedriver
sel.py
ProSpam.py
.vscode/settings.json
kblayout.py
.idea/

View File

@@ -0,0 +1,206 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
import contextlib
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/color/344/antivirus-scanner--v1.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/color/344/antivirus-scanner--v1.png&title=Check%20module&description=Module%20for%20check%20modules
__version__ = (3, 3, 0)
import logging
import re
import requests
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
checker_regex = {
"critical": [
{"command": "DeleteAccountRequest", "perms": "delete account"},
{"command": "edit_2fa", "perms": "change 2FA password"},
{"command": "get_me", "perms": "presumably get your profile account data"},
{"command": "disconnect", "perms": "disconnect account"},
{"command": "log_out", "perms": "disconnect account"},
{"command": "ResetAuthorizationRequest", "perms": "kill account sessions"},
{
"command": "GetAuthorizationsRequest",
"perms": "get telegram api_id and api_hash",
},
{"command": "AddRequest", "perms": "get telegram api_id and api_hash"},
{"command": "pyarmor", "perms": "all(obfuscated script)"},
{"command": "pyrogram", "perms": "another tg client"},
{"command": "system", "perms": "presumably eval commands"},
{"command": "eval", "perms": "presumably eval python code"},
{"command": "exec", "perms": "presumably exec python code"},
{
"command": "sessions",
"perms": "get all sessions data, delete sessoins, copy and send sessions",
},
{"command": "subprocess", "perms": "eval commands"},
{"command": "torpy", "perms": "download viruses"},
{"command": "httpimport", "perms": "import malicious scripts"},
],
"warn": [
{"command": "list_sessions", "perms": "get all account sessions"},
{"command": "LeaveChannelRequest", "perms": "leave channel and chats"},
{"command": "JoinChannelRequest", "perms": "join channel and chats"},
{
"command": "ChannelAdminRights",
"perms": "edit channel and chats users perms",
},
{"command": "EditBannedRequest", "perms": "kick and ban users"},
{"command": "remove", "perms": "presumably remove files"},
{"command": "rmdir", "perms": "presumably remove dirs"},
{"command": "telethon", "perms": "telethon funcs"},
{"command": "get_response", "perms": "get telegram messages"},
],
"council": [
{"command": "requests", "perms": "send requests"},
{"command": "get_entity", "perms": "get entities"},
{"command": "get_dialogs", "perms": "get dialogs"},
{"command": "os", "perms": "presumably get os info"},
{"command": "sys", "perms": "presumably get sys info"},
{"command": "import", "perms": "import modules"},
{"command": "client", "perms": "all client functions"},
{"command": "send_message", "perms": "send messages"},
{"command": "send_file", "perms": "send files"},
{"command": "TelegramClient", "perms": "create new session"},
{"command": "download_file", "perms": "download telegram files"},
{"command": "ModuleConfig", "perms": "create configs"},
],
}
@loader.tds
class CheckModulesMod(loader.Module):
"""Module for check modules"""
strings = {
"name": "Check module",
"cfg_lingva_url": (
"Check the module for suspicious features, scam, and find out what the"
" module has access to"
),
"answer": (
"🔍 <b>Module check complete</b>:\n\n⛔️ Criticals:\n{0}\n🟡 Warns:\n{1}\n"
" Councils:\n{2}"
),
"component": " ▪️ «<code>{0}</code>» in module have permissions on <i>{1}</i>",
"error": (
"Error!\n\n.checkmod <module_link>\n.checkmod"
" https://raw.githubusercontent.com/vsecoder/hikka_modules/main/googleit.py"
),
}
strings_ru = {
"cfg_lingva_url": (
"Проверьте модуль на подозрительные возможности, скам, и узнайте к чему"
" есть доступ у модуля"
),
"answer": (
"🔍 <b>Проверка модуля завершена</b>:\n\n⛔️ Критические:\n{0}\n🟡"
" Предупреждения:\n{1}\n✅ Советы:\n{2}"
),
"component": " ▪️ «<code>{0}</code>» в модуле имеет разрешения на <i>{1}</i>",
"error": (
"Ошибка!\n\n.checkmod <module_link>\n.checkmod"
" https://raw.githubusercontent.com/vsecoder/hikka_modules/main/googleit.py"
),
}
async def client_ready(self, client, db):
self.client = client
self.db = db
async def check_m(self, args):
string = args
critical = ""
warn = ""
council = ""
for command in checker_regex["critical"]:
r = re.search(command["command"], string)
if r is not None:
critical = (
critical
+ self.strings["component"].format(
command["command"], command["perms"]
)
+ "\n"
)
if not critical:
critical = " ▪️ \n"
for command in checker_regex["warn"]:
r = re.search(command["command"], string)
if r is not None:
warn = (
warn
+ self.strings["component"].format(
command["command"], command["perms"]
)
+ "\n"
)
if not warn:
warn = " ▪️ \n"
for command in checker_regex["council"]:
r = re.search(command["command"], string)
if r is not None:
council = (
council
+ self.strings["component"].format(
command["command"], command["perms"]
)
+ "\n"
)
if not council:
council = " ▪️ \n"
return self.strings["answer"].format(critical, warn, council, args)
@loader.unrestricted
@loader.ratelimit
async def checkmodcmd(self, message):
"""
<module_link> or "reply file" or "send file" - perform module check
"""
args = utils.get_args_raw(message)
if args:
with contextlib.suppress(Exception):
r = await utils.run_sync(requests.get, args)
string = r.text
await utils.answer(message, await self.check_m(string))
return
try:
code_from_message = (
await self._client.download_file(message.media, bytes)
).decode("utf-8")
except Exception:
code_from_message = ""
try:
reply = await message.get_reply_message()
code_from_reply = (
await self._client.download_file(reply.media, bytes)
).decode("utf-8")
except Exception:
code_from_reply = ""
args = code_from_message or code_from_reply
await utils.answer(message, await self.check_m(args))

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Всеволод
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,295 @@
# meta developer: @limokanews
from whoosh.index import create_in
from whoosh.fields import TEXT, ID, Schema
from whoosh.qparser import QueryParser, OrGroup
from whoosh.query import FuzzyTerm, Wildcard
import aiohttp
import random
import logging
import os
import html
import json
from telethon.types import Message
from .. import utils, loader
logger = logging.getLogger("Limoka")
class Search:
def __init__(self, query: str):
self.schema = Schema(
title=TEXT(stored=True), path=ID(stored=True), content=TEXT(stored=True)
)
self.query = query
def search_module(self, content):
if not os.path.exists("limoka_search"):
os.makedirs("limoka_search")
ix = create_in("limoka_search", self.schema)
writer = ix.writer()
for module_content in content:
writer.add_document(
title=module_content["id"],
path=module_content["id"],
content=module_content["content"],
)
writer.commit()
with ix.searcher() as searcher:
parser = QueryParser("content", ix.schema, group=OrGroup)
query = parser.parse(self.query)
fuzzy_query = FuzzyTerm("content", self.query, maxdist=1, prefixlength=2)
wildcard_query = Wildcard("content", f"*{self.query}*")
results = searcher.search(query)
if not results:
results = searcher.search(fuzzy_query)
if not results:
results = searcher.search(wildcard_query)
if results:
best_match = results[0]
return best_match["path"]
else:
return 0
class LimokaAPI:
async def get_all_modules(self) -> dict:
async with aiohttp.ClientSession() as session:
async with session.get(
"https://git.vsecoder.dev/root/limoka/-/raw/main/modules.json"
) as response:
text = await response.text()
return json.loads(text)
async def get_module_raw(self, module_path: str) -> str:
async with aiohttp.ClientSession() as session:
async with session.get(
f"https://git.vsecoder.dev/root/limoka/-/raw/main/{module_path}"
) as response:
return await response.text()
@loader.tds
class Limoka(loader.Module):
"""Hikka modules are now in one place with easy searching!"""
strings = {
"name": "Limoka",
"wait": (
"Just wait"
"\n<emoji document_id=5404630946563515782>🔍</emoji> A search is underway among {count} modules for the query: <code>{query}</code>"
"\n"
"\n<i>{fact}</i>"
),
"found": (
"<emoji document_id=5413334818047940135>🔍</emoji> Found the module <b>{name}</b> by query: <b>{query}</b>"
"\n"
"\n<b><emoji document_id=5418376169055602355></emoji> Description:</b> {description}"
"\n<b><emoji document_id=5418299289141004396>🧑‍💻</emoji> Developer:</b> {username}"
"\n\n{commands}"
"\n<emoji document_id=5411143117711624172>🪄</emoji> <code>{prefix}dlm https://git.vsecoder.dev/root/limoka/-/raw/main/{module_path}</code>"
),
"command_template": "{emoji} <code>{prefix}{command}</code> {description}\n",
"emojis": {
1: "<emoji document_id=5416037945909987712>1⃣</emoji>",
2: "<emoji document_id=5413855071731470617>2⃣</emoji>",
3: "<emoji document_id=5416068826724850291>3⃣</emoji>",
4: "<emoji document_id=5415843998071803071>4⃣</emoji>",
5: "<emoji document_id=5415684843763686989>5⃣</emoji>",
6: "<emoji document_id=5415975458430796879>6⃣</emoji>",
7: "<emoji document_id=5415769763857060166>7⃣</emoji>",
8: "<emoji document_id=5416006506749383505>8⃣</emoji>",
9: "<emoji document_id=5415963015910544694>9⃣</emoji>",
},
"404": "<emoji document_id=5210952531676504517>❌</emoji> <b>Not found by query: <i>{query}</i></b>",
"noargs": "<emoji document_id=5210952531676504517>❌</emoji> <b>No args</b>",
"?": "<emoji document_id=5951895176908640647>🔎</emoji> Request too short / not found",
"no_info": "No information",
"facts": [
"<emoji document_id=5472193350520021357>🛡</emoji> The limoka catalog is carefully moderated!",
"<emoji document_id=5940434198413184876>🚀</emoji> Limoka performance allows you to search for modules quickly!",
],
}
strings_ru = {
"wait": (
"Подождите"
"\n<emoji document_id=5404630946563515782>🔍</emoji> Идёт поиск среди {count} модулей по запросу: <code>{query}</code>"
"\n"
"\n<i>{fact}</i>"
),
"found": (
"<emoji document_id=5413334818047940135>🔍</emoji> Найден модуль <b>{name}</b> по запросу: <b>{query}</b>"
"\n"
"\n<b><emoji document_id=5418376169055602355></emoji> Описание:</b> {description}"
"\n<b><emoji document_id=5418299289141004396>🧑‍💻</emoji> Разработчик:</b> {username}"
"\n"
"\n{commands}"
"\n"
"\n<emoji document_id=5411143117711624172>🪄</emoji> <code>{prefix}dlm https://git.vsecoder.dev/root/limoka/-/raw/main/{module_path}</code>"
),
"command_template": "{emoji} <code>{prefix}{command}</code> {description}\n",
"404": "<emoji document_id=5210952531676504517>❌</emoji> <b>Не найдено по запросу: <i>{query}</i></b>",
"noargs": "<emoji document_id=5210952531676504517>❌</emoji> <b>Нет аргументов</b>",
"?": "<emoji document_id=5951895176908640647>🔎</emoji> Запрос слишком короткий / не найден",
"no_info": "Нет информации",
"facts": [
"<emoji document_id=5472193350520021357>🛡</emoji> Каталог лимоки тщательно модерируется!",
"<emoji document_id=5940434198413184876>🚀</emoji> Производительность лимоки позволяет вам искать модули с невероятной скоростью",
],
}
async def client_ready(self, client, db):
self.client = client
self.db = db
def __init__(self):
self.api = LimokaAPI()
@loader.command()
async def limoka(self, message: Message):
"""[query] - Search module"""
args = utils.get_args_raw(message)
if len(args) <= 1:
return await utils.answer(message, self.strings["?"])
if not args:
return await utils.answer(message, self.strings["noargs"])
modules = await self.api.get_all_modules()
await utils.answer(
message,
self.strings["wait"].format(
count=len(modules),
fact=random.choice(self.strings["facts"]),
query=args,
),
)
modules = await self.api.get_all_modules()
contents = []
for module_path, module_data in modules.items():
contents.append(
{
"id": module_path,
"content": module_data["name"],
}
)
for module_path, module_data in modules.items():
contents.append(
{
"id": module_path,
"content": module_data["description"],
}
)
for module_path, module_data in modules.items():
for func in module_data["commands"]:
for command, description in func.items():
contents.append({"id": module_path, "content": command})
contents.append({"id": module_path, "content": description})
searcher = Search(args.lower())
try:
result = searcher.search_module(contents)
except IndexError:
return await utils.answer(message, self.strings["?"])
module_path = result
if module_path is None or module_path == 0:
return await utils.answer(message, self.strings["404"].format(query=args))
module_info = modules[module_path]
dev_username = module_info["meta"].get("developer", "Unknown")
commands = []
command_count = 0
end_count_cmds = False
for func in module_info["commands"]:
if end_count_cmds:
break
for command, description in func.items():
if command_count == 9:
commands.append("...")
end_count_cmds = True
break
command_count += 1
emoji = self.strings["emojis"].get(command_count, "")
commands.append(
self.strings["command_template"].format(
prefix=self.get_prefix(),
command=html.escape(command.replace("cmd", "")),
emoji=emoji,
description=(
html.escape(description)
if description
else self.strings["no_info"]
),
)
)
name = module_info["name"]
description = (
html.escape(module_info["description"])
if module_info["description"]
else self.strings["no_info"]
)
banner = module_info["meta"]["banner"]
if description:
translated_desc = await self._client.translate(
message.peer_id,
message,
to_lang=self._db.get("hikka.translations", "lang", "en")[0:2],
raw_text=description,
entities=message.entities,
)
try:
await utils.answer_file(
message,
banner,
self.strings["found"].format(
query=args,
name=name if name else self.strings["no_info"],
description=(
translated_desc if description else self.strings["no_info"]
),
username=dev_username,
commands="".join(commands),
prefix=self.get_prefix(),
module_path=module_path.replace("\\", "/"),
),
)
except Exception:
await utils.answer(
message,
self.strings["found"].format(
query=args,
name=name if name else self.strings["no_info"],
description=(
translated_desc if description else self.strings["no_info"]
),
username=dev_username,
commands="".join(commands),
prefix=self.get_prefix(),
module_path=module_path,
),
)

View File

@@ -0,0 +1,162 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/color/256/kakashi-hatake.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/color/256/kakashi-hatake.png&title=MangaSlider&description=Read%20manga%20in%20Telegram%20%F0%9F%91%8D
__version__ = (2, 0, 1)
import logging
from aiogram.types import Message as AiogramMessage
from .. import loader # type: ignore
from ..inline.types import InlineCall # type: ignore
import requests
logger = logging.getLogger(__name__)
@loader.tds
class MangaSliderMod(loader.Module):
strings = {"name": "MangaSlider"}
async def client_ready(self, client, db):
self.client = client
self.__doc__ = (
"Модуль для чтения манги 👨‍💻[beta]\n\n🔗 Ссылка:"
f" t.me/{self.inline.bot_username}?start=manga\n\n"
"В будущем ожидается или перенос в бота, "
"или добавление полноценного функционала."
)
async def requests(self, data):
_api = "https://api.newmanga.org/"
_storage = "https://storage.newmanga.org/"
_all_chapters = _api + "v3/branches/{}/chapters/all" # paste manga id
_all_pages = _api + "v3/chapters/{}/pages" # paste chapter id
_image = (
_storage + "origin_proxy/{}/{}/{}"
) # paste disk name, chapter id and file name
chapters = requests.get(_all_chapters.format(data["name"])).json()
charapter = chapters[data["chapter"]]
charapter_id = charapter["id"]
disk = charapter["origin"]
tom = charapter["tom"]
pages_count = charapter["pages"]
if data["page"] > pages_count:
return {"error": "❗️ Это последняя страница"}
pages = requests.get(_all_pages.format(charapter_id)).json()
page = pages["pages"][data["page"]]["slices"][0]["path"]
return {
"image": _image.format(disk, charapter_id, page),
"page": f"{data['page'] + 1}/{pages_count}",
"chapter": f"{data['chapter'] + 1}/{len(chapters)}",
"tom": tom,
"error": None,
}
async def _markup(self, data):
return self.inline.generate_markup(
[
[
{
"text": "◀️",
"data": f"manga/undo/{data['name']}/{data['page']}/{data['chapter']}",
},
{
"text": "▶️",
"data": f"manga/next/{data['name']}/{data['page']}/{data['chapter']}",
},
],
[
{
"text": "▶️ Следующая глава",
"data": f"manga/next_chapter/{data['name']}/{data['page']}/{data['chapter']}",
}
],
]
)
async def aiogram_watcher(self, message: AiogramMessage):
if self._client._tg_id == message.chat.id and message.text:
if message.text == "/start manga":
await self.inline.bot.send_message(
self._tg_id,
"""
👨‍💻 <b>Привет, чтобы продолжить введи <code>/read</code> с параметром - номером манги, который можно получить с сайта https://newmanga.org, пример:</b>
▪️ Клинок, рассекающий демонов - https://newmanga.org/p/blade-of-demon-destruction/<code>4774</code>/r/85016
Для чтения манги введите команду <code>/read 4774</code> что бы начать с первой главы
""",
)
elif message.text.split(" ")[0] == "/read":
args = message.text.split(" ")
if len(args) != 2:
return await self.inline.bot.send_message(
self._tg_id, "❗️ Неправильно указан агрумент"
)
page = 0
data = {"name": args[1], "page": page, "chapter": 0}
_markup = await self._markup(data)
r = await self.requests(data)
await self.inline.bot.send_photo(
self._tg_id,
r["image"],
r["page"],
reply_markup=_markup,
)
async def feedback_callback_handler(self, call: InlineCall):
if not call.data.startswith("manga"):
return
args = call.data.replace("manga/", "").split("/")
data = {
"name": args[1],
"page": int(args[2]),
"chapter": int(args[3]),
}
if args[0] == "undo":
if data["page"] == 0:
return await self.inline.bot.answer_callback_query(
call.id, "❗️ Это первая страница"
)
data["page"] -= 1
elif args[0] == "next":
data["page"] += 1
elif args[0] == "next_chapter":
data["page"] = 0
data["chapter"] += 1
_markup = await self._markup(data)
r = await self.requests(data)
if r["error"]:
return await self.inline.bot.answer_callback_query(call.id, r["error"])
text = f"<b>📚 Том</b>: {r['tom']}\n<b>📙 Глава:</b> {r['chapter']}\n<b>📄 Страница:</b> {r['page']}"
await self.inline.bot.send_photo(
self._tg_id, r["image"], text, reply_markup=_markup
)
await self.inline.bot.delete_message(self._tg_id, call.message.message_id)

View File

@@ -0,0 +1 @@
https://t.me/vsecoder_m

View File

@@ -0,0 +1,90 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/external-flaticons-lineal-color-flat-icons/344/external-roulette-casino-flaticons-lineal-color-flat-icons-3.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/external-flaticons-lineal-color-flat-icons/344/external-roulette-casino-flaticons-lineal-color-flat-icons-3.png&title=Russian%20roulette&description=Telegram%20Russian%20roulette%20game
__version__ = (2, 3, 2)
import logging
import asyncio
import random
from .. import loader, utils # type: ignore
from telethon import functions
logger = logging.getLogger(__name__)
@loader.tds
class RussianRouletteMod(loader.Module):
"""Module for "Russian roulette" game"""
strings = {
"name": "Russian roulette",
"cfg_lingva_url": (
"1/8 chance of destroying the account, are you taking a chance or are you"
" afraid?)"
),
"answer": "😒 You're lucky, but only now...",
"answer2": "😏 * the sound of a gunshot *",
"answer3": "🤨 Were you seriously expecting account deletion?",
"error": "😡 Ah, EMAE, the revolver broke...",
"cfg_real": "If set to `True`, if you lose, your account will be deleted",
}
strings_ru = {
"cfg_lingva_url": "Шанс 1/8 на уничтожение аккаунта, рискнешь или боишься?)",
"answer": "😒 Тебе повезло, но только сейчас...",
"answer2": "😏 * звук выстрела *",
"answer3": "🤨 Ты серьёзно ожидал удаление аккаунта?",
"error": "😡 Ах, ЁМАЁ, сломался то, револьвер...",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"real",
False,
self.strings["cfg_real"],
validator=loader.validators.Link(),
)
)
self.name = self.strings["name"]
async def client_ready(self, client, db):
self.client = client
self.db = db
@loader.unrestricted
@loader.ratelimit
async def revolvercmd(self, message):
"""
- to start "Russian roulette"
"""
try:
roulette = [1]
roulette.extend(0 for _ in range(7))
result = random.choice(roulette)
if result != 1:
await utils.answer(message, self.strings["answer"])
else:
await utils.answer(message, self.strings["answer2"])
await asyncio.sleep(3)
await utils.answer(message, "gg")
if self.config["real"]:
self.client(
functions.account.DeleteAccountRequest(
reason="Lose in Russian roulette"
)
)
except Exception:
await utils.answer(message, self.strings["error"])

View File

@@ -0,0 +1,185 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/fluency/344/timer.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/fluency/344/timer.png&title=Account%20Time&description=Get%20the%20account%20registration%20date%20and%20time!
__version__ = (2, 5, 0)
import logging
import asyncio
from typing import Callable, Tuple
import time
from dateutil.relativedelta import relativedelta
import numpy as np
from datetime import datetime
from .. import loader, utils # type: ignore
data = {
"1000000": 1380326400,
"2768409": 1383264000,
"7679610": 1388448000,
"11538514": 1391212000,
"15835244": 1392940000,
"23646077": 1393459000,
"38015510": 1393632000,
"44634663": 1399334000,
"46145305": 1400198000,
"54845238": 1411257000,
"63263518": 1414454000,
"101260938": 1425600000,
"101323197": 1426204000,
"111220210": 1429574000,
"103258382": 1432771000,
"103151531": 1433376000,
"116812045": 1437696000,
"122600695": 1437782000,
"109393468": 1439078000,
"112594714": 1439683000,
"124872445": 1439856000,
"130029930": 1441324000,
"125828524": 1444003000,
"133909606": 1444176000,
"157242073": 1446768000,
"143445125": 1448928000,
"148670295": 1452211000,
"152079341": 1453420000,
"171295414": 1457481000,
"181783990": 1460246000,
"222021233": 1465344000,
"225034354": 1466208000,
"278941742": 1473465000,
"285253072": 1476835000,
"294851037": 1479600000,
"297621225": 1481846000,
"328594461": 1482969000,
"337808429": 1487707000,
"341546272": 1487782000,
"352940995": 1487894000,
"369669043": 1490918000,
"400169472": 1501459000,
"616816630": 1529625600,
"727572658": 1543708800,
"782000000": 1546300800,
"925078064": 1563290000,
"1974255900": 1634000000,
"3318845111": 1618028800,
"4317845111": 1620028800,
"5336336790": 1646368100,
"5396587273": 1648014800,
"6020888206": 1675534800,
"6057123350": 1676198350,
"6554264430": 1695654800,
}
class Function:
def __init__(self, order: int = 3):
self.order = 3
self.x, self.y = self._unpack_data()
self._func = self._fit_data()
def _unpack_data(self) -> Tuple[list, list]:
x_data = np.array(list(map(int, data.keys())))
y_data = np.array(list(data.values()))
return (x_data, y_data)
def _fit_data(self) -> Callable[[int], int]:
fitted = np.polyfit(self.x, self.y, self.order)
return np.poly1d(fitted)
def add_datapoint(self, pair: tuple):
pair[0] = str(pair[0])
data.update([pair])
# update the model with new data
# self.x, self.y = self._unpack_data()
self._func = self._fit_data()
def func(self, tg_id: int) -> int:
value = self._func(tg_id)
current = time.time()
if value > current:
value = current
return value
logger = logging.getLogger(__name__)
@loader.tds
class AcTimeMod(loader.Module):
"""Module for get account time"""
strings = {
"name": "Account Time",
"info": "Get the account registration date and time!",
"error": "Error!",
"answer": (
"⏳ This account: {0}\n🕰 A registered: {1}\n\nP.S. The module script is"
" trained with the number of requests from different ids, so the data can"
" be refined"
),
}
strings_ru = {
"info": "Узнай дату регистрации аккаунта, и время, которое вы его используете!",
"error": "Ошибка!",
}
def __init__(self):
self.name = self.strings["name"]
async def client_ready(self, client, db):
self.client = client
self.db = db
def time_format(self, unix_time: int, fmt="%Y-%m-%d") -> list:
result = [str(datetime.utcfromtimestamp(unix_time).strftime(fmt))]
d = relativedelta(datetime.now(), datetime.utcfromtimestamp(unix_time))
result.append(f"{d.years} years, {d.months} months, {d.days} days")
return result
@loader.unrestricted
@loader.ratelimit
async def actimecmd(self, message):
"""
- get the account registration date and time [beta]
P.S. You can also send a command in response to a message
"""
try:
interpolation = Function()
reply = await message.get_reply_message()
if reply:
date = self.time_format(
unix_time=round(interpolation.func(int(reply.sender.id)))
)
else:
date = self.time_format(
unix_time=round(interpolation.func(int(message.from_id)))
)
await utils.answer(message, self.strings["answer"].format(date[0], date[1]))
except Exception as e:
await utils.answer(message, f'{self.strings["error"]}\n\n{e}')
if message.out:
await asyncio.sleep(5)
await message.delete()

View File

@@ -0,0 +1,126 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/color/344/asc.png
# requires: Pillow
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/color/344/asc.png&title=AsciiMod&description=Module%20for%20convert%20image%20to%20ascii
__version__ = (0, 0, 1)
import logging
from .. import loader, utils # type: ignore
import imgkit # type: ignore
from PIL import Image
from image2ascii.core import Image2ASCII # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class AsciiMod(loader.Module):
"""Module for convert image to ascii"""
strings = {
"name": "AsciiMod",
"loading_image": "⏳ Downloading image...",
"converting_image": "⏳ Converting image...",
"save_image": "⏳ Saving image...",
"os_error": (
"❗️ Install 'wkhtmltopdf'\n\n.terminal sudo apt install wkhtmltopdf"
),
"type_error": "❗️ Unknown image format!",
"another_error": "❗️ Unknown error, please check logs!\n\n{}",
"complete": "🖍 Look:",
}
strings_ru = {
"loading_image": "⏳ Скачивается изображение...",
"converting_image": "⏳ Конвертируется изображение...",
"save_image": "⏳ Сохраняется изображение...",
"os_error": (
"❗️ Установите 'wkhtmltopdf'\n\n.terminal sudo apt install wkhtmltopdf"
),
"type_error": "❗️ Неизвестный формат изображения!",
"another_error": "❗️ Неизвестная ошибка, пожайлуйста проверьте логи!\n\n{}",
"complete": "🖍 Смотри:",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"background",
"black",
lambda m: "Background",
),
loader.ConfigValue(
"color",
"white",
lambda m: "Color",
),
)
self.name = self.strings["name"]
async def client_ready(self, client, db):
self.client = client
self.db = db
@loader.unrestricted
@loader.ratelimit
async def asciicmd(self, message):
"""
<reply_to_image> - convert image to ascii
"""
try:
reply = await message.get_reply_message()
await utils.answer(message, self.strings["loading_image"])
f = await self._client.download_media(message=reply, file="test.png")
await utils.answer(message, self.strings["converting_image"])
r = Image2ASCII("test.png").render()
background = self.config["background"]
color = self.config["color"]
im = Image.open("test.png")
width, height = im.size
options = {"crop-w": width, "crop-h": height, "encoding": "UTF-8"}
ascii = "".join(
str(line).replace(" ", "&nbsp;") + "<br>" for line in str(r).split("\n")
)
await utils.answer(message, self.strings["save_image"])
with open("test.html", "w") as f:
f.write(
f'<html style="background: {background}; color:'
f' {color}"><code>{ascii}</code></html>'
)
try:
imgkit.from_file("test.html", "out.jpg", options=options)
await self._client.send_file(
utils.get_chat_id(message),
open("out.jpg", "rb"),
)
await utils.answer(message, self.strings["complete"])
except OSError:
await utils.answer(message, self.strings["os_error"])
except TypeError:
await utils.answer(message, self.strings["type_error"])
except Exception as e:
await utils.answer(message, self.strings["another_error"].format(e))

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,34 @@
<!doctype html>
<html lang="ru" style="background-color: gray;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Profile</title>
</head>
<body style="width: 540px;height: 220px; background-image: url({});color: black;background-size: 100% 100%;">
<div class="container">
<div class="row align-items-center justify-content-center">
<div class="col-4" style="padding: 6px;">
<img src="data:image/png;base64, {}" class="img-thumbnail" alt="...">
</div>
<div class="col-8 row align-items-center justify-content-center" style="padding: 6px;">
<div class="col-5" style="padding: 6px;">
<h1>{}</h1>
<p>Chats: {}</p>
<p>Channels: {}</p>
<p>Users: {}</p>
<p>Bots: {}</p>
</div>
<div class="col-7" style="padding: 15px;">
<p>{}</p>
</div>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,95 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/external-filled-outline-wichaiwi/344/external-page-uxui-design-filled-outline-wichaiwi.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/external-filled-outline-wichaiwi/344/external-page-uxui-design-filled-outline-wichaiwi.png&title=BioPage&description=Module%20for%20create%20bio%20page
__version__ = (2, 0, 0)
import logging
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class BioPageMod(loader.Module):
"""Module for create bio page"""
strings = {
"name": "Bio Page",
"answer": (
'📦 The configuration of the <b>BioPage</b> is set to <code>"{0}"</code>'
),
"error": "❗️ Error, check logs!",
}
strings_ru = {
"answer": '📦 Конфигурация <b>BioPage</b> установлена <code>"{0}"</code>',
"error": "❗️ Ошибка, проверьте логи!",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"toggle",
"off",
"Toggle bio page on/off",
validator=loader.validators.Boolean(),
),
loader.ConfigValue(
"bio_url",
"https://vsecoder.github.io/tg-web-app/",
"Bio page url (restart required to apply)",
validator=loader.validators.Link(),
),
)
self.name = self.strings["name"]
async def client_ready(self, client, db):
self._db = db
self._client = client
self.botfather = "@BotFather"
async def bot_conifg(self):
if self.config["toggle"]:
async with self._client.conversation(self.botfather) as conv:
await conv.send_message("/setmenubutton")
await conv.mark_read()
await conv.send_message(f"@{self.inline.bot_username}")
await conv.mark_read()
await conv.send_message(self.config["bio_url"])
await conv.mark_read()
await conv.send_message("🔗 Bio")
await conv.mark_read()
else:
async with self._client.conversation(self.botfather) as conv:
await conv.send_message("/setmenubutton")
await conv.mark_read()
await conv.send_message(f"@{self.inline.bot_username}")
await conv.mark_read()
await conv.send_message("/empty")
await conv.mark_read()
@loader.unrestricted
@loader.ratelimit
async def biotogglecmd(self, message):
"""
- toggle bio page(default: off)
Based on... my code)
"""
self.config["toggle"] = not self.config["toggle"]
await self.bot_conifg()
await utils.answer(
message,
self.strings["answer"].format("on" if self.config["toggle"] else "off"),
)

View File

@@ -0,0 +1,121 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/color/344/calculate.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/color/344/calculate.png&title=Calc&description=Module%20for%20inline%20calc
__version__ = (2, 0, 0)
import logging
from telethon import TelegramClient
from .. import loader # type: ignore
from ..inline.types import InlineCall # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class CalcMod(loader.Module):
"""Module for inline calc"""
strings = {
"name": "📟 Calc",
"answer": "🧮 <b>Start calculating(press inline buttons):</b>",
"calc": "🧮 <i>{0}</i>=<code>{1}</code>",
"error": "❗️ Error!",
}
strings_ru = {
"answer": "🧮 <b>Начните подсчитывать(нажимайте на инлайн кнопки):</b>",
"error": "❗️ Ошибка!",
}
async def client_ready(self, client: TelegramClient, db):
self._db = db
self._client = client
async def return_keyboard(self, expression):
return [
# 1⃣2⃣3⃣4⃣5⃣6⃣7⃣8⃣9⃣0⃣➗✖🧮
[
{"text": "1", "callback": self.calc, "args": ["1", expression]},
{"text": "2", "callback": self.calc, "args": ["2", expression]},
{"text": "3", "callback": self.calc, "args": ["3", expression]},
],
[
{"text": "4", "callback": self.calc, "args": ["4", expression]},
{"text": "5", "callback": self.calc, "args": ["5", expression]},
{"text": "6", "callback": self.calc, "args": ["6", expression]},
],
[
{"text": "7", "callback": self.calc, "args": ["7", expression]},
{"text": "8", "callback": self.calc, "args": ["8", expression]},
{"text": "9", "callback": self.calc, "args": ["9", expression]},
],
[
{"text": "", "callback": self.calc, "args": ["+", expression]},
{"text": "0", "callback": self.calc, "args": ["0", expression]},
{"text": "", "callback": self.calc, "args": ["-", expression]},
],
[
{"text": "", "callback": self.calc, "args": ["/", expression]},
{"text": "🔙", "callback": self.calc, "args": ["C", expression]},
{"text": "✖️", "callback": self.calc, "args": ["*", expression]},
],
]
async def calc(self, message: InlineCall, press, expression):
if expression == "0":
expression = ""
if press == "C":
expression = expression[:-1]
else:
expression = expression + press
a = ["+", "-", "*", "/", "=", "C"]
b = False
for i in a:
if press == i:
b = True
if b is False and expression != "" and expression != "0":
result = eval(expression)
else:
result = ""
text = self.strings["calc"].format(expression, result)
keyboard = await self.return_keyboard(expression)
await message.edit(text=text, reply_markup=keyboard)
@loader.unrestricted
@loader.ratelimit
async def calccmd(self, message):
"""
- init calc
Based on... my code)
"""
text = self.strings["answer"]
expression = "0"
keyboard = await self.return_keyboard(expression)
await message.delete()
await self.inline.form(
text=text,
message=message,
always_allow=[message.from_id],
reply_markup=keyboard,
)

View File

@@ -0,0 +1,110 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
version = (1, 0, 0)
from telethon import functions
from telethon.tl.types import Message
import asyncio
import logging
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class ChatGPTfreeMod(loader.Module):
"""
Бесплатный модуль для ChatGPT
https://t.me/Jarvis_IT_Assistant_bot
Сначала запустите бота и отключите уведомления
"""
strings = {
"name": "ChatGPTfree",
"loading": "🔄 Ваш запрос обрабатывается...",
"no_args": "🚫 Не указан текст для обработки!",
"start_text": "<b>🤖 ChatGPT:</b>\n",
"context_text": "❕ Создался новый диалог. Предыдущие запросы удалены.",
}
def __init__(self):
self.name = self.strings["name"]
async def client_ready(self, client, db):
self.client = client
self.db = db
self.gpt_free = "@Jarvis_IT_Assistant_bot"
async def message_q(
self,
text: str,
user_id: int,
mark_read: bool = False,
delete: bool = False,
ignore_answer: bool = False,
):
"""Отправляет сообщение и возращает ответ"""
async with self.client.conversation(user_id) as conv:
msg = await conv.send_message(text)
while True:
await asyncio.sleep(1)
response = await conv.get_response()
if mark_read:
await conv.mark_read()
if delete:
await msg.delete()
await response.delete()
if ignore_answer:
return response
if "✅ Запрос отправлен" in response.text:
continue
if "Ожидание ответа" in response.text:
continue
return response
async def chatgptfreecmd(self, message: Message):
"""
{text} - обработать текст через ChatGPT
"""
args = utils.get_args_raw(message)
if not args:
return await utils.answer(message, self.strings["no_args"])
await utils.answer(message, self.strings["loading"])
response = await self.message_q(
args, self.gpt_free, mark_read=True, delete=True, ignore_answer=False
)
text = self.strings["start_text"] + response.text.replace(
"/context", "<code>.contextgpt</code>"
)
return await utils.answer(message, text)
async def contextgptcmd(self, message: Message):
"""
- сбросить диалог и начать новый
"""
await self.message_q(
"/context", self.gpt_free, mark_read=True, delete=True, ignore_answer=True
)
return await utils.answer(message, self.strings["context_text"])

View File

@@ -0,0 +1,158 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/fluency/344/feedback.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/fluency/344/feedback.png&title=Feedback&description=Feedback%20bot%20for%20Hikka%20modules
__version__ = (3, 0, 1)
import logging
import time
from telethon.utils import get_display_name
from aiogram.types import Message as AiogramMessage
from .. import loader, utils # type: ignore
from ..inline.types import InlineCall # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class FeedbackBotMod(loader.Module):
"""FeedbackBot"""
strings = {
"name": "📥 Feedback",
"start": "✌️ Hi, I'm feedback bot as {}",
"fb_message": "📝 Take to send message",
"wait": "⏳ You can send next message in {} second(-s)",
"start_feedback": (
"📝 Write 1 message, and I'll send it to {}\n\n[{} per minute]"
),
"sent": "📩 Message sent",
"banned": "🚫 You are banned",
"user_banned": "🚫 {} is banned",
}
strings_ru = {
"start": "✌️ Привет, я бот обратной связи {}",
"fb_message": "📝 Нажмите для отправки сообщения",
"wait": "⏳ Вы можете отправить сообщение через {} секунд(-ы)",
"start_feedback": "📝 Напишите сообщение, и я отправлю его {}\n\n[{} в минуту]",
"sent": "📩 Сообщение отправлено",
"banned": "🚫 Вы забанены",
"user_banned": "🚫 {} забанен",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"ratelimit",
"1",
"Rate limit(in minutes)",
validator=loader.validators.Integer(minimum=0),
)
)
self.name = self.strings["name"]
async def client_ready(self, client, db):
self._client = client
self._name = utils.escape_html(get_display_name(await client.get_me()))
self._ratelimit = {}
self._ban_list = []
self.__doc__ = (
"Module from add feedback bot 👨‍💻\n\n"
"📝 Dev: @vsecoder\n"
"📥 Source: github.com/vsecoder/hikka_modules"
f"🔗 Feedback link: t.me/{self.inline.bot_username}?start=feedback\n\n"
'❌ Toggle in .security "✅ Everyone (inline)" to use'
)
async def aiogram_watcher(self, message: AiogramMessage):
if message.text == "/start feedback":
if str(message.from_user.id) in map(str, self._ban_list):
return await message.answer(self.strings["banned"])
_markup = self.inline.generate_markup(
{"text": self.strings["fb_message"], "data": "fb_message"}
)
await message.answer(
self.strings["start"].format(self._name),
reply_markup=_markup,
)
if self.inline.gs(message.from_user.id) == "fb_send_message":
await self.inline.bot.forward_message(
self._tg_id,
message.chat.id,
message.message_id,
)
_markup = self.inline.generate_markup(
{"text": "🚫 Ban", "data": f"fb_ban/{message.from_user.id}"}
)
await self.inline.bot.send_message(
self._tg_id,
f"{message.chat.id}",
reply_markup=_markup,
)
await message.answer(self.strings["sent"])
self._ratelimit[message.from_user.id] = (
time.time() + self.config["ratelimit"] * 60
)
self.inline.ss(message.from_user.id, False)
@loader.inline_everyone
async def feedback_callback_handler(self, call: InlineCall):
if call.data == "fb_cancel":
self.inline.ss(call.from_user.id, False)
await self.inline.bot.delete_message(
call.message.chat.id,
call.message.message_id,
)
return
if call.data.split("/")[0] == "fb_ban":
fb_ban_id = call.data.split("/")[1]
if str(fb_ban_id) in str(self._ban_list):
pass
else:
self._ban_list.append(fb_ban_id)
await call.answer(self.strings["user_banned"].format(fb_ban_id))
if call.data != "fb_message":
return
if str(call.from_user.id) in str(self._ban_list):
await call.answer(
self.strings["banned"],
show_alert=True,
)
if (
call.from_user.id in self._ratelimit
and self._ratelimit[call.from_user.id] > time.time()
):
await call.answer(
self.strings["wait"].format(
self._ratelimit[call.from_user.id] - time.time()
),
show_alert=True,
)
return
self.inline.ss(call.from_user.id, "fb_send_message")
await call.answer(
self.strings["start_feedback"].format(self._name, self.config["ratelimit"]),
)

View File

@@ -0,0 +1,128 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/fluency/344/pen-1.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/fluency/344/pen-1.png&title=FormatterMod&description=Module%20for%20prettifying%20the%20formatting%20of%20messages
__version__ = (1, 0, 1)
import logging
from .. import loader, utils # type: ignore
import datetime as dt
import re
from telethon.tl.types import Message
logger = logging.getLogger(__name__)
def _copy_tl(o, **kwargs):
d = o.to_dict()
del d["_"]
d.update(kwargs)
return o.__class__(**d)
@loader.tds
class FormatterMod(loader.Module):
"""
Module for prettifying the formatting of messages 🪛
📌 For example write:
--------------------
Hi, now is {now}, today is {today}, yesterday is {yesterday}, my id is {id}, username is @{username}...
⌨️ Keyboard:
~
📥 Modules $ https://t.me/vsecoder_m
👨‍💻 Dev $ https://t.me/vsecoder
--------------------
P.S. "~" is a separator for keyboard and message.
"$" is a separator for button and link.
"""
strings = {"name": "Formatter"}
async def client_ready(self, client, db):
self._client = client
self.me = await client.get_me()
self.html = await self.import_lib(
"https://raw.githubusercontent.com/vsecoder/hikka_modules/main/libs/html2.py",
suspend_on_error=True,
)
async def watcher(self, message: Message):
if (
not isinstance(message, Message)
or not message.out
or message.text.split()
and message.text.split()[0].lower() in self.allmodules.commands
or utils.remove_html(message.text).startswith(self.get_prefix())
):
return
text = message.text
keyboard = []
if len(text.split("\n~\n")) == 2:
for key in message.raw_text.split("\n~\n")[1].split("\n"):
if len(key.split(" $ ")) == 2:
button = key.split(" $ ")[0]
CLEANR = re.compile(
"<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});"
)
link = re.sub(CLEANR, "", key.split(" $ ")[1])
keyboard.append([{"text": button, "url": link}])
text = text.split("\n~\n")[0]
""" Deleted because of bugs
raw_cut_text = message.raw_text.split("\n~\n")
logger.error(str(raw_cut_text))
entities = self.html.parse(message.text)[1]
for entity in entities.copy():
if not hasattr(entity, "offset") or not hasattr(entity, "length"):
continue
if entity.offset > len(raw_cut_text):
entities.remove(entity)
continue
if entity.offset + entity.length > len(raw_cut_text):
entities[entities.index(entity)] = _copy_tl(
entity,
length=len(raw_cut_text) - entity.offset,
)
text = self.html.unparse(raw_cut_text, entities)
"""
formats = {
"{now}": dt.datetime.now(),
"{today}": dt.date.today(),
"{yesterday}": dt.date.today() - dt.timedelta(days=1),
"{id}": self._client.tg_id,
"{username}": self.me.username,
"{phone}": self.me.phone,
"{msg}": message,
}
for key, value in formats.items():
text = text.replace(key, utils.escape_html(value))
if text and text != message.text or keyboard and keyboard != [[]]:
await utils.answer(message, text, reply_markup=keyboard)

View File

@@ -0,0 +1,23 @@
CheckMods
MangaSlider
RussianRoulette
accounttime
ascii
biopage
calc
chatgptfree
feedbackbot
formatter
googleit
hentaimanga
hypixel
lmfify
mazemod
monkeytype
octocode
profile
quotes
searx
speechcensorship
steam
hh

View File

@@ -0,0 +1,66 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/bubbles/344/google-logo.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/bubbles/344/google-logo.png&title=GoogleIT&description=Google%20search%20module%20for%20userbot
__version__ = (2, 0, 0)
import logging
import asyncio
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class GoogleItMod(loader.Module):
"""Module for google search"""
strings = {
"name": "Google it",
"cfg_searc_url": "Searcher",
"answer": "😒 I advise you to look in the search engine first: ",
"error": "Error!\n .googleit | text",
}
strings_ru = {
"cfg_searc_url": "Поисковик",
"answer": "😒 Советую для начала заглянуть в поисковик: ",
"error": "Ошибка!\n \n .googleit | text",
}
def __init__(self):
self.config = loader.ModuleConfig(
"search_url",
"https://www.google.com/search?q={query}",
self.strings["cfg_searc_url"],
)
self.name = self.strings["name"]
async def client_ready(self, client, db):
self._client = client
@loader.unrestricted
@loader.ratelimit
async def googleitcmd(self, message):
"""
{text} - text to search
"""
args = message.text.replace(f"{self.get_prefix()}googleit ", "")
if args:
url = self.config["search_url"].format(query=args).replace(" ", "+")
await utils.answer(message, f'{self.strings["answer"]}{url}')
else:
await utils.answer(message, self.strings["error"])
await asyncio.sleep(5)
await message.delete()

View File

@@ -0,0 +1,191 @@
# █▀ █░█ ▄▀█ █▀▄ █▀█ █░█░█
# ▄█ █▀█ █▀█ █▄▀ █▄█ ▀▄▀▄▀
# Copyright 2023 t.me/shadow_modules
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# meta developer: @shadow_modules, @toxicuse, @vsecoder
# meta banner: https://i.imgur.com/8UYznku.jpeg
import requests # type: ignore
from .. import loader, utils # type: ignore
from telethon.tl.types import Message # type: ignore
from ..inline.types import InlineCall # type: ignore
async def request(url: str) -> dict:
"""Manga handler"""
return (await utils.run_sync(requests.get, url)).json()["data"]
@loader.tds
class HentaiMangaMod(loader.Module):
strings = {
"name": "HentaiManga",
"message": "<b>Title:</b> <code>{title}</code>\n<b>Pages:</b> {total}\n<b>Tags:</b> {tags}\n\n"
"Command to get this manga: <code>.ghm {api} {id}</code>",
"time": "<b>Wait...</b>",
"warn-form": (
"<b>⚠️ Attention!</b>\n<b>😰 This module is 18+\n"
"✉️ In many chats it is prohibited</b>\n<b>✅ If you agree with what you can get"
"ban - click on the button below</b>"
),
"yes": "✅ Yes",
"no": "❌ No",
"args_error": "<b>Not enough arguments</b>",
"not_found": "<b>Not found</b>",
}
strings_ru = {
"message": "<b>Название:</b> <code>{title}</code>\n<b>Страниц:</b> {total}\n<b>Теги:</b> {tags}\n\n"
"Команда для получения этой манги: <code>.ghm {api} {id}</code>",
"time": "<b>Ожидайте...</b>",
"warn-form": (
"<b>⚠️ Внимание!</b>\n<b>😰 Данный модуль 18+\n"
"✉️ Во многих чатах он запрещен</b>\n<b>✅ Если вы согласны с тем что можете получить"
" бан - нажмите на кнопку ниже</b>"
),
"yes": "✅ Да",
"no": "❌ Нет",
"args_error": "<b>Недостаточно аргументов</b>",
"not_found": "<b>Не найдено</b>",
}
def __init__(self):
self.name = self.strings["name"]
self.config = loader.ModuleConfig(
loader.ConfigValue(
"janda",
"144.22.39.141:3333",
"https://github.com/sinkaroid/jandapress",
validator=loader.validators.Hidden(),
),
)
async def client_ready(self, client, db):
self.client = client
self.db = db
self.apis = {
"3hentai": {
"random": "http://{janda}/3hentai/random",
"get": "http://{janda}/3hentai/get?book={id}",
},
"asmhentai": {
"random": "http://{janda}/asmhentai/random",
"get": "http://{janda}/asmhentai/get?book={id}",
},
"hentaifox": {
"random": "http://{janda}/hentaifox/random",
"get": "http://{janda}/hentaifox/get?book={id}",
},
# now not working
# "hentai2read": {
# "random": "https://{janda}/hentai2read/random",
# "get": "https://{janda}/hentai2read/get?book={id}",
# },
# "nhentai": {
# "random": "https://{janda}/nhentai/random",
# "get": "https://{janda}/nhentai/get?book={id}",
# },
# "pururin": {
# "random": "https://{janda}/pururin/random",
# "get": "https://{janda}/pururin/get?book={id}",
# },
}
async def gallery(self, message: Message, mang: dict, api: str = "3hentai"):
await self.inline.gallery(
caption=self.strings["message"].format(
title=mang["title"].replace("[", "").replace("]", ""),
total=mang["total"],
tags=", ".join(mang["tags"]),
api=api,
id=mang["id"],
),
message=message,
next_handler=mang["image"],
)
async def warn(self, message: Message):
await self.inline.form(
message=message,
text=self.strings["warn-form"],
reply_markup=[
[
{
"text": self.strings["yes"],
"callback": self.inline_call_answer,
},
],
[
{
"text": self.strings["no"],
"callback": self.delete_module,
"args": (message,),
},
],
],
)
@loader.command(alias="rhm")
async def rnd_hentai_mangacmd(self, message: Message):
"""
{hentai_api_name: optional} - рандомная хентай-манга
"""
args = utils.get_args_raw(message).split(" ")
if not self.db.get(__name__, "warn", False):
await self.warn(message)
return
await utils.answer(message, self.strings["time"])
api = args[0] if args and args[0] in self.apis else "3hentai"
mang = await request(self.apis[api]["random"].format(janda=self.config["janda"]))
await self.gallery(message, mang, api)
@loader.command(alias="ghm")
async def get_hentai_mangacmd(self, message: Message):
"""
{hentai_api_name} {id} - получить хентай-мангу
"""
args = utils.get_args_raw(message).split(" ")
if len(args) < 2:
return await utils.answer(message, self.strings["args_error"])
if not self.db.get(__name__, "warn", False):
await self.warn(message)
return
await utils.answer(message, self.strings["time"])
if args[0] not in self.apis:
return await utils.answer(message, self.strings["args_error"])
mang = await request(self.apis[args[0]]["get"].format(id=args[1], janda=self.config['janda']))
if not mang:
return await utils.answer(message, self.strings["not_found"])
await self.gallery(message, mang, args[0])
async def inline_call_answer(self, call: InlineCall):
self.db.set(__name__, "warn", True)
await call.delete()
@loader.owner
async def delete_module(self, call: InlineCall, message):
await call.delete()
await self.invoke("unloadmod", "HentaiManga", message.peer_id)

View File

@@ -0,0 +1,570 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://avatars.githubusercontent.com/u/128410002
# meta banner: https://chojuu.vercel.app/api/banner?img=https://avatars.githubusercontent.com/u/128410002&title=HH&description=Hikkahost%20userbot%20manager%20module
import os
import enum
import aiohttp
from aiohttp import ClientConnectorError
from datetime import datetime, timezone
from typing import Union, Optional, Tuple, List, Dict
from .. import loader, utils
__version__ = (2, 0, 0)
FLAGS = {
"ad": "🇦🇩", # Андорра
"ae": "🇦🇪", # ОАЭ
"af": "🇦🇫", # Афганистан
"ag": "🇦🇬", # Антигуа и Барбуда
"ai": "🇦🇮", # Ангилья
"al": "🇦🇱", # Албания
"am": "🇦🇲", # Армения
"ao": "🇦🇴", # Ангола
"aq": "🇦🇶", # Антарктика
"ar": "🇦🇷", # Аргентина
"at": "🇦🇹", # Австрия
"au": "🇦🇺", # Австралия
"aw": "🇦🇼", # Аруба
"ax": "🇦🇽", # Аландские острова
"az": "🇦🇿", # Азербайджан
"ba": "🇧🇦", # Босния и Герцеговина
"bb": "🇧🇧", # Барбадос
"bd": "🇧🇩", # Бангладеш
"be": "🇧🇪", # Бельгия
"bf": "🇧🇫", # Буркина-Фасо
"bg": "🇧🇬", # Болгария
"bh": "🇧🇭", # Бахрейн
"bi": "🇧🇮", # Бурунди
"bj": "🇧🇯", # Бенин
"bl": "🇧🇱", # Сен-Бартельми
"bm": "🇧🇲", # Бермудские острова
"bn": "🇧🇳", # Бруней
"bo": "🇧🇴", # Боливия
"bq": "🇧🇶", # Бонэйр, Синт-Эстатиус и Саба
"br": "🇧🇷", # Бразилия
"bs": "🇧🇸", # Багамы
"bt": "🇧🇹", # Бутан
"bv": "🇧🇻", # остров Буве
"bw": "🇧🇼", # Ботсвана
"by": "🇧🇾", # Беларусь
"bz": "🇧🇿", # Белиз
"ca": "🇨🇦", # Канада
"cc": "🇨🇨", # Кокосовые (Килинг) острова
"cd": "🇨🇩", # Конго - Киншаса
"cf": "🇨🇫", # Центральноафриканская Республика
"cg": "🇨🇬", # Конго - Браззавиль
"ch": "🇨🇭", # Швейцария
"ci": "🇨🇮", # Кот-д’Ивуар
"ck": "🇨🇰", # Острова Кука
"cl": "🇨🇱", # Чили
"cm": "🇨🇲", # Камерун
"cn": "🇨🇳", # Китай
"co": "🇨🇴", # Колумбия
"cr": "🇨🇷", # Коста-Рика
"cu": "🇨🇺", # Куба
"cv": "🇨🇻", # Кабо-Верде
"cw": "🇨🇼", # Кюрасао
"cx": "🇨🇽", # остров Рождества
"cy": "🇨🇾", # Кипр
"cz": "🇨🇿", # Чехия
"de": "🇩🇪", # Германия
"dj": "🇩🇯", # Джибути
"dk": "🇩🇰", # Дания
"dm": "🇩🇲", # Доминика
"do": "🇩🇴", # Доминиканская Республика
"dz": "🇩🇿", # Алжир
"ec": "🇪🇨", # Эквадор
"ee": "🇪🇪", # Эстония
"eg": "🇪🇬", # Египет
"eh": "🇪🇭", # Западная Сахара
"er": "🇪🇷", # Эритрея
"es": "🇪🇸", # Испания
"et": "🇪🇹", # Эфиопия
"fi": "🇫🇮", # Финляндия
"fj": "🇫🇯", # Фиджи
"fk": "🇫🇰", # Фолклендские острова
"fm": "🇫🇲", # Микронезия
"fo": "🇫🇴", # Фарерские острова
"fr": "🇫🇷", # Франция
"ga": "🇬🇦", # Габон
"gb": "🇬🇧", # Великобритания
"gd": "🇬🇩", # Гренада
"ge": "🇬🇪", # Грузия
"gf": "🇬🇫", # Французская Гвиана
"gg": "🇬🇬", # Гернси
"gh": "🇬🇭", # Гана
"gi": "🇬🇮", # Гибралтар
"gl": "🇬🇱", # Гренландия
"gm": "🇬🇲", # Гамбия
"gn": "🇬🇳", # Гвинея
"gp": "🇬🇵", # Гваделупа
"gq": "🇬🇶", # Экваториальная Гвинея
"gr": "🇬🇷", # Греция
"gs": "🇬🇸", # Южная Георгия и Южные Сандвичевы острова
"gt": "🇬🇹", # Гватемала
"gu": "🇬🇺", # Гуам
"gw": "🇬🇼", # Гвинея-Бисау
"gy": "🇬🇾", # Гайана
"hk": "🇭🇰", # Гонконг
"hm": "🇭🇲", # остров Херд и острова Макдональд
"hn": "🇭🇳", # Гондурас
"hr": "🇭🇷", # Хорватия
"ht": "🇭🇹", # Гаити
"hu": "🇭🇺", # Венгрия
"id": "🇮🇩", # Индонезия
"ie": "🇮🇪", # Ирландия
"il": "🇮🇱", # Израиль
"im": "🇮🇲", # остров Мэн
"in": "🇮🇳", # Индия
"io": "🇮🇴", # Британская территория в Индийском океане
"iq": "🇮🇶", # Ирак
"ir": "🇮🇷", # Иран
"is": "🇮🇸", # Исландия
"it": "🇮🇹", # Италия
"je": "🇯🇪", # Джерси
"jm": "🇯🇲", # Ямайка
"jo": "🇯🇴", # Иордания
"jp": "🇯🇵", # Япония
"ke": "🇰🇪", # Кения
"kg": "🇰🇬", # Киргизия
"kh": "🇰🇭", # Камбоджа
"ki": "🇰🇮", # Кирибати
"km": "🇰🇲", # Коморы
"kn": "🇰🇳", # Сент-Китс и Невис
"kp": "🇰🇵", # Корейская Народно-Демократическая Республика
"kr": "🇰🇷", # Республика Корея
"kw": "🇰🇼", # Кувейт
"ky": "🇰🇾", # Каймановы острова
"kz": "🇰🇿", # Казахстан
"la": "🇱🇦", # Лаос
"lb": "🇱🇧", # Ливан
"lc": "🇱🇨", # Сент-Люсия
"li": "🇱🇮", # Лихтенштейн
"lk": "🇱🇰", # Шри-Ланка
"lr": "🇱🇷", # Либерия
"ls": "🇱🇸", # Лесото
"lt": "🇱🇹", # Литва
"lu": "🇱🇺", # Люксембург
"lv": "🇱🇻", # Латвия
"ly": "🇱🇾", # Ливия
"my": "🇲🇾",
"md": "🇲🇩",
"mv": "🇲🇻",
"mw": "🇲🇼",
"mx": "🇲🇽",
"my": "🇲🇾",
"mz": "🇲🇿",
"na": "🇳🇦",
"nc": "🇳🇨",
"ne": "🇳🇪",
"nf": "🇳🇫",
"ng": "🇳🇬",
"ni": "🇳🇮",
"nl": "🇳🇱",
"no": "🇳🇴",
"np": "🇳🇵",
"nr": "🇳🇷",
"nu": "🇳🇺",
"nz": "🇳🇿",
"om": "🇴🇲",
"pa": "🇵🇦",
"pe": "🇵🇪",
"pf": "🇵🇫",
"pg": "🇵🇬",
"ph": "🇵🇭",
"pk": "🇵🇰",
"pl": "🇵🇱",
"pm": "🇵🇲",
"pn": "🇵🇳",
"pr": "🇵🇷",
"ps": "🇵🇸",
"pt": "🇵🇹",
"pw": "🇵🇼",
"py": "🇵🇾",
"qa": "🇶🇦",
"re": "🇷🇪",
"ro": "🇷🇴",
"rs": "🇷🇸",
"ru": "🇷🇺",
"rw": "🇷🇼",
"sa": "🇸🇦",
"sb": "🇸🇧",
"sc": "🇸🇨",
"sd": "🇸🇩",
"se": "🇸🇪",
"sg": "🇸🇬",
"sh": "🇸🇭",
"si": "🇸🇮",
"sj": "🇸🇯",
"sk": "🇸🇰",
"sl": "🇸🇱",
"sm": "🇸🇲",
"sn": "🇸🇳",
"so": "🇸🇴",
"sr": "🇸🇷",
"ss": "🇸🇸",
"st": "🇸🇹",
"sv": "🇸🇻",
"sx": "🇸🇽",
"sy": "🇸🇾",
"sz": "🇸🇿",
"tc": "🇹🇨",
"td": "🇹🇩",
"tf": "🇹🇫",
"tg": "🇹🇬",
"th": "🇹🇭",
"tj": "🇹🇯",
"tk": "🇹🇰",
"tl": "🇹🇱",
"tm": "🇹🇲",
"tn": "🇹🇳",
"to": "🇹🇴",
"tr": "🇹🇷",
"tt": "🇹🇹",
"tv": "🇹🇻",
"tw": "🇹🇼",
"tz": "🇹🇿",
"ua": "🇺🇦",
"ug": "🇺🇬",
"um": "🇺🇲",
"us": "🇺🇸",
"va": "🇻🇦",
"vc": "🇻🇨",
"ve": "🇻🇪",
"vg": "🇻🇬",
"vi": "🇻🇮",
"vn": "🇻🇳",
"vu": "🇻🇺",
"wf": "🇼🇫",
"ws": "🇼🇸",
"xk": "🇽🇰",
"ye": "🇾🇪",
"yt": "🇾🇹",
"za": "🇿🇦",
"zm": "🇿🇲",
"zw": "🇿🇼",
}
class Error(enum.Enum):
critical = 500
not_found = 404
unauthorized = 403
unknown = 0
class Host:
def __init__(
self,
id: int,
name: str,
server_id: int,
port: int,
start_date: str,
end_date: str,
password_hash: str,
rate: float,
userbot: str,
):
self.id = id
self.name = name
self.server_id = server_id
self.port = port
self.start_date = datetime.strptime(start_date, "%Y-%m-%dT%H:%M:%S.%f%z")
self.end_date = datetime.strptime(end_date, "%Y-%m-%dT%H:%M:%S.%f%z")
self.userbot = userbot
self.rate = rate
class API:
async def _request(
self,
url: str,
method: str = "GET",
params: Optional[Dict] = None,
data: Optional[Dict] = None,
headers: Optional[Dict] = None,
) -> Union[Dict, List[Union[Dict, int]]]:
async with aiohttp.ClientSession() as session:
try:
async with session.request(
method, url, params=params, data=data, headers=headers
) as response:
if response.status == 200:
answer = await response.json()
if "status_code" in answer:
return [{"detail": answer["detail"]}, answer["status_code"]]
return answer if isinstance(answer, dict) else {"data": answer}
return [{"detail": await response.text()}, response.status]
except ClientConnectorError:
return [{"detail": "Connection error"}, 500]
except Exception as e:
return [{"detail": f"Unknown error: {e}"}, 500]
class HostAPI(API):
def __init__(self, url: str, token: str):
self.auth_header = {"token": token}
self._url = f"{url}/api/host"
async def check_answer(
self, res: Union[Dict, List]
) -> Tuple[bool, Union["Error", Dict]]:
if isinstance(res, list):
for error in Error:
if error.value == res[1]:
return False, error
return False, Error.unknown
return True, res
async def get_host(self, user_id: Union[str, int]) -> Union[Host, "Error"]:
route = f"{self._url}/{user_id}"
res = await self._request(route, method="GET", headers=self.auth_header)
answer = await self.check_answer(res)
if not answer[0]:
return answer[1]
host = res["host"]
return Host(**host)
async def action(self, user_id, action):
route = f"{self._url}/{user_id}"
payload = {"action": action}
await self._request(
route,
method="PUT",
params=payload,
headers=self.auth_header,
)
async def get_stats(self, user_id) -> Dict:
return await self._request(
f"{self._url}/{user_id}/stats", headers=self.auth_header
)
async def get_status(self, user_id) -> Dict:
return await self._request(
f"{self._url}/{user_id}/status", headers=self.auth_header
)
async def get_servers(self) -> List:
return await self._request(
"https://api.hikka.host/api/server/get/all-open"
)
async def get_logs(
self, tg_id: Union[str, int], lines: Union[str, int] = "all"
) -> Dict:
route = f"{self._url}/{tg_id}/logs/{lines}"
return await self._request(route, method="GET", headers=self.auth_header)
@loader.tds
class HHMod(loader.Module):
"""@hikkahost userbot manager module"""
strings = {
"name": "HH",
"info": (
"<emoji document_id=5413334818047940135>👤</emoji> <b>Info for</b> <code>{id}</code>\n\n"
"<emoji document_id=5418136591484865679>📶</emoji> <b>Status:</b> {status}\n"
"<emoji document_id=5415992848753379520>⚙️</emoji> <b>Server:</b> {server}\n"
"<emoji document_id=5416042764863293485>❤️</emoji> <b>The subscription expires after</b> <code>{days_end} days</code>\n"
"{stats}\n"
"{warns}"
),
"logs": "<emoji document_id=5411608069396254249>📄</emoji> All docker container logs from the userbot\n\n<i>In t.me/hikkahost_bot/hhapp logs more readable</i>",
"stats": "<emoji document_id=5413394354884596702>💾</emoji> <b>Used now:</b> <code>{cpu_percent}%</code> CPU, <code>{memory}MB</code> RAM\n",
"loading_info": "<emoji document_id=5416094132672156295>⌛️</emoji> Loading...",
"no_apikey": "<emoji document_id=5411402525146370107>🚫</emoji> Not specified API Key, need get token:\n\n1. Go to the @hikkahost_bot\n2. Send /token\n3. Paste token to .config HH",
"warn_sub_left": "<emoji document_id=5411402525146370107>🚫</emoji> <i>There are less than 5 days left until the end of the subscription</i>\n",
"statuses": {
"running": "🟢",
"stopped": "🔴",
},
"server": "{flag} {name}",
"not_hh": "Your userbot is not running on hikkahost, please, go to @hikkahost_bot",
"restart": "<emoji document_id=5418136591484865679>🌘</emoji> Your bot user goes to reboot",
}
strings_ru = {
"name": "HH",
"info": (
"<emoji document_id=5413334818047940135>👤</emoji> <b>Информация о</b> <code>{id}</code>\n\n"
"<emoji document_id=5418136591484865679>📶</emoji> <b>Статус:</b> {status}\n"
"<emoji document_id=5415992848753379520>⚙️</emoji> <b>Сервер:</b> {server}\n"
"<emoji document_id=5416042764863293485>❤️</emoji> <b>Подписка истечёт через</b> <code>{days_end} дней</code>\n"
"{stats}\n"
"{warns}"
),
"logs": "<emoji document_id=5411608069396254249>📄</emoji> Все логи docker контейнера от hikka\n\n<i>В t.me/hikkahost_bot/hhapp логи более читабельны</i>",
"stats": "<emoji document_id=5413394354884596702>💾</emoji> <b>Используется:</b> <code>{cpu_percent}%</code> CPU, <code>{memory}MB</code> RAM\n",
"loading_info": "<emoji document_id=5416094132672156295>⌛️</emoji> Загрузка...",
"no_apikey": "<emoji document_id=5411402525146370107>🚫</emoji> Не задан ключ API, нужно получить токен:\n\n1. Зайдите в @hikkahost_bot\n2. Отправьте /token\n3. Запишите токен в .config HH",
"warn_sub_left": "<emoji document_id=5411402525146370107>🚫</emoji> <i>Менее чем через 5 дней подписка истечёт</i>\n",
"statuses": {
"running": "🟢",
"stopped": "🔴",
},
"server": "{flag} {name}",
"not_hh": "Ваш юзербот запущен не через hikkahost, пожалуйста, зайдите в @hikkahost_bot",
"restart": "<emoji document_id=5418136591484865679>🌘</emoji> Ваш юзербот отправлен в перезагрузку",
}
def __init__(self):
self.name = self.strings["name"]
self.config = loader.ModuleConfig(
loader.ConfigValue(
"token",
None,
validator=loader.validators.Hidden(),
),
)
async def client_ready(self, client, db):
self.host = True
self.url = "https://api.hikka.host"
if "HIKKAHOST" not in os.environ:
self.host = False
await self.inline.bot.send_message(
self._tg_id, self.strings("not_hh")
)
self._client = client
self._db = db
self.me = await client.get_me()
self.bot = "@hikkahost_bot"
@loader.command(
en_doc=" - ub status",
)
async def hinfocmd(self, message):
""" - статус юзербота"""
message = await utils.answer(message, self.strings("loading_info"))
if self.config["token"] is None:
await utils.answer(message, self.strings("no_apikey"))
return
token = self.config["token"]
user_id = token.split(":")[0]
api = HostAPI(self.url, token)
host = await api.get_host(user_id)
if isinstance(host, Error):
await utils.answer(message, str(host))
return
status = await api.get_status(user_id)
stats = (await api.get_stats(user_id))["stats"]
working = True if status["status"] == "running" else False
if working:
cpu_stats = stats["cpu_stats"]
cpu_total_usage = cpu_stats['cpu_usage']['total_usage']
system_cpu_usage = cpu_stats['system_cpu_usage']
ram_usage = round(stats["memory_stats"]["usage"] / (1024 * 1024), 2)
cpu_percent = round((cpu_total_usage / system_cpu_usage) * 100.0, 2)
stats = self.strings["stats"].format(
cpu_percent=cpu_percent, memory=ram_usage
)
else:
stats = ""
end_date = host.end_date.replace(tzinfo=timezone.utc)
warns = ""
days_end = (end_date - datetime.now(timezone.utc)).days
if days_end < 5:
warns += self.strings["warn_sub_left"]
servers = (await api.get_servers())["data"]
servers_dict = {s["id"]: s for s in servers}
server = servers_dict.get(host.server_id)
server = self.strings["server"].format(
flag=FLAGS[server["country_code"]],
name=server["name"],
)
await utils.answer(
message,
self.strings["info"].format(
id=user_id,
warns=warns,
stats=stats,
server=server,
days_end=days_end,
status=self.strings["statuses"][status["status"]],
),
)
@loader.command(
en_doc=" - ub logs",
)
async def hlogscmd(self, message):
""" - логи юзербота"""
if self.config["token"] is None:
await utils.answer(message, self.strings("no_apikey"))
return
token = self.config["token"]
user_id = token.split(":")[0]
api = HostAPI(self.url, token)
data = await api.get_logs(user_id)
files_log = data["logs"].split("\\r\\n")
with open("logs.txt", "w") as log_file:
for log in files_log:
log_file.write(log + "\n")
await utils.answer_file(message, "logs.txt", self.strings("logs"))
@loader.command(
en_doc=" - ub restart",
)
async def hrestartcmd(self, message):
""" - перезагрузить юзербота"""
await utils.answer(message, self.strings("restart"))
if self.config["token"] is None:
await utils.answer(message, self.strings("no_apikey"))
return
token = self.config["token"]
user_id = token.split(":")[0]
api = HostAPI(self.url, token)
data = await api.action(user_id, "restart")
if isinstance(data, Error):
await utils.answer(message, str(data))
return

View File

@@ -0,0 +1,182 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta desc: Module for getting information about minecraft Hypixel player
# meta pic: https://img.icons8.com/cute-clipart/64/minecraft-logo.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/cute-clipart/64/minecraft-logo.png&title=Hypixel&description=Module%20for%20getting%20information%20about%20minecraft%20Hypixel%20player
__version__ = (1, 1, 1)
import logging
import aiohttp
from telethon import TelegramClient
from telethon.tl.types import Message
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
class HypixelAPI:
def __init__(self, token):
self.token = token
self.api = "https://api.hypixel.net"
self.uuid_link = "https://api.mojang.com/users/profiles/minecraft/{}"
async def _request(self, url: str, method: str = "GET") -> dict:
async with aiohttp.ClientSession() as session:
async with session.request(method, url) as response:
return await response.json()
async def get_uuid(self, nickname):
link = self.uuid_link.format(nickname)
return await self._request(link)
async def get_player_data(self, uuid):
link = f"{self.api}/player?key={self.token}&uuid={uuid}"
return await self._request(link)
async def recent_games(self, uuid):
link = f"{self.api}/recentgames?key={self.token}&uuid={uuid}"
return await self._request(link)
async def player_status(self, uuid):
link = f"{self.api}/status?key={self.token}&uuid={uuid}"
return await self._request(link)
def to_string(self, data: dict, last_games: dict, online: dict) -> str:
nick = data["player"]["displayname"]
data = data["player"]["stats"]
last_game = last_games["games"][-1]
return f"""{nick} {"🟢" if online["session"]["online"] else "🔴"}
🛌 Bedwars:
Coins: {data["Bedwars"]["coins"]}
Win Streak: {data["Bedwars"]["winstreak"]}
Games: {data["Bedwars"]["wins_bedwars"]} wins, {data["Bedwars"]["losses_bedwars"]} losses
🏝 Skywars:
Coins: {data["SkyWars"]["coins"]}
Win Streak: {data["SkyWars"]["win_streak"]}
Games: {data["SkyWars"]["wins"]} wins, {data["SkyWars"]["losses"]} losses
Kills: {data["SkyWars"]["kills"]}
🔪 Murder Mystery:
Coins: {data["MurderMystery"]["coins"]}
Games: {data["MurderMystery"]["wins"]} wins in {data["MurderMystery"]["games"]} games
Kills: {data["MurderMystery"]["kills"]}
👷 Build Battle:
Wins: {data["BuildBattle"]["wins"]}
Coins: {data["BuildBattle"]["coins"]}
⚔️ Duels:
Wins: {data["Duels"]["wins"]} wins, {data["Duels"]["losses"]} losses
Kills: {data["Duels"]["kills"]}
Coins: {data["Duels"]["coins"]}
📄 Last game: {last_game['gameType']}
"""
@loader.tds
class HypixelMod(loader.Module):
"""
Module for getting information about minecraft Hypixel player (beta)
"""
strings = {
"name": "Hypixel",
"template": '<pre><code class="language-output">{}</code></pre>',
"not_token": "Token not found in config!",
"_cfg_token": "Yandex.Music account token",
"_cfg_uuid": "Minecraft UUID",
"_cfg_nickname": "Minecraft nickname",
"guide": (
"Получите токен и запишите в .config по "
'<a href="https://developer.hypixel.net/dashboard">ссылке</a>!'
),
}
strings_ru = {
"not_token": "Токен не найден в конфиге!",
"_cfg_token": "Токен аккаунта Яндекс.Музыка",
"_cfg_uuid": "Майнкрафт UUID",
"_cfg_nickname": "Никнейм в игре",
"guide": (
"Get a token and write it in .config by "
'<a href="https://developer.hypixel.net/dashboard">link</a>!'
),
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"HypixelToken",
None,
self.strings["_cfg_token"],
validator=loader.validators.Hidden(),
),
loader.ConfigValue(
"UUID",
None,
self.strings["_cfg_uuid"],
validator=loader.validators.String(),
),
loader.ConfigValue(
"nickname",
"Pain_4986",
self.strings["_cfg_nickname"],
validator=loader.validators.String(),
),
)
async def on_dlmod(self):
if not self.get("guide_send", False):
await self.inline.bot.send_message(
self._tg_id,
self.strings["guide"],
)
self.set("guide_send", True)
async def client_ready(self, client: TelegramClient, db):
self.client = client
self.db = db
@loader.command()
async def statcmd(self, message: Message):
"""Get stats about Hypixel player"""
token = self.config["HypixelToken"]
if not token:
return await utils.answer(message, self.strings["not_token"])
hypixel = HypixelAPI(token)
uuid = self.config["UUID"]
if not self.config["UUID"]:
uuid = (await hypixel.get_uuid(self.config["nickname"]))["id"]
self.config["UUID"] = uuid
try:
data = await hypixel.get_player_data(uuid)
last_games = await hypixel.recent_games(uuid)
online = await hypixel.player_status(uuid)
except Exception as e:
return await utils.answer(message, str(e))
answer = hypixel.to_string(data, last_games, online)
return await utils.answer(message, self.strings["template"].format(answer))

View File

@@ -0,0 +1,277 @@
# this is a @hikariatama library, im modified this for my modules
# thk @hikariatama <3
import struct
from collections import deque
from html import escape
from html.parser import HTMLParser
from typing import Iterable, List, Optional, Tuple
from telethon import helpers
from telethon.tl.types import (
MessageEntityBlockquote,
MessageEntityBold,
MessageEntityCode,
MessageEntityEmail,
MessageEntityItalic,
MessageEntityMentionName,
MessageEntityPre,
MessageEntitySpoiler,
MessageEntityStrike,
MessageEntityTextUrl,
MessageEntityUnderline,
MessageEntityUrl,
TypeMessageEntity,
MessageEntityCustomEmoji,
)
from .. import loader
# Helpers from markdown.py
def _add_surrogate(text):
return "".join(
"".join(chr(y) for y in struct.unpack("<HH", x.encode("utf-16le")))
if (0x10000 <= ord(x) <= 0x10FFFF)
else x
for x in text
)
def _del_surrogate(text):
return text.encode("utf-16", "surrogatepass").decode("utf-16")
class HTMLToTelegramParser(HTMLParser):
def __init__(self):
super().__init__()
self.text = ""
self.entities = []
self._building_entities = {}
self._open_tags = deque()
self._open_tags_meta = deque()
def handle_starttag(self, tag, attrs):
self._open_tags.appendleft(tag)
self._open_tags_meta.appendleft(None)
attrs = dict(attrs)
EntityType = None
args = {}
if tag in ["strong", "b"]:
EntityType = MessageEntityBold
elif tag in ["em", "i"]:
EntityType = MessageEntityItalic
elif tag in ["tg-spoiler"]:
EntityType = MessageEntitySpoiler
elif tag == "u":
EntityType = MessageEntityUnderline
elif tag in ["del", "s"]:
EntityType = MessageEntityStrike
elif tag == "blockquote":
EntityType = MessageEntityBlockquote
elif tag == "code":
try:
# If we're in the middle of a <pre> tag, this <code> tag is
# probably intended for syntax highlighting.
#
# Syntax highlighting is set with
# <code class='language-...'>codeblock</code>
# inside <pre> tags
pre = self._building_entities["pre"]
try:
pre.language = attrs["class"][len("language-") :]
except KeyError:
pass
except KeyError:
EntityType = MessageEntityCode
# @vsecoder append start #
elif tag == "emoji":
try:
# Emoji have document_id parameter
# <emoji document_id="...">❤️</emoji>
emoji = self._building_entities["emoji"]
try:
emoji.document_id = attrs["document_id"]
except KeyError:
pass
except KeyError:
EntityType = MessageEntityCode
# @vsecoder append end #
elif tag == "pre":
EntityType = MessageEntityPre
args["language"] = ""
elif tag == "a":
try:
url = attrs["href"]
except KeyError:
return
if url.startswith("mailto:"):
url = url[len("mailto:") :]
EntityType = MessageEntityEmail
elif self.get_starttag_text() == url:
EntityType = MessageEntityUrl
else:
EntityType = MessageEntityTextUrl
args["url"] = url
url = None
self._open_tags_meta.popleft()
self._open_tags_meta.appendleft(url)
if EntityType and tag not in self._building_entities:
self._building_entities[tag] = EntityType(
offset=len(self.text),
# The length will be determined when closing the tag.
length=0,
**args,
)
def handle_data(self, text):
previous_tag = self._open_tags[0] if len(self._open_tags) > 0 else ""
if previous_tag == "a":
if url := self._open_tags_meta[0]:
text = url
for tag, entity in self._building_entities.items():
entity.length += len(text)
self.text += text
def handle_endtag(self, tag):
try:
self._open_tags.popleft()
self._open_tags_meta.popleft()
except IndexError:
pass
if entity := self._building_entities.pop(tag, None):
self.entities.append(entity)
class HTMLParserLib(loader.Library):
developer = "@hikariatama" # and @vsecoder
version = (2, 0, 0)
def parse(self, html: str) -> Tuple[str, List[TypeMessageEntity]]:
"""
Parses the given HTML message and returns its stripped representation
plus a list of the MessageEntity's that were found.
:param html: the message with HTML to be parsed.
:return: a tuple consisting of (clean message, [message entities]).
"""
if not html:
return html, []
parser = HTMLToTelegramParser()
parser.feed(_add_surrogate(html))
text = helpers.strip_text(parser.text, parser.entities)
return _del_surrogate(text), parser.entities
def unparse(
self,
text: str,
entities: Iterable[TypeMessageEntity],
_offset: int = 0,
_length: Optional[int] = None,
) -> str:
"""
Performs the reverse operation to .parse(), effectively returning HTML
given a normal text and its MessageEntity's.
:param text: the text to be reconverted into HTML.
:param entities: the MessageEntity's applied to the text.
:return: a HTML representation of the combination of both inputs.
"""
if not text:
return text
elif not entities:
return escape(text)
text = _add_surrogate(text)
if _length is None:
_length = len(text)
html = []
last_offset = 0
for i, entity in enumerate(entities):
if entity.offset >= _offset + _length:
break
relative_offset = entity.offset - _offset
if relative_offset > last_offset:
html.append(escape(text[last_offset:relative_offset]))
elif relative_offset < last_offset:
continue
skip_entity = False
length = entity.length
# If we are in the middle of a surrogate nudge the position by +1.
# Otherwise we would end up with malformed text and fail to encode.
# For example of bad input: "Hi \ud83d\ude1c"
# https://en.wikipedia.org/wiki/UTF-16#U+010000_to_U+10FFFF
while helpers.within_surrogate(text, relative_offset, length=_length):
relative_offset += 1
while helpers.within_surrogate(
text, relative_offset + length, length=_length
):
length += 1
entity_text = self.unparse(
text=text[relative_offset : relative_offset + length],
entities=entities[i + 1 :],
_offset=entity.offset,
_length=length,
)
entity_type = type(entity)
if entity_type == MessageEntityBold:
html.append("<strong>{}</strong>".format(entity_text))
elif entity_type == MessageEntityItalic:
html.append("<em>{}</em>".format(entity_text))
elif entity_type == MessageEntitySpoiler:
html.append("<tg-spoiler>{}</tg-spoiler>".format(entity_text))
elif entity_type == MessageEntityCode:
html.append("<code>{}</code>".format(entity_text))
elif entity_type == MessageEntityUnderline:
html.append("<u>{}</u>".format(entity_text))
elif entity_type == MessageEntityStrike:
html.append("<del>{}</del>".format(entity_text))
elif entity_type == MessageEntityBlockquote:
html.append("<blockquote>{}</blockquote>".format(entity_text))
elif entity_type == MessageEntityPre:
if entity.language:
html.append(
"<pre>\n"
" <code class='language-{}'>\n"
" {}\n"
" </code>\n"
"</pre>".format(entity.language, entity_text)
)
else:
html.append("<pre><code>{}</code></pre>".format(entity_text))
# @vsecoder append start
elif entity_type == MessageEntityCustomEmoji:
html.append('<emoji document_id="{}">{}</emoji>'.format(entity.document_id, entity_text))
# @vsecoder append end
elif entity_type == MessageEntityEmail:
html.append('<a href="mailto:{0}">{0}</a>'.format(entity_text))
elif entity_type == MessageEntityUrl:
html.append('<a href="{0}">{0}</a>'.format(entity_text))
elif entity_type == MessageEntityTextUrl:
html.append(
'<a href="{}">{}</a>'.format(escape(entity.url), entity_text)
)
elif entity_type == MessageEntityMentionName:
html.append(
'<a href="tg://user?id={}">{}</a>'.format(
entity.user_id, entity_text
)
)
else:
skip_entity = True
last_offset = relative_offset + (0 if skip_entity else length)
while helpers.within_surrogate(text, last_offset, length=_length):
last_offset += 1
html.append(escape(text[last_offset:]))
return _del_surrogate("".join(html))

View File

@@ -0,0 +1,15 @@
from .. import loader
class CheckSubscribe(loader.Library):
developer = "@vsecoder"
version = (1, 0, 0)
async def check(self, client, channel_name="vsecoder_m"):
try:
channel = await client.get_entity(f"t.me/{channel_name}")
if channel.title:
return True
return False
except Exception:
return False

View File

@@ -0,0 +1,66 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2024 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/bubbles/344/google-logo.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/bubbles/344/google-logo.png&title=LMFIFY&description=Let%20me%20find%20it%20for%20you%20in%20Google%20/%20Yandex
__version__ = (2, 0, 0)
import logging
import asyncio
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class LMFIFYMod(loader.Module):
"""Let me find it for you in Google / Yandex"""
strings = {
"name": "LMFIFY",
"cfg_searc_engine": "Searcher, http://yaforyou.ru/?= or https://track24.ru/google/?q=",
"answer": "<b><emoji document_id=5276167890624585747>👩‍💻</emoji> Let me find it for you: </b><a href='{}'>👉click</a>",
"error": "Need text!",
}
strings_ru = {
"cfg_searc_url": "Поисковик, http://yaforyou.ru/?= или https://track24.ru/google/?q=",
"answer": "<b><emoji document_id=5276167890624585747>👩‍💻</emoji> Дай-ка я найду: </b><a href='{}'>👉click</a>",
"error": "Нужен текст!",
}
def __init__(self):
self.config = loader.ModuleConfig(
"search_url",
"https://track24.ru/google/?q={query}",
self.strings["cfg_searc_engine"],
)
self.name = self.strings["name"]
async def client_ready(self, client, db):
self._client = client
@loader.unrestricted
@loader.ratelimit
async def finditcmd(self, message):
"""
{text} - find it in search engine
"""
args = utils.get_args_raw(message)
if args:
url = self.config["search_url"].format(query=args).replace(" ", "+")
await utils.answer(message, self.strings["answer"].format(url))
else:
await utils.answer(message, self.strings["error"])
await asyncio.sleep(5)
await message.delete()

View File

@@ -0,0 +1,261 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/external-icongeek26-linear-colour-icongeek26/344/external-maze-game-development-icongeek26-linear-colour-icongeek26.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/external-icongeek26-linear-colour-icongeek26/344/external-maze-game-development-icongeek26-linear-colour-icongeek26.png&title=MazeMod&description=Telegram%20Maze%20Game
__version__ = (2, 0, 0)
import logging
import random
from telethon import TelegramClient
from .. import loader # type: ignore
from ..inline.types import InlineCall # type: ignore
logger = logging.getLogger(__name__)
class Maze:
def __init__(self, rowsNumber, columnsNumber):
self.rowsNumber = rowsNumber
self.columnsNumber = columnsNumber
self.maze = []
def isEven(self, number):
return number % 2 == 0
def getRandomFrom(self, array):
return random.choice(array)
def setField(self, x, y, value):
if x < 0 or x >= self.columnsNumber or y < 0 or y >= self.rowsNumber:
return False
self.maze[y][x] = value
def getField(self, x, y):
if x < 0 or x >= self.columnsNumber or y < 0 or y >= self.rowsNumber:
return False
return self.maze[y][x]
def isMaze(self):
for x in range(self.columnsNumber):
for y in range(self.rowsNumber):
if self.isEven(x) and self.isEven(y) and self.getField(x, y) == "⬜️":
return False
return True
def moveTractor(self, tractor):
directs = []
if tractor["x"] > 0:
directs.append("left")
n = self.columnsNumber - 2
if tractor["x"] < n:
directs.append("right")
if tractor["y"] > 0:
directs.append("up")
n = self.rowsNumber - 2
if tractor["y"] < n:
directs.append("down")
direct = self.getRandomFrom(directs)
if direct == "left":
if self.getField(tractor["x"] - 2, tractor["y"]) == "⬜️":
self.setField(tractor["x"] - 1, tractor["y"], "⬛️")
self.setField(tractor["x"] - 2, tractor["y"], "⬛️")
tractor["x"] -= 2
if direct == "right":
if self.getField(tractor["x"] + 2, tractor["y"]) == "⬜️":
self.setField(tractor["x"] + 1, tractor["y"], "⬛️")
self.setField(tractor["x"] + 2, tractor["y"], "⬛️")
tractor["x"] += 2
if direct == "up":
if self.getField(tractor["x"], tractor["y"] - 2) == "⬜️":
self.setField(tractor["x"], tractor["y"] - 1, "⬛️")
self.setField(tractor["x"], tractor["y"] - 2, "⬛️")
tractor["y"] -= 2
if direct == "down":
if self.getField(tractor["x"], tractor["y"] + 2) == "⬜️":
self.setField(tractor["x"], tractor["y"] + 1, "⬛️")
self.setField(tractor["x"], tractor["y"] + 2, "⬛️")
tractor["y"] += 2
def generate_map(self):
for _ in range(self.rowsNumber):
row = ["⬜️" for _ in range(self.columnsNumber)]
self.maze.append(row)
evenColums = []
for column in range(self.columnsNumber):
if self.isEven(column):
evenColums.append(column)
startX = 2
startY = 2
tractor = {"x": startX, "y": startY}
self.setField(startX, startY, "⬛️")
while not self.isMaze():
self.moveTractor(tractor)
return self.maze
@loader.tds
class MazeModMod(loader.Module):
"""Module for play maze"""
strings = {
"name": "MazeMod",
"cfg_maze_width": "Maze width and height, default is 30x30",
"answer": "🕹 <b>Click on the inline buttons to move:</b> <i>{0}</i>",
"doc": "\n 🟦 - player \n ⬛️ - road\n ⬜️ - wall\n 🟩 - finish\n",
"move": "▶️ Moved\n",
"not_allowed": "💭 Not allowed\n",
"win": "🎉 You win!",
"error": "❗️ Error!",
}
strings_ru = {
"answer": "🕹 <b>Нажимайте на инлайн кнопки что двигаться:</b> <i>{0}</i>",
"doc": "\n 🟦 - игрок \n ⬛️ - дорога\n ⬜️ - стена\n 🟩 - финиш\n",
"move": "▶️ Передвинулся\n",
"not_allowed": "💭 Не разрешено\n",
"win": "🎉 Вы победили!",
"error": "❗️ Ошибка!",
}
def __init__(self):
self.config = loader.ModuleConfig(
"maze_width",
"10",
self.strings["cfg_maze_width"],
)
self.name = self.strings["name"]
async def client_ready(self, client: TelegramClient, db):
self._db = db
self._client = client
async def render(self, message: InlineCall, press, maze, player):
text = self.strings["answer"].format(self.strings["not_allowed"])
move = {"x": player["x"], "y": player["y"]}
if press == "up":
move["y"] = move["y"] - 1
if press == "left":
move["x"] = move["x"] - 1
if press == "down":
move["y"] = move["y"] + 1
if press == "right":
move["x"] = move["x"] + 1
if maze[move["y"]][move["x"]] == "":
pass
elif maze[move["y"]][move["x"]] == "⬛️":
maze[player["y"]][player["x"]] = "⬛️"
player = move
text = self.strings["answer"].format(self.strings["move"])
elif maze[move["y"]][move["x"]] == "🟩":
text = self.strings["win"]
return await message.edit(text=text)
maze[player["y"]][player["x"]] = "🟦"
keyboard = [
[
{"text": "🔼", "callback": self.render, "args": ["up", maze, player]},
],
[
{"text": "◀️", "callback": self.render, "args": ["left", maze, player]},
{
"text": "▶️",
"callback": self.render,
"args": ["right", maze, player],
},
],
[
{"text": "🔽", "callback": self.render, "args": ["down", maze, player]},
],
]
for column in maze:
for row in column:
for i in row:
text = text + i
text = text + "\n"
await message.edit(text=text, reply_markup=keyboard)
@loader.unrestricted
@loader.ratelimit
async def mazecmd(self, message):
"""
- generate maze and start play
Based on... my code)
"""
size = int(self.config["maze_width"])
generate = Maze(size, size)
maze = generate.generate_map()
player = {"x": 0, "y": 0}
maze[player["y"]][player["x"]] = "🟦"
maze[size - 2][size - 2] = "🟩"
text = self.strings["answer"].format(self.strings["doc"])
keyboard = [
[
{"text": "🔼", "callback": self.render, "args": ["up", maze, player]},
],
[
{"text": "◀️", "callback": self.render, "args": ["left", maze, player]},
{
"text": "▶️",
"callback": self.render,
"args": ["right", maze, player],
},
],
[
{
"text": "🔽",
"callback": self.render,
"args": [
"down",
maze,
player,
],
},
],
]
for column in maze:
for row in column:
for i in row:
text = text + i
text = text + "\n"
await message.delete()
await self.inline.form(
text=text,
message=message,
always_allow=[message.from_id],
reply_markup=keyboard,
)

View File

@@ -0,0 +1,165 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
Thk @hikariatama
"""
# meta developer: @vsecoder_m
# meta desc: Module for getting information about monkeytype.com stats
# meta pic: https://img.icons8.com/stickers/100/keyboard.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/stickers/100/keyboard.png&title=MonkeyType&description=Module%20for%20getting%20information%20about%20monkeytype.com%20stats
__version__ = (1, 0, 1)
import logging
import aiohttp
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class MonkeyTypeMod(loader.Module):
"""
Module for getting information about monkeytype.com stats
{15/30/60/120:times} - dividing tests by time (default: 15)
Need only account username (not full link)!
"""
strings = {
"name": "MonkeyType",
"error": "<emoji document_id=5467928559664242360>❗️</emoji> Error: \n{}",
"loading": "<emoji document_id=5451732530048802485>⏳</emoji> Loading...",
"template_message": (
"<blockquote><emoji document_id=5879770735999717115>👤</emoji> <b>MonkeyType.com stats for {}:</b></blockquote>\n\n"
"<emoji document_id=5877485980901971030>📊</emoji> Completed tests: <code>{}</code>\n"
"<emoji document_id=5778202206922608769>🔄</emoji> All time typing: <code>{}s</code>\n\n"
"<emoji document_id=5870684638195748414>🏆</emoji> <b>Personal bests:</b>\n"
"{}"
"<emoji document_id=5994378914636500516>📈</emoji> <b>XP</b>: <code>{}</code>"
),
"time": " <emoji document_id=5960751816084820359>⏲️</emoji> <i>{}s</i>: \n",
"not_time": " <emoji document_id=5960751816084820359>⏲️</emoji> <i>{}s</i>: -\n\n",
"template_time": (
" <emoji document_id=5100434699503797219>🟠</emoji><code>{}</code>:\n"
" <emoji document_id=5098279308821005089>🔵</emoji> {}: <code>{}</code>\n"
" <emoji document_id=5098279308821005089>🔵</emoji> Accuracy: <code>{}%</code>\n"
" <emoji document_id=5098279308821005089>🔵</emoji> Consistency: <code>{}%</code>\n"
" <emoji document_id=5098279308821005089>🔵</emoji> Difficulty: <code>{}</code>\n\n"
),
}
strings_ru = {
"error": "<emoji document_id=5467928559664242360>❗️</emoji> Ошибка: \n{}",
"loading": "<emoji document_id=5451732530048802485>⏳</emoji> Загрузка...",
"template_message": (
"<blockquote><emoji document_id=5879770735999717115>👤</emoji> <b>Статистика MonkeyType.com для {}:</b></blockquote>\n\n"
"<emoji document_id=5877485980901971030>📊</emoji> Пройденных тестов: <code>{}</code>\n"
"<emoji document_id=5778202206922608769>🔄</emoji> Всего времени печати: <code>{}s</code>\n\n"
"<emoji document_id=5870684638195748414>🏆</emoji> <b>Лучшие результаты:</b>\n"
"{}"
"<emoji document_id=5994378914636500516>📈</emoji> <b>XP</b>: <code>{}</code>"
),
"time": " <emoji document_id=5960751816084820359>⏲️</emoji> <i>{}s</i>: \n",
"not_time": " <emoji document_id=5960751816084820359>⏲️</emoji> <i>{}s</i>: -\n\n",
"template_time": (
" <emoji document_id=5100434699503797219>🟠</emoji><code>{}</code>:\n"
" <emoji document_id=5098279308821005089>🔵</emoji> {}: <code>{}</code>\n"
" <emoji document_id=5098279308821005089>🔵</emoji> Точность: <code>{}%</code>\n"
" <emoji document_id=5098279308821005089>🔵</emoji> Согласованность: <code>{}%</code>\n"
" <emoji document_id=5098279308821005089>🔵</emoji> Сложность: <code>{}</code>\n\n"
),
}
def __init__(self):
self.name = self.strings["name"]
self.config = loader.ModuleConfig(
loader.ConfigValue(
"default_typing_speed",
"wpm",
"Which typing speed to use by default",
validator=loader.validators.Choice(["cps", "wpm", "cpm", "wps"]),
),
)
async def request(self, username=""):
url = "https://api.monkeytype.com/users/{}/profile"
async with aiohttp.ClientSession() as session:
async with session.get(url.format(username)) as response:
if response.status != [500, 503]:
return await response.json()
return {"message": "Server error"}
async def client_ready(self, client, db):
self._client = client
@loader.command(alias="mts")
async def monkeytypestatscmd(self, message):
"""
{username} {15/30/60/120:times} - get monkeytype.com user stats
"""
args = utils.get_args_raw(message).split(" ")
if not args:
return await utils.answer(
message, self.strings["error"].format("Invalid args")
)
time = args[1] if len(args) > 1 else "15"
await utils.answer(message, self.strings["loading"])
data = await self.request(args[0])
if data["message"] != "Profile retrieved":
return await utils.answer(
message, self.strings["error"].format(data["message"])
)
data = data["data"]
best = ""
if time in data["personalBests"]["time"]:
best += self.strings["time"].format(time)
for i in data["personalBests"]["time"][time]:
typing_speeds = {
"wpm": 1,
"cpm": 5,
"wps": 1 / 60,
"cps": 5 / 60,
}
speed = round(
i["wpm"] * typing_speeds[self.config["default_typing_speed"]], 2
)
best += self.strings["template_time"].format(
i["language"],
self.config["default_typing_speed"].upper(),
speed,
i["acc"],
i["consistency"],
i["difficulty"],
)
else:
best += self.strings["not_time"].format(time)
answer = self.strings["template_message"].format(
args[0],
data["typingStats"]["completedTests"],
round(data["typingStats"]["timeTyping"]),
best,
data["xp"],
)
return await utils.answer(message, answer)

View File

@@ -0,0 +1,130 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/cotton/344/code.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/cotton/344/code.png&title=OctoCode&description=OctoCode%20is%20a%20module%20for%20octopussed%20code%20in%20Telegram
__version__ = (3, 0, 0)
import logging
import asyncio
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class OctoCodeMod(loader.Module):
"""
Module for octopussed code
https://github.com/charmbracelet/freeze based
To use, run this in .terminal:
wget https://github.com/charmbracelet/freeze/releases/download/v0.1.6/freeze_0.1.6_amd64.deb
sudo dpkg -i freeze_0.1.6_amd64.deb
"""
strings = {
"name": "OctoCode",
"answer": "🐙 <b>Code</b> <i>octopussed</i>: ",
"loading": "🐙 <b>Loading</b>...",
"cfg_theme": "🦎 Change theme",
"cfg_line_numbers": "🦎 Type True/False to manage a number of line numbers",
"cfg_default_lang": "🦎 Enter the programming language to use by default",
"error": "❗️ Error: {0}",
}
strings_ru = {
"answer": "🐙 <b>Код</b> <i>осьмоножен</i>: ",
"loading": "🐙 <b>Загрузка</b>...",
"cfg_theme": "🦎 Выбери тему",
"cfg_line_numbers": "🦎 Введите True/False для управления ряда номеров строк",
"cfg_default_lang": (
"🦎 Введите язык программирования для использования по умолчанию"
),
"error": "❗️ Ошибка: {0}",
}
async def client_ready(self, client, db):
self.client = client
self.db = db
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"theme",
"monokai",
self.strings["cfg_theme"],
validator=loader.validators.Choice(
["charm", "dracula", "github-dark", "monokai", "nord", "onedark"]
),
),
)
self.name = self.strings["name"]
async def get_code(self, message):
reply = await message.get_reply_message()
if message.media:
await self._client.download_file(message.media, "file.txt")
return "file.txt"
if reply:
if reply.media:
await self._client.download_file(reply.media, "file.txt")
return "file.txt"
return
@loader.unrestricted
@loader.ratelimit
async def octocmd(self, message):
"""
"reply file" or "send file"
Octopussed your code
"""
await utils.answer(message, self.strings["loading"])
path = await self.get_code(message)
if not path:
return await utils.answer(
message, self.strings["error"].format("not file changed")
)
try:
process = await asyncio.create_subprocess_exec(
"freeze",
path,
"-t",
self.config["theme"],
"-l",
"py",
)
await process.wait()
except Exception as e:
logger.exception(e)
await utils.answer(message, self.strings["error"].format(e))
return
await self._client.send_file(
utils.get_chat_id(message),
file=open("freeze.png", "rb"),
reply_to=getattr(message, "reply_to_msg_id", None),
)
await utils.answer(message, self.strings["answer"])

View File

@@ -0,0 +1,108 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/office/344/administrator-male--v1.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/office/344/administrator-male--v1.png&title=Profilemod&description=Telegram%20Profile%20Statistic
__version__ = (0, 0, 1)
import logging
from .. import loader, utils # type: ignore
from telethon import functions
import imgkit # type: ignore
import base64
import requests
logger = logging.getLogger(__name__)
@loader.tds
class Profilemod(loader.Module):
"""Module for get beautiful picture profile statistic"""
strings = {"name": "Profilemod"}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"background",
"https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/office/344/administrator-male--v1.png&title=.&description=.",
"Url to background (540x220 is perfect)",
validator=loader.validators.Link(),
),
loader.ConfigValue(
"html_template",
"https://raw.githubusercontent.com/vsecoder/hikka_modules/main/assets/profile.html",
"link to html template (if you don't know how, don't touch!)",
validator=loader.validators.Link(),
),
)
self.name = self.strings["name"]
async def client_ready(self, client, db):
self.client = client
self.db = db
@loader.unrestricted
@loader.ratelimit
async def profilecmd(self, message):
"""
- get
"""
chats, channels, bots, users = 0, 0, 0, 0
message = await utils.answer(message, "Geting profile info...")
async for dialog in self._client.iter_dialogs(ignore_migrated=True):
if dialog.is_group:
chats += 1
elif dialog.is_channel:
channels += 1
elif dialog.entity.bot:
bots += 1
else:
users += 1
options = {"crop-w": 540, "crop-h": 220, "encoding": "UTF-8"}
me = await self._client.get_me()
desc = await self._client(functions.users.GetFullUserRequest(me.id))
message = await utils.answer(message, "Downloading profile photo...")
await self._client.download_profile_photo("me", "profile.jpg")
message = await utils.answer(message, "Converting profile photo...")
base64EncodedStr = base64.b64encode(open("profile.jpg", "rb").read()).decode(
"utf-8"
)
message = await utils.answer(message, "Formating info to template...")
with open("profile.html", "w") as f:
template = requests.get(self.config["html_template"]).text
f.write(
template.format(
self.config["background"],
base64EncodedStr,
f"@{me.username}",
chats,
channels,
users,
bots,
desc.full_user.about,
)
)
message = await utils.answer(message, "Converting to image...")
imgkit.from_file("profile.html", "profile.jpg", options=options)
message = await utils.answer(message, "Complete:")
await self._client.send_file(
utils.get_chat_id(message),
open("profile.jpg", "rb"),
)

View File

@@ -0,0 +1,708 @@
__version__ = (0, 0, 7)
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
Thk @Fl1yd, based on his module
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/sf-black-filled/64/quote.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/sf-black-filled/64/quote.png&title=Quotes&description=Quote%20a%20message%20using%20vsecoder%20API
# requires: pydub speechrecognition python-ffmpeg
# scope: ffmpeg
import base64
import hashlib
import io
import os
import tempfile
import asyncio
import logging
from time import gmtime
from typing import List, Union
import speech_recognition as sr
from pydub import AudioSegment
import requests
import telethon # type: ignore
from telethon.tl import types # type: ignore
from telethon.tl.patched import Message # type: ignore
from telethon.tl.types import PeerUser, PeerChat, PeerChannel, PeerBlocked # type: ignore
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
class EntityPayload:
def __init__(
self,
type_: str,
offset: int,
length: int,
url: Union[str, None] = None,
user: Union[dict, None] = None,
language: Union[str, None] = None,
**kwargs,
):
self.type = type_
self.offset = offset
self.length = length
self.url = url
self.user = user
self.language = language
self._ = kwargs
def to_dict(self):
return {
"_": "MessageEntity",
"type": self.type,
"offset": self.offset,
"length": self.length,
"url": self.url,
"user": self.user,
"language": self.language,
("custom_emoji_id" if self.type == "custom_emoji" else None): (
str(self._["document_id"]) if self.type == "custom_emoji" else None
),
}
class UserPayload:
def __init__(
self,
id_: int,
first_name: str,
last_name: str,
username: Union[str, None],
language_code: str,
title: Union[str, None],
emoji_status: Union[str, None],
photo: Union[dict, None],
type_: str,
):
self.id = id_
self.first_name = first_name
self.last_name = last_name
self.username = username
self.language_code = language_code
self.title = title
self.emoji_status = emoji_status
self.photo = photo
self.type = type_
self.name = f"{self.first_name or ''} {self.last_name or ''}"
def to_dict(self):
return {
"id": self.id,
"first_name": self.first_name,
"last_name": self.last_name,
"username": self.username,
"language_code": self.language_code,
"title": self.title,
"emoji_status": self.emoji_status,
"photo": self.photo,
"type": self.type,
"name": self.name,
}
class MessagePayload:
def __init__(
self,
text: str,
entities: Union[List[EntityPayload], None],
chat_id: int,
avatar: bool,
from_: UserPayload,
reply: Union[dict, None],
media: Union[dict, None] = None,
media_type: Union[str, None] = None,
voice: Union[str, None] = None,
is_forward: Union[bool, None] = False,
via_bot: Union[bool, None] = None,
):
self.text = text
self.media = media
self.media_type = media_type
self.voice = {"waveform": voice} if type(voice) == list else None
self.entities = entities
self.chat_id = chat_id
self.avatar = avatar
self.from_ = from_
self.reply = reply
if is_forward:
self.from_.name = f"Forwarded from {self.from_.name}"
if via_bot:
self.from_.name = f"via @{via_bot}"
def to_dict(self):
return {
"text": self.text,
"media": {"base64": self.media} if self.media else None,
"mediaType": self.media_type,
"voice": self.voice,
"entities": [entity.to_dict() for entity in self.entities]
if self.entities
else None,
"chatId": self.chat_id,
"avatar": self.avatar,
"from": self.from_.to_dict(),
"replyMessage": self.reply,
}
class QuotePayload:
def __init__(
self,
messages: List[MessagePayload],
type_: str = "quote",
background: str = "",
**kwargs,
):
self.type = type_
self.messages = messages
self.background = background
self._ = kwargs
def to_dict(self):
r = {
"type": self.type,
"format": "webp",
"width": 512,
"height": 768,
"scale": 2,
"messages": [message.to_dict() for message in self.messages],
**self._,
}
if self.background:
r["backgroundColor"] = self.background
return r
def get_message_media(message: Message):
return (
message.photo
or message.sticker
or message.video
or message.video_note
or message.gif
or message.web_preview
if message and message.media
else None
)
def get_entities(entities: types.TypeMessageEntity):
# coded by @droox
r = [] # EntityPayload
if entities:
for entity in entities:
entity = entity.to_dict()
entity["type"] = entity.pop("_").replace("MessageEntity", "").lower()
if entity["type"] == "customemoji":
entity["type"] = "custom_emoji"
type_ = entity["type"]
offset = entity["offset"]
length = entity["length"]
del entity["type"], entity["offset"], entity["length"]
r.append(
EntityPayload(
type_,
offset,
length,
**entity,
)
)
return r
def get_message_text(message: Message, reply: bool = False, voice_text: str = ""):
mb = 1024 * 1024
if message.photo and reply:
return "📷 Photo"
elif message.sticker and reply:
return f"{message.file.emoji} Sticker"
elif message.video_note and reply:
return "📹 Video note"
elif message.video and reply:
return "📹 Video"
elif message.gif and reply:
return "🖼 GIF"
elif message.poll and reply:
return "📊 Questioning"
elif message.geo and reply:
return "📍 Geolocation"
elif message.contact and reply:
return "👤 Contact"
elif message.voice:
duration = strftime(message.voice.attributes[0].duration)
size = (
message.voice.size / 1024
if message.voice.size < mb
else (message.voice.size / 1024 / 1024)
)
return f"▶️ {duration}, {size:.1f} {('KB' if message.voice.size < mb else 'MB')}\n{voice_text}"
elif message.audio:
audio_attributes = message.audio.attributes[0]
duration = strftime(audio_attributes.duration)
return f"🎧 Music: {duration} | {audio_attributes.performer} - {audio_attributes.title}"
elif type(message.media) == types.MessageMediaDocument and not get_message_media(
message
):
media = message.media.document.size
size = media / 1024 if media < mb else (media / 1024 / 1024)
return (
f"💾 File: {message.file.name}, {size:.1f} {('KB' if media < mb else 'MB')}"
)
elif type(message.media) == types.MessageMediaDice:
return f"{message.media.emoticon} {message.media.value} points"
elif type(message) == types.MessageService:
return f"Service message: {message.action.to_dict()['_']}"
else:
return message.raw_text
def strftime(time: Union[int, float]):
t = gmtime(time)
return (
f"{t.tm_hour:02d}:" if t.tm_hour > 0 else ""
) + f"{t.tm_min:02d}:{t.tm_sec:02d}"
def decode_waveform(wf):
if not wf:
return [0 for _ in range(0, 20)]
bits_count = len(wf) * 8
values_count = bits_count // 5
if not values_count:
return []
last_idx = values_count - 1
result = []
for i in range(last_idx):
j = i * 5
byte_idx = j // 8
bit_shift = j % 8
result.append((wf[byte_idx] >> bit_shift) & 0b11111)
last_byte_idx = (last_idx * 5) // 8
last_bit_shift = (last_idx * 5) % 8
last_value = (
wf[last_byte_idx]
if last_byte_idx == len(wf) - 1
else int.from_bytes(wf[last_byte_idx : last_byte_idx + 2], "little")
)
result.append((last_value >> last_bit_shift) & 0b11111)
return result
async def get_reply(message: Message) -> Union[dict, None]:
reply_name = reply_text = None
if not message.fwd_from:
if reply := await message.get_reply_message():
reply_name = telethon.utils.get_display_name(reply.sender)
reply_text = get_message_text(reply, True)
return (
{
"chatId": message.chat_id,
"text": reply_text,
"name": reply_name,
}
if reply_name
else None
)
@loader.tds
class QuotesMod(loader.Module):
"""
Quotes by @vsecoder [beta]
Now doesn't work stickers, gifs, video.
(Fake stories later)
Thk t.me/Fl1yd, based on his SQuotes module
Thk t.me/hikariatama, recognize from VTT module
"""
strings = {
"name": "Quotes",
"no_reply": "<b>[Quotes]</b> No reply",
"api_error": "<b>[Quotes]</b> API error",
"no_args_or_reply": "<b>[Quotes]</b> No args or reply",
"args_error": (
"<b>[Quotes]</b> An error ocurred while parsing args. Request was:"
" <code>{}</code>"
),
}
async def client_ready(self, client: telethon.TelegramClient, db: dict) -> None:
self.client = client
self.db = db
self.api_endpoint = "http://q.api.vsecoder.dev/generate"
self.settings = self.get_settings()
async def qcmd(self, message: Message) -> None:
"""
<reply> [quantity] [!story] [!rec] [color] - Create nice quote from message(-s)
"""
args: List[str] = utils.get_args(message)
if not await message.get_reply_message():
await utils.answer(message, self.strings["no_reply"])
return
stories = "!story" in args
recognize = "!rec" in args
[count] = [int(arg) for arg in args if arg.isdigit() and int(arg) > 0] or [1]
bg_color = self.settings["bg_color"]
payload = QuotePayload(
await self.quote_parse_messages(message, count, recognize),
("stories" if stories else "quote"),
**({"background": bg_color}),
)
await self.send_quote(message, payload, stories)
async def fqcmd(self, message: Message) -> None:
"""
<@ or id> <text> -r <@ or id> <text> ... - Create fake quote
"""
args: List[str] = utils.get_args(message)
stories = False # "!story" in args
[bg_color] = [self.settings["bg_color"]]
msgs = await self.fake_quote_parse_messages(message)
if not msgs:
await utils.answer(message, self.strings["args_error"].format(args))
return
payload = QuotePayload(
msgs,
("stories" if stories else "quote"),
**({"background": bg_color}),
)
await self.send_quote(message, payload, stories)
async def send_quote(
self, message: Message, payload: QuotePayload, stories: bool = False
) -> None:
r = await self._api_request(payload.to_dict())
if r.status_code != 200:
await utils.answer(message, self.strings["api_error"])
return
quote = r.json()["image"]
img_data = quote.encode()
content = base64.b64decode(img_data)
quote = io.BytesIO(content)
quote.name = f'Quote.{"png" if stories else "webp"}'
await utils.answer(message, quote, force_document=stories)
await (
message[0] if isinstance(message, (list, tuple, set)) else message
).delete()
async def quote_parse_messages(
self, message: Message, count: int, recognize: bool = False
) -> Union[List[MessagePayload], bool]:
payloads = []
messages = [
msg
async for msg in self.client.iter_messages(
message.chat_id,
count,
reverse=True,
add_offset=1,
offset_id=(await message.get_reply_message()).id,
)
]
if len(messages) > self.settings["max_messages"]:
await utils.answer(
message,
f"<b>[Quotes]</b> Maximum messages count is {self.settings['max_messages']}",
)
return False
payloads.extend(await self.parse_messages(messages, recognize))
return payloads
async def fake_quote_parse_messages(
self, message: Message
) -> Union[List[MessagePayload], bool]:
# example text: @vsecoder hi my friend -r @enestasy7 hi ; <!story> (optional)
payloads = []
messages = []
args: List[str] = utils.get_args_raw(message).split(" -r ")
for arg in args:
name, text = arg.split(" ", 1)
if not name or not text:
return False
try:
user = await self.client.get_entity(
int(name) if name.isdigit() else name
)
except Exception:
return False
messages.append(
Message(
id=0,
message=text,
from_id=user.id,
date=message.date,
out=False,
media=None,
reply_to=None,
fwd_from=None,
via_bot_id=None,
reply_markup=None,
entities=None,
views=None,
edit_date=None,
post_author=None,
restriction_reason=None,
ttl_period=None,
)
)
payloads.extend(await self.parse_messages(messages))
return payloads
async def get_entity(self, message: Message) -> UserPayload:
chat = message.peer_id
peer = message.from_id or chat
fwd = message.fwd_from
if fwd:
peer = fwd.from_id
name = fwd.post_author or fwd.from_name
t = type(peer)
if t is int:
uid = peer
elif t is PeerUser:
uid = peer.user_id
elif t is PeerChannel:
uid = peer.channel_id
elif t is PeerChat:
uid = peer.chat_id
elif t is PeerBlocked:
uid = peer.peer_id
elif not peer:
uid = int(hashlib.shake_256(name.encode("utf-8")).hexdigest(6), 16)
entity = None
try:
entity = await self.client.get_entity(peer)
except Exception:
entity = await message.get_chat()
if t is PeerChannel or t is PeerChat:
formated_entity = UserPayload(
entity.id,
entity.title,
"",
"",
"ru",
None,
None,
{"small_file_id": entity.photo.photo_id} if entity.photo else None,
"private",
)
else:
try:
formated_entity = UserPayload(
entity.id,
entity.first_name,
entity.last_name,
entity.username
if entity.username
else entity.usernames[0].username
if entity.usernames
else "",
"ru",
None,
str(entity.emoji_status.document_id) if entity.premium else None,
{"small_file_id": entity.photo.photo_id} if entity.photo else None,
"private",
)
except:
formated_entity = UserPayload(
0,
fwd.from_name if fwd else "Unknown",
"",
None,
"ru",
None,
None,
None,
"private",
)
return formated_entity
async def recognize(self, message: Message):
try:
with tempfile.TemporaryDirectory() as tmpdir:
file = os.path.join(
tmpdir,
"audio.mp3" if message.audio else "audio.ogg",
)
data = await message.download_media(bytes)
with open(file, "wb") as f:
f.write(data)
song = AudioSegment.from_file(
file, format="mp3" if message.audio else "ogg"
)
song.export(os.path.join(tmpdir, "audio.wav"), format="wav")
r = sr.Recognizer()
with sr.AudioFile(os.path.join(tmpdir, "audio.wav")) as source:
audio_data = r.record(source)
text = await utils.run_sync(
r.recognize_google, audio_data, language="ru-RU"
)
return text
except Exception:
logger.exception("Can't recognize")
return "Can't recognize"
async def parse_messages(
self, messages: List[Message], recognize: bool = False
) -> List[MessagePayload]:
payloads = []
for message in messages:
media = get_message_media(message)
base64_media = None
if media:
base64_media = base64.b64encode(
await self.client.download_file(media)
).decode()
voice_text = ""
if message.voice and recognize:
voice_text = "Recognize:\n" + str(await self.recognize(message))
text = get_message_text(message, False, voice_text)
entities = get_entities(message.entities)
from_ = await self.get_entity(message)
reply = await get_reply(message)
"""
text: str,
entities: Union[List[EntityPayload], None],
chat_id: int,
avatar: bool,
from_: UserPayload,
reply: Union[dict, None],
media: Union[dict, None] = None,
media_type: Union[str, None] = None,
voice: Union[str, None] = None,
is_forward: Union[bool, None] = False,
via_bot: Union[bool, None] = None,
"""
payloads.append(
MessagePayload(
text,
entities,
message.chat_id,
True,
from_,
reply,
base64_media,
None,
decode_waveform(message.voice.attributes[0].waveform)
if message.voice
else None,
True if message.fwd_from else False,
message.via_bot.username if message.via_bot else None,
)
)
return payloads
async def sqsetcmd(self, message: Message) -> None:
"""<bg_color/max_messages> <value> - Configure Quotes (text color automatically adjust to the background)"""
args: List[str] = utils.get_args_raw(message).split(maxsplit=1)
if not args:
return await utils.answer(
message,
(
"<b>[Quotes]</b> Settings:\n\nMax messages"
" (<code>max_messages</code>):"
f" {self.settings['max_messages']}\nBackground color"
f" (<code>bg_color</code>): {self.settings['bg_color']}"
),
)
if args[0] == "reset":
self.get_settings(True)
await utils.answer(message, "<b>[Quotes]</b> Settings has been reset")
return
if len(args) < 2:
await utils.answer(message, "<b>[Quotes]</b> Insufficient args")
return
mods = ["max_messages", "bg_color"]
if args[0] not in mods:
await utils.answer(message, f"<b>[Quotes]</b> Unknown param")
return
elif args[0] == "max_messages":
if not args[1].isdigit():
await utils.answer(message, "<b>[Quotes]</b> Number is expected")
return
self.settings[args[0]] = int(args[1])
else:
self.settings[args[0]] = args[1]
self.db.set("Quotes", "settings", self.settings)
return await utils.answer(
message, f"<b>[Quotes]</b> Param {args[0]} value is now {args[1]}"
)
def get_settings(self, force: bool = False):
settings: dict = self.db.get("Quotes", "settings", {})
if not settings or force:
settings.update({"max_messages": 15, "bg_color": "#162330"})
self.db.set("Quotes", "settings", settings)
return settings
async def _api_request(self, data: dict):
# logger.error(data)
return await utils.run_sync(requests.post, self.api_endpoint, json=data)

View File

@@ -0,0 +1,217 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
Thk @fleef
"""
import contextlib
# meta developer: @vsecoder_m
# meta pic: https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSCO9v08B8wLGwL4UMxZzlf7tNOvsRvWQjMypjq5uyvxhAa03NbOO40DY1m-Rr4aYeK7WE&usqp=CAU
# meta banner: https://chojuu.vercel.app/api/banner?img=https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSCO9v08B8wLGwL4UMxZzlf7tNOvsRvWQjMypjq5uyvxhAa03NbOO40DY1m-Rr4aYeK7WE&usqp=CAU&title=SearX&description=Telegram%20SearX%20Engine
__version__ = (1, 0, 1)
import logging
import json
import urllib3
from datetime import datetime
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
with contextlib.suppress(Exception):
urllib3.disable_warnings()
engines = (
"bing_images",
"mediawiki",
"searchcode_code",
"yahoo_news",
"semantic_scholar",
"btdigg",
"nyaa",
"1337x",
"bing_news",
"reddit",
"startpage",
"apkmirror",
"bandcamp",
"genius",
"wolframalpha_noapi",
"torrentz",
"youtube_noapi",
"archlinux",
"vimeo",
"sepiasearch",
"fdroid",
"piratebay",
"soundcloud",
"bing",
"frinkiac",
"ina",
"google_videos",
"openstreetmap",
"pdbe",
"rumble",
"openverse",
"ebay",
"tvmaze",
"mediathekviewweb",
"onesearch",
"mixcloud",
"duckduckgo",
"bing_videos",
"duckduckgo_images",
"pubmed",
"yahoo",
"github",
"microsoft_academic",
"digg",
"google_images",
"tineye",
"google_scholar",
"framalibre",
"duckduckgo_definitions",
"xpath",
"currency_convert",
"gentoo",
"translated",
"unsplash",
"json_engine",
"invidious",
"google",
"kickass",
"etools",
"dictzone",
"photon",
"yggtorrent",
"deezer",
"duden",
"seznam",
"gigablast",
"deviantart",
"wikidata",
"tokyotoshokan",
"flickr_noapi",
"peertube",
"qwant",
"stackexchange",
"imdb",
"wordnik",
"loc",
"www1x",
"solidtorrents",
"google_news",
"sjp",
"wikipedia",
"dailymotion",
"arxiv",
"yandex",
)
engines_str = "| "
for engine in engines:
engines_str += f"{engine} | "
@loader.tds
class SearXMod(loader.Module):
"""Module for multi search"""
strings = {
"name": "SearX",
"cfg_engine": f"Search engine, all: \n{engines_str}",
"cfg_searx_link": "SearX link, get from https://searx.space/",
"error": "<emoji document_id=5467928559664242360>❗️</emoji> Error: \n{}",
"loading": "<emoji document_id=5381942305081010778>⏳</emoji> Loading...",
}
strings_ru = {
"cfg_engine": f"Поисковик, все: \n{engines_str}",
"cfg_searx_link": "Ссылка на SearX, получить можно на https://searx.space/",
"error": "<emoji document_id=5467928559664242360>❗️</emoji> Ошибка: \n{}",
"loading": "<emoji document_id=5381942305081010778>⏳</emoji> Загрузка...",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"engine",
"duckduckgo",
self.strings["cfg_engine"],
validator=loader.validators.String(),
),
loader.ConfigValue(
"searx_link",
"https://searx.thegpm.org/",
self.strings["cfg_searx_link"],
validator=loader.validators.Link(),
),
)
async def request(
self, session, query: str, engine: str = "yandex", count_results: int = 3
):
if engine not in engines:
return self.strings["error"].format("This engine is not found")
if not query:
return self.strings["error"].format("Specify a request")
def_params = dict(
category_general="1",
q=query,
language="ru-RU",
format="json",
engines=engine,
)
url = self.config["searx_link"]
start_time = datetime.now()
raw_results = json.loads(
session.request("GET", url, fields={**def_params}).data.decode("UTF-8")
)["results"]
len_raw_result = len(raw_results)
raw_results = (
raw_results[:2]
if len_raw_result < count_results
else raw_results[:count_results]
)
pretty_result = "".join(
f" 💡: <i>{result['title']}</i>\n 🔗: {result['url']}\n\n"
for result in raw_results
)
return (
f"📟 <b>{engine}</b>\n\n{pretty_result}\n⏱: {datetime.now() - start_time}"
)
async def client_ready(self, client, db):
self._client = client
async def searxcmd(self, message):
"""
{text} - search text in the internet
Based on SearX and t.me/fleef code
"""
args = utils.get_args_raw(message).split("&")
await utils.answer(message, self.strings["loading"])
session = urllib3.PoolManager()
result = await self.request(session, args[0], self.config["engine"], 3)
await utils.answer(message, result)

View File

@@ -0,0 +1,103 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# requires: git+https://github.com/vsecoder/py-censure
__version__ = (2, 0, 0)
import logging
from .. import loader, utils # type: ignore
from censure import Censor
logger = logging.getLogger(__name__)
@loader.tds
class SpeechCensorshipMod(loader.Module):
"""Module for censoring your speech"""
strings = {
"name": "SpeechCensorship",
"_cfg_replace_symbols": "Replace symbols for censoring",
"_cfg_language": "Language",
"turn": "<emoji document_id=5235698355119596341>😆</emoji> <b>Censorship mode {}</b>",
}
strings_ru = {
"_cfg_replace_symbols": "Символы для замены мата",
"_cfg_language": "Язык",
"turn": "<emoji document_id=5235698355119596341>😆</emoji> <b>Режим цензуры {}</b>",
}
async def client_ready(self, client, db):
self.client = client
self.db = db
self.get("censorship_work", False)
self.me = await client.get_me()
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"replace_symbols",
"#",
lambda m: self.strings["_cfg_replace_symbols"],
),
loader.ConfigValue(
"language",
["ru", "en"],
lambda m: self.strings["_cfg_language"],
validator=loader.validators.MultiChoice(["ru", "en"]),
),
)
self.name = self.strings["name"]
async def censorshipcmd(self, message):
"""
Turn on/off censorship mode
"""
censorship_work = self.get("censorship_work", False)
work = "on" if not censorship_work else "off"
self.set("censorship_work", not censorship_work)
await utils.answer(message, self.strings["turn"].format(work))
async def censor_task(self, text):
languages = self.config["language"]
replace_symbols = self.config["replace_symbols"]
censors = [Censor.get(lang) for lang in languages]
for censor in censors:
text = censor.clean_line(text, beep=replace_symbols)[0]
return text
async def watcher(self, message):
try:
if self.me.id != message.from_id:
return
censorship_work = self.get("censorship_work", False)
if not censorship_work:
return
text = message.text
if text:
censored = await self.censor_task(text)
if text != censored:
await message.edit(censored)
except:
return

View File

@@ -0,0 +1,197 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/3d-fluency/94/steam.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/3d-fluency/94/steam.png&title=Steam&description=Module%20for%20get%20Steam%20account%20information
__version__ = (1, 0, 0)
import logging
import aiohttp
from datetime import datetime
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
def minutes_to_hours(minutes: int) -> str:
hours, minutes = divmod(minutes, 60)
return f'{hours}h {minutes}m'
def get_created_ago(timestamp: int) -> str:
created_at = datetime.fromtimestamp(timestamp)
now = datetime.now()
ago = now - created_at
return f"{ago.days // 365} years"
class SteamSDK:
def __init__(self, apikey: str, steamid: str):
self.apikey = apikey
self.steamid = steamid
self.api_url = "https://api.steampowered.com/{method}/"
self.profile_method = "ISteamUser/GetPlayerSummaries/v0002"
self.recent_games_method = "IPlayerService/GetRecentlyPlayedGames/v0001"
self.owned_games_method = "IPlayerService/GetOwnedGames/v0001"
async def _request(
self,
url: str,
params: dict,
) -> dict:
async with aiohttp.ClientSession() as session:
try:
async with session.request("GET", url, params=params) as response:
if response.status != 200:
return {"code": response.status, "detail": response.reason}
return await response.json()
except aiohttp.ClientConnectorError:
return {"detail": "Connection error", "code": 0}
except Exception as e:
return {"detail": f"Unknown error: {e}", "code": 0}
async def get_profile(self) -> dict:
url = self.api_url.format(method=self.profile_method)
params = {"key": self.apikey, "steamids": self.steamid}
return await self._request(url, params)
async def get_recent_games(self) -> dict:
url = self.api_url.format(method=self.recent_games_method)
params = {"key": self.apikey, "steamid": self.steamid}
return await self._request(url, params)
async def get_owned_games(self) -> dict:
url = self.api_url.format(method=self.owned_games_method)
params = {"key": self.apikey, "steamid": self.steamid}
return await self._request(url, params)
@loader.tds
class SteamMod(loader.Module):
"""
Module for get Steam account information
Later (TODO):
- achivments list
- {STEAM} widget
"""
strings = {
"name": "Steam",
"profile": (
'<a href="{avatar}"></a><emoji document_id=5328115953861408177>🎮</emoji> <b>{name}</b> <i>aka</i> '
'<a href="https://steamcommunity.com/id/{username}/"><i>{username}</i></a> (created {created_ago} ago)\n'
"<emoji document_id=5938413566624272793>🎮</emoji> Now {state}\n\n"
"<emoji document_id=5879813604068298387>❗️</emoji> <b>More info:</b>\n"
" <emoji document_id=5274034223886389748>❤️</emoji><i>Level:</i> <code>{level}</code>\n"
" <emoji document_id=5274034223886389748>❤️</emoji><i>Total time spent:</i> <code>{time_spent}</code>\n"
" <emoji document_id=5274034223886389748>❤️</emoji><i>Owned games:</i> <code>{games_count}</code>\n\n"
"<emoji document_id=5843799474362652262>🔄</emoji> <b>Recently played in:</b>\n"
"{games}"
),
"game": (
' <emoji document_id=5274034223886389748>❤️</emoji><a href="https://steamcommunity.com/app/{game_id}">'
"<i>{game}</i></a> (<code>{time_spent}</code>)\n"
),
"state_online": 'in "<i><a href="https://steamcommunity.com/app/{game_id}">{game}</a></i>"',
"state_offline": "offline",
"loading": "<emoji document_id=5323331656646407005>🎮</emoji> <b>Loading...</b>",
"error_403": "<emoji document_id=5778527486270770928>❌</emoji> <b>Access denied, token is invalid or another error occurred</b>\nDEBUG INFO: <code>{error}</code>",
"error_empty": "<emoji document_id=5778527486270770928>❌</emoji> <b>Empty API key or steamID64</b> (open <code> .config steam</code>)",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"apikey",
"",
"Get token from https://steamcommunity.com/dev/apikey",
validator=loader.validators.Hidden(),
),
loader.ConfigValue(
"steamid",
"",
"Get steamID64 from https://steamid.io/lookup/",
validator=loader.validators.Hidden(),
),
)
async def client_ready(self, client, db):
self._client = client
self._db = db
async def steamcmd(self, message):
"""
- get steam profile
"""
await utils.answer(message, self.strings["loading"])
if not self.config["apikey"] or not self.config["steamid"]:
return await utils.answer(message, self.strings["error_empty"])
sdk = SteamSDK(self.config["apikey"], self.config["steamid"])
profile = await sdk.get_profile()
owned_games = await sdk.get_owned_games()
recent_games = await sdk.get_recent_games()
if "code" in profile:
return await utils.answer(
message,
self.strings["error_403"].format(error=str(profile))
)
profile = profile["response"]["players"][0]
if "gameid" in profile:
game_id = profile["gameid"]
game = profile["gameextrainfo"]
state = self.strings["state_online"].format(game=game, game_id=game_id)
else:
state = self.strings["state_offline"]
games_count = owned_games["response"]["game_count"]
created_ago = get_created_ago(profile["timecreated"])
time_spent = sum(
[game["playtime_forever"] for game in owned_games["response"]["games"]]
)
games = "".join(
[
self.strings["game"].format(
game=x["name"],
game_id=x["appid"],
time_spent=minutes_to_hours(x["playtime_forever"]),
)
for x in recent_games["response"]["games"]
]
)
await utils.answer(
message,
self.strings["profile"].format(
avatar=profile["avatar"],
name=profile["realname"] if "realname" in profile else "NoName",
username=profile["personaname"],
level=profile["communityvisibilitystate"],
created_ago=created_ago,
games_count=games_count,
time_spent=minutes_to_hours(time_spent),
state=state,
games=games,
),
link_preview=True,
)

View File

@@ -0,0 +1,153 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/external-vitaliy-gorbachev-lineal-color-vitaly-gorbachev/344/external-translate-online-learning-vitaliy-gorbachev-lineal-color-vitaly-gorbachev.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/external-vitaliy-gorbachev-lineal-color-vitaly-gorbachev/344/external-translate-online-learning-vitaliy-gorbachev-lineal-color-vitaly-gorbachev.png&title=VsecoderTranlate&description=Telegram%20Translate%20Bot
__version__ = (2, 2, 1)
import logging
import translators as vt # type: ignore
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class VseTranslateMod(loader.Module):
"""Traslate text"""
strings = {
"name": "💠 Vsecoder Translate",
"invalid_args": "📥 Invalid arguments!",
"answer": (
"💠 <b>{}</b> <i>from:</i><b>[{}]</b>"
" <i>to:</i><b>[{}]</b>\n\n<code>{}</code>"
),
"error": "📥 Error!",
}
strings_ru = {
"invalid_args": "📥 Неправильные аргументы!",
"answer": (
"💠 <b>{}</b> <i>с:</i><b>[{}]</b> <i>на:</i><b>[{}]</b>\n\n<code>{}</code>"
),
"error": "📥 Ошибка!",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"default_lang",
"ru",
"Which language to translate by default",
validator=loader.validators.Choice(
["ru", "en", "de", "fr", "es", "it", "pt", "ja", "zh", "ko"]
),
),
loader.ConfigValue(
"default_translator",
"google",
"Which translator to use by default",
validator=loader.validators.Choice(
["google", "yandex", "bing", "iciba"]
),
),
)
async def client_ready(self, client, _):
self._client = client
async def translate(
self,
text: str,
lang_from: str = "auto",
lang_to: str = "ru",
translator: str = "google",
) -> dict:
translators = {
"google": vt.google,
"yandex": vt.yandex,
"bing": vt.bing,
"iciba": vt.iciba,
}
if translator not in translators:
return {"error": self.strings["invalid_translator"]}
translater = translators[translator]
return {
"translator": translator,
"from": lang_from,
"to": lang_to,
"text": translater(text, from_language=lang_from, to_language=lang_to),
}
async def vsetranslatecmd(self, message):
"""
[from_language] [to_language] [text]
.vsetranslate en ru Hello, world!
"""
args = utils.get_args(message)
langs = ["auto", "ru", "en", "de", "fr", "es", "it", "pt", "ja", "zh", "ko"]
translators = ["google", "yandex", "bing", "iciba"]
text = message.text.replace(f"{self.get_prefix()}vsetranslate", "")
t = ""
if not args:
return await utils.answer(message, self.strings["invalid_args"])
if args[0] not in langs: # .vsetranslate text
t = await self.translate(
text,
translator=self.config["default_translator"],
lang_to=self.config["default_lang"],
)
elif args[1] not in langs: # .vsetranslate from_language text
text = message.text.replace(
f"{self.get_prefix()}vsetranslate {args[0]}", ""
)
t = await self.translate(
text,
translator=self.config["default_translator"],
lang_to=self.config["default_lang"],
lang_from=args[0],
)
elif args[2] not in translators: # .vsetranslate from_language to_language text
text = message.text.replace(
f"{self.get_prefix()}vsetranslate {args[0]} {args[1]}", ""
)
t = await self.translate(
text,
translator=self.config["default_translator"],
lang_to=args[1],
lang_from=args[0],
)
else: # .vsetranslate from_language to_language translator text
text = message.text.replace(
f"{self.get_prefix()}vsetranslate {args[0]} {args[1]} {args[2]}", ""
)
t = await self.translate(
text,
translator=args[2],
lang_to=args[1],
lang_from=args[0],
)
try:
await utils.answer(
message,
self.strings["answer"].format(
t["translator"],
t["from"],
t["to"],
t["text"],
),
)
except Exception:
await utils.answer(message, self.strings["error"])

View File

@@ -0,0 +1,72 @@
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# meta pic: https://img.icons8.com/cute-clipart/344/wikipedia.png
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.icons8.com/cute-clipart/344/wikipedia.png&title=Wikipedia&description=Module%20for%20wikipedia%20search
__version__ = (1, 0, 0)
import logging
import wikipedia # type: ignore
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
@loader.tds
class WikiMod(loader.Module):
"""Module for wikipedia search"""
strings = {
"name": "Wikipedia🌐",
"answer": "🌐 <b>{0}</b><a href='{1}'>:</a>\n\n<i>{2}</i>\n\n{3}",
"error": "❗️ <b>{0}</b>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"wiki_lang",
"en",
"Language of wikipedia",
validator=loader.validators.Choice(["en", "ru"]),
),
)
async def client_ready(self, client, db):
self._client = client
async def wikicmd(self, message):
"""
<text> - search in wikipedia
"""
args = utils.get_args_raw(message)
try:
wikipedia.set_lang(self.config["wiki_lang"])
except Exception:
wikipedia.set_lang("en")
try:
page = wikipedia.page(args)
await utils.answer(
message,
self.strings["answer"].format(
utils.escape_html(page.title),
page.images[0],
utils.escape_html(page.summary),
page.url,
),
)
except wikipedia.exceptions.DisambiguationError as e:
await utils.answer(message, self.strings["error"].format(e))
except wikipedia.exceptions.PageError as e:
await utils.answer(message, self.strings["error"].format(e))

View File

@@ -0,0 +1,455 @@
__version__ = (3, 1, 1)
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# requires: yandex-music aiohttp
# meta desc: Module for yandex music. Based on SpotifyNow, YaNow and WakaTime [beta]
# meta pic: https://img.freepik.com/premium-vector/yandex-music-logo_578229-242.jpg
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.freepik.com/premium-vector/yandex-music-logo_578229-242.jpg&title=YMNow&description=Module%20for%20yandex%20music
import logging
import asyncio
import logging
import aiohttp
import random
import json
import string
from asyncio import sleep
from yandex_music import ClientAsync
from telethon import TelegramClient
from telethon.tl.types import Message
from telethon.errors.rpcerrorlist import FloodWaitError, MessageNotModifiedError
from telethon.tl.functions.account import UpdateProfileRequest
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
logging.getLogger("yandex_music").propagate = False
# https://github.com/FozerG/YandexMusicRPC/blob/main/main.py#L133
async def get_current_track(client, token):
device_info = {
"app_name": "Chrome",
"type": 1,
}
ws_proto = {
"Ynison-Device-Id": "".join(
[random.choice(string.ascii_lowercase) for _ in range(16)]
),
"Ynison-Device-Info": json.dumps(device_info),
}
timeout = aiohttp.ClientTimeout(total=15, connect=10)
try:
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.ws_connect(
url="wss://ynison.music.yandex.ru/redirector.YnisonRedirectService/GetRedirectToYnison",
headers={
"Sec-WebSocket-Protocol": f"Bearer, v2, {json.dumps(ws_proto)}",
"Origin": "http://music.yandex.ru",
"Authorization": f"OAuth {token}",
},
timeout=10,
) as ws:
recv = await ws.receive()
data = json.loads(recv.data)
if "redirect_ticket" not in data or "host" not in data:
print(f"Invalid response structure: {data}")
return {"success": False}
new_ws_proto = ws_proto.copy()
new_ws_proto["Ynison-Redirect-Ticket"] = data["redirect_ticket"]
to_send = {
"update_full_state": {
"player_state": {
"player_queue": {
"current_playable_index": -1,
"entity_id": "",
"entity_type": "VARIOUS",
"playable_list": [],
"options": {"repeat_mode": "NONE"},
"entity_context": "BASED_ON_ENTITY_BY_DEFAULT",
"version": {
"device_id": ws_proto["Ynison-Device-Id"],
"version": 9021243204784341000,
"timestamp_ms": 0,
},
"from_optional": "",
},
"status": {
"duration_ms": 0,
"paused": True,
"playback_speed": 1,
"progress_ms": 0,
"version": {
"device_id": ws_proto["Ynison-Device-Id"],
"version": 8321822175199937000,
"timestamp_ms": 0,
},
},
},
"device": {
"capabilities": {
"can_be_player": True,
"can_be_remote_controller": False,
"volume_granularity": 16,
},
"info": {
"device_id": ws_proto["Ynison-Device-Id"],
"type": "WEB",
"title": "Chrome Browser",
"app_name": "Chrome",
},
"volume_info": {"volume": 0},
"is_shadow": True,
},
"is_currently_active": False,
},
"rid": "ac281c26-a047-4419-ad00-e4fbfda1cba3",
"player_action_timestamp_ms": 0,
"activity_interception_type": "DO_NOT_INTERCEPT_BY_DEFAULT",
}
async with session.ws_connect(
url=f"wss://{data['host']}/ynison_state.YnisonStateService/PutYnisonState",
headers={
"Sec-WebSocket-Protocol": f"Bearer, v2, {json.dumps(new_ws_proto)}",
"Origin": "http://music.yandex.ru",
"Authorization": f"OAuth {token}",
},
timeout=10,
method="GET",
) as ws:
await ws.send_str(json.dumps(to_send))
recv = await asyncio.wait_for(ws.receive(), timeout=10)
ynison = json.loads(recv.data)
track_index = ynison["player_state"]["player_queue"][
"current_playable_index"
]
if track_index == -1:
print("No track is currently playing.")
return {"success": False}
track = ynison["player_state"]["player_queue"]["playable_list"][
track_index
]
await session.close()
info = await client.tracks_download_info(track["playable_id"], True)
track = await client.tracks(track["playable_id"])
return {
"paused": ynison["player_state"]["status"]["paused"],
"duration_ms": ynison["player_state"]["status"]["duration_ms"],
"progress_ms": ynison["player_state"]["status"]["progress_ms"],
"entity_id": ynison["player_state"]["player_queue"]["entity_id"],
"repeat_mode": ynison["player_state"]["player_queue"]["options"][
"repeat_mode"
],
"entity_type": ynison["player_state"]["player_queue"]["entity_type"],
"track": track,
"info": info,
"success": True,
}
except Exception as e:
return {"success": False, "error": str(e), "track": None}
@loader.tds
class YmNowBetaMod(loader.Module):
"""
Module for yandex music. Based on SpotifyNow, YaNow and WakaTime. [BETA]
Now on Ynison API.
"""
strings = {
"name": "YmNow",
"no_token": "<b><emoji document_id=5843952899184398024>🚫</emoji> Specify a token in config!</b>",
"playing": "<b><emoji document_id=5188705588925702510>🎶</emoji> Now playing: </b><code>{}</code><b> - </b><code>{}</code>\n<b>🕐 {}</b>",
"no_args": "<b><emoji document_id=5843952899184398024>🚫</emoji> Provide arguments!</b>",
"state": "🙂 <b>Widgets are now {}</b>\n{}",
"tutorial": (
" <b>To enable widget, send a message to a preffered chat with text"
" </b><code>{YANDEXMUSIC}</code>"
),
"no_results": "<b><emoji document_id=5285037058220372959>☹️</emoji> No results found :(</b>",
"autobioe": "<b>🔁 Autobio enabled</b>",
"autobiod": "<b>🔁 Autobio disabled</b>",
"_cfg_yandexmusictoken": "Yandex.Music account token",
"_cfg_autobiotemplate": "Template for AutoBio",
"_cfg_automesgtemplate": "Template for AutoMessage",
"_cfg_update_interval": "Update interval",
"guide": (
'<a href="https://github.com/MarshalX/yandex-music-api/discussions/513#discussioncomment-2729781">'
"Instructions for obtaining a Yandex.Music token</a>"
),
"configuring": "🙂 <b>Widget is ready and will be updated soon</b>",
}
strings_ru = {
"no_token": "<b><emoji document_id=5843952899184398024>🚫</emoji> Укажи токен в конфиге!</b>",
"playing": "<b><emoji document_id=5188705588925702510>🎶</emoji> Сейчас играет: </b><code>{}</code><b> - </b><code>{}</code>\n<b>🕐 {}</b>",
"no_args": "<b><emoji document_id=5843952899184398024>🚫</emoji> Укажи аргументы!</b>",
"state": "🙂 <b>Виджеты теперь {}</b>\n{}",
"tutorial": (
" <b>Чтобы включить виджет, отправь сообщение в нужный чат с текстом"
" </b><code>{YANDEXMUSIC}</code>"
),
"no_results": "<b><emoji document_id=5285037058220372959>☹️</emoji> Ничего не найдено :(</b>",
"autobioe": "<b>🔁 Autobio включен</b>",
"autobiod": "<b>🔁 Autobio выключен</b>",
"_cls_doc": "Модуль для Яндекс.Музыка. Основан на SpotifyNow, YaNow и WakaTime. [BETA]",
"_cfg_yandexmusictoken": "Токен аккаунта Яндекс.Музыка",
"_cfg_autobiotemplate": "Шаблон для AutoBio, параметры: {artists}, {track}, {time}",
"_cfg_automesgtemplate": "Шаблон для AutoMessage, параметры: {artists}, {track}, {time}, {link}",
"_cfg_update_interval": "Интервал обновления виджета",
"guide": (
'<a href="https://github.com/MarshalX/yandex-music-api/discussions/513#discussioncomment-2729781">'
"Инструкция по получению токена Яндекс.Музыка</a>"
),
"configuring": "🙂 <b>Виджет готов и скоро будет обновлен</b>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"YandexMusicToken",
None,
lambda: self.strings["_cfg_yandexmusictoken"],
validator=loader.validators.Hidden(),
),
loader.ConfigValue(
"AutoBioTemplate",
"🎧 {artists} - {track} / {time}",
lambda: self.strings["_cfg_autobiotemplate"],
validator=loader.validators.String(),
),
loader.ConfigValue(
"AutoMessageTemplate",
"🎧 {artists} - {track} / {time} {link}",
lambda: self.strings["_cfg_automesgtemplate"],
validator=loader.validators.String(),
),
loader.ConfigValue(
"update_interval",
300,
lambda: self.strings["_cfg_update_interval"],
validator=loader.validators.Integer(minimum=100),
),
)
async def on_dlmod(self):
if not self.get("guide_send", False):
await self.inline.bot.send_message(
self._tg_id,
self.strings["guide"],
)
self.set("guide_send", True)
async def client_ready(self, client: TelegramClient, db):
self.client = client
self.db = db
self._premium = getattr(await self.client.get_me(), "premium", False)
self.set("widgets", list(map(tuple, self.get("widgets", []))))
self._task = asyncio.ensure_future(self._parse())
if self.get("autobio", False):
self.autobio.start()
@loader.command()
async def ynowcmd(self, message: Message):
"""Get now playing track"""
if not self.config["YandexMusicToken"]:
await utils.answer(message, self.strings["no_token"])
return
try:
client = ClientAsync(self.config["YandexMusicToken"])
await client.init()
except: # noqa: E722
await utils.answer(message, self.strings["no_token"])
return
res = await get_current_track(client, self.config["YandexMusicToken"])
if not res["success"]:
await utils.answer(message, self.strings["no_results"])
return
track = res["track"][0] # type: ignore
link = res["info"][0]["direct_link"] # type: ignore
title = track["title"]
artists = [artist["name"] for artist in track["artists"]]
duration_ms = int(track["duration_ms"])
caption = self.strings["playing"].format(
utils.escape_html(", ".join(artists)),
utils.escape_html(title),
f"{duration_ms // 1000 // 60:02}:{duration_ms // 1000 % 60:02}",
)
lnk = track["id"]
await self.inline.form(
message=message,
text=caption,
reply_markup={
"text": "song.link",
"url": f"https://song.link/ya/{lnk}",
},
silent=True,
audio={
"url": link,
"title": utils.escape_html(title),
"performer": utils.escape_html(", ".join(artists)),
},
)
@loader.command()
async def ybio(self, message: Message):
"""Show now playing track in your bio"""
if not self.config["YandexMusicToken"]:
await utils.answer(message, self.strings["no_token"])
return
try:
client = ClientAsync(self.config["YandexMusicToken"])
await client.init()
except: # noqa: E722
await utils.answer(message, self.strings["no_token"])
return
current = self.get("autobio", False)
new = not current
self.set("autobio", new)
if new:
await utils.answer(message, self.strings["autobioe"])
self.autobio.start()
else:
await utils.answer(message, self.strings["autobiod"])
self.autobio.stop()
@loader.command()
async def automsgcmd(self, message: Message):
"""Toggle YandexMusic widgets' updates"""
state = not self.get("state", False)
self.set("state", state)
await utils.answer(
message,
self.strings["state"].format(
"on" if state else "off", self.strings("tutorial") if state else ""
),
)
@loader.loop(interval=60)
async def autobio(self):
client = ClientAsync(self.config["YandexMusicToken"])
await client.init()
res = await get_current_track(client, self.config["YandexMusicToken"])
track = res["track"][0] # type: ignore
title = track["title"]
artists = [artist["name"] for artist in track["artists"]]
duration_ms = int(track["duration_ms"])
text = self.config["AutoBioTemplate"].format(
artists=utils.escape_html(", ".join(artists)),
track=utils.escape_html(title),
time=f"{duration_ms // 1000 // 60:02}:{duration_ms // 1000 % 60:02}",
)
try:
await self.client(
UpdateProfileRequest(about=text[: 140 if self._premium else 70])
)
except FloodWaitError as e:
logger.info(f"Sleeping {e.seconds}")
await sleep(e.seconds)
return
async def _parse(self, do_not_loop: bool = False):
while True:
for widget in self.get("widgets", []):
client = ClientAsync(self.config["YandexMusicToken"])
await client.init()
res = await get_current_track(client, self.config["YandexMusicToken"])
track = res["track"][0] # type: ignore
title = track["title"]
artists = [artist["name"] for artist in track["artists"]]
duration_ms = int(track["duration_ms"])
try:
await self._client.edit_message(
*widget[:2],
self.config["AutoMessageTemplate"].format(
artists=utils.escape_html(", ".join(artists)),
track=utils.escape_html(title),
time=f"{duration_ms // 1000 // 60:02}:{duration_ms // 1000 % 60:02}",
link=f"https://song.link/ya/{track['id']}",
),
)
except MessageNotModifiedError:
pass
except FloodWaitError:
pass
except Exception:
logger.debug("YmNow widget update failed")
self.set(
"widgets", list(set(self.get("widgets", [])) - set([widget]))
)
continue
if do_not_loop:
break
await asyncio.sleep(int(self.config["update_interval"]))
async def on_unload(self):
self._task.cancel()
async def watcher(self, message: Message):
try:
if "{YANDEXMUSIC}" not in getattr(message, "text", "") or not message.out:
return
chat_id = utils.get_chat_id(message)
message_id = message.id
self.set(
"widgets",
self.get("widgets", []) + [(chat_id, message_id, message.text)], # type: ignore
)
await utils.answer(message, self.strings["configuring"])
await self._parse(do_not_loop=True)
except Exception as e:
logger.exception("Can't send widget")
await utils.respond(message, self.strings["error"].format(e))

View File

@@ -0,0 +1,339 @@
__version__ = (2, 1, 1)
"""
_
__ _____ ___ ___ ___ __| | ___ _ __
\ \ / / __|/ _ \/ __/ _ \ / _` |/ _ \ '__|
\ V /\__ \ __/ (_| (_) | (_| | __/ |
\_/ |___/\___|\___\___/ \__,_|\___|_|
Copyleft 2022 t.me/vsecoder
This program is free software; you can redistribute it and/or modify
"""
# meta developer: @vsecoder_m
# requires: yandex-music aiohttp
# meta desc: Module for yandex music. Based on SpotifyNow, YaNow and WakaTime [beta]
# meta pic: https://img.freepik.com/premium-vector/yandex-music-logo_578229-242.jpg
# meta banner: https://chojuu.vercel.app/api/banner?img=https://img.freepik.com/premium-vector/yandex-music-logo_578229-242.jpg&title=YMNow&description=Module%20for%20yandex%20music
import logging
import asyncio
import logging
import aiohttp
import random
import json
import string
from asyncio import sleep
from yandex_music import ClientAsync
from telethon import TelegramClient
from telethon.tl.types import Message
from telethon.errors.rpcerrorlist import FloodWaitError
from telethon.tl.functions.account import UpdateProfileRequest
from .. import loader, utils # type: ignore
logger = logging.getLogger(__name__)
logging.getLogger("yandex_music").propagate = False
# https://github.com/FozerG/YandexMusicRPC/blob/main/main.py#L133
async def get_current_track(client, token):
device_info = {
"app_name": "Chrome",
"type": 1,
}
ws_proto = {
"Ynison-Device-Id": "".join(
[random.choice(string.ascii_lowercase) for _ in range(16)]
),
"Ynison-Device-Info": json.dumps(device_info),
}
timeout = aiohttp.ClientTimeout(total=15, connect=10)
try:
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.ws_connect(
url="wss://ynison.music.yandex.ru/redirector.YnisonRedirectService/GetRedirectToYnison",
headers={
"Sec-WebSocket-Protocol": f"Bearer, v2, {json.dumps(ws_proto)}",
"Origin": "http://music.yandex.ru",
"Authorization": f"OAuth {token}",
},
timeout=10,
) as ws:
recv = await ws.receive()
data = json.loads(recv.data)
if "redirect_ticket" not in data or "host" not in data:
print(f"Invalid response structure: {data}")
return {"success": False}
new_ws_proto = ws_proto.copy()
new_ws_proto["Ynison-Redirect-Ticket"] = data["redirect_ticket"]
to_send = {
"update_full_state": {
"player_state": {
"player_queue": {
"current_playable_index": -1,
"entity_id": "",
"entity_type": "VARIOUS",
"playable_list": [],
"options": {"repeat_mode": "NONE"},
"entity_context": "BASED_ON_ENTITY_BY_DEFAULT",
"version": {
"device_id": ws_proto["Ynison-Device-Id"],
"version": 9021243204784341000,
"timestamp_ms": 0,
},
"from_optional": "",
},
"status": {
"duration_ms": 0,
"paused": True,
"playback_speed": 1,
"progress_ms": 0,
"version": {
"device_id": ws_proto["Ynison-Device-Id"],
"version": 8321822175199937000,
"timestamp_ms": 0,
},
},
},
"device": {
"capabilities": {
"can_be_player": True,
"can_be_remote_controller": False,
"volume_granularity": 16,
},
"info": {
"device_id": ws_proto["Ynison-Device-Id"],
"type": "WEB",
"title": "Chrome Browser",
"app_name": "Chrome",
},
"volume_info": {"volume": 0},
"is_shadow": True,
},
"is_currently_active": False,
},
"rid": "ac281c26-a047-4419-ad00-e4fbfda1cba3",
"player_action_timestamp_ms": 0,
"activity_interception_type": "DO_NOT_INTERCEPT_BY_DEFAULT",
}
async with session.ws_connect(
url=f"wss://{data['host']}/ynison_state.YnisonStateService/PutYnisonState",
headers={
"Sec-WebSocket-Protocol": f"Bearer, v2, {json.dumps(new_ws_proto)}",
"Origin": "http://music.yandex.ru",
"Authorization": f"OAuth {token}",
},
timeout=10,
method="GET",
) as ws:
await ws.send_str(json.dumps(to_send))
recv = await asyncio.wait_for(ws.receive(), timeout=10)
ynison = json.loads(recv.data)
track_index = ynison["player_state"]["player_queue"][
"current_playable_index"
]
if track_index == -1:
print("No track is currently playing.")
return {"success": False}
track = ynison["player_state"]["player_queue"]["playable_list"][
track_index
]
await session.close()
info = await client.tracks_download_info(track["playable_id"], True)
track = await client.tracks(track["playable_id"])
res = {
"paused": ynison["player_state"]["status"]["paused"],
"duration_ms": ynison["player_state"]["status"]["duration_ms"],
"progress_ms": ynison["player_state"]["status"]["progress_ms"],
"entity_id": ynison["player_state"]["player_queue"]["entity_id"],
"repeat_mode": ynison["player_state"]["player_queue"]["options"][
"repeat_mode"
],
"entity_type": ynison["player_state"]["player_queue"]["entity_type"],
"track": track,
"info": info,
"success": True,
}
return res
except Exception as e:
print(f"Failed to get current track: {str(e)}")
return {"success": False}
@loader.tds
class YmNowBetaMod(loader.Module):
"""
Module for yandex music. Based on SpotifyNow, YaNow and WakaTime. [BETA]
"""
strings = {
"name": "YmNowBeta",
"no_token": "<b><emoji document_id=5843952899184398024>🚫</emoji> Specify a token in config!</b>",
"playing": "<b><emoji document_id=5188705588925702510>🎶</emoji> Now playing: </b><code>{}</code><b> - </b><code>{}</code>\n<b>🕐 {}</b>",
"no_args": "<b><emoji document_id=5843952899184398024>🚫</emoji> Provide arguments!</b>",
"tutorial": (
" <b>To enable widget, send a message to a preffered chat with text"
" </b><code>{YANDEXMUSIC}</code>"
),
"no_results": "<b><emoji document_id=5285037058220372959>☹️</emoji> No results found :(</b>",
"autobioe": "<b>🔁 Autobio enabled</b>",
"autobiod": "<b>🔁 Autobio disabled</b>",
"_cfg_yandexmusictoken": "Yandex.Music account token",
"_cfg_autobiotemplate": "Template for AutoBio",
"_cfg_automesgtemplate": "Template for AutoMessage",
"_cfg_update_interval": "Update interval",
"no_lyrics": "<b><emoji document_id=5843952899184398024>🚫</emoji> Track doesn't have lyrics.</b>",
"guide": (
'<a href="https://github.com/MarshalX/yandex-music-api/discussions/513#discussioncomment-2729781">'
"Instructions for obtaining a Yandex.Music token</a>"
),
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"YandexMusicToken",
None,
lambda: self.strings["_cfg_yandexmusictoken"],
validator=loader.validators.Hidden(),
),
loader.ConfigValue(
"AutoBioTemplate",
"🎧 {}",
lambda: self.strings["_cfg_autobiotemplate"],
validator=loader.validators.String(),
),
)
async def on_dlmod(self):
if not self.get("guide_send", False):
await self.inline.bot.send_message(
self._tg_id,
self.strings["guide"],
)
self.set("guide_send", True)
async def client_ready(self, client: TelegramClient, db):
self.client = client
self.db = db
self._premium = getattr(await self.client.get_me(), "premium", False)
if self.get("autobio", False):
self.autobio.start()
@loader.command()
async def ynowcmd(self, message: Message):
"""Get now playing track"""
if not self.config["YandexMusicToken"]:
await utils.answer(message, self.strings["no_token"])
return
try:
client = ClientAsync(self.config["YandexMusicToken"])
await client.init()
except: # noqa: E722
await utils.answer(message, self.strings["no_token"])
return
res = await get_current_track(client, self.config["YandexMusicToken"])
track = res["track"]
if not track:
await utils.answer(message, self.strings["no_results"])
return
track = track[0] # type: ignore
link = res["info"][0]["direct_link"] # type: ignore
title = track["title"]
artists = [artist["name"] for artist in track["artists"]]
duration_ms = int(track["duration_ms"])
caption = self.strings["playing"].format(
utils.escape_html(", ".join(artists)),
utils.escape_html(title),
f"{duration_ms // 1000 // 60:02}:{duration_ms // 1000 % 60:02}",
)
lnk = track["id"]
await self.inline.form(
message=message,
text=caption,
reply_markup={
"text": "song.link",
"url": f"https://song.link/ya/{lnk}",
},
silent=True,
audio={
"url": link,
"title": utils.escape_html(title),
"performer": utils.escape_html(", ".join(artists)),
},
)
@loader.command()
async def ybio(self, message: Message):
"""Show now playing track in your bio"""
if not self.config["YandexMusicToken"]:
await utils.answer(message, self.strings["no_token"])
return
try:
client = ClientAsync(self.config["YandexMusicToken"])
await client.init()
except:
await utils.answer(message, self.strings["no_token"])
return
current = self.get("autobio", False)
new = not current
self.set("autobio", new)
if new:
await utils.answer(message, self.strings["autobioe"])
self.autobio.start()
else:
await utils.answer(message, self.strings["autobiod"])
self.autobio.stop()
@loader.loop(interval=60)
async def autobio(self):
client = ClientAsync(self.config["YandexMusicToken"])
await client.init()
res = await get_current_track(client, self.config["YandexMusicToken"])
track = res["track"]
track = track[0] # type: ignore
title = track["title"]
artists = [artist["name"] for artist in track["artists"]]
duration_ms = int(track["duration_ms"])
text = self.config["AutoBioTemplate"].format(
f"{', '.join(artists)} - {title} | {duration_ms // 1000 // 60:02}:{duration_ms // 1000 % 60:02}",
)
try:
await self.client(
UpdateProfileRequest(about=text[: 140 if self._premium else 70])
)
except FloodWaitError as e:
logger.info(f"Sleeping {e.seconds}")
await sleep(e.seconds)
return