Merge pull request #112 from MuRuLOSE/update-submodules_2e2ebd512fc870a3002dde752d479dee9645cf86

Update of repositories 2025-11-30 12:54:04
This commit is contained in:
Zahar Vanilovv
2025-11-30 16:03:58 +03:00
committed by GitHub
80 changed files with 25741 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
name: Generate Index Page
on:
push:
branches:
- main
permissions:
contents: write
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- name: Generate index.html
run: |
cat <<EOF > _site/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>H:Mods</title>
<link type=text/css href="https://github.com/C0dwiz/H.Modules/raw/assets/style.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<h1>H:Mods modules</h1>
<ul class="module-list">
$(for file in *.py; do echo " <li class="module-item"><a href=\"$file\" class="module-link">$file</a></li>"; done)
</ul>
</div>
</body>
</html>
EOF
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: .

6
archquise/H.Modules/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
full.py
autocleaner.py
silent.py
# Ruff Format
.ruff_cache/

View File

@@ -0,0 +1,133 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: ASCIIArt
# Description: Converting images to ASCII art
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: ASCIIArt
# scope: ASCIIArt 0.0.1
# requires: pillow
# ---------------------------------------------------------------------------------
import os
import tempfile
from PIL import Image
from .. import loader, utils
@loader.tds
class ASCIIArtMod(loader.Module):
"""Converting images to ASCII art"""
strings = {
"name": "ASCIIArt",
"no_media_reply": "<b>Please reply to the image!</b>",
"loading": "<emoji document_id=5116240346656801621>❓</emoji> <b>Converting an image to ASCII...</b>",
"error": "<emoji document_id=5121063440311386962>👎</emoji> <b>Error when converting an image.</b>",
"done": "<emoji document_id=5123163417326126159>✅</emoji> <b>Here is your ASCII art:</b>",
}
strings_ru = {
"no_media_reply": "<b>Пожалуйста, ответьте на изображение!</b>",
"loading": "<emoji document_id=5116240346656801621>❓</emoji> <b>Конвертирую изображение в ASCII...</b>",
"error": "<emoji document_id=5121063440311386962>👎</emoji> <b>Ошибка при конвертации изображения.</b>",
"done": "<emoji document_id=5123163417326126159>✅</emoji> <b>Вот ваш ASCII-арт:</b>",
}
@loader.command(
ru_doc="<реплай на изображение> сделать ascii art",
en_doc="<replay on image> make ascii art",
)
async def cascii(self, message):
reply = await message.get_reply_message()
if not self._is_image(reply):
await utils.answer(message, self.strings("no_media_reply"))
return
await utils.answer(message, self.strings("loading"))
ascii_art = await self._generate_ascii_art(reply)
if ascii_art:
await self._send_ascii_file(message, ascii_art)
await message.delete()
else:
await utils.answer(message, self.strings("error"))
def _is_image(self, reply):
"""Проверка, является ли ответ изображением"""
return reply and (
reply.photo
or (reply.document and reply.file.mime_type.startswith("image/"))
)
async def _generate_ascii_art(self, reply):
"""Генерирует ASCII-арт из изображения"""
try:
image_path = await reply.download_media(tempfile.gettempdir())
if not image_path:
return None
with Image.open(image_path) as img:
img = img.convert("L")
img = img.resize(self._get_new_dimensions(img), Image.NEAREST)
chars = "@#S%?*+;:,. "
pixels = img.getdata()
ascii_str = "".join(chars[pixel // 25] for pixel in pixels)
return "\n".join(
ascii_str[i : i + img.width]
for i in range(0, len(ascii_str), img.width)
)
except Exception as e:
print(f"Error generating ASCII art: {e}")
return None
finally:
if image_path and os.path.exists(image_path):
os.remove(image_path)
def _get_new_dimensions(self, img):
"""Получаем новые размеры для изображения"""
new_width = 100
aspect_ratio = img.height / img.width
new_height = int(aspect_ratio * new_width * 0.55)
return new_width, new_height
async def _send_ascii_file(self, message, ascii_art):
"""Сохраняет ASCII-арт во временный файл и отправляет его"""
try:
with tempfile.NamedTemporaryFile(
mode="w", encoding="utf-8", suffix=".txt", delete=False
) as tmp_file:
tmp_file_path = tmp_file.name
tmp_file.write(ascii_art)
await message.client.send_file(
message.chat_id,
tmp_file_path,
caption=self.strings("done"),
force_document=True,
reply_to=getattr(message, "reply_to_msg_id", None),
)
finally:
if tmp_file_path and os.path.exists(tmp_file_path):
os.remove(tmp_file_path)

View File

@@ -0,0 +1,91 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: AccountData
# Description: Find out the approximate date of registration of the telegram account
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api AccountData
# scope: Api AccountData 0.0.1
# ---------------------------------------------------------------------------------
import aiohttp
from datetime import datetime
from .. import loader, utils
@loader.tds
class AccountData(loader.Module):
"""Find out the approximate date of registration of the telegram account"""
strings = {
"name": "AccountData",
"_cls_doc": "Find out the approximate date of registration of the telegram account",
"date_text": "<emoji document_id=5983150113483134607>⏰️</emoji> Date of registration of this account: {data} (Accuracy: {accuracy}%)",
"date_text_ps": "<emoji document_id=6028435952299413210></emoji> <i>Tip: To increase accuracy, the person whose registration date is being checked can write any message to</i> @mewpl2.\n\nDon't worry, this account is not run by a person, but by a userbot just like yours, which will check the registration date using Telegram's built-in tool.",
"no_reply": "<emoji document_id=6030512294109122096>💬</emoji> You did not reply to the user's message",
}
strings_ru = {
"date_text": "<emoji document_id=5983150113483134607>⏰️</emoji> Дата регистрации этого аккаунта: {data} (Точность: {accuracy}%)",
"_cls_doc": "Узнайте примерную дату регистрации Telegram-аккаунта",
"date_text_ps": "<emoji document_id=6028435952299413210></emoji> <i>Совет: Для повышения точности, человек, дата регистрации которого проверяется, может написать любое сообщение</i> @mewpl2.\n\nНе бойтесь, на этом аккаунте сидит не человек, а такой же юзербот, как и у вас, который проверит дату регистрации при помощи встроенного инструмента Telegram.",
"no_reply": "<emoji document_id=6030512294109122096>💬</emoji> Вы не ответили на сообщение пользователя",
}
async def get_creation_date(self, user_id: int) -> str:
api_token = "7518491974:1ea2284eec9dc40a9838cfbcb48a2b36"
url = "https://api.datereg.pro/api/v1/users/getCreationDateFast"
params = {"token": api_token, "user_id": user_id}
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as response:
if response.status == 200:
json_response = await response.json()
if json_response["success"]:
return {
"creation_date": json_response["creation_date"],
"accuracy_percent": json_response["accuracy_percent"],
} # type: ignore
else:
return {"error": json_response["error"]["message"]} # type: ignore
else:
return {"error": f"HTTP {response.status}"} # type: ignore
@loader.command(
ru_doc="Узнать примерную дату регистрации Telergam-аккаунта",
en_doc="Find out the approximate date of registration of the telegram account",
)
async def accdata(self, message):
if reply := await message.get_reply_message():
result = await self.get_creation_date(user_id=reply.sender.id)
month, year = map(int, result['creation_date'].split('.'))
date_object = datetime(year, month, 1)
formatted = date_object.strftime('%B %Y')
if "error" in result:
await utils.answer(message, f"Ошибка: {result['error']}")
else:
await utils.answer(
message,
f"{self.strings('date_text').format(data=formatted, accuracy=result['accuracy_percent'])}\n\n{self.strings('date_text_ps')}",
)
else:
await utils.answer(message, self.strings("no_reply"))

View File

@@ -0,0 +1,187 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: AniLibria
# Description: Searches and gives random agtme on the AniLibria database.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: AniLibria
# scope: AniLibria 0.0.1
# requires: git+https://github.com/C0dwiz/anilibria.py.git
# ---------------------------------------------------------------------------------
from ..inline.types import InlineQuery
from aiogram.types import InlineQueryResultPhoto, CallbackQuery
from anilibria import AniLibriaClient
from .. import loader
ani_client = AniLibriaClient()
@loader.tds
class AniLibriaMod(loader.Module):
"""Searches and gives random agtme on the AniLibria database."""
strings = {
"name": "AniLibria",
"announce": "<b>The announcement</b>:",
"status": "<b>Status</b>:",
"type": "<b>Type</b>:",
"genres": "<b>Genres</b>:",
"favorite": "<b>Favourites &lt;3</b>:", # &lt; == <
"season": "<b>Season</b>:",
}
strings_ru = {
"announce": "<b>Анонс</b>:",
"status": "<b>Статус</b>:",
"type": "<b>Тип</b>:",
"genres": "<b>Жанры</b>:",
"favorite": "<b>Избранное &lt;3</b>:", # &lt; == <
"season": "<b>Сезон</b>:",
}
link = "https://anilibria.tv"
@loader.command(
ru_doc="Возвращает случайный тайтл из базы",
en_doc="Returns a random title from the database",
)
async def arandom(self, message) -> None:
anime_title = await ani_client.get_random_title()
text = f"{anime_title.names.ru} \n"
text += f"{self.strings['status']} {anime_title.status.string}\n\n"
text += f"{self.strings['type']} {anime_title.type.full_string}\n"
text += f"{self.strings['season']} {anime_title.season.string}\n"
text += f"{self.strings['genres']} {' '.join(anime_title.genres)}\n\n"
text += f"<code>{anime_title.description}</code>\n\n"
text += f"{self.strings['favorite']} {anime_title.in_favorites}"
kb = [
[
{
"text": "Ссылка",
"url": f"https://anilibria.tv/release/{anime_title.code}.html",
}
]
]
kb.extend(
[
{
"text": f"{torrent.quality.string}",
"url": f"https://anilibria.tv/{torrent.url}",
}
]
for torrent in anime_title.torrents.list
)
kb.append([{"text": "🔃 Обновить", "callback": self.inline__update}])
kb.append([{"text": "🚫 Закрыть", "callback": self.inline__close}])
await self.inline.form(
text=text,
photo=self.link + anime_title.posters.original.url,
message=message,
reply_markup=kb,
silent=True,
)
@loader.inline_handler(
ru_doc="Возвращает список найденных по названию тайтлов",
en_doc="Returns a list of titles found by name",
)
async def asearch_inline_handler(self, query: InlineQuery) -> None:
text = query.args
if not text:
return
anime_titles = await ani_client.search_titles(search=text)
inline_query = []
for anime_title in anime_titles:
title_text = (
f"{anime_title.names.ru} | {anime_title.names.en}\n"
f"{self.strings['status']} {anime_title.status.string}\n\n"
f"{self.strings['type']} {anime_title.type.full_string}\n"
f"{self.strings['season']} {anime_title.season.string} {anime_title.season.year}\n"
f"{self.strings['genres']} {' '.join(anime_title.genres)}\n\n"
f"<code>{anime_title.description}</code>\n\n"
f"{self.strings['favorite']} {anime_title.in_favorites}"
)
inline_query.append(
InlineQueryResultPhoto(
id=str(anime_title.code),
title=anime_title.names.ru,
description=anime_title.type.full_string,
caption=title_text,
thumb_url=self.link + anime_title.posters.small.url,
photo_url=self.link + anime_title.posters.original.url,
parse_mode="html",
)
)
await query.answer(inline_query, cache_time=0)
async def inline__close(self, call: CallbackQuery) -> None:
await call.delete()
async def inline__update(self, call: CallbackQuery) -> None:
anime_title = await ani_client.get_random_title()
text = (
f"{anime_title.names.ru} \n"
f"{self.strings['status']} {anime_title.status.string}\n\n"
f"{self.strings['type']} {anime_title.type.full_string}\n"
f"{self.strings['season']} {anime_title.season.string}\n"
f"{self.strings['genres']} {' '.join(anime_title.genres)}\n\n"
f"<code>{anime_title.description}</code>\n\n"
f"{self.strings['favorite']} {anime_title.in_favorites}"
)
kb = [
[
{
"text": "Ссылка",
"url": f"https://anilibria.tv/release/{anime_title.code}.html",
}
]
]
kb.extend(
[
{
"text": f"{torrent.quality.string}",
"url": f"https://anilibria.tv/{torrent.url}",
}
]
for torrent in anime_title.torrents.list
)
kb.append([{"text": "🔃 Обновить", "callback": self.inline__update}])
kb.append([{"text": "🚫 Закрыть", "callback": self.inline__close}])
await call.edit(
text=text,
photo=self.link + anime_title.posters.original.url,
reply_markup=kb,
)

View File

@@ -0,0 +1,81 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: AnimeQuotes
# Description: A module for sending random quotes from anime
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: AnimeQuotes
# scope: AnimeQuotes 0.0.1
# requires: requests
# ---------------------------------------------------------------------------------
import aiohttp
from .. import loader, utils
@loader.tds
class AnimeQuotesMod(loader.Module):
"""A module for sending random quotes from anime"""
strings = {
"name": "AnimeQuotes",
"quote_template": (
'<b>Quote:</b> "{quote}"\n\n'
"<b>Character:</b> {character}\n"
"<b>Anime:</b> {anime}"
),
"error": "<b>Couldn't get a quote. Try again later!</b>",
}
strings_ru = {
"quote_template": (
'<b>Цитата:</b> "{quote}"\n\n'
"<b>Персонаж:</b> {character}\n"
"<b>Аниме:</b> {anime}"
),
"error": "<b>Не удалось получить цитату. Попробуйте позже!</b>",
}
@loader.command(
ru_doc="Получить случайную цитату из аниме",
en_doc="Get a random quote from the anime",
)
async def quote(self, message):
url = "https://api.animechan.io/v1/quotes/random"
try:
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
response.raise_for_status()
data = await response.json()
quote_content = data["data"]["content"]
character_name = data["data"]["character"]["name"]
anime_name = data["data"]["anime"]["name"]
quote = self.strings["quote_template"].format(
quote=quote_content, character=character_name, anime=anime_name
)
await utils.answer(message, quote)
except aiohttp.ClientError:
await utils.answer(message, self.strings["error"])

View File

@@ -0,0 +1,73 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Article
# Description: Displays your article Criminal Code of the Russian Federation
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Article
# scope: Article 0.0.1
# requires: requests
# ---------------------------------------------------------------------------------
import requests
import json
import random
from typing import Dict
from .. import loader, utils
@loader.tds
class ArticleMod(loader.Module):
"""Displays your article Criminal Code of the Russian Federation"""
strings = {
"name": "Article",
"article": "<emoji document_id=5226512880362332956>📖</emoji> <b>Your article of the Criminal Code of the Russian Federation</b>:\n\n<blockquote>Number {}\n\n{}</blockquote>",
}
strings_ru = {
"article": "<emoji document_id=5226512880362332956>📖</emoji> <b>Твоя статья УК РФ</b>:\n\n<blockquote>Номер {}\n\n{}</blockquote>",
}
@loader.command(
ru_doc="Отображается ваша статья Уголовного кодекса Российской Федерации",
en_doc="Displays your article Criminal Code of the Russian Federation",
)
async def arccmd(self, message):
if values := self._load_values():
random_key = random.choice(list(values.keys()))
random_value = values[random_key]
await utils.answer(
message, self.strings("article").format(random_key, random_value)
)
def _load_values(self) -> Dict[str, str]:
url = "https://raw.githubusercontent.com/Codwizer/ReModules/main/assets/zakon.json"
try:
response = requests.get(url)
if response.ok:
data = json.loads(response.text)
return data
except (requests.RequestException, json.JSONDecodeError):
pass
return {}

View File

@@ -0,0 +1,204 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: AutofarmCookies
# Description: Autofarm in the bot @cookies_game_bot
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: AutofarmCookies
# scope: AutofarmCookies 0.0.1
# ---------------------------------------------------------------------------------
import random
from datetime import timedelta
from telethon import functions
from .. import loader, utils
__version__ = (1, 0, 0)
@loader.tds
class AutofarmCookiesMod(loader.Module):
"""Autofarm in the bot @cookies_game_bot"""
strings = {
"name": "AutofarmCookies",
"farmon": (
"<i>The deferred task has been created, autofarming has been started, everything will start in 10 minutes"
" seconds...</i>"
),
"farmon_already": "<i>It has already been launched :)</i>",
"farmoff": "<i>The autopharm is stopped\nSelected:</i> <b>%coins% Cookies</b>",
"farm": "<i>I typed:</i> <b>%coins% Cookies</b>",
}
strings_ru = {
"farmon": (
"<i>Отложенная задача создана, автофарминг запущен, всё начнётся через 10"
" секунд...</i>"
),
"farmon_already": "<i>Уже запущено :)</i>",
"farmoff": "<i>Автофарм остановлен.\nНвброно:</i> <b>%coins% Cookies</b>",
"farm": "<i>Я набрал:</i> <b>%coins% Cookies</b>",
}
def __init__(self):
self.name = self.strings["name"]
async def client_ready(self, client, db):
self.client = client
self.db = db
self.myid = (await client.get_me()).id
self.cookies = "@cookies_game_bot"
@loader.command(
ru_doc="Запустить автофарминг",
en_doc="Launch auto-farming",
)
async def cookon(self, message):
status = self.db.get(self.name, "status", False)
if status:
return await message.edit(self.strings["farmon_already"])
self.db.set(self.name, "status", True)
await self.client.send_message(
self.cookies, "/cookie", schedule=timedelta(seconds=10)
)
await message.edit(self.strings["farmon"])
@loader.command(
ru_doc="Остановить автофарминг",
en_doc="Stop auto-farming",
)
async def cookoff(self, message):
self.db.set(self.name, "status", False)
coins = self.db.get(self.name, "coins", 0)
if coins:
self.db.set(self.name, "coins", 0)
await message.edit(self.strings["farmoff"].replace("%coins%", str(coins)))
@loader.command(
ru_doc="Вывод кол-ва коинов, добытых этим модулем",
en_doc="Output of the number of coins mined by this module",
)
async def cookies(self, message):
coins = self.db.get(self.name, "coins", 0)
await message.edit(self.strings["farm"].replace("%coins%", str(coins)))
async def watcher(self, event):
if not isinstance(event, Message): # noqa: F821
return
chat = utils.get_chat_id(event)
if chat != self.cookies:
return
status = self.db.get(self.name, "status", False)
if not status:
return
if event.raw_text == "/cookie":
return await self.client.send_message(
self.cookies, "/cookie", schedule=timedelta(hours=2)
)
if event.sender_id != self.cookies:
return
if "🙅‍♂️!" in event.raw_text:
args = [int(x) for x in event.raw_text.split() if x.isnumeric()]
randelta = random.randint(20, 60)
if len(args) == 4:
delta = timedelta(
hours=args[1], minutes=args[2], seconds=args[3] + randelta
)
elif len(args) == 3:
delta = timedelta(minutes=args[1], seconds=args[2] + randelta)
elif len(args) == 2:
delta = timedelta(seconds=args[1] + randelta)
else:
return
sch = (
await self.client(
functions.messages.GetScheduledHistoryRequest(self.cookies, 1488)
)
).messages
await self.client(
functions.messages.DeleteScheduledMessagesRequest(
self.cookies, id=[x.id for x in sch]
)
)
return await self.client.send_message(
self.cookies, "/cookie", schedule=delta
)
if "" in event.raw_text:
args = event.raw_text.split()
for x in args:
if x[0] == "+":
return self.db.set(
self.name,
"coins",
self.db.get(self.name, "coins", 0) + int(x[1:]),
)
async def message_q(
self,
text: str,
user_id: int,
mark_read: bool = False,
delete: bool = False,
):
async with self.client.conversation(user_id) as conv:
msg = await conv.send_message(text)
response = await conv.get_response()
if mark_read:
await conv.mark_read()
if delete:
await msg.delete()
await response.delete()
return response
@loader.command(
ru_doc="Показывает ваш мешок",
en_doc="Shows your bag",
)
async def me(self, message):
bot = "@cookies_game_bot"
bags = await self.message_q(
"/me",
bot,
delete=True,
)
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, bags.text)
@loader.command(
ru_doc="Помощь по модулю AutofarmCookies",
en_doc="Help with the AutofarmCookies module",
)
async def ckies(self, message):
chelp = """
🍀| <b>Помощь по командам:</b>
.cookon - Включает авто фарм.
.cookoff - Выключает авто фарм.
.farm - Показывает сколько вы нафармили.
.me - Показывает ваш ммешок"""
await utils.answer(message, chelp)

View File

@@ -0,0 +1,242 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: BirthdayTime
# Description: Counting down to your birthday
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: BirthdayTime
# scope: Api BirthdayTime 0.0.1
# ---------------------------------------------------------------------------------
import random
import asyncio
import calendar
from datetime import datetime
from telethon.tl.functions.users import GetFullUserRequest
from telethon.tl.functions.account import UpdateProfileRequest
from telethon.errors.rpcerrorlist import UserPrivacyRestrictedError
from .. import loader, utils
D_MSG = [
"Ждешь его?",
"Осталось немного)",
"Дни пролетят, даже не заметишь",
"Уже знаешь что хочешь получить в подарок?)",
"Сколько исполняется?",
"Жду не дождусь уже",
]
@loader.tds
class DaysToMyBirthday(loader.Module):
"""Counting down to your birthday"""
strings = {
"name": "BirthdayTime",
"date_error": "<emoji document_id=5422840512681877946>❗️</emoji> <b>Your birthdate is not specified in the config, please correct this :)</b>",
"msg": (
"<emoji document_id=5377476217698001788>🎉</emoji> <b>"
"There are {} days, {} hours, {} minutes, and {} seconds left until your birthday. \n<emoji document_id=5377442914521588226>"
"💙</emoji> {}</b>"
),
"conf": "<i>Open config...</i>",
"name_changed": "<b>Name updated!</b>",
"name_not_changed": "<b>Name was not updated.</b>",
"name_privacy_error": "<b>Unable to change name due to privacy settings.</b>",
"error": "<b>An error occurred. Please check the logs.</b>",
}
strings_ru = {
"date_error": "<emoji document_id=5422840512681877946>❗️</emoji> <b>В конфиге не указан день вашего рождения, пожалуйста, исправь это :)</b>",
"msg": (
"<emoji document_id=5377476217698001788>🎉</emoji> <b>"
"До вашего дня рождения осталось {} дней, {} часов, {} "
"минут, {} секунд. \n<emoji document_id=5377442914521588226>"
"💙</emoji> {}</b>"
),
"conf": "<i>Открываю конфиг...</i>",
"btname_yes": (
"<b><emoji document_id=6327560044845991305>😶</emoji> Хорошо, теперь я "
"буду изменять ваше имя в зависимости от количества дней до дня рождения</b>"
),
"btname_no": "<emoji document_id=6325696222313055607>😶</emoji>Хорошо, я больше не буду изменять ваше имя",
"name_changed": "<b>Имя обновлено!</b>",
"name_not_changed": "<b>Имя не было обновлено.</b>",
"name_privacy_error": "<b>Не удалось изменить имя из-за настроек приватности.</b>",
"error": "<b>Произошла ошибка. Пожалуйста, проверьте логи.</b>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"birthday_date",
None,
lambda: "Дата вашего рождения. Указывать только день",
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"birthday_month",
None,
"Месяц вашего рождения",
validator=loader.validators.Choice(
[
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
]
),
),
)
self._task = None
async def client_ready(self):
if self._task:
self._task.cancel()
self._task = asyncio.create_task(self.checker())
async def checker(self):
while True:
if not self.db.get(__name__, "change_name", False):
await asyncio.sleep(60)
continue
try:
now = datetime.now()
day = self.config["birthday_date"]
monthy = self.config["birthday_month"]
month = list(calendar.month_name).index(monthy)
birthday = datetime(now.year, month, day)
if now.month > month or (now.month == month and now.day > day):
birthday = datetime(now.year + 1, month, day)
time_to_birthday = abs(birthday - now)
days = time_to_birthday.days
user = await self.client(GetFullUserRequest(self.client.hikka_me.id))
if not user or not user.users:
await asyncio.sleep(60)
continue
name = user.users[0].last_name or ""
ln = f"{self.db.get(__name__, 'last_name', '')}{days} d."
if name == ln:
await asyncio.sleep(60)
continue
else:
await self.client(UpdateProfileRequest(last_name=ln))
self.db.set(__name__, "last_name", name)
except UserPrivacyRestrictedError:
self.db.set(__name__, "change_name", False)
print("Error: Can't change name due to privacy settings.")
except Exception as e:
print(f"Error in checker: {e}")
finally:
await asyncio.sleep(60)
@loader.command(
ru_doc="Выставить таймер дней в ник (нестабильно)",
en_doc="Set the timer of days in the nickname (unstable)",
)
async def btname(self, message):
try:
user = await self.client(GetFullUserRequest(self.client.hikka_me.id))
name = user.users[0].last_name or ""
except Exception as e:
print(f"Error getting user info: {e}")
await utils.answer(message, self.strings("error"))
return
self.db.set(__name__, "last_name", name)
change_name = self.db.get(__name__, "change_name", False)
if change_name:
self.db.set(__name__, "change_name", False)
await utils.answer(message, self.strings("btname_no"))
try:
await self.client(
UpdateProfileRequest(last_name=self.db.get(__name__, "last_name"))
)
await utils.answer(message, self.strings("name_not_changed"))
except UserPrivacyRestrictedError:
await utils.answer(message, self.strings("name_privacy_error"))
except Exception as e:
print(f"Error removing name: {e}")
await utils.answer(message, self.strings("error"))
else:
self.db.set(__name__, "change_name", True)
await utils.answer(message, self.strings("btname_yes"))
@loader.command(
ru_doc="Вывести таймер",
en_doc="Display the timer",
)
async def bt(self, message):
if (
self.config["birthday_date"] is None
or self.config["birthday_month"] is None
):
await utils.answer(message, self.strings("date_error"))
msg = await self.client.send_message(message.chat_id, self.strings("conf"))
await self.allmodules.commands["config"](
await utils.answer(msg, f"{self.get_prefix()}config BirthdayTime")
)
return
try:
now = datetime.now()
day = self.config["birthday_date"]
monthy = self.config["birthday_month"]
month = list(calendar.month_name).index(monthy)
birthday = datetime(now.year, month, day)
if now.month > month or (now.month == month and now.day > day):
birthday = datetime(now.year + 1, month, day)
time_to_birthday = abs(birthday - now)
await utils.answer(
message,
self.strings("msg").format(
time_to_birthday.days,
(time_to_birthday.seconds // 3600),
(time_to_birthday.seconds // 60 % 60),
(time_to_birthday.seconds % 60),
random.choice(D_MSG),
),
)
except Exception as e:
print(f"Error in bt command: {e}")
await utils.answer(message, self.strings("error"))

View File

@@ -0,0 +1 @@
mods.archquise.ru

View File

@@ -0,0 +1,54 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: CheckSpamBan
# Description: Check spam ban for your account.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: CheckSpamBan
# scope: CheckSpamBan 0.0.1
# ---------------------------------------------------------------------------------
import logging
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class SpamBanCheckMod(loader.Module):
"""Checks spam ban for your account."""
strings = {
"name": "CheckSpamBan",
}
@loader.command(
ru_doc="Проверяет вашу учетную запись на спам-бан с помощью бота @SpamBot",
en_doc="Checks your account for spam ban via @SpamBot bot",
)
async def spambot(self, message):
async with self.client.conversation(178220800) as conv:
user_message = await conv.send_message("/start")
await user_message.delete()
spam_message = await conv.get_response()
await utils.answer(message, spam_message.text)
await spam_message.delete()

View File

@@ -0,0 +1,107 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: CryptoCurrency
# Description: Module for displaying current cryptocurrency exchange rates.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api CryptoCurrency
# scope: Api CryptoCurrency 0.0.1
# ---------------------------------------------------------------------------------
import aiohttp
from .. import loader, utils
@loader.tds
class CryptoCurrencyMod(loader.Module):
"""Module for displaying current cryptocurrency exchange rates."""
strings = {
"name": "CryptoCurrency",
"query_missing": "Please specify a cryptocurrency ticker or name.",
"coin_not_found": "Cryptocurrency '{query}' not found.",
}
strings_ru = {
"query_missing": "Пожалуйста, укажите тикер или название криптовалюты.",
"coin_not_found": "Криптовалюта '{query}' не найдена.",
}
async def fetch_json(self, url):
"""Fetch JSON data from a given URL."""
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
response.raise_for_status()
return await response.json()
async def get_exchange_rates(self):
"""Get exchange rates for RUB and EUR based on USD."""
data = await self.fetch_json("https://open.er-api.com/v6/latest/USD")
return data["rates"]["RUB"], data["rates"]["EUR"]
async def find_coin(self, query):
"""Find a cryptocurrency by its name or symbol."""
data = await self.fetch_json(
"https://api.coinlore.net/api/tickers/?start=0&limit=100"
)
return next(
(
item
for item in data["data"]
if query.lower() in item["name"].lower()
or query.lower() in item["symbol"].lower()
),
None,
)
@loader.command(
ru_doc="Отображает текущий курс криптовалюты в рублях, долларах США и евро",
en_doc="Displays the current cryptocurrency rate in RUB, USD, and EUR",
)
async def crypto(self, message):
query = utils.get_args_raw(message)
if not query:
return await utils.answer(message, self.strings("query_missing"))
coin = await self.find_coin(query)
if not coin:
return await utils.answer(
message, self.strings("coin_not_found").format(query=query)
)
price_usd = float(coin["price_usd"])
usd_rub_rate, usd_eur_rate = await self.get_exchange_rates()
price_rub = price_usd * usd_rub_rate
price_eur = price_usd * usd_eur_rate
response = self.format_response(coin, price_usd, price_rub, price_eur)
await utils.answer(message, response)
def format_response(self, coin, price_usd, price_rub, price_eur):
"""Format the response message with cryptocurrency information."""
return (
f"💰 {coin['name']} ({coin['symbol']})\n"
f"USD: ${price_usd:.2f}\n"
f"RUB: ₽{price_rub:.2f}\n"
f"EUR: €{price_eur:.2f}\n"
)

View File

@@ -0,0 +1,83 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: EnvsSH
# Description: Module for reuploading files to envs.sh
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api EnvsSH
# scope: Api EnvsSH 0.0.1
# requires: aiohttp
# ---------------------------------------------------------------------------------
import aiohttp
from .. import loader, utils # pylint: disable=relative-beyond-top-level
@loader.tds
class EnvsMod(loader.Module):
"""Module for reuploading files to envs.sh"""
strings = {
"name": "EnvsSH",
"connection_error": "🚫 Host is unreachable for now, try again later.",
"no_reply": "⚠️ <b>You must reply to a message with media</b>",
"success": "✅ URL for <code>{}</code>:\n\n<code>{}</code>",
"error": "❌ An error occurred:\n<code>{}</code>",
"uploading": "⏳ <b>Uploading {} ({}{})...</b>",
}
strings_ru = {
"connection_error": "🚫 Хост в настоящее время недоступен, попробуйте позже.",
"no_reply": "⚠️ <b>Вы должны ответить на сообщение с медиа</b>",
"success": "✅ URL для <code>{}</code>:\n\n<code>{}</code>",
"error": "❌ Произошла ошибка:\n<code>{}</code>",
"uploading": "⏳ <b>Загрузка {} ({}{})...</b>",
}
async def client_ready(self, client, db):
self.hmodslib = await self.import_lib(
"https://raw.githubusercontent.com/C0dwiz/H.Modules/refs/heads/main-fix/HModsLibrary.py"
)
async def envcmd(self, message):
"""Reupload to envs.sh."""
reply = await message.get_reply_message()
if not reply or not reply.media:
return await utils.answer(message, self.strings["no_reply"])
size_len, size_unit = self.hmodslib.convert_size(reply.file.size)
await utils.answer(
message,
self.strings["uploading"].format(reply.file.name, size_len, size_unit),
)
path = await self.client.download_media(reply)
try:
uploaded_url = await self.hmodslib.upload_to_envs(path)
except aiohttp.ClientConnectionError:
await utils.answer(message, self.strings["connection_error"])
except aiohttp.ClientResponseError as e:
await utils.answer(message, self.strings["error"].format(str(e)))
else:
await utils.answer(
message, self.strings["success"].format(path, uploaded_url)
)

View File

@@ -0,0 +1,88 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: FakeActions
# Description: Module for simulating various actions in chat
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api FakeActions
# scope: Api FakeActions 0.0.1
# ---------------------------------------------------------------------------------
import asyncio
from .. import loader, utils
@loader.tds
class FakeActionsMod(loader.Module):
"""Module for simulating various actions in chat"""
strings = {"name": "FakeActions"}
def __init__(self):
self.config = loader.ModuleConfig(
"DEFAULT_DURATION", 5, "Default duration for actions in seconds"
)
async def ftcmd(self, message):
"""<seconds> - Simulates typing in chat for the specified number of seconds."""
await self._simulate_action_command(message, "typing")
async def ffcmd(self, message):
"""<seconds> - Simulates sending a file."""
await self._simulate_action_command(message, "document")
async def fgcmd(self, message):
"""<seconds> - Simulates recording a voice message."""
await self._simulate_action_command(message, "record-audio")
async def fvgcmd(self, message):
"""<seconds> - Simulates recording a video message."""
await self._simulate_action_command(message, "record-round")
async def fpgcmd(self, message):
"""<seconds> - Simulates playing a game."""
await self._simulate_action_command(message, "game")
async def _simulate_action_command(self, message, action):
"""General function for handling action simulation commands."""
duration = self._parse_duration(message)
if duration is None:
await utils.answer(
message,
f"Usage: {self.get_prefix()}{message.raw_text.split()[0][1:]} <seconds>",
)
return
await message.delete()
await self._simulate_action(message, action, duration)
def _parse_duration(self, message):
"""Parse the duration from the message."""
args = message.raw_text.split()
if len(args) == 2 and args[1].isdigit():
return int(args[1])
return self.config["DEFAULT_DURATION"]
async def _simulate_action(self, message, action, duration):
"""Simulate the specified action in chat."""
async with message.client.action(message.chat_id, action):
await asyncio.sleep(duration)

View File

@@ -0,0 +1,176 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: FakeWallet
# Description: Fun joke - fake crypto wallet. You can change cryptocurrency values using .cfg FakeWallet.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: hikka_only
# scope: hikka_min 1.4.2
# -----------------------------------------------------------------------------------
from .. import loader, utils
@loader.tds
class FakeWallet(loader.Module):
"""Fun joke - fake crypto wallet. You can change cryptocurrency values using .cfg FakeWallet."""
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"Toncoin",
0,
lambda: self.strings("ton"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"Tether",
0,
lambda: self.strings("tether"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"Bitcoin",
0,
lambda: self.strings("btc"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"Etherium",
0,
lambda: self.strings("ether"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"Binance",
0,
lambda: self.strings("binc"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"Tron",
0,
lambda: self.strings("tron"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"USDT",
0,
lambda: self.strings("usdt"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"Gram",
0,
lambda: self.strings("gram"),
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"Litecoin",
0,
lambda: self.strings("lite"),
validator=loader.validators.Integer(),
),
)
strings = {
"name": "FakeWallet",
"crypto": "Enter a value for your cryptovalute",
"wallet": "<emoji document_id=5438626338560810621>👛</emoji> <b>Wallet</b>\n\n"
"<emoji document_id=5215276644620586569>☺️</emoji> <a href='https://ton.org'>Toncoin</a>: {} TON\n\n"
"<emoji document_id=5215699136258524363>☺️</emoji> <a href='https://tether.to'>Tether</a>: {} USDT\n\n"
"<emoji document_id=5215590800003451651>☺️</emoji> <a href='https://bitcoin.org'>Bitcoin</a>: {} BTC\n\n"
"<emoji document_id=5217867240044512715>☺️</emoji> <a href='https://etherium.org'>Etherium</a>: {} ETH\n\n"
"<emoji document_id=5215595550237279768>☺️</emoji> <a href='https://binance.org'>Binance coin</a>: {} BNB\n\n"
"<emoji document_id=5215437796088499410>☺️</emoji> <a href='https://tron.network'>TRON</a>: {} TRX\n\n"
"<emoji document_id=5215440441788351459>☺️</emoji> <a href='https://www.centre.io/usdc'>USD Coin</a>: {} USDC\n\n"
"<emoji document_id=5215267041073711005>☺️</emoji> <a href='https://gramcoin.org'>Gram</a>: {} GRAM\n\n"
"<emoji document_id=5217877586620729050>☺️</emoji> <a href='https://litecoin.org'>Litecoin</a>: {} LTC",
"ton": "Enter a value for Toncoin",
"teth": "Enter a value for Tethcoin",
"btc": "Enter a value for Bitcoin",
"ether": "Enter a value for Etherium",
"binc": "Enter a value for Binance coin",
"tron": "Enter a value for Tron",
"usdt": "Enter a value for USDT coin",
"gram": "Enter a value for Gramcoin",
"lite": "Enter a value for Litecoin",
"info": "<b><emoji document_id=5305467350064047192>🫥</emoji><i>Attention!</b>\n\n"
"<i><emoji document_id=5915991028430542030>☝️</emoji>This module is strictly prohibited from being used for the purposes of <b>scam, fraud and advertising</b>.\n\n"
"<emoji document_id=5787190061644647815>🗣</emoji>The module is provided solely for entertainment purposes, and any violation of the <b>Rules for using the module</b>, if detected, will be subject <b>to appropriate punishment</i>",
}
strings_ru = {
"wallet": "<emoji document_id=5438626338560810621>👛</emoji> <b>Кошелёк</b>\n\n"
"<emoji document_id=5215276644620586569>☺️</emoji> <a href='https://ton.org'>Toncoin</a>: {} TON\n\n"
"<emoji document_id=5215699136258524363>☺️</emoji> <a href='https://tether.to'>Tether</a>: {} USDT\n\n"
"<emoji document_id=5215590800003451651>☺️</emoji> <a href='https://bitcoin.org'>Bitcoin</a>: {} BTC\n\n"
"<emoji document_id=5217867240044512715>☺️</emoji> <a href='https://etherium.org'>Etherium</a>: {} ETH\n\n"
"<emoji document_id=5215595550237279768>☺️</emoji> <a href='https://binance.org'>Binance coin</a>: {} BNB\n\n"
"<emoji document_id=5215437796088499410>☺️</emoji> <a href='https://tron.network'>TRON</a>: {} TRX\n\n"
"<emoji document_id=5215440441788351459>☺️</emoji> <a href='https://www.centre.io/usdc'>USD Coin</a>: {} USDC\n\n"
"<emoji document_id=5215267041073711005>☺️</emoji> <a href='https://gramcoin.org'>Gram</a>: {} GRAM\n\n"
"<emoji document_id=5217877586620729050>☺️</emoji> <a href='https://litecoin.org'>Litecoin</a>: {} LTC",
"ton": "Введите количество валюты для Toncoin",
"teth": "Введите количество валюты для Tethcoin",
"btc": "Введите количество валюты для Bitcoin",
"ether": "Введите количество валюты для Etherium",
"binc": "Введите количество валюты для Binance coin",
"tron": "Введите количество валюты для Tron",
"usdt": "Введите количество валюты для USDT coin",
"gram": "Введите количество валюты для Gramcoin",
"lite": "Введите количество валюты для Litecoin",
"info": "<b><emoji document_id=5305467350064047192>🫥</emoji><i> Внимание!</b>\n\n"
"<i><emoji document_id=5915991028430542030>☝️</emoji> Использование этого модуля в целях <b>скама, обмана и рекламы</b> строго запрещено.\n\n"
"<emoji document_id=5787190061644647815>🗣</emoji> Модуль предоставлен исключительно в развлекательных целях, и любое нарушение <b>Правил использования модуля</b>, если его обнаружат, будет подлежать соответствующему наказанию.</i>",
}
@loader.command(
ru_doc="Чтобы заполучить поддельный кошелек",
en_doc="To get a fake wallet",
)
@loader.command()
async def fwalletcmd(self, message):
ton = self.config["Toncoin"]
teth = self.config["Tether"]
btc = self.config["Bitcoin"]
ether = self.config["Etherium"]
binc = self.config["Binance"]
tron = self.config["Tron"]
usdt = self.config["USDT"]
gram = self.config["Gram"]
lite = self.config["Litecoin"]
await utils.answer(
message,
self.strings("wallet").format(
ton, teth, btc, ether, binc, tron, usdt, gram, lite
),
)
@loader.command(
ru_doc="Информация о FakeModule",
en_doc="Info about FakeModule",
)
@loader.command()
async def fwinfocmd(self, message):
await utils.answer(message, self.strings("info"))

View File

@@ -0,0 +1,137 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 Archquise
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact archquise@gmail.com.
# ---------------------------------------------------------------------------------
# Name: FolderAutoRead
# Description: Automatically reads chats in selected folders
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# ---------------------------------------------------------------------------------
import os
import logging
from telethon import functions
from telethon.tl.types import DialogFilter, InputPeerChannel
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class FolderAutoReadMod(loader.Module):
"""Automatically reads chats in selected folders"""
strings = {
"name": "FolderAutoRead",
"not_exists_or_already_added": "<emoji document_id=5278578973595427038>🚫</emoji> <b>This folder does not exists or it is already added for tracking!</b>",
"_cls_doc": "Automatically reads chats in selected folders every 60 seconds!",
"_cmd_doc_addfolder": "Adds folder to the tracking list by it's name. Usage: .addfolder FolderName",
"_cmd_doc_listfolders": "Prints list of tracked folders",
"_cmd_doc_delfolder": "Deletes folder from the tracking list",
"wrong_args": "<emoji document_id=5278578973595427038>🚫</emoji> <b>Wrong arguments!</b> Usage: .addfolder/delfolder FolderName\n\n<i>Tip: If you trying to delete the folder from the tracking list, double-check that it really still tracking using .listfolders</i>",
"listfolders": "<emoji document_id=5278227821364275264>📁</emoji> <b>List of tracked folders:</b>\n",
"delfolder": "<emoji document_id=5276384644739129761>🗑</emoji> <b>Folder is successfully deleted from the tracking list!</b>",
"addfolder": "<emoji document_id=5278227821364275264>📁</emoji> <b>Folder is successfully added to the tracking list!</b>"
}
strings_ru = {
"not_exists_or_already_added": "<emoji document_id=5278578973595427038>🚫</emoji> <b>Такой папки не существует, или она уже добавлена для отслеживания!</b>",
"_cls_doc": "Автоматически читает чаты в выбранных папках каждые 60 секунд!",
"_cmd_doc_addfolder": "Добавляет папки в список отслеживания по их названию. Использование: .addfolder НазваниеПапки",
"_cmd_doc_listfolders": "Выводит список отслеживаемых папок",
"_cmd_doc_delfolder": "Удаляет папку из списка для отслежнивания",
"wrong_args": "<emoji document_id=5278578973595427038>🚫</emoji> <b>Неверные аргументы!</b> Использование: .addfolder/delfolder НазваниеПапки\n\n<i>Совет: Если вы пытаетесь удалить папку из списка отслеживания, проверьте, что она вообще отслеживается, используя .listfolders</i>",
"listfolders": "<emoji document_id=5278227821364275264>📁</emoji> <b>Список отслеживаемых папок:</b>\n",
"delfolder": "<emoji document_id=5276384644739129761>🗑</emoji> <b>Папка успешно удалена из листа отслеживания!</b>",
"addfolder": "<emoji document_id=5278227821364275264>📁</emoji> <b>Папка успешно добавлена в лист отслеживания!</b>"
}
def __init__(self):
self.tracked_folders = []
async def client_ready(self, client, db):
self.tracked_folders = self.get("tracked_folders", [])
async def on_unload(self):
self.tracked_folders = []
self.set("tracked_folders", [])
@loader.loop(interval=60, autostart=True)
async def read_chats_in_folders(self):
if self.tracked_folders:
all_folders = await self._client(functions.messages.GetDialogFiltersRequest())
for i in range(len(self.tracked_folders)):
match = next(
(f for f in all_folders.filters
if isinstance(f, DialogFilter) and f.title.text == self.tracked_folders[i]),
None
)
for peer in match.pinned_peers:
await self._client(functions.messages.ReadMentionsRequest(peer=peer))
await self._client(functions.messages.ReadReactionsRequest(peer=peer))
if isinstance(peer, InputPeerChannel):
await self._client(functions.channels.ReadHistoryRequest(channel=peer, max_id=0))
else:
await self._client(functions.messages.ReadHistoryRequest(peer=peer, max_id=0))
for peer in match.include_peers:
await self._client(functions.messages.ReadMentionsRequest(peer=peer))
await self._client(functions.messages.ReadReactionsRequest(peer=peer))
if isinstance(peer, InputPeerChannel):
await self._client(functions.channels.ReadHistoryRequest(channel=peer, max_id=0))
else:
await self._client(functions.messages.ReadHistoryRequest(peer=peer, max_id=0))
@loader.command()
async def addfolder(self, message):
arg = utils.get_args_raw(message)
if arg:
all_folders = await self._client(functions.messages.GetDialogFiltersRequest())
match = next(
(f for f in all_folders.filters
if isinstance(f, DialogFilter) and f.title.text == arg),
None
)
if match and match not in self.tracked_folders:
self.tracked_folders.append(arg)
self.set("tracked_folders", self.tracked_folders)
await utils.answer(message, self.strings['addfolder'])
else:
await utils.answer(message, self.strings["not_exists_or_already_added"])
@loader.command()
async def delfolder(self, message):
arg = utils.get_args_raw(message)
if arg and arg in self.tracked_folders:
self.tracked_folders.remove(arg)
self.set("tracked_folders", self.tracked_folders)
await utils.answer(message, self.strings['delfolder'])
else:
await utils.answer(message, self.strings["wrong_args"])
@loader.command()
async def listfolders(self, message):
await utils.answer(message, self.strings["listfolders"] + "\n".join(
f"{folder}" for folder in self.tracked_folders
))

View File

@@ -0,0 +1,121 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: GigaChat
# Description: Module for using GigaChat
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api GigaChat
# scope: Api GigaChat 0.0.1
# ---------------------------------------------------------------------------------
from .. import loader, utils
@loader.tds
class GigaChatMod(loader.Module):
"""Module for using GigaChat"""
strings = {
"name": "GigaChat",
"api_key_missing": "Please set the API key in the module configuration.",
"query_missing": "Please enter a query after the command.",
"response_error": "Failed to get a response from GigaChat.",
"error_occurred": "An error occurred: {}",
"formatted_response": (
"<emoji document_id=6030848053177486888>❓</emoji> Query: {}\n"
"<emoji document_id=6030400221232501136>🤖</emoji> GigaChat: {}"
),
"giga_model": "List of GigaChat models:\n{}",
}
strings_ru = {
"api_key_missing": "Пожалуйста, установите API ключ в конфигурации модуля.",
"query_missing": "Пожалуйста, введите запрос после команды.",
"response_error": "Не удалось получить ответ от GigaChat.",
"error_occurred": "Произошла ошибка: {}",
"formatted_response": (
"<emoji document_id=6030848053177486888>❓</emoji> Запрос: {}\n"
"<emoji document_id=6030400221232501136>🤖</emoji> GigaChat: {}"
),
"giga_model": "Список моделей GigaChat:\n{}",
}
async def client_ready(self, client, db):
self.hmodslib = await self.import_lib(
"https://raw.githubusercontent.com/C0dwiz/H.Modules/refs/heads/main-fix/HModsLibrary.py"
)
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"GIGACHAT_API_KEY",
None,
"Введите ваш API ключ для GigaChat, Чтобы получить ключ API, перейдите сюда: https://developers.sber.ru/studio/workspaces",
validator=loader.validators.Hidden(),
),
loader.ConfigValue(
"GIGACHAT_MODEL",
"GigaChat",
"Введите модель, ее можно получить при команде .gigamodel",
),
)
@loader.command(
ru_doc="Получите исчерпывающий ответ на свой вопрос",
en_doc="Get GigaResponse to your question",
)
async def giga(self, message):
api_key = self.config["GIGACHAT_API_KEY"]
if not api_key:
return await utils.answer(message, self.strings("api_key_missing"))
query = utils.get_args_raw(message)
if not query:
return await utils.answer(message, self.strings("query_missing"))
try:
response = await self.hmodslib.get_giga_response(api_key, query)
if response:
await utils.answer(
message, self.strings("formatted_response").format(query, response)
)
else:
await utils.answer(message, self.strings("response_error"))
except Exception as e:
await utils.answer(message, self.strings("error_occurred").format(str(e)))
@loader.command(
ru_doc="Получить список моделей",
en_doc="Get a list of models",
)
async def gigamodel(self, message):
api_key = self.config["GIGACHAT_API_KEY"]
if not api_key:
return await utils.answer(message, self.strings("api_key_missing"))
try:
response = await self.hmodslib.get_giga_models(api_key)
if response:
await utils.answer(message, self.strings("giga_model").format(response))
else:
await utils.answer(message, self.strings("response_error"))
except Exception as e:
await utils.answer(message, self.strings("error_occurred").format(str(e)))

44
archquise/H.Modules/H.py Normal file
View File

@@ -0,0 +1,44 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: H
# Description: H.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: H
# scope: H 0.0.1
# ---------------------------------------------------------------------------------
from .. import loader, utils
@loader.tds
class H(loader.Module):
"""H"""
strings = {"name": "H", "h": "H"}
strings_ru = {"h": "H"}
@loader.command(
ru_doc="H",
)
async def h(self, message):
"""H"""
await utils.answer(message, self.strings("h"))

307
archquise/H.Modules/HAFK.py Normal file
View File

@@ -0,0 +1,307 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: HAFK
# Description: Your personal assistant while you are in AFK mode
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: HAFK
# scope: HAFK 0.0.1
# ---------------------------------------------------------------------------------
import datetime
import logging
import time
import asyncio
from telethon import types
from telethon.utils import get_peer_id
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class HAFK(loader.Module):
strings = {
"name": "HAFK",
"afk_on": "<emoji document_id=5357107687484038897>🫶</emoji> <b>AFK mode is on!</b>",
"afk_on_reason": "<emoji document_id=5357107687484038897>🫶</emoji> <b>AFK mode is on!</b>\n\n<b>Reason:</b> <i>{}</i>",
"afk_here_on": "<emoji document_id=5357107687484038897>🫶</emoji> <b>AFK mode is on in this chat!</b>",
"afk_here_on_reason": "<emoji document_id=5357107687484038897>🫶</emoji> <b>AFK mode is on in this chat!</b>\n\n<b>Reason:</b> <i>{}</i>",
"afk_off": "<emoji document_id=5472234792659458002>🤌</emoji> <b>AFK mode is off!</b>",
"afk_off_time": "<emoji document_id=5472234792659458002>🤌</emoji> <b>AFK mode is off!</b>\n\n<b>You were AFK for:</b> {}",
"afk_off_here_time": "<emoji document_id=5472234792659458002>🤌</emoji> <b>AFK mode is off in this chat!</b>\n\n<b>You were AFK for:</b> {}",
"already_afk": "<emoji document_id=5465665476971471368>❌</emoji> <b>You are already in AFK mode!</b>",
"already_afk_here": "<emoji document_id=5465665476971471368>❌</emoji> <b>You are already in AFK mode in this chat!</b>",
"not_afk": "<emoji document_id=5330365133745038003>😐</emoji> <b>AFK mode is already off.</b>",
"not_afk_here": "<emoji document_id=5330365133745038003>😐</emoji> <b>AFK mode is already off in this chat.</b>",
"afk_message": "<emoji document_id=5330130448142049118>🫤</emoji> <b>I'm currently not accepting messages!</b>\n<b>Reason:</b> <i>{}</i>\n\n<i>Inactive mode has been on for:</i> {}",
"afk_message_reason": "<emoji document_id=5330130448142049118>🫤</emoji> <b>I'm currently not accepting messages!</b>\n<b>Reason:</b> <i>{}</i>\n\n<i>Inactive mode has been on for:</i> {}",
"afk_set_failed": "<b>Failed to set AFK status. Check logs.</b>",
"added_excluded_chat": "<b>Chat {} added to excluded chats.</b>",
"removed_excluded_chat": "<b>Chat {} removed from excluded chats.</b>",
"excluded_chats_list": "<b>Excluded chats:</b>\n{}",
"no_excluded_chats": "<b>No chats are excluded.</b>",
"invalid_chat_id": "<b>Invalid chat ID.</b>",
"already_excluded": "<b>Chat {} is already excluded.</b>",
"not_excluded": "<b>Chat {} is not excluded.</b>",
}
strings_ru = {
"afk_on": "<emoji document_id=5357107687484038897>🫶</emoji> <b>AFK-режим включен!</b>",
"afk_on_reason": "<emoji document_id=5357107687484038897>🫶</emoji> <b>AFK-режим включен!</b>\n\n<b>Причина:</b> <i>{}</i>",
"afk_here_on": "<emoji document_id=5357107687484038897>🫶</emoji> <b>AFK-режим включен в этом чате!</b>",
"afk_here_on_reason": "<emoji document_id=5357107687484038897>🫶</emoji> <b>AFK-режим включен в этом чате!</b>\n\n<b>Причина:</b> <i>{}</i>",
"afk_off": "<emoji document_id=5472234792659458002>🤌</emoji> <b>AFK-режим отключен!</b>",
"afk_off_time": "<emoji document_id=5472234792659458002>🤌</emoji> <b>AFK-режим отключен!</b>\n\n<b>Вы были AFK:</b> {}",
"afk_off_here_time": "<emoji document_id=5472234792659458002>🤌</emoji> <b>AFK-режим отключен в этом чате!</b>\n\n<b>Вы были AFK:</b> {}",
"already_afk": "<emoji document_id=5465665476971471368>❌</emoji> <b>Вы уже находитесь в AFK-режиме!</b>",
"already_afk_here": "<emoji document_id=5465665476971471368>❌</emoji> <b>Вы уже находитесь в AFK-режиме в этом чате!</b>",
"not_afk": "<emoji document_id=5330365133745038003>😐</emoji> <b>AFK-режим уже отключён.</b>",
"not_afk_here": "<emoji document_id=5330365133745038003>😐</emoji> <b>AFK-режим уже отключен в этом чате.</b>",
"afk_message": "<emoji document_id=5330130448142049118>🫤</emoji> <b>На данный момент я не принимаю сообщения!</b>\n<b>Причина:</b> <i>{}</i>\n\n<i>С момента включения режима неактивности:</i> {}",
"afk_message_reason": "<emoji document_id=5330130448142049118>🫤</emoji> <b>На данный момент я не принимаю сообщения!</b>\n<b>Причина:</b> <i>{}</i>\n\n<i>С момента включения режима неактивности:</i> {}",
"afk_set_failed": "<b>Не удалось установить AFK-статус. Проверьте логи.</b>",
"added_excluded_chat": "<b>Чат {} добавлен в список исключений.</b>",
"removed_excluded_chat": "<b>Чат {} удален из списка исключений.</b>",
"excluded_chats_list": "<b>Список исключенных чатов:</b>\n{}",
"no_excluded_chats": "<b>Нет исключенных чатов.</b>",
"invalid_chat_id": "<b>Неверный ID чата.</b>",
"already_excluded": "<b>Чат {} уже исключен.</b>",
"not_excluded": "<b>Чат {} не исключен.</b>",
}
DEFAULT_AFK_TIMEOUT = 60
DEFAULT_DELETE_TIMEOUT = 5
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"excluded_chats",
[],
lambda: "List of chat IDs where AFK mode will not be activated",
validator=loader.validators.Series(
validator=loader.validators.Integer()
),
)
)
async def client_ready(self, client, db):
self.client = client
self.db = db
self._me = await client.get_me()
self._ratelimit_cache = {}
self.global_afk = self.db.get(__name__, "afk", False)
self.global_afk_reason = self.db.get(__name__, "afk_reason", None)
self.global_gone_time = self.db.get(__name__, "gone_afk", None)
logger.debug(
f"Initial global AFK state: afk={self.global_afk}, reason={self.global_afk_reason}, gone_time={self.global_gone_time}"
)
@loader.command(
ru_doc="[reason / none] Установить режим AFK",
en_doc="[reason / none] Set AFK mode globally",
)
async def afk(self, message):
await self._afk_toggle(message, global_afk=True)
@loader.command(
ru_doc="[reason / none] Установить режим AFK только в этом чате.",
en_doc="[reason / none] Set AFK mode in current chat only.",
)
async def afkhere(self, message):
await self._afk_toggle(message, global_afk=False)
async def _afk_toggle(self, message, global_afk: bool):
chat_id = utils.get_chat_id(message)
db_key = "afk" if global_afk else f"afk_here_{chat_id}"
already_afk_string = "already_afk" if global_afk else "already_afk_here"
afk_on_string = "afk_on" if global_afk else "afk_here_on"
afk_on_reason_string = "afk_on_reason" if global_afk else "afk_here_on_reason"
if self._is_afk_enabled(chat_id, global_afk):
await utils.answer(message, self.strings(already_afk_string, message))
return
reason = utils.get_args_raw(message) or None
success = self._set_afk(
True, reason=reason, chat_id=chat_id if not global_afk else None
)
if not success:
await utils.answer(message, self.strings("afk_set_failed", message))
return
await utils.answer(
message,
self.strings(afk_on_reason_string, message).format(reason)
if reason
else self.strings(afk_on_string, message),
)
@loader.command(
ru_doc="Выйти из режима AFK",
en_doc="Exit AFK mode",
)
async def unafk(self, message):
await self._unafk_toggle(message, global_afk=True)
@loader.command(
ru_doc="Выйти из режима AFK в этом чате",
en_doc="Exit AFK mode in this chat",
)
async def unafkhere(self, message):
await self._unafk_toggle(message, global_afk=False)
async def _unafk_toggle(self, message, global_afk: bool):
chat_id = utils.get_chat_id(message)
not_afk_string = "not_afk" if global_afk else "not_afk_here"
afk_off_time_string = "afk_off_time" if global_afk else "afk_off_here_time"
if not self._is_afk_enabled(chat_id, global_afk):
await utils.answer(message, self.strings(not_afk_string, message))
return
total_gone_time = self._calculate_total_afk_time(
datetime.datetime.now().replace(microsecond=0),
chat_id=chat_id if not global_afk else None,
)
self._set_afk(False, chat_id=chat_id if not global_afk else None)
await self.allmodules.log("unafk" if global_afk else "unafkhere")
await utils.answer(
message, self.strings(afk_off_time_string, message).format(total_gone_time)
)
async def watcher(self, message):
if not isinstance(message, types.Message):
return
chat_id = get_peer_id(message.peer_id)
user_id = getattr(message.to_id, "user_id", None)
is_mentioned = message.mentioned or user_id == self._me.id
is_private = isinstance(message.to_id, types.PeerUser)
if not (is_mentioned or is_private) or chat_id in self.config["excluded_chats"]:
return
reason = None
gone_time = None
if self._is_afk_enabled(chat_id, False):
reason = self.db.get(__name__, f"afk_here_{chat_id}_reason", None)
gone_time = self.db.get(__name__, f"gone_afk_here_{chat_id}", None)
elif self.global_afk:
reason = self.global_afk_reason
gone_time = self.global_gone_time
else:
return
if gone_time is None:
logger.warning(f"No 'gone' time found for chat {chat_id}. Cannot send AFK.")
return
afk_message = await self._send_afk_message(message, reason, gone_time)
if afk_message:
await self._delete_message(afk_message, self.DEFAULT_DELETE_TIMEOUT)
def _set_afk(self, value: bool, reason: str = None, chat_id: int = None) -> bool:
try:
key_prefix = f"afk_here_{chat_id}" if chat_id else "afk"
self.db.set(__name__, key_prefix, value)
self.db.set(__name__, f"{key_prefix}_reason", reason)
gone_key = f"gone_{key_prefix}"
gone_time = time.time() if value else None
self.db.set(__name__, gone_key, gone_time)
logger.debug(f"AFK status updated. {gone_key} set to {gone_time}")
if chat_id is None:
self.global_afk = value
self.global_afk_reason = reason
self.global_gone_time = gone_time
logger.debug("Updated global AFK vars")
return True
except Exception as e:
logger.exception(f"Error setting AFK status: {e}")
return False
def _calculate_total_afk_time(
self, now: datetime.datetime, chat_id: int = None
) -> datetime.timedelta:
key_prefix = f"afk_here_{chat_id}" if chat_id else "afk"
gone_time = self.db.get(__name__, f"gone_{key_prefix}")
if gone_time is None:
return datetime.timedelta(seconds=0)
try:
gone = datetime.datetime.fromtimestamp(gone_time).replace(microsecond=0)
return now - gone
except Exception as e:
logger.error(f"Error calculating AFK time: {e}")
return datetime.timedelta(seconds=0)
async def _send_afk_message(self, message, reason, gone_time):
user = await utils.get_user(message)
if user.is_self or user.bot or user.verified:
logger.debug("User is self, bot, or verified. Not sending AFK message.")
return None
now = datetime.datetime.now().replace(microsecond=0)
try:
gone = datetime.datetime.fromtimestamp(gone_time).replace(microsecond=0)
except (TypeError, ValueError) as e:
logger.error(f"Error converting timestamp: {e}")
return None
time_string = str(now - gone)
ret = (
self.strings("afk_message_reason", message).format(reason, time_string)
if reason
else self.strings("afk_message", message).format(time_string)
)
try:
reply = await utils.answer(message, ret, reply_to=message)
return reply
except Exception as e:
logger.exception(f"Error sending AFK message: {e}")
return None
def _is_afk_enabled(self, chat_id: int = None, global_afk: bool = False) -> bool:
return (
self.global_afk
if global_afk
else self.db.get(__name__, f"afk_here_{chat_id}", False)
)
async def _delete_message(self, message, delay):
await asyncio.sleep(delay)
try:
await self.client.delete_messages(message.chat_id, message.id)
except Exception as e:
logger.exception(f"Error deleting message: {e}")

View File

@@ -0,0 +1,338 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: HModsLibrary
# Description: Library required for most H:Mods modules.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: HModsLibrary
# scope: HModsLibrary 0.0.1
# ---------------------------------------------------------------------------------
import logging
import re
import aiohttp
import random
import asyncio
import os
from bs4 import BeautifulSoup
from gigachat import GigaChat
from typing import Optional, Dict, Any
from .. import loader, utils
logger = logging.getLogger(__name__)
__version__ = (0, 0, 2)
class HModsLib(loader.Library):
"""Library required for most H:Mods modules."""
developer = "@hikka_mods"
version = __version__
async def config_complete(self):
self.set("goy_guru_api_token", None) # Initialize with None, user will set it
async def parse_time(self, time_str):
time_units = {"d": 86400, "h": 3600, "m": 60, "s": 1}
if not re.fullmatch(r"(\d+[dhms])+", time_str):
return None
seconds = 0
matches = re.findall(r"(\d+)([dhms])", time_str)
for amount, unit in matches:
seconds += int(amount) * time_units[unit]
return seconds if seconds > 0 else None
@staticmethod
def convert_size(size):
"""Convert file size to human-readable format."""
power = 2**10
n = 0
units = {0: "B", 1: "KB", 2: "MB", 3: "GB", 4: "TB"}
while size > power:
size /= power
n += 1
return round(size, 2), units[n]
async def upload_to_envs(self, path):
"""Upload file to envs.sh and return the URL."""
url = "https://envs.sh"
async with aiohttp.ClientSession() as session:
async with session.post(url, data={"file": open(path, "rb")}) as response:
if response.status != 200:
os.remove(path)
raise aiohttp.ClientResponseError(
request_info=response.request_info,
history=response.history,
status=response.status,
message=await response.text(),
headers=response.headers,
)
result = await response.text()
os.remove(path)
return result
async def get_creation_date(self, user_id: int) -> str:
api_token = "7518491974:1ea2284eec9dc40a9838cfbcb48a2b36"
url = "https://api.goy.guru/api/v1/users/getCreationDateFast"
params = {"token": api_token, "user_id": user_id}
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params) as response:
if response.status == 200:
json_response = await response.json()
if json_response["success"]:
return {
"creation_date": json_response["creation_date"],
"accuracy_text": json_response["accuracy_text"],
} # type: ignore
else:
return {"error": json_response["error"]["message"]} # type: ignore
else:
return {"error": f"HTTP {response.status}"} # type: ignore
async def get_giga_response(self, api_key, query):
"""Gets a response from GigaChat with the specified query."""
async with GigaChat(
credentials=api_key,
scope="GIGACHAT_API_PERS",
model=self.config["GIGACHAT_MODEL"],
verify_ssl_certs=False,
) as giga:
response = giga.chat(query)
if response.choices:
return response.choices[0].message.content
return None
async def get_giga_models(self, api_key):
"""Gets a response from GigaChat with the specified query."""
async with GigaChat(
credentials=api_key, scope="GIGACHAT_API_PERS", verify_ssl_certs=False
) as giga:
response = giga.get_models()
if response:
return (
[model.id_ for model in response.data]
if hasattr(response, "data")
else []
)
return None
async def get_random_image():
random_site = random.randint(1, 3389)
url = f"https://www.memify.ru/memes/{random_site}"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
content = await response.text()
soup = BeautifulSoup(content, "html.parser")
items = soup.find_all("div", {"class": "infinite-item card"})
random_item = random.choice(items)
second_a = random_item.find_all("a")[1]
img = second_a.get("href")
return img
async def virustotal_request(
self,
session: aiohttp.ClientSession,
url: str,
headers: Dict[str, str],
method: str = "GET",
data: Optional[Dict[str, Any]] = None,
files: Optional[Dict[str, Any]] = None,
) -> Optional[Dict[str, Any]]:
"""
Generic function to make requests to the VirusTotal API.
"""
try:
if files:
form = aiohttp.FormData()
for k, v in files.items():
form.add_field(k, v)
async with session.request(
method, url, headers=headers, data=form
) as response:
logger.debug(f"Response status: {response.status}")
logger.debug(f"Response body: {await response.text()}")
if response.status == 200:
return await response.json()
else:
logger.error(
f"VirusTotal API request failed with status: {response.status}, reason: {response.reason}"
)
return None
else:
async with session.request(
method, url, headers=headers, json=data
) as response:
logger.debug(f"Response status: {response.status}")
logger.debug(f"Response body: {await response.text()}")
if response.status == 200:
return await response.json()
else:
logger.error(
f"VirusTotal API request failed with status: {response.status}, reason: {response.reason}"
)
return None
except aiohttp.ClientError as e:
logger.exception(f"AIOHTTP Client error: {e}")
return None
except Exception as e:
logger.exception(f"An unexpected error occurred: {e}")
return None
async def get_upload_url(self, api_key: str) -> Optional[str]:
"""
Retrieves a special upload URL for large files.
"""
headers = {"x-apikey": api_key, "accept": "application/json"}
url = "https://www.virustotal.com/api/v3/files/upload_url"
async with aiohttp.ClientSession() as session:
response = await self.virustotal_request(session, url, headers)
if response and "data" in response and isinstance(response["data"], str):
return response["data"]
else:
logger.error(f"Failed to retrieve upload URL: {response}")
return None
async def scan_file_virustotal(
self, file_path: str, api_key: str, is_large_file: bool = False
) -> Optional[Dict[str, Any]]:
"""
Uploads a file to VirusTotal and retrieves the analysis results. Handles files larger than 32MB.
"""
headers = {"x-apikey": api_key}
url = "https://www.virustotal.com/api/v3/files"
async with aiohttp.ClientSession() as session:
try:
with open(file_path, "rb") as file:
files = {"file": file}
if is_large_file:
upload_url = await self.get_upload_url(api_key)
if not upload_url:
logger.error("Failed to get upload URL for large file.")
return None
url = upload_url
upload_response = await self.virustotal_request(
session, url, headers, method="POST", files=files
)
if (
upload_response
and "data" in upload_response
and "id" in upload_response["data"]
):
analysis_id = upload_response["data"]["id"]
analysis_url = (
f"https://www.virustotal.com/api/v3/analyses/{analysis_id}"
)
for attempt in range(20):
analysis_response = await self.virustotal_request(
session, analysis_url, headers
)
logger.debug(
f"Analysis response (attempt {attempt + 1}): {analysis_response}"
)
if (
analysis_response
and "data" in analysis_response
and "attributes" in analysis_response["data"]
and analysis_response["data"]["attributes"].get(
"status"
)
== "completed"
):
return analysis_response
await asyncio.sleep(10)
logger.warning(
f"Analysis not completed after multiple retries for ID: {analysis_id}"
)
return None
else:
logger.error(
f"File upload or analysis request failed: {upload_response}"
)
return None
except FileNotFoundError:
logger.error(f"File not found: {file_path}")
return None
except Exception as e:
logger.exception(f"An error occurred during file scanning: {e}")
return None
def format_analysis_results(self, analysis_results: Dict[str, Any]) -> str:
"""
Formats the analysis results into a user-friendly message, including specific detections.
"""
if (
not analysis_results
or "data" not in analysis_results
or "attributes" not in analysis_results["data"]
or "stats" not in analysis_results["data"]["attributes"]
or "results" not in analysis_results["data"]["attributes"]
):
logger.warning(
f"Unexpected structure in analysis_results: {analysis_results}"
)
return self.strings("error")
stats = analysis_results["data"]["attributes"]["stats"]
harmless = stats.get("harmless", 0)
malicious = stats.get("malicious", 0)
suspicious = stats.get("suspicious", 0)
undetected = stats.get("undetected", 0)
total_scans = harmless + malicious + suspicious + undetected
analysis_id = analysis_results["data"]["id"]
url = f"https://www.virustotal.com/gui/file-analysis/{analysis_id}"
text = (
f"<b>📊 VirusTotal Scan Results</b>\n\n"
f"🦠 <b>Detections:</b> {malicious} / {total_scans}\n"
f"🟢 <b>Harmless:</b> {harmless}\n"
f"⚠️ <b>Suspicious:</b> {suspicious}\n"
f"❓ <b>Undetected:</b> {undetected}\n\n"
)
if malicious > 0:
text += "<b>⚠️ Detections by engine:</b>\n"
results = analysis_results["data"]["attributes"]["results"]
for engine, result in results.items():
if result["category"] == "malicious":
engine_name = engine.replace("_", " ").title()
text += f" • <b>{engine_name}:</b> {result['result']}\n"
text += "\n"
return {"text": text, "url": url}

View File

@@ -0,0 +1,104 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 Archquise
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact archquise@gmail.com.
# ---------------------------------------------------------------------------------
# Name: InfoBannersManager
# Description: Автоматически меняет баннеры на случайные из выбранного списка через заданный промежуток времени
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
from .. import loader, utils
import logging
import random
import asyncio
logger = logging.getLogger(__name__)
@loader.tds
class InfoBannersManagerMod(loader.Module):
"""Автоматически меняет баннеры на случайные из выбранного списка через заданный промежуток времени"""
strings = {"name": "InfoBannersManager"}
def __init__(self):
self.changer_instance = None
self.config = loader.ModuleConfig(
loader.ConfigValue(
"delay",
60,
"Задержка между изменениями баннеров в секундах",
validator=loader.validators.Integer(minimum=1),
),
loader.ConfigValue(
"bannerslist",
None,
"Список ссылок на баннеры",
validator=loader.validators.Series(validator=loader.validators.Link()),
),
)
async def banner_changer(self):
while True:
try:
if not self.config["bannerslist"]:
logger.warning("Banners list is empty!")
await asyncio.sleep(10)
return
banner = random.choice(self.config["bannerslist"])
instance = self.lookup("HerokuInfo")
if not instance:
instance = self.lookup("HikkaInfo")
instance.config["banner_url"] = banner
except Exception as e:
logger.exception(f"Caught exception: {e}")
await asyncio.sleep(10)
await asyncio.sleep(self.config["delay"])
async def on_unload(self):
if self.changer_instance:
self.changer_instance.cancel()
self.changer_instance = None
@loader.command(
ru_doc="Включить или выключить модуль",
)
async def autobannertoggle(self, message):
if not self.db.get(__name__, "enabled", False):
try:
if self.changer_instance:
self.changer_instance.cancel()
self.db.set(__name__, "enabled", True)
self.changer_instance = asyncio.create_task(self.banner_changer())
await utils.answer(message, "Модуль запущен!")
except Exception as e:
logger.exception(f"Caught exception: {e}")
else:
try:
self.db.set(__name__, "enabled", False)
await utils.answer(message, "Модуль остановлен!")
if self.changer_instance:
self.changer_instance.cancel()
self.changer_instance = None
except Exception as e:
logger.exception(f"Caught exception: {e}")

View File

@@ -0,0 +1,77 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: InlineButton
# Description: Create inline button
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: InlineButton
# scope: InlineButton 0.0.1
# ---------------------------------------------------------------------------------
from ..inline.types import InlineQuery
from .. import loader, utils
@loader.tds
class InlineButtonMod(loader.Module):
"""Create inline button"""
strings = {
"name": "InlineButton",
"titles": "Create a message with the Inline Button",
"error_title": "Error",
"error_description": "Invalid input format. Please provide exactly three comma-separated values.",
"error_message": "Make sure your input is formatted as: message, name, url.",
}
strings_ru = {
"titles": "Создай сообщение с Inline Кнопкой",
"error_title": "Ошибка",
"error_description": "Неверный формат ввода. Пожалуйста, укажите ровно три значения, разделенных запятыми.",
"error_message": "Убедитесь, что ваш ввод имеет следующий формат: сообщение, имя, url.",
}
@loader.command(
ru_doc="Создать inline кнопку\nНапример: @username_bot crinl Текст сообщения, Текст кнопки, Ссылка в кнопке",
en_doc="Create an inline button\nexample: @username_bot crinl Message text, Button text, Link in the button",
)
async def crinl_inline_handler(self, query: InlineQuery):
args = utils.get_args_raw(query.query)
if args:
args_list = [arg.strip() for arg in args.split(",")]
if len(args_list) == 3:
message, name, url = args_list
return {
"title": self.strings("titles"),
"description": f"{message}, {name}, {url}",
"message": message,
"reply_markup": [{"text": name, "url": url}],
}
return {
"title": self.strings("error_title"),
"description": self.strings("error_description"),
"message": self.strings("error_message"),
}

View File

@@ -0,0 +1,74 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: InlineCoin
# Description: Mini game heads or tails.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: InlineCoin
# scope: InlineCoin 0.0.1
# ---------------------------------------------------------------------------------
import random
from ..inline.types import InlineQuery
from .. import loader
@loader.tds
class CoinSexMod(loader.Module):
"""Mini game heads or tails"""
strings = {
"name": "InlineCoin",
"titles": "Heads or tails?",
"description": "Let's find out!",
"heads": "🌚 An eagle fell out!",
"tails": "🌝 Tails fell out!",
"edge": "🙀 Miraculously, the coin remained on the edge!",
}
strings_ru = {
"titles": "Орёл или решка?",
"description": "Давай узнаем!",
"heads": "🌚 Выпал орёл!",
"tails": "🌝 Выпала решка!",
"edge": "🙀 Чудо, монетка осталась на ребре!",
}
def get_coin_flip_result(self) -> dict:
results = [self.strings("heads"), self.strings("tails")]
if random.random() < 0.1:
return self.strings("edge")
else:
return random.choice(results)
@loader.command(
ru_doc="Подбросит монетку ",
en_doc="Flip a coin",
)
async def coin_inline_handler(self, query: InlineQuery):
result = self.get_coin_flip_result()
return {
"title": self.strings("titles"),
"description": self.strings("description"),
"message": f"<b>{result}</b>",
"thumb": "https://github.com/Codwizer/ReModules/blob/main/assets/images.png",
}

View File

@@ -0,0 +1,213 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: InlineHelper
# Description: Basic management of the UB in case only the inline works
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: InlineHelper
# scope: InlineHelper 0.0.1
# ---------------------------------------------------------------------------------
import sys
import os
import asyncio
import logging
from ..inline.types import InlineQuery
from .. import loader, utils, main
@loader.tds
class InlineHelperMod(loader.Module):
"""Basic management of the UB in case only the inline works"""
strings = {
"name": "InlineHelper",
"call_restart": "Restarting...",
"call_update": "Updating...",
"res_prefix": "Successfully reset prefix to default",
"restart_inline_handler_title": "Restart Userbot",
"restart_inline_handler_description": "Restart your userbot via inline",
"restart_inline_handler_message": "Press the button below to restart your userbot",
"restart_inline_handler_reply_text": "Restart",
"update_inline_handler_title": "Update Userbot",
"update_inline_handler_description": "Update your userbot via inline",
"update_inline_handler_message": "Press the button below to update your userbot",
"update_inline_handler_reply_text": "Update",
"terminal_inline_handler_title": "Command Executed!",
"terminal_inline_handler_description": "Command executed successfully",
"terminal_inline_handler_message": "Command {text} executed successfully in terminal",
"modules_inline_handler_title": "Modules",
"modules_inline_handler_description": "List all installed modules",
"modules_inline_handler_result": "☘️ Installed modules:\n",
"resetprefix_inline_handler_title": "Reset Prefix",
"resetprefix_inline_handler_description": "Reset your prefix back to default",
"resetprefix_inline_handler_message": "Are you sure you want to reset your prefix to default dot?",
"resetprefix_inline_handler_reply_text_yes": "Yes",
"resetprefix_inline_handler_reply_text_no": "No",
}
strings_ru = {
"call_restart": "Перезагружаю...",
"call_update": "Обновляю...",
"res_prefix": "Префикс успешно сброшен по умолчанию",
"restart_inline_handler_title": "Перезагрузить юзербота",
"restart_inline_handler_description": "Перезагрузить юзербота через инлайн",
"restart_inline_handler_message": "<b>Нажмите на кнопку ниже для рестарта юзербота</b>",
"restart_inline_handler_reply_text": "Перезапуск",
"update_inline_handler_title": "Обновить юзербота",
"update_inline_handler_description": "Обновить юзербота через инлайн",
"update_inline_handler_message": "<b>Нажмите на кнопку ниже для обновления юзербота</b>",
"update_inline_handler_reply_text": "Обновить",
"terminal_inline_handler_title": "Команда выполнена!",
"terminal_inline_handler_description": "Команда завершена.",
"terminal_inline_handler_message": "Команда <code>{text}</code> была успешно выполнена в терминале",
"modules_inline_handler_title": "Модули",
"modules_inline_handler_description": "Вывести список установленных моудей",
"modules_inline_handler_result": "☘️ Все установленные модули:\n",
"resetprefix_inline_handler_title": "Сбросить префикс",
"resetprefix_inline_handler_description": "Сбросить префикс по умолчанию",
"resetprefix_inline_handler_message": "Вы действительно хотите сбросить ваш префикс и установить стандартную точку?",
"resetprefix_inline_handler_reply_text_yes": "Да",
"resetprefix_inline_handler_reply_text_no": "Нет",
}
async def client_ready(self, client, db):
self.client = client
self.db = db
async def restart(self, call):
"""Restart callback"""
logging.error("InlineHelper: restarting userbot...")
await call.edit(self.strings("call_restart"))
await sys.exit(0)
async def update(self, call):
"""Update callback"""
logging.error("InlineHelper: updating userbot...")
os.system(f"cd {utils.get_base_dir()} && cd .. && git reset --hard HEAD")
os.system("git pull")
await call.edit(self.strings("call_update"))
await sys.exit(0)
async def reset_prefix(self, call):
"""Reset prefix"""
self.db.set(main.__name__, "command_prefix", ".")
await call.edit(self.strings("res_prefix"))
@loader.inline_handler(
ru_doc="Перезагрузить юзербота",
en_doc="Reboot the userbot",
)
async def restart_inline_handler(self, _: InlineQuery):
return {
"title": self.strings("restart_inline_handler_title"),
"description": self.strings("restart_inline_handler_description"),
"message": self.strings("restart_inline_handler_message"),
"reply_markup": [
{
"text": self.strings("restart_inline_handler_reply_text"),
"callback": self.restart,
}
],
}
@loader.inline_handler(
ru_doc="Обновить юзербота",
en_doc="Update the userbot",
)
async def update_inline_handler(self, _: InlineQuery):
return {
"title": self.strings("update_inline_handler_title"),
"description": self.strings("update_inline_handler_description"),
"message": self.strings("update_inline_handler_message"),
"reply_markup": [
{
"text": self.strings("update_inline_handler_reply_text"),
"callback": self.update,
}
],
}
@loader.inline_handler(
ru_doc="Выполнить команду в терминале (лучше сразу подготовить команду и просто вставить)",
en_doc="Execute the command in the terminal (it is better to prepare the command immediately and just paste it)",
)
async def terminal_inline_handler(self, _: InlineQuery):
text = _.args
await asyncio.create_subprocess_shell(
f"{text}",
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=utils.get_base_dir(),
)
return {
"title": self.strings("terminal_inline_handler_title"),
"description": self.strings("terminal_inline_handler_description"),
"message": self.strings("terminal_inline_handler_message").format(
text=text
),
}
@loader.inline_handler(
ru_doc="Вывести список установленных модулей через инлайн",
en_doc="Display a list of installed modules via the inline",
)
async def modules_inline_handler(self, _: InlineQuery):
result = self.strings("modules_inline_handler_result")
for mod in self.allmodules.modules:
try:
name = mod.strings["name"]
except KeyError:
name = mod.__clas__.__name__
result += f"{name}\n"
return {
"title": self.strings("modules_inline_handler_title"),
"description": self.strings("modules_inline_handler_description"),
"message": result,
}
@loader.inline_handler(
ru_doc="Сбросить префикс (осторожнее, сбрасывает ваш префикс на . )",
en_doc="Reset the prefix (be careful, resets your prefix to . )",
)
async def resetprefix_inline_handler(self, _: InlineQuery):
return {
"title": self.strings("resetprefix_inline_handler_title"),
"description": self.strings("resetprefix_inline_handler_description"),
"message": self.strings("resetprefix_inline_handler_message"),
"reply_markup": [
{
"text": self.strings("resetprefix_inline_handler_reply_text_yes"),
"callback": self.reset_prefix,
},
{
"text": self.strings("resetprefix_inline_handler_reply_text_no"),
"action": "close",
},
],
}

View File

@@ -0,0 +1,78 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: IrisSimpleMod
# Description: Module for basic interaction with Iris.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: IrisSimpleMod
# scope: IrisSimpleMod 1.0.1
# ---------------------------------------------------------------------------------
from .. import loader, utils
__version__ = (1, 0, 1)
@loader.tds
class IrisSimpleMod(loader.Module):
"""Модуль для базового взаимодействия с Ирисом"""
strings = {"name": "IrisSimpleMod"}
@loader.command(ru_doc="Проверить мешок")
async def bag(self, message):
"""Check bag"""
async with self.client.conversation(5443619563) as conv:
usermessage = await conv.send_message("мешок")
await usermessage.delete()
bagmessage = await conv.get_response()
await utils.answer(message, "Ваш мешок:\n" + bagmessage.text)
await bagmessage.delete()
@loader.command(ru_doc="Зафармить ирис-коины")
async def farm(self, message):
"""Farm iris-coins"""
async with self.client.conversation(5443619563) as conv:
usermessage = await conv.send_message("ферма")
await usermessage.delete()
farmmessage = await conv.get_response()
await utils.answer(message, farmmessage.text)
await farmmessage.delete()
@loader.command(ru_doc="Вывести анкету")
async def irisstats(self, message):
"""Display user stats"""
async with self.client.conversation(5443619563) as conv:
usermessage = await conv.send_message("анкета")
await usermessage.delete()
statsmessage = await conv.get_response()
await utils.answer(message, statsmessage.text)
await statsmessage.delete()
@loader.command(ru_doc="Вывести статистику ботов")
async def irisping(self, message):
"""Display bot stats"""
async with self.client.conversation(5443619563) as conv:
usermessage = await conv.send_message("🌺 Семейство ирисовых")
await usermessage.delete()
pingmessage = await conv.get_response()
await utils.answer(message, pingmessage.text)
await pingmessage.delete()

View File

@@ -0,0 +1,100 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: KBSwapper
# Description: KBSwapper is a module for changing the keyboard layout
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: KBSwapper
# scope: KBSwapper 0.0.1
# ---------------------------------------------------------------------------------
import string
from .. import loader, utils
EN_TO_RU = str.maketrans(
"qwertyuiop[]asdfghjkl;'zxcvbnm,./`" + 'QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?~',
"йцукенгшщзхъфывапролджэячсмитьбю.ё" + "ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,Ё",
)
RU_TO_EN = str.maketrans(
"йцукенгшщзхъфывапролджэячсмитьбю.ё" + "ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,Ё",
"qwertyuiop[]asdfghjkl;'zxcvbnm,./`" + 'QWERTYUIOP{}ASDFGHJKL:"ZXCVBNM<>?~',
)
@loader.tds
class KBSwapperMod(loader.Module):
"""KBSwapper is a module for changing the keyboard layout"""
strings = {
"name": "KBSwapper",
"no_reply": "<emoji document_id=5774077015388852135>❌</emoji> <b>Please reply to a message.</b>",
"no_text": "<emoji document_id=5774077015388852135>❌</emoji> <b>The replied message does not contain text.</b>",
"original_message": "<emoji document_id=5260450573768990626>➡️</emoji> <b>Original message:</b>\n<code>{original}</code>",
"fixed_message": "<emoji document_id=5774022692642492953>✅</emoji> <b>Fixed message:</b>\n<code>{fixed}</code>",
"error": "<emoji document_id=5774077015388852135>❌</emoji> <b>An error occurred while processing the message.</b>",
}
strings_ru = {
"no_reply": "<emoji document_id=5774077015388852135>❌</emoji> <b>Пожалуйста, ответьте на сообщение.</b>",
"no_text": "<emoji document_id=5774077015388852135>❌</emoji> <b>Отвеченное сообщение не содержит текста.</b>",
"original_message": "<emoji document_id=5260450573768990626>➡️</emoji> <b>Оригинальное сообщение:</b>\n<code>{original}</code>",
"fixed_message": "<emoji document_id=5774022692642492953>✅</emoji> <b>Исправленное сообщение:</b>\n<code>{fixed}</code>",
"error": "<emoji document_id=5774077015388852135>❌</emoji> <b>Произошла ошибка при обработке сообщения.</b>",
}
@loader.command(
ru_doc="При ответе на своё сообщение меняет раскладку путем редактирования, на чужое — в отдельном сообщении.",
en_doc="Change keyboard layout for the replied message.",
)
async def swap(self, message):
reply = await message.get_reply_message()
if not reply:
await utils.answer(message, self.strings("no_reply"))
return
original_text = reply.text
if not original_text:
await utils.answer(message, self.strings("no_text"))
return
try:
first_char = original_text[0].lower()
if first_char in string.ascii_lowercase:
fixed_text = original_text.translate(EN_TO_RU)
elif first_char in "йцукенгшщзхъфывапролджэячсмитьбю.ё":
fixed_text = original_text.translate(RU_TO_EN)
else:
fixed_text = original_text
if message.sender_id == reply.sender_id:
await reply.edit(fixed_text)
else:
await utils.answer(
message,
f"{self.strings('original_message').format(original=original_text)}\n"
f"{self.strings('fixed_message').format(fixed=fixed_text)}",
)
except Exception as e:
print(f"Error during swap: {e}")
await utils.answer(message, self.strings("error"))

View File

@@ -0,0 +1,17 @@
Proprietary License Agreement
Copyright (c) 2024-29 Archquise
Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
3. 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 non-infringement. In no event shall the author or copyright holder 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.
4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
5. By using the Software, you agree to be bound by the terms and conditions of this license.
For any inquiries or requests for permissions, please contact archquise@gmail.com.

View File

@@ -0,0 +1,109 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Meme
# Description: Random memes
# Author: @hikka_mods
# Commands:
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Meme
# scope: Meme 0.0.1
# ---------------------------------------------------------------------------------
from bs4 import BeautifulSoup
import aiohttp
import random
from .. import loader
@loader.tds
class MemesMod(loader.Module):
"""Random memes"""
strings = {
"name": "Memes",
"done": "☄️ Catch the meme",
"still": "🔄 Update",
"dell": "❌ Close",
}
strings_ru = {
"done": "☄️ Лови мем",
"still": "🔄 Обновить",
"dell": "❌ Закрыть",
}
async def client_ready(self, client, db):
self.hmodslib = await self.import_lib(
"https://raw.githubusercontent.com/C0dwiz/H.Modules/refs/heads/main-fix/HModsLibrary.py"
)
@loader.command(
ru_doc="",
en_doc="",
)
async def memescmd(self, message):
img = await self.hmodslib.get_random_image()
await self.inline.form(
text=self.strings("done"),
photo=img,
message=message,
reply_markup=[
[
{
"text": self.strings("still"),
"callback": self.ladno,
}
],
[
{
"text": self.strings("dell"),
"callback": self.dell,
}
],
],
silent=True,
)
async def ladno(self, call):
img = await self.hmodslib.get_random_image()
await call.edit(
text=self.strings("done"),
photo=img,
reply_markup=[
[
{
"text": self.strings("still"),
"callback": self.ladno,
}
],
[
{
"text": self.strings("dell"),
"callback": self.dell,
}
],
],
)
async def dell(self, call):
"""Callback button"""
await call.delete()

View File

@@ -0,0 +1,277 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: MessageMonitor
# Description: Monitor messages for trigger words in all chats.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: MessageMonitor
# scope: MessageMonitor 0.0.1
# ---------------------------------------------------------------------------------
import logging
import re
from herokutl.types import Message
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class MessageMonitor(loader.Module):
"""
Monitor messages for trigger words in all chats.
"""
strings = {
"name": "MessageMonitor",
"triggers_set": "Trigger words have been set: {}",
"triggers_not_set": "Trigger words have not been set",
"target_set": "Target chat for notifications has been set",
"target_not_set": "Target chat for notifications has not been set",
"monitoring_started": "Monitoring has started",
"monitoring_stopped": "Monitoring has stopped",
"monitoring_status": "Monitoring {}",
"triggers_example": "Example: <code>.triggers word1 word2</code>",
"monitoring_status_on": "enabled",
"monitoring_status_off": "disabled",
"ignore_set": "Ignored chats have been set: {}",
"ignore_none": "Ignored chats have not been set",
"ignore_example": "Example: <code>.ignore 123456789 -987654321</code> (chat IDs)",
"no_reply": "Reply to a message in the desired chat or specify its ID",
"monitoring_msg": (
"🚨 **Trigger word detected!** 🚨\n\n"
"**Chat:** {} (`{}`)\n"
"**User:** {}\n"
"**Link:** {}\n\n"
"**Messages:**\n{}"
),
}
strings_ru = {
"triggers_set": "Триггерные слова установлены: {}",
"triggers_not_set": "Триггерные слова не установлены",
"target_set": "Целевой чат для уведомлений установлен",
"target_not_set": "Целевой чат для уведомлений не установлен",
"monitoring_started": "Мониторинг запущен",
"monitoring_stopped": "Мониторинг остановлен",
"monitoring_status": "Мониторинг {}",
"triggers_example": "Пример: <code>.triggers слово1 слово2</code>",
"monitoring_status_on": "включен",
"monitoring_status_off": "выключен",
"ignore_set": "Игнорируемые чаты установлены: {}",
"ignore_none": "Игнорируемые чаты не установлены",
"ignore_example": "Пример: <code>.ignore 123456789 -987654321</code> (ID чатов)",
"no_reply": "Ответьте на сообщение в нужном чате или укажите его ID",
"monitoring_msg": (
"🚨 **Обнаружено триггерное слово!** 🚨\n\n"
"**Чат:** {} (`{}`)\n"
"**Пользователь:** {}\n"
"**Ссылка:** {}\n\n"
"**Сообщение:**\n{}"
),
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"triggers",
[],
"Список триггерных слов",
validator=loader.validators.Series(),
),
loader.ConfigValue(
"target_chat",
None,
"ID целевого чата для отправки уведомлений",
validator=loader.validators.Integer(),
),
loader.ConfigValue(
"ignore_chats",
[],
"Список ID чатов, которые будут игнорироваться",
validator=loader.validators.Series(),
),
)
self._triggers = []
self._target_chat = None
self._ignore_chats = []
async def client_ready(self, client, db):
self._triggers = self.config["triggers"]
self._target_chat = self.config["target_chat"]
self._ignore_chats = [
int(i)
for i in self.config["ignore_chats"]
if str(i).isdigit() or str(i).startswith("-")
]
self.client = client
@loader.command(
ru_doc="Установить триггерные слова. Пример: .triggers слово1 слово2",
en_doc="Set trigger words. Example: .triggers word1 word2",
)
async def triggers(self, message: Message):
"""Set trigger words"""
args = utils.get_args(message)
if not args:
await utils.answer(message, self.strings["triggers_example"])
return
self._triggers = [arg.lower() for arg in args]
self.config["triggers"] = self._triggers
await utils.answer(
message, self.strings["triggers_set"].format(", ".join(self._triggers))
)
@loader.command(
ru_doc="Установить целевой чат для уведомлений. Ответьте на сообщение или укажите ID",
en_doc="Set target chat for notifications. Reply to a message or provide its ID",
)
async def settarget(self, message: Message):
"""Set target chat"""
args = utils.get_args_raw(message)
chat_id = None
if getattr(message, "is_reply", False):
get_reply_method = getattr(message, "get_reply_message", None)
if get_reply_method:
reply_message = await get_reply_method()
if reply_message and getattr(reply_message, "chat_id", None):
chat_id = reply_message.chat_id
elif args and (args.isdigit() or args.startswith("-") and args[1:].isdigit()):
chat_id = int(args)
if chat_id:
self.config["target_chat"] = chat_id
self._target_chat = chat_id
await utils.answer(message, self.strings["target_set"])
else:
await utils.answer(message, self.strings["no_reply"])
@loader.command(
ru_doc="Установить игнорируемые чаты. Укажите ID чатов через пробел.",
en_doc="Set ignored chats. Provide chat IDs separated by space.",
)
async def ignore(self, message: Message):
"""Set ignored chats"""
args = utils.get_args(message)
if not args:
await utils.answer(message, self.strings["ignore_example"])
return
valid_ids = []
for arg in args:
if arg.isdigit() or (arg.startswith("-") and arg[1:].isdigit()):
valid_ids.append(int(arg))
self.config["ignore_chats"] = valid_ids
self._ignore_chats = valid_ids
if valid_ids:
await utils.answer(
message,
self.strings["ignore_set"].format(", ".join(map(str, valid_ids))),
)
else:
await utils.answer(
message, "Не удалось распознать ID чатов. Используйте только числа."
)
@loader.watcher(out=False, only_messages=True)
async def message_watcher(self, message: Message):
"""Watch for messages containing trigger words"""
if not self._target_chat or not self._triggers:
return
chat_id = getattr(message, "chat_id", None)
if chat_id and chat_id in self._ignore_chats:
logger.debug(f"Message in ignored chat: {chat_id}. Skipping monitoring.")
return
text = getattr(message, "text", "")
if not text:
return
text_lower = text.lower()
found_triggers = [
trigger
for trigger in self._triggers
if re.search(r"\b" + re.escape(trigger) + r"\b", text_lower)
]
if not found_triggers:
return
try:
get_chat_method = getattr(message, "get_chat", None)
if get_chat_method:
chat = await get_chat_method()
chat_title = getattr(
chat,
"title",
"Личные сообщения"
if getattr(message, "is_private", False)
else "Чат с ID " + str(chat_id),
)
else:
chat_title = "Неизвестный чат"
get_sender_method = getattr(message, "get_sender", None)
if get_sender_method:
sender = await get_sender_method()
sender_name = sender.first_name
if getattr(sender, "last_name", None):
sender_name += f" {sender.last_name}"
if not sender_name:
sender_name = getattr(
sender, "username", "Неизвестный пользователь"
)
else:
sender_name = "Неизвестный пользователь"
to_id_obj = getattr(message, "to_id", None)
if to_id_obj and getattr(to_id_obj, "channel_id", None):
link = f"https://t.me/c/{to_id_obj.channel_id}/{message.id}"
elif getattr(message, "is_private", False) and getattr(
sender, "username", None
):
link = f"https://t.me/{sender.username}/{message.id}"
await self.client.send_message(
self._target_chat,
self.strings["monitoring_msg"].format(
chat_title,
chat_id,
sender_name,
link,
text,
),
parse_mode="Markdown",
)
logger.debug(
f"Sent notification about trigger word(s) {found_triggers} to chat {self._target_chat}"
)
except Exception as e:
logger.error(f"Error processing message: {e}")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,202 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Music
# Description: Searches for music using Telegram music bots.
# Author: @hikka_mods
# meta developer: @hikka_mods
# scope: Music
# scope: Music 0.0.2
# ---------------------------------------------------------------------------------
# Thanks to @murpizz for the search code yandex
import logging
from telethon.errors.rpcerrorlist import (
BotMethodInvalidError,
FloodWaitError,
MessageNotModifiedError,
)
from telethon.tl.types import Message
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class MusicMod(loader.Module):
strings = {
"name": "Music",
"no_query": "<emoji document_id=5337117114392127164>🤷‍♂</emoji> <b>Provide a search query.</b>",
"searching": "<emoji document_id=4918235297679934237>⌨️</emoji> <b>Searching...</b>",
"found": "<emoji document_id=5336965905773504919>🗣</emoji> <b>Possible match:</b>",
"not_found": "<emoji document_id=5228947933545635555>😫</emoji> <b>Track not found: <code>{}</code>.</b>",
"invalid_service": "<emoji document_id=5462295343642956603>🚫</emoji> <b>Invalid service. (yandex, vk)</b>",
"usage": "<b>Usage:</b> <code>.music [yandex|vk] [track name]</code>",
"error": "<emoji document_id=5228947933545635555>⚠️</emoji> <b>Error:</b> <code>{}</code>",
"no_results": "<emoji document_id=5228947933545635555>😫</emoji> <b>No results: <code>{}</code>.</b>",
"flood_wait": "<emoji document_id=5462295343642956603>⏳</emoji> <b>Wait {}s (Telegram limits).</b>",
"bot_error": "<emoji document_id=5228947933545635555>🤖</emoji> <b>Bot error: <code>{}</code></b>",
"no_audio": "<emoji document_id=5228947933545635555>🎵</emoji> <b>No audio.</b>",
"generic_result": "<emoji document_id=5336965905773504919></emoji> <b>Non-media result. Check the bot's chat.</b>",
"yafind_searching": "<emoji document_id=5258396243666681152>🔎</emoji> <b>Searching Yandex.Music...</b>",
"yafind_not_found": "<emoji document_id=5843952899184398024>🚫</emoji> <b>Track not found on Yandex.Music.</b>",
"yafind_error": "<emoji document_id=5843952899184398024>🚫</emoji> <b>Error (Yandex): {}</b>",
}
strings_ru = {
"name": "Music",
"no_query": "<emoji document_id=5337117114392127164>🤷‍♂</emoji> <b>Укажите запрос.</b>",
"searching": "<emoji document_id=4918235297679934237>⌨️</emoji> <b>Поиск...</b>",
"found": "<emoji document_id=5336965905773504919>🗣</emoji> <b>Возможно, это оно:</b>",
"not_found": "<emoji document_id=5228947933545635555>😫</emoji> <b>Трек не найден: <code>{}</code>.</b>",
"invalid_service": "<emoji document_id=5462295343642956603>🚫</emoji> <b>Неверный сервис. (yandex, vk)</b>",
"usage": "<b>Использование:</b> <code>.music [yandex|vk] [название трека]</code>",
"error": "<emoji document_id=5228947933545635555>⚠️</emoji> <b>Ошибка:</b> <code>{}</code>",
"no_results": "<emoji document_id=5228947933545635555>😫</emoji> <b>Нет результатов: <code>{}</code>.</b>",
"flood_wait": "<emoji document_id=5462295343642956603>⏳</emoji> <b>Подождите {}с (лимиты Telegram).</b>",
"bot_error": "<emoji document_id=5228947933545635555>🤖</emoji> <b>Ошибка бота: <code>{}</code></b>",
"no_audio": "<emoji document_id=5228947933545635555>🎵</emoji> <b>Нет аудио.</b>",
"generic_result": "<emoji document_id=5336965905773504919></emoji> <b>Немедийный результат. Проверьте чат с ботом.</b>",
"yafind_searching": "<emoji document_id=5258396243666681152>🔎</emoji> <b>Поиск в Яндекс.Музыке...</b>",
"yafind_not_found": "<emoji document_id=5843952899184398024>🚫</emoji> <b>Трек не найден в Яндекс.Музыке.</b>",
"yafind_error": "<emoji document_id=5843952899184398024>🚫</emoji> <b>Ошибка (Яндекс): {}</b>",
}
def __init__(self):
self.murglar_bot = "@murglar_bot"
self.vk_bot = "@vkmusic_bot"
@loader.command(
ru_doc="Найти трек в Yandex Music или VK: `.music yandex {название}` или `.music vk {название}`",
en_doc="Find a track in Yandex Music or VK: `.music yandex {name}` or `.music vk {name}`",
)
async def music(self, message):
args = utils.get_args(message)
if not args:
if reply := await message.get_reply_message():
await self._yafind(message, reply.raw_text.strip())
else:
await utils.answer(message, self.strings("usage", message))
return
service, query = args[0].lower(), " ".join(args[1:])
if service == "yandex":
await self._yafind(message, query)
elif service == "vk":
await self._vkfind(message, query)
else:
await utils.answer(message, self.strings("invalid_service", message))
async def _yafind(self, message: Message, query: str):
if not query:
return await utils.answer(message, self.strings("no_query", message))
await utils.answer(message, self.strings("yafind_searching", message))
try:
results = await message.client.inline_query(
self.murglar_bot, f"s:ynd {query}"
)
if not results:
return await utils.answer(
message, self.strings("yafind_not_found", message)
)
await results[0].click(
entity=message.chat_id,
hide_via=True,
reply_to=message.reply_to_msg_id if message.reply_to_msg_id else None,
)
await message.delete()
except Exception as e:
logger.exception("Yandex search error:")
await utils.answer(message, self.strings("yafind_error", message).format(e))
async def _vkfind(self, message, query: str):
if not query:
return await utils.answer(message, self.strings("no_query", message))
await utils.answer(message, self.strings("searching", message))
try:
music = await message.client.inline_query(self.vk_bot, query)
if not music or len(music) <= 1:
return await utils.answer(
message, self.strings("not_found", message).format(query)
)
for i in range(1, len(music), 2):
try:
result = music[i].result
if hasattr(result, "audio") and result.audio:
await message.client.send_file(
message.to_id,
result.audio,
caption=self.strings("found", message),
reply_to=utils.get_topic(message)
if message.reply_to_msg_id
else None,
)
await message.delete()
return
if hasattr(result, "document") and result.document:
await message.client.send_file(
message.to_id,
result.document,
caption=self.strings("found", message),
reply_to=utils.get_topic(message)
if message.reply_to_msg_id
else None,
)
await message.delete()
return
logger.warning(f"No audio/document in result {i}")
await utils.answer(message, self.strings("no_audio", message))
await message.delete()
return
except MessageNotModifiedError:
logger.warning("MessageNotModifiedError, skipping.")
except Exception as e:
logger.error(f"Send error: {e}")
await utils.answer(
message, self.strings("not_found", message).format(query)
)
except BotMethodInvalidError as e:
logger.error(f"VK bot error: {e}")
await utils.answer(message, self.strings("bot_error", message).format(e))
except FloodWaitError as e:
logger.warning(f"Flood wait: {e.seconds}s")
await utils.answer(
message, self.strings("flood_wait", message).format(e.seconds)
)
except Exception as e:
logger.exception("VK search error:")
await utils.answer(message, self.strings("error", message).format(e))

View File

@@ -0,0 +1,94 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: PastebinAPI
# Description: fills in the code on pastebin
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: PastebinAPI
# scope: PastebinAPI 0.0.1
# requires: aiohttp
# ---------------------------------------------------------------------------------
import aiohttp
from .. import loader, utils
@loader.tds
class PastebinAPIMod(loader.Module):
"""PastebinAPI"""
strings = {
"name": "PastebinAPI",
"no_reply": (
"<emoji document_id=5462882007451185227>🚫</emoji> You didn't specify the text"
),
"no_key": "<emoji document_id=5843952899184398024>🚫</emoji> The key was not found",
"done": "Your link with the code\n<emoji document_id=5985571061993837069>➡️</emoji> <code>{response_text}</code>",
}
strings_ru = {
"no_reply": (
"<emoji document_id=5462882007451185227>🚫</emoji> Вы не указали текст"
),
"no_key": "<emoji document_id=5843952899184398024>🚫</emoji> Ключ не найден",
"done": "Ваша ссылка с кодом\n<emoji document_id=5985571061993837069>➡️</emoji> <code>{response_text}</code>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"pastebin",
None,
lambda: "link to get api https://pastebin.com/doc_api#1",
validator=loader.validators.Hidden(),
)
)
@loader.command(
ru_doc="Заливает код в Pastebin",
en_doc="Uploads the code to Pastebin",
)
async def past(self, message):
text = utils.get_args(message)
if self.config["pastebin"] is None:
await utils.answer(message, self.strings("no_key"))
return
if not text:
await utils.answer(message, self.strings("no_reply"))
return
async with aiohttp.ClientSession() as Session:
async with Session.post(
url="https://pastebin.com/api/api_post.php",
data={
"api_dev_key": self.config["pastebin"],
"api_paste_code": text,
"api_option": "paste",
},
) as response:
response_text = await response.text()
await utils.answer(
message, self.strings("done").format(response_text=response_text)
)

View File

@@ -0,0 +1,91 @@
<div align="center">
<img src="https://github.com/Codwizer/ReModules/blob/main/assets/Vector.png" alt="hikka_mods" width="600">
</div>
<h1 align="center">H:Mods - Hikka Modules</h1>
<p align="center">
Enhance your <a href="https://github.com/hikariatama/Hikka">Hikka</a>/<a href="https://github.com/coddrago/Heroku">Heroku</a> experience with a curated collection of community modules.
</p>
<p align="center">
<a href="https://t.me/hikka_mods">
<img src="https://img.shields.io/badge/Join%20the-Telegram%20Channel-blue?style=flat-square&logo=telegram" alt="Telegram Channel">
</a>
</p>
---
## ✨ Installation Guide
### 1. Direct Installation
To install a module directly from the repository, use the following command:
```bash
.dlm https://raw.githubusercontent.com/archquise/H.Modules/main/<module_name>.py
```
**Example:** To install example_module.py, use:
```bash
.dlm https://raw.githubusercontent.com/archquise/H.Modules/main/example_module.py
```
### 2. Repository Installation (Recommended)
For easier module management and updates, install the entire repository.
**Step 1: Add the Repository**
```bash
.addrepo https://github.com/archquise/H.Modules/raw/main
```
**Step 2: Install Modules from the Repository**
```bash
.dlm <module_name>
```
**Example:** After adding the repository, to install example_module, use:
```bash
.dlm example_module
```
---
## 📜 License
This project is licensed under a **Proprietary License**. By using this software, you agree to the terms and conditions outlined below.
> **You are granted permission to use the Software for personal and non-commercial purposes, subject to the following conditions:**
1. Modification or alteration of the Software is strictly prohibited without explicit written permission from the author.
2. Redistribution of the Software, in original or modified form, is strictly prohibited without explicit written permission from the author.
3. The Software is provided "as is", without any warranty, express or implied.
4. The copyright notice and this permission notice must be included in all copies or substantial portions of the Software.
5. By using the Software, you agree to be bound by the terms and conditions of this license.
**Contact:** For any inquiries or requests for permissions, please contact archquise@gmail.com.
<details>
<summary>Show Full License Details</summary>
> <i>All files of this repository are under <b>Proprietary License.</b></i><br>
> <b>Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:</b>
1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
3. The Software is provided "as is", without warranty of any kind, express or implied.
4. The copyright notice and this permission notice must be included in all copies or substantial portions of the Software.
5. By using the Software, you agree to be bound by the terms and conditions of this license.
For any inquiries or requests for permissions, please contact archquise@gmail.com.
</details>
---
<p align="center">
<img src="https://raw.githubusercontent.com/trinib/trinib/82213791fa9ff58d3ca768ddd6de2489ec23ffca/images/footer.svg" width="100%">
</p>

View File

@@ -0,0 +1,78 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: VowelReplacer
# Description: Replaces vowel letters with ё
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: VowelReplacer
# scope: VowelReplacer 0.0.1
# ---------------------------------------------------------------------------------
from telethon.tl.types import Message
from .. import loader, utils
@loader.tds
class VowelReplacer(loader.Module):
"""Replaces vowel letters with ё"""
strings = {
"name": "Vowel Replacer",
"on": "✅ Vowel substitution for ё has been successfully enabled.",
"off": "🚫 Vowel substitution for ё is disabled.",
}
strings_ru = {
"on": "✅ Замена гласных на ё успешно включена.",
"off": "🚫 Замена гласных на ё отключена.",
}
async def client_ready(self, client, db):
self.db = db
self._client = client
self.enabled = self.db.get("vowel_replacer", "enabled", False)
@loader.command(
ru_doc="Включить или отключить замену гласных на ё.",
en_doc="Enable or disable vowel substitution for ё.",
)
async def vowelreplace(self, message):
self.enabled = not self.enabled
self.db.set("vowel_replacer", "enabled", self.enabled)
if self.enabled:
response = self.strings("on")
else:
response = self.strings("off")
await utils.answer(message, response)
async def watcher(self, message: Message):
"""Автоматическая замена гласных на ё при получении собственного сообщения."""
if self.enabled and message.out:
vowels = "аеёиоуыэюяАЕЁИОУЫЭЮЯ"
message_text = message.text
replaced_text = "".join(
"ё" if char in vowels else char for char in message_text
)
await message.edit(replaced_text)

View File

@@ -0,0 +1,149 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: SMArchiver
# Description: unloads all messages from Favorites
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: SMArchiver
# scope: SMArchiver 0.0.1
# requires: zipfile
# ---------------------------------------------------------------------------------
import zipfile
import os
from datetime import datetime
from .. import loader, utils
@loader.tds
class SMArchiver(loader.Module):
"""unloads all messages from Favorites"""
strings = {
"name": "SMArchiver",
"archive_created": "🎉 Archive with messages has been successfully created: {filename}",
"no_messages": "⚠️ There are no messages in Saved Messages.",
"error": "❌ An error occurred: {error}",
"processing": "🛠️ Processing messages... Please wait.\n\nP.S: Be careful, if you have a lot of messages, you may get flooding, and if you have a lot of heavy files, the download will be slower than usual.",
}
strings_ru = {
"archive_created": "🎉 Архив с сообщениями успешно создан: {filename}",
"no_messages": "⚠️ В Сохраненных сообщениях нет сообщений.",
"error": "❌ Произошла ошибка: {error}",
"processing": "🛠️ Обработка сообщений... Пожалуйста, подождите.\n\nP.S: Будьте осторожны, если у вас много сообщений, вы можете получить флуд, а если у вас много тяжелых файлов, загрузка будет медленнее обычного.",
}
@loader.command(
ru_doc="выгружает все сообщения из Избранного / Saved Messages и собирает их в одном архиве.",
en_doc="downloads all messages from Favorites / Saved Messages and collects them in one archive.",
)
async def smdump(self, message):
await utils.answer(message, self.strings["processing"])
saved_messages = await message.client.get_messages("me", limit=None)
if not saved_messages:
await utils.answer(message, self.strings["no_messages"])
return
archive_path = self.create_archive(saved_messages)
try:
await message.client.send_file(
message.chat_id,
archive_path,
caption=self.strings["archive_created"].format(
filename=os.path.basename(archive_path)
),
)
except Exception as e:
await utils.answer(message, self.strings["error"].format(error=str(e)))
finally:
self.cleanup(archive_path)
def create_archive(self, saved_messages):
current_month = datetime.now().strftime("%B %Y")
archive_path = "saved_messages.zip"
with zipfile.ZipFile(archive_path, "w") as archive:
self.initialize_archive_structure(archive, current_month)
for msg in saved_messages:
await self.add_message_to_archive(msg, archive, current_month)
return archive_path
def initialize_archive_structure(self, archive, current_month):
month_folder = f"{current_month}/"
archive.writestr(month_folder, "")
message_folders = {
"Text Messages": f"{month_folder}Text Messages/",
"Voice Messages": f"{month_folder}Voice Messages/",
"Video Messages": f"{month_folder}Video Messages/",
"Videos": f"{month_folder}Videos/",
"Audios": f"{month_folder}Audios/",
"GIFs": f"{month_folder}GIFs/",
"Files": f"{month_folder}Files/",
}
for folder in message_folders.values():
archive.writestr(folder, "")
async def add_message_to_archive(self, msg, archive, current_month):
"""Обрабатывает отдельное сообщение и добавляет его в архив."""
if msg.message:
await self.add_text_message_to_archive(msg, archive, current_month)
if msg.media:
await self.add_media_to_archive(msg, archive, current_month)
async def add_text_message_to_archive(self, msg, archive, current_month):
timestamp = datetime.fromtimestamp(msg.date.timestamp()).strftime(
"%Y%m%d_%H%M%S"
)
safe_name = f"message_{timestamp}.txt"
archive.writestr(
os.path.join(f"{current_month}/Text Messages/", safe_name), msg.message
)
async def add_media_to_archive(self, msg, archive, current_month):
media_file = await msg.client.download_media(msg.media)
if media_file:
mime_type = (
msg.media.document.mime_type if hasattr(msg.media, "document") else None
)
folder = self.get_media_folder(mime_type, current_month)
archive.write(
media_file, os.path.join(folder, os.path.basename(media_file))
)
def get_media_folder(self, mime_type, current_month):
if mime_type:
if mime_type.startswith("audio/"):
return f"{current_month}/Audios/"
elif mime_type.startswith("video/"):
return f"{current_month}/Videos/"
elif mime_type.startswith("image/gif"):
return f"{current_month}/GIFs/"
return f"{current_month}/Files/"
def cleanup(self, archive_path):
if os.path.exists(archive_path):
os.remove(archive_path)

View File

@@ -0,0 +1,103 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: SafetyMod
# Description: generate random password
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api SafetyMod
# scope: Api SafetyMod 0.0.1
# ---------------------------------------------------------------------------------
import random
import string
from .. import loader, utils
def generate_password(
length: int, letters: bool = True, numbers: bool = True, symbols: bool = True
) -> str:
"""Generates a random password with customizable options.
Args:
length: The desired length of the password.
letters: Include lowercase and uppercase letters (default: True).
numbers: Include digits (default: True).
symbols: Include common symbols (default: True).
Returns:
A randomly generated password string.
Raises:
ValueError: If all character sets are disabled (letters, numbers, symbols).
"""
character_sets = []
if letters:
character_sets.append(string.ascii_letters)
if numbers:
character_sets.append(string.digits)
if symbols:
character_sets.append(string.punctuation)
if not character_sets:
raise ValueError("At least one of letters, numbers, or symbols must be True")
combined_characters = "".join(character_sets)
password = "".join(random.choice(combined_characters) for _ in range(length))
return password
@loader.tds
class SafetyMod(loader.Module):
"""generate random password"""
strings = {
"name": "Safety",
"pass": "<emoji document_id=5472287483318245416>*⃣</emoji> <b>Here is your secure password:</b> <code>{}</code>",
}
strings_ru = {
"pass": "<emoji document_id=5472287483318245416>*⃣</emoji> <b>Вот ваш безопасный пароль:</b> <code>{}</code>"
}
@loader.command(
ru_doc="Случайный пароль\n-n - цифры\n-s - символы \n -l - буквы",
en_doc="Random password\n-n - numbers\n-s - symbols \n -l - letters",
)
async def password(self, message):
"""random password\n-n - numbers\n-s - symbols \n -l - letters"""
text = message.text.split()
length = 10
letters = True
numbers = False
symbols = False
for i in text:
if i.startswith("password"):
length = int(i.split("password")[1])
elif i == "-n":
numbers = True
elif i == "-s":
symbols = True
elif i == "-l":
letters = True
password = generate_password(
length=length, letters=letters, numbers=numbers, symbols=symbols
)
await utils.answer(message, self.strings("pass").format(password))

View File

@@ -0,0 +1,350 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: TaskManager
# Description: Manages tasks with Telegram commands and inline keyboards.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: TaskManager
# scope: TaskManager 0.0.1
# ---------------------------------------------------------------------------------
import datetime
import json
import logging
import os
from dataclasses import dataclass, field
from typing import Dict, List, Optional
from .. import loader, utils
logger = logging.getLogger(__name__)
@dataclass
class Task:
"""Represents a task."""
description: str
due_date: Optional[datetime.datetime] = None
completed: bool = False
created_at: datetime.datetime = field(default_factory=datetime.datetime.now)
class TaskManager:
"""Manages tasks, storing them in a JSON file."""
def __init__(self, data_file: str):
self.data_file = data_file
self.tasks: Dict[int, List[Task]] = {}
self.load_data()
def load_data(self):
"""Loads task data from the JSON file."""
if os.path.exists(self.data_file):
try:
with open(self.data_file, "r") as f:
data = json.load(f)
self.tasks = {
int(user_id): [
Task(
description=task["description"],
due_date=datetime.datetime.fromisoformat(
task["due_date"]
)
if task["due_date"]
else None,
completed=task["completed"],
created_at=datetime.datetime.fromisoformat(
task["created_at"]
),
)
for task in task_list
]
for user_id, task_list in data.items()
}
except (FileNotFoundError, json.JSONDecodeError) as e:
logger.warning(f"Failed to load task data: {e}. Starting empty.")
self.tasks = {}
else:
self.tasks = {}
logger.info("Task data file not found. Starting empty.")
def save_data(self):
"""Saves task data to the JSON file."""
try:
with open(self.data_file, "w") as f:
data = {
user_id: [
{
"description": task.description,
"due_date": task.due_date.isoformat()
if task.due_date
else None,
"completed": task.completed,
"created_at": task.created_at.isoformat(),
}
for task in task_list
]
for user_id, task_list in self.tasks.items()
}
json.dump(data, f, indent=4, default=str)
except IOError as e:
logger.error(f"Failed to save task data: {e}")
def add_task(self, user_id: int, task: Task):
self.tasks.setdefault(user_id, []).append(task)
self.save_data()
def remove_task(self, user_id: int, index: int):
if user_id in self.tasks and 0 <= index < len(self.tasks[user_id]):
del self.tasks[user_id][index]
self.save_data()
else:
logger.warning(f"Invalid index for removal: {index}, user: {user_id}")
def complete_task(self, user_id: int, index: int):
if user_id in self.tasks and 0 <= index < len(self.tasks[user_id]):
self.tasks[user_id][index].completed = True
self.save_data()
else:
logger.warning(f"Invalid index for completion: {index}, user: {user_id}")
def get_tasks(self, user_id: int) -> List[Task]:
return self.tasks.get(user_id, [])
def clear_tasks(self, user_id: int):
if user_id in self.tasks:
self.tasks[user_id] = []
self.save_data()
else:
logger.info(f"No tasks to clear for user: {user_id}")
def get_task(self, user_id: int, index: int) -> Optional[Task]:
return (
self.tasks[user_id][index]
if user_id in self.tasks and 0 <= index < len(self.tasks[user_id])
else None
)
@loader.tds
class TaskManagerModule(loader.Module):
"""Manages tasks with Telegram commands and inline keyboards."""
strings = {
"name": "TaskManager",
"task_added": "<emoji document_id=5776375003280838798>✅</emoji> Task added.",
"task_removed": "<emoji document_id=5776375003280838798>✅</emoji> Task removed.",
"task_completed": "<emoji document_id=5776375003280838798>✅</emoji> Task completed.",
"task_not_found": "<emoji document_id=5778527486270770928>❌</emoji> Task not found.",
"no_tasks": "<emoji document_id=5956561916573782596>📄</emoji> No active tasks.",
"task_list": "<emoji document_id=5956561916573782596>📄</emoji> Your tasks:\n{}",
"invalid_index": "<emoji document_id=5778527486270770928>❌</emoji> Invalid index. Provide valid integer.",
"description_required": "<emoji document_id=5879813604068298387>❗️</emoji> Provide task description.",
"clear_confirmation": "⚠️ Delete all tasks?",
"tasks_cleared": "✅ All tasks deleted.",
"due_date_format": "<emoji document_id=5778527486270770928>❌</emoji> Invalid date. Use YYYY-MM-DD HH:MM.",
"task_info": "<emoji document_id=6028435952299413210></emoji> Task: {description}\n<emoji document_id=5967412305338568701>📅</emoji> Due: {due_date}\n<emoji document_id=5825794181183836432>✔️</emoji> Completed: {completed}\n<emoji document_id=5936170807716745162>🎛</emoji> Created: {created_at}",
"confirm_clear": "Confirm",
"cancel_clear": "Cancel",
"clear_cancelled": "❌ Deletion cancelled.",
"index_required": "⚠️ Provide task index.",
"clear_confirmation_text": "Are you sure you want to clear all tasks?",
"confirm": "Confirm",
"cancel": "Cancel",
}
strings_ru = {
"task_added": "<emoji document_id=5776375003280838798>✅</emoji> Задача добавлена.",
"task_removed": "<emoji document_id=5776375003280838798>✅</emoji> Задача удалена.",
"task_completed": "<emoji document_id=5776375003280838798>✅</emoji> Задача выполнена.",
"task_not_found": "<emoji document_id=5778527486270770928>❌</emoji> Задача не найдена.",
"no_tasks": "<emoji document_id=5956561916573782596>📄</emoji> Нет активных задач.",
"task_list": "<emoji document_id=5956561916573782596>📄</emoji> Ваши задачи:\n{}",
"invalid_index": "<emoji document_id=5778527486270770928>❌</emoji> Неверный индекс. Укажите целое число.",
"description_required": "<emoji document_id=5879813604068298387>❗️</emoji> Укажите описание задачи.",
"clear_confirmation": "⚠️ Удалить все задачи?",
"tasks_cleared": "Все задачи удалены.",
"due_date_format": "<emoji document_id=5778527486270770928>❌</emoji> Неверный формат даты. Используйте ГГГГ-ММ-ДД ЧЧ:ММ.",
"task_info": "<emoji document_id=6028435952299413210></emoji> Задача: {description}\n<emoji document_id=5967412305338568701>📅</emoji> Срок: {due_date}\n<emoji document_id=5825794181183836432>✔️</emoji> Выполнена: {completed}\n<emoji document_id=5936170807716745162>🎛</emoji> Создана: {created_at}",
"confirm_clear": "Подтвердить",
"cancel_clear": "Отменить",
"clear_cancelled": "❌ Удаление отменено.",
"index_required": "⚠️ Укажите индекс задачи.",
"clear_confirmation_text": "Вы уверены, что хотите удалить все задачи?",
"confirm": "Подтвердить",
"cancel": "Отменить",
}
def __init__(self):
self.task_manager: Optional[TaskManager] = None
async def client_ready(self, client, db):
self.task_manager = TaskManager(os.path.join(os.getcwd(), "tasks.json"))
@loader.command(
ru_doc="Добавить задачу:\n.taskadd <описание> | <дата (необязательно)>",
en_doc="Add task:\n.taskadd <description> | <date (opt)>",
)
async def taskadd(self, message):
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, self.strings("description_required"))
return
try:
description, due_date_str = (
args.split("|", 1) if "|" in args else (args, None)
)
description = description.strip()
due_date_str = due_date_str.strip() if due_date_str else None
due_date = (
datetime.datetime.fromisoformat(due_date_str) if due_date_str else None
)
except ValueError:
await utils.answer(message, self.strings("due_date_format"))
return
except Exception as e:
logger.error(f"Error adding task: {e}")
await utils.answer(
message, f"<emoji document_id=5778527486270770928>❌</emoji> Error: {e}"
)
return
task = Task(description=description, due_date=due_date)
self.task_manager.add_task(message.sender_id, task)
await utils.answer(message, self.strings("task_added"))
@loader.command(ru_doc="[index] - удалить задачу", en_doc="[index] - remove task")
async def taskremove(self, message):
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, self.strings("index_required"))
return
try:
index = int(args) - 1
except ValueError:
await utils.answer(message, self.strings("invalid_index"))
return
if self.task_manager.get_task(message.sender_id, index) is None:
await utils.answer(message, self.strings("task_not_found"))
return
self.task_manager.remove_task(message.sender_id, index)
await utils.answer(message, self.strings("task_removed"))
@loader.command(
ru_doc="[index] - Завершите задачу", en_doc="[index] - Complete task"
)
async def taskcomplete(self, message):
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, self.strings("index_required"))
return
try:
index = int(args) - 1
except ValueError:
await utils.answer(message, self.strings("invalid_index"))
return
if self.task_manager.get_task(message.sender_id, index) is None:
await utils.answer(message, self.strings("task_not_found"))
return
self.task_manager.complete_task(message.sender_id, index)
await utils.answer(message, self.strings("task_completed"))
@loader.command(ru_doc="Список задач", en_doc="List tasks")
async def tasklist(self, message):
tasks = self.task_manager.get_tasks(message.sender_id)
if not tasks:
await utils.answer(message, self.strings("no_tasks"))
return
task_list_str = "\n".join(
[
f" {i + 1}. {'<emoji document_id=5776375003280838798>✅</emoji>' if task.completed else '<emoji document_id=5778527486270770928>❌</emoji>'} {task.description} (Due: {task.due_date.strftime('%Y-%m-%d %H:%M') if task.due_date else 'None'})"
for i, task in enumerate(tasks)
]
)
await utils.answer(message, self.strings("task_list").format(task_list_str))
@loader.command(
ru_doc="[index] - Посмотреть информацию о задаче",
en_doc="[index] - Show task info",
)
async def taskinfo(self, message):
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, self.strings("index_required"))
return
try:
index = int(args) - 1
except ValueError:
await utils.answer(message, self.strings("invalid_index"))
return
task = self.task_manager.get_task(message.sender_id, index)
if not task:
await utils.answer(message, self.strings("task_not_found"))
return
due_date_str = (
task.due_date.strftime("%Y-%m-%d %H:%M") if task.due_date else "None"
)
created_at_str = task.created_at.strftime("%Y-%m-%d %H:%M")
await utils.answer(
message,
self.strings("task_info").format(
description=task.description,
due_date=due_date_str,
completed="Yes" if task.completed else "No",
created_at=created_at_str,
),
)
@loader.command(ru_doc="Удалить все задачи", en_doc="Clear all tasks")
async def taskclear(self, message):
await self.inline.form(
text=self.strings("clear_confirmation_text"),
message=message,
reply_markup=[
[
{"text": self.strings("confirm"), "callback": self.clear_confirm},
{"text": self.strings("cancel"), "callback": self.clear_cancel},
]
],
silent=True,
)
async def clear_confirm(self, call):
"""Callback for confirming task clearing."""
self.task_manager.clear_tasks(call.from_user.id)
await call.edit(self.strings("tasks_cleared"))
async def clear_cancel(self, call):
"""Callback for canceling task clearing."""
await call.edit(self.strings("clear_cancelled"))

View File

@@ -0,0 +1,133 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: TelegramStatusCodes
# Description: Dictionary of telegram status codes
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api TelegramStatusCodes
# scope: Api TelegramStatusCodes 0.0.1
# ---------------------------------------------------------------------------------
from .. import loader, utils
responses = {
300: (
"⛔ SEE_OTHER",
"The request must be repeated, but directed to a different data center.",
),
400: (
"⛔ BAD_REQUEST",
"The query contains errors. In the event that a request was created using a form and contains user generated data, the user should be notified that the data must be corrected before the query is repeated.",
),
401: (
"⛔ UNAUTHORIZED",
"There was an unauthorized attempt to use functionality available only to authorized users.",
),
403: (
"⛔ FORBIDDEN",
"Privacy violation. For example, an attempt to write a message to someone who has blacklisted the current user.",
),
404: (
"⛔ NOT_FOUND",
"An attempt to invoke a non-existent object, such as a method",
),
406: (
"⛔ NOT_ACCEPTABLE",
"""
Similar to <b>400 BAD_REQUESTS</b>, but the app must display the error to the user a bit differently.
Do not display any visible error to the user when receiving the <b>rpc_error</b> constructor: instead, wait for an <a href="https://core.telegram.org/constructor/updateServiceNotification ">updateServiceNotification</a> update, and handle it as usual.
Basically, an <a href="https://core.telegram.org/constructor/updateServiceNotification"updateServiceNotification</a> <b>pop-up</b> update will be emitted independently (ie NOT as an <a href="https://core.telegram.org/type/Updates">Updates</a> constructor inside <b>rpc_result</b> but as a normal update) immediately after emission of a 406 <b>rpc_error</b>: the update will contain the actual localized error message to show to the user with a UI popup.
An exception to this is the <b>AUTH_KEY_DUPLICATED</b> error, which is only emitted if any of the non-media DC detects that an authorized session is sending requests in parallel from two separate TCP connections, from the same or different IP addresses.
Note that parallel connections are still allowed and actually recommended for media DCs.
Also note that by session we mean a logged-in session identified by an <a href="https://core.telegram.org/constructor/authorization">authorization</a> constructor, fetchable using <a href="https://core.telegram.org/method/account.getAuthorizations">account.getAuthorizations</a>, not an MTProto session.
If the client receives an <b>AUTH_KEY_DUPLICATED</b> error, the session was already invalidated by the server and the user must generate a new auth key and login again.""",
),
420: (
"⛔ FLOOD",
"The maximum allowed number of attempts to invoke the given method with the given input parameters has been exceeded. For example, in an attempt to request a large number of text messages (SMS) for the same phone number.",
),
500: (
"⛔ INTERNAL",
"""An internal server error occurred while a request was being processed; for example, there was a disruption while accessing a database or file storage.
If a client receives a 500 error, or you believe this error should not have occurred, please collect as much information as possible about the query and error and send it to the developers""",
),
}
@loader.tds
class TelegramStatusCodes(loader.Module):
"""Dictionary of telegram status codes"""
strings = {
"name": "TelegramStatusCodes",
"args_incorrect": "<b>Incorrect args</b>",
"not_found": "<b>Code not found</b>",
"syntax_error": "<b>Args are mandatory</b>",
"scode": "<b>{} {}</b>\n⚜️ Code Description: <i>{}</i>",
}
strings_ru = {
"args_incorrect": "<b>Неверные аргументы</b>",
"not_found": "<b>Код не найден</b>",
"syntax_error": "<b>Аргументы обязательны</b>",
"_cmd_doc_httpsc": "<код> - Получить информацию о Telegram error",
"_cmd_doc_httpscs": "Показать все доступные коды",
"_cls_doc": "Словарь telegram error",
}
@loader.unrestricted
@loader.command(
ru_doc="<код состояния> - Получение информации о коде состояния",
en_doc="<statuscode> - Get status code info",
)
async def tgccmd(self, message):
args = utils.get_args(message)
if not args:
await utils.answer(message, self.strings("syntax_error", message))
try:
if int(args[0]) not in responses:
await utils.answer(message, self.strings("not_found", message))
except ValueError:
await utils.answer(message, self.strings("args_incorrect", message))
await utils.answer(
message,
self.strings("scode", message).format(
responses[int(args[0])][0], args[0], responses[int(args[0])][1]
),
)
@loader.unrestricted
@loader.command(
ru_doc="Получите все коды статуса telegram",
en_doc="Get all telegram status codes",
)
async def tgcscmd(self, message):
await utils.answer(
message,
"\n".join(
[f"<b>{str(sc)}: {text}</b>" for sc, (text, _) in responses.items()]
),
)

View File

@@ -0,0 +1,159 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: TempChat
# Description: Creates a temporary private chat with a message forwarding restriction and adds the specified user to it.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: TempChat
# scope: TempChat 0.0.1
# ---------------------------------------------------------------------------------
import logging
import asyncio
from hikkatl import functions
from datetime import datetime as dt
from .. import loader, utils
logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)
@loader.tds
class TempChatMod(loader.Module):
"""Creates a temporary private chat with a message forwarding restriction and adds the specified user to it."""
strings = {
"name": "TempChat",
"selfchat": "You can't create a chat with yourself.",
"wrongargs": "<emoji document_id=5980953710157632545>❌</emoji> <b>Wrong arguments. Use </b><code>.tmpchat [@user/reply] [time]</code><b>",
"alreadychatting": "<emoji document_id=5980953710157632545>❌</emoji> <b>You already have an active conversation with this person.</b>",
"invalidtime": "<emoji document_id=5980953710157632545>❌</emoji> <b>Invalid time format. Use combinations like 1h30m.</b>",
"invitemsg": "<emoji document_id=5818967120213445821>🛡</emoji> You've been invited to a temporary private chat!\n\n<emoji document_id=5451646226975955576>⌛️</emoji> Auto-deletes in ",
"joinlink": "🔗 Join link: ",
"chatcreated": "<emoji document_id=5980930633298350051>✅</emoji> The temporary chat has been successfully created!",
}
strings_ru = {
"selfchat": "Ты не можешь создать чат сам с собой.",
"wrongargs": "<emoji document_id=5980953710157632545>❌</emoji> <b>Неверные аргументы. Используй </b><code>.tmpchat [@user/reply] [время]</code>",
"alreadychatting": "<emoji document_id=5980953710157632545>❌</emoji> <b>У вас уже есть открытая переписка с этим человеком.</b>",
"invalidtime": "<emoji document_id=5980953710157632545>❌</emoji> <b>Неверный формат времени. Убедитесь, что вы вводите время в формате 1h, 2h30m.</b>",
"invitemsg": "<emoji document_id=5818967120213445821>🛡</emoji> Вы были приглашены во временный приватный чат!\n\n<emoji document_id=5451646226975955576>⌛️</emoji> Авто-удаление через ",
"joinlink": "🔗 Ссылка: ",
"chatcreated": "<emoji document_id=5980930633298350051>✅</emoji> Временный чат успешно создан!",
}
def __init__(self):
self.temp_chats = {}
@loader.loop(interval=30, autostart=True)
async def check_expired_chats(self):
now = dt.now().timestamp()
for chat_id in list(self.temp_chats.keys()):
if self.temp_chats[chat_id][1] <= now:
try:
await self.client(
functions.channels.DeleteChannelRequest(chat_id)
)
del self.temp_chats[chat_id]
self.set("temp_chats", self.temp_chats)
except Exception as e:
logger.error(f"Error deleting chat {chat_id}: {e}")
try:
self.client(
functions.channels.GetFullChannelRequest(
channel=chat_id
)
)
except Exception:
del self.temp_chats[chat_id]
self.set("temp_chats", self.temp_chats)
async def client_ready(self, client, db):
self.hmodslib = await self.import_lib(
"https://raw.githubusercontent.com/archquise/H.Modules/refs/heads/main/HModsLibrary.py"
)
self.temp_chats = self.get("temp_chats", {})
@loader.command(
ru_doc="Создает временный чат. Использование: .tmpchat [@user/reply] [time]"
)
async def tmpchat(self, message):
"""Create temporary chat. Usage: .tmpchat [@user/reply] [time]"""
args = utils.get_args_raw(message)
reply = await message.get_reply_message()
if reply:
user = await self.client.get_entity(reply.sender_id)
time_str = args.strip() if args else None
else:
parts = args.split(",", 1) if "," in args else args.rsplit(" ", 1)
if len(parts) != 2:
return await utils.answer(message, self.strings["wrongargs"])
user_str, time_str = parts[0].strip(), parts[1].strip()
try:
user = await self.client.get_entity(user_str)
except Exception:
return await utils.answer(message, self.strings["wrongargs"])
if not time_str:
return await utils.answer(message, self.strings["wrongargs"])
seconds = await self.hmodslib.parse_time(time_str)
if not seconds:
return await utils.answer(message, self.strings["invalidtime"])
if any(user.id == uid for uid, _ in self.temp_chats.values()):
return await utils.answer(message, self.strings["alreadychatting"])
try:
created = await self.client(
functions.channels.CreateChannelRequest(
title=f"TempChat #{user.id}",
about=f"Temporary private chat with {user.id} | Expires after: {time_str}",
megagroup=True,
)
)
chat_id = created.chats[0].id
expires_at = dt.now().timestamp() + seconds
await self.client(
functions.messages.ToggleNoForwardsRequest(peer=chat_id, enabled=True)
)
self.temp_chats[chat_id] = (user.id, expires_at)
self.set("temp_chats", self.temp_chats)
invite = await self.client(
functions.messages.ExportChatInviteRequest(peer=chat_id, usage_limit=1)
)
invite_message = (
self.strings["invitemsg"]
+ time_str
+ f"\n{self.strings['joinlink']} {invite.link}"
)
await self.client.send_message(user.id, invite_message)
await utils.answer(message, self.strings["chatcreated"])
except Exception as e:
logger.error(f"Error creating temp chat: {e}")
await utils.answer(message, "❌ Error! Check log-chat.")

View File

@@ -0,0 +1,75 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Text2File
# Description: Module for convertation your text to file
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Text2File
# scope: Text2File 0.0.1
# ---------------------------------------------------------------------------------
import io
from .. import loader, utils
@loader.tds
class Text2File(loader.Module):
"""Module for convertation your text to file"""
strings = {
"name": "Text2File",
"no_args": "Don't have any args! Use .ttf text/code",
"cfg_name": "You can change the extension and file name",
}
strings_ru = {
"no_args": "Недостаточно аргументов! Используйте: .ttf текст/код",
"cfg_name": "Вы можете выбрать расширение и название для файла",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"name",
"file.txt",
lambda: self.strings("cfg_name"),
),
)
@loader.command(
ru_doc="Создать файл с вашим текстом или кодом",
en_doc="Create a file with your text or code",
)
async def ttfcmd(self, message):
args = utils.get_args_raw(message)
if not args:
await utils.answer(message, self.strings("no_args"))
else:
text = args
by = io.BytesIO(text.encode("utf-8"))
by.name = self.config["name"]
await utils.answer_file(
message,
by,
reply_to=getattr(message, "reply_to_msg_id", None),
)

View File

@@ -0,0 +1,108 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Text in sticker
# Description: Text in sticker
# Author: @hikka_mods
# Commands:
# .st <hex color> [text]
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Text in sticker
# scope: Text in sticker 0.0.1
# requires: requests
# ---------------------------------------------------------------------------------
import io
from textwrap import wrap
import requests
from PIL import Image, ImageColor, ImageDraw
from PIL import ImageFont
from .. import loader, utils
@loader.tds
class TextinstickerMod(loader.Module):
"""Text to sticker"""
strings = {
"name": "Text in sticker",
"error": "white st <color name> [text]",
}
strings_ru = {
"error": "Укажите .st <color name> [text]",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"font",
"https://github.com/CodWize/ReModules/blob/main/assets/Samson.ttf?raw=true",
lambda: "add a link to the font you want",
)
)
@loader.command(
ru_doc="<название цвета> [текст]",
en_doc="<color name> [text]",
)
@loader.owner
async def stcmd(self, message):
await message.delete()
text = utils.get_args_raw(message)
reply = await message.get_reply_message()
if not text:
if not reply:
text = self.strings("error")
elif not reply.message:
text = self.strings("error")
else:
text = reply.raw_text
color_name = text.split(" ", 1)[0].lower()
color = None
if len(text.split(" ", 1)) > 1:
text = text.split(" ", 1)[1]
else:
if reply and reply.message:
text = reply.raw_text
try:
color = ImageColor.getrgb(color_name)
except ValueError:
color = (255, 255, 255)
txt = []
for line in text.split("\n"):
txt.append("\n".join(wrap(line, 30)))
text = "\n".join(txt)
bytes_font = requests.get(self.config["font"]).content
font = io.BytesIO(bytes_font)
font = ImageFont.truetype(font, 100)
image = Image.new("RGBA", (1, 1), (0, 0, 0, 0))
draw = ImageDraw.Draw(image)
w, h = draw.multiline_textsize(text=text, font=font)
image = Image.new("RGBA", (w + 100, h + 100), (0, 0, 0, 0))
draw = ImageDraw.Draw(image)
draw.multiline_text((50, 50), text=text, font=font, fill=color, align="center")
output = io.BytesIO()
output.name = f"{color_name}.webp"
image.save(output, "WEBP")
output.seek(0)
await self.client.send_file(message.to_id, output, reply_to=reply)

View File

@@ -0,0 +1,327 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: TikTokDownloader
# Description: A module for downloading videos and photos from TikTok without watermark
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api TikTokDownloader
# scope: Api TikTokDownloader 0.0.1
# ---------------------------------------------------------------------------------
import aiohttp
import asyncio
import re
import os
import warnings
import functools
import logging
from dataclasses import dataclass
from urllib.parse import urljoin
from typing import Union, Optional, List
from tqdm import tqdm
from .. import loader, utils
@dataclass
class data:
dir_name: str
media: Union[str, List[str]]
type: str
class TikTok:
def __init__(self, host: Optional[str] = None):
self.headers = {
"User-Agent": "Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) "
"Version/4.0.4 Mobile/7B334b Safari/531.21.10"
}
self.host = host or "https://www.tikwm.com/"
self.session = aiohttp.ClientSession()
self.data_endpoint = "api"
self.search_videos_keyword_endpoint = "api/feed/search"
self.search_videos_hashtag_endpoint = "api/challenge/search"
self.link = None
self.result = None
self.logger = logging.getLogger("damirtag-TikTok")
handler = logging.StreamHandler()
formatter = logging.Formatter(
"[damirtag-TikTok:%(funcName)s]: %(levelname)s - %(message)s"
)
handler.setFormatter(formatter)
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)
def _warn(reason: str = "This function is NOT used but may be useful"):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
warnings.warn(
f"Warning! Deprecated: {func.__name__}\nReason: {reason}",
category=DeprecationWarning,
stacklevel=2,
)
return func(*args, **kwargs)
return wrapper
return decorator
async def close_session(self):
await self.session.close()
async def _ensure_data(self, link: str):
try:
if self.result is None or self.link != link:
self.link = link
self.result = await self.fetch(link)
self.logger.info("Successfully ensured data from the link")
except Exception as e:
self.logger.error(f"Error occurred when trying to get data from tikwm: {e}")
raise
async def __getimages(self, download_dir: Optional[str] = None):
download_dir = download_dir or self.result["id"]
os.makedirs(download_dir, exist_ok=True)
tasks = [
self._download_file(url, os.path.join(download_dir, f"image_{i + 1}.jpg"))
for i, url in enumerate(self.result["images"])
]
await asyncio.gather(*tasks)
self.logger.info(f"Images - Downloaded and saved photos to {download_dir}")
return data(
dir_name=download_dir,
media=[
os.path.join(download_dir, f"image_{i + 1}.jpg")
for i in range(len(self.result["images"]))
],
type="images",
)
async def __getvideo(self, video_filename: Optional[str] = None, hd: bool = False):
video_url = self.result["hdplay"] if hd else self.result["play"]
video_filename = video_filename or f"{self.result['id']}.mp4"
async with self.session.get(video_url) as response:
response.raise_for_status()
total_size = int(response.headers.get("content-length", 0))
with open(video_filename, "wb") as file:
with tqdm(
total=total_size, unit="B", unit_scale=True, desc=video_filename
) as pbar:
async for chunk in response.content.iter_any():
file.write(chunk)
pbar.update(len(chunk))
self.logger.info(f"Video - Downloaded and saved video as {video_filename}")
return data(
dir_name=os.path.dirname(video_filename), media=video_filename, type="video"
)
async def _makerequest(self, endpoint: str, params: dict) -> dict:
async with self.session.get(
urljoin(self.host, endpoint), params=params, headers=self.headers
) as response:
response.raise_for_status()
data = await response.json()
return data.get("data", {})
@staticmethod
def get_url(text: str) -> Optional[str]:
urls = re.findall(r"http[s]?://[^\s]+", text)
return urls[0] if urls else None
@_warn()
async def convert_share_urls(self, url: str) -> Optional[str]:
url = self.get_url(url)
if "@" in url:
return url
async with self.session.get(
url, headers=self.headers, allow_redirects=False
) as response:
if response.status == 301:
return response.headers["Location"].split("?")[0]
return None
@_warn()
async def get_tiktok_video_id(self, original_url: str) -> Optional[str]:
original_url = await self.convert_share_urls(original_url)
matches = re.findall(r"/video|v|photo/(\d+)", original_url)
return matches[0] if matches else None
async def fetch(self, link: str) -> dict:
url = self.get_url(link)
params = {"url": url, "hd": 1}
return await self._makerequest(self.data_endpoint, params=params)
async def _download_file(self, url: str, path: str):
async with self.session.get(url) as response:
response.raise_for_status()
with open(path, "wb") as file:
while chunk := await response.content.read(1024):
file.write(chunk)
async def download_sound(
self,
link: Union[str],
audio_filename: Optional[str] = None,
audio_ext: Optional[str] = ".mp3",
):
await self._ensure_data(link)
if not audio_filename:
audio_filename = f"{self.result['music_info']['title']}{audio_ext}"
else:
audio_filename += audio_ext
await self._download_file(self.result["music_info"]["play"], audio_filename)
self.logger.info(f"Sound - Downloaded and saved sound as {audio_filename}")
return audio_filename
async def download(
self, link: Union[str], video_filename: Optional[str] = None, hd: bool = True
) -> data:
"""
Asynchronously downloads a TikTok video or photo post.
Args:
video_filename (Optional[str]): The name of the file for the TikTok video or photo. If None, the file will be named based on the video or photo ID.
hd (bool): If True, downloads the video in HD format. Defaults to False.
Returns:
dir_name (str): Directory name
media (Union[str, List[str]]): Full list of downloaded media
type (str): The type of downloaded objects: Images or video
Raises:
Exception: No downloadable content found in the provided link.
"""
await self._ensure_data(link)
if "images" in self.result:
self.logger.info("Starting to download images")
return await self.__getimages(video_filename)
elif "hdplay" in self.result or "play" in self.result:
self.logger.info("Starting to download video.")
return await self.__getvideo(video_filename, hd)
else:
self.logger.error("No downloadable content found in the provided link.")
raise Exception("No downloadable content found in the provided link.")
def _get_video_link(self, unique_id: str, aweme_id: str) -> str:
return f"https://www.tiktok.com/@{unique_id}/video/{aweme_id}"
def _get_uploader_link(self, unique_id: str) -> str:
return f"https://www.tiktok.com/@{unique_id}"
@loader.tds
class TikTokDownloader(loader.Module):
"""TikTok Downloader module"""
strings = {
"name": "TikTokDownloader",
"downloading": "<emoji document_id=5436024756610546212>⚡</emoji> <b>Downloading…</b>",
"success_photo": "<emoji document_id=5436246187944460315>❤️</emoji> <b>The photo(s) has/have been successfully downloaded!</b>!",
"success_video": "<emoji document_id=5436246187944460315>❤️</emoji> <b>The video has been successfully downloaded!</b>",
"success_sound": "<emoji document_id=5436246187944460315>❤️</emoji> <b>The sound has been successfully downloaded!</b>",
"error": "Error occurred while downloading.\n{}",
}
strings_ru = {
"downloading": "<emoji document_id=5436024756610546212>⚡</emoji> <b>Загружаем…</b>",
"success_photo": "<emoji document_id=5436246187944460315>❤️</emoji> <b>Фотография(-и) была(-и) успешно загружены!</b>!",
"success_video": "<emoji document_id=5436246187944460315>❤️</emoji> <b>Видео было успешно загружено!</b>",
"success_sound": "<emoji document_id=5436246187944460315>❤️</emoji> <b>Звук был успешно загружен!</b>",
"error": "Во время загрузки произошла ошибка.\n{}",
}
@loader.command(
ru_doc="Скачать звук с TikTok",
en_doc="Download sound from TikTok",
)
async def ttsound(self, message):
args = utils.get_args(message)
if not args:
await utils.answer(message, "Please provide a TikTok URL.")
return
url = args[0]
await utils.answer(message, self.strings("downloading"))
tiktok_downloader = TikTok()
try:
download_result = await tiktok_downloader.download_sound(url)
await message.client.send_file(
message.to_id, download_result, caption=self.strings("success_sound")
)
await message.delete()
except Exception as e:
await utils.answer(
message,
f"{self.strings('error').format(e)}\n Убедитесь, что ссылка ведет именно на видео или фото с нужным звуком, прямая ссылка на звук не сработает!",
)
finally:
await tiktok_downloader.close_session()
@loader.command(
ru_doc="Скачать видео или фото с TikTok",
en_doc="Download videos or photos from TikTok",
)
async def tt(self, message):
args = utils.get_args(message)
if not args:
await utils.answer(message, "Please provide a TikTok URL.")
return
url = args[0]
await utils.answer(message, self.strings("downloading"))
tiktok_downloader = TikTok()
try:
download_result = await tiktok_downloader.download(url)
if download_result.type == "video":
await message.client.send_file(
message.to_id,
download_result.media,
caption=self.strings("success_video"),
)
await message.delete()
elif download_result.type == "images":
await message.client.send_file(
message.to_id,
download_result.media,
caption=self.strings("success_photo"),
)
await message.delete()
except Exception as e:
await utils.answer(message, self.strings("error").format(e))
finally:
await tiktok_downloader.close_session()

View File

@@ -0,0 +1,964 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: UserbotAvast
# Description: A module for checking modules for security.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: UserbotAvast
# scope: UserbotAvast 0.0.1
# ---------------------------------------------------------------------------------
import logging
import ast
import astor
import requests
import base64
import zlib
import re
import urllib.parse
from .. import loader, utils
logger = logging.getLogger(__name__)
try:
import g4f
G4F_AVAILABLE = True
except ImportError:
G4F_AVAILABLE = False
logger.warning("g4f is not installed. AI analysis will be disabled.")
class SecurityAnalyzer:
"""
Продвинутый анализатор безопасности Python-кода с эвристическим анализом.
"""
SECURITY_KEYWORDS = {
"critical": [
{
"keyword": "DeleteAccountRequest",
"description": "Удаление аккаунта",
"relevance": "Высокая",
},
{
"keyword": "ResetAuthorizationRequest",
"description": "Сброс авторизации",
"relevance": "Высокая",
},
{
"keyword": "client.export_session_string",
"description": "Экспорт сессии",
"relevance": "Высокая",
},
{
"keyword": "edit_2fa",
"description": "Изменение 2FA",
"relevance": "Высокая",
},
{
"keyword": "os.system",
"description": "Системные команды",
"relevance": "Высокая",
},
{
"keyword": "subprocess.Popen",
"description": "Внешние процессы",
"relevance": "Высокая",
},
{
"keyword": "eval",
"description": "Выполнение кода (eval)",
"relevance": "Критическая",
},
{
"keyword": "exec",
"description": "Выполнение кода (exec)",
"relevance": "Критическая",
},
{
"keyword": "MessagePacker.append",
"description": "Патч MessagePacker",
"relevance": "Средняя",
},
{
"keyword": "MessagePacker.extend",
"description": "Патч MessagePacker",
"relevance": "Средняя",
},
{
"keyword": "Scrypt",
"description": "Класс Scrypt (подозрительно)",
"relevance": "Высокая",
},
{
"keyword": "socket.socket",
"description": "Создание сокета",
"relevance": "Высокая",
},
{
"keyword": "shell=True",
"description": "Использование shell=True в subprocess",
"relevance": "Критическая",
},
{
"keyword": "codecs.decode",
"description": "Декодирование с использованием codecs",
"relevance": "Средняя",
},
{
"keyword": "pickle.loads",
"description": "Десериализация (pickle)",
"relevance": "Высокая",
},
{
"keyword": "marshal.loads",
"description": "Десериализация (marshal)",
"relevance": "Высокая",
},
{
"keyword": "__import__",
"description": "Динамический импорт",
"relevance": "Средняя",
},
{
"keyword": "ctypes.CDLL",
"description": "Загрузка динамической библиотеки",
"relevance": "Высокая",
},
{
"keyword": "create_connection",
"description": "Установка соединения",
"relevance": "Высокая",
},
{
"keyword": "http.server",
"description": "Запуск веб-сервера",
"relevance": "Средняя",
},
{
"keyword": "asyncio.create_subprocess_shell",
"description": "Асинхронный запуск процесса через shell",
"relevance": "Критическая",
},
],
"warning": [
{
"keyword": "requests",
"description": "HTTP-запросы",
"relevance": "Средняя",
},
{
"keyword": "aiohttp",
"description": "Асинхронные HTTP-запросы",
"relevance": "Средняя",
},
{
"keyword": "os.remove",
"description": "Удаление файлов",
"relevance": "Средняя",
},
{
"keyword": "os.mkdir",
"description": "Создание каталогов",
"relevance": "Низкая",
},
{
"keyword": "json.loads",
"description": "Парсинг JSON",
"relevance": "Низкая",
},
{
"keyword": "open(..., 'w')",
"description": "Открытие файла на запись",
"relevance": "Средняя",
},
{
"keyword": "open(..., 'a')",
"description": "Открытие файла на добавление",
"relevance": "Средняя",
},
{
"keyword": "telnetlib.Telnet",
"description": "Telnet соединение",
"relevance": "Средняя",
},
{
"keyword": "ftplib.FTP",
"description": "FTP соединение",
"relevance": "Средняя",
},
{
"keyword": "shutil.move",
"description": "Перемещение файлов",
"relevance": "Средняя",
},
{
"keyword": "shutil.copy",
"description": "Копирование файлов",
"relevance": "Средняя",
},
{
"keyword": "threading.Thread",
"description": "Создание потока",
"relevance": "Низкая",
},
{
"keyword": "multiprocessing.Process",
"description": "Создание процесса",
"relevance": "Низкая",
},
{
"keyword": "queue.Queue",
"description": "Использование очереди",
"relevance": "Низкая",
},
{
"keyword": "subprocess.check_output",
"description": "Запуск процесса с захватом вывода",
"relevance": "Средняя",
},
{
"keyword": "subprocess.run",
"description": "Запуск процесса",
"relevance": "Средняя",
},
{
"keyword": "codecs.encode",
"description": "Кодирование с использованием codecs",
"relevance": "Средняя",
},
],
"info": [
{
"keyword": "telethon",
"description": "Использование Telethon",
"relevance": "Низкая",
},
{
"keyword": "pyrogram",
"description": "Использование Pyrogram",
"relevance": "Низкая",
},
{
"keyword": "import",
"description": "Импорт модулей",
"relevance": "Низкая",
},
{
"keyword": "print",
"description": "Вывод в консоль",
"relevance": "Низкая",
},
{
"keyword": "logging.info",
"description": "Логирование",
"relevance": "Низкая",
},
],
}
def __init__(self, ai_enabled: bool = False):
"""Инициализация анализатора."""
self.results = {"critical": [], "warning": [], "info": []}
self.reported_issues = set()
self.code_lines = []
self.is_decoded = False
self.ai_enabled = ai_enabled
def reset(self):
"""Сброс результатов анализа."""
self.results = {"critical": [], "warning": [], "info": []}
self.reported_issues = set()
self.code_lines = []
self.is_decoded = False
async def analyze(self, code: str, strings: dict) -> str:
"""
Выполняет анализ предоставленного Python-кода.
Args:
code: Python-код для анализа.
strings: Словарь строк для локализации.
Returns:
Форматированный отчет об анализе.
"""
self.reset()
original_code = code
try:
code = self._try_decode(code)
self.is_decoded = True
except Exception:
logger.warning("Не удалось расшифровать код, анализ как есть.")
self.code_lines = code.splitlines()
try:
tree = ast.parse(code)
self._visit_tree(tree)
self._heuristic_analysis(code, tree)
if self.ai_enabled and G4F_AVAILABLE:
ai_analysis_result = await self._ai_analysis(code)
if ai_analysis_result:
self.results["critical"].append(
{
"keyword": "AI Analysis",
"description": ai_analysis_result,
"relevance": "Критическая",
"line": 0,
"col": 0,
}
)
except SyntaxError as e:
logger.error(f"Ошибка синтаксиса в коде: {e}")
return strings["syntax_error"].format(error=e)
except Exception as e:
logger.exception("Unexpected error during analysis")
return strings["syntax_error"].format(error=str(e))
return self._format_report(strings, original_code)
async def _ai_analysis(self, code: str) -> str or None:
"""
Использует g4f для анализа кода и выявления потенциальных угроз.
"""
try:
prompt = f"Проанализируйте следующий Python-код на предмет потенциальных угроз безопасности, уязвимостей и вредоносных действий. Предоставьте подробное объяснение, если что-то будет обнаружено:\n\n{code}"
response = await utils.run_sync(
g4f.ChatCompletion.create,
model=g4f.models.default,
messages=[{"role": "user", "content": prompt}],
)
return str(response)
except Exception as e:
logger.error(f"Ошибка при анализе с помощью g4f: {e}")
return None
def _try_decode(self, code):
"""Попытка расшифровать base64 + zlib код."""
if re.search(r"__import__\('zlib'\).decompress\(", code) and re.search(
r"__import__\('base64'\).b64decode\(", code
):
try:
match = re.search(r"b'([A-Za-z0-9+/=]+)'", code)
if match:
encoded_string = match.group(1)
decoded_code = self._decode_base64_zlib(encoded_string)
logger.info("Код успешно расшифрован.")
return decoded_code
except Exception as e:
logger.error(f"Ошибка при расшифровке кода: {e}")
raise
return code
def _decode_base64_zlib(self, encoded_string):
"""Расшифровывает base64 + zlib код."""
try:
decoded_bytes = base64.b64decode(encoded_string)
decompressed_bytes = zlib.decompress(decoded_bytes)
return decompressed_bytes.decode("utf-8")
except Exception as e:
logger.error(f"Ошибка при расшифровке base64+zlib: {e}")
raise
def _get_line_from_code(self, lineno):
"""Получает строку кода по номеру строки."""
try:
return self.code_lines[lineno - 1]
except IndexError:
return ""
def _visit_tree(self, tree):
"""Рекурсивно обходит AST-дерево."""
for node in ast.walk(tree):
self._analyze_node(node)
def _analyze_node(self, node):
"""Анализирует отдельный узел AST."""
if isinstance(node, ast.Name):
self._check_keyword(node.id, node)
elif isinstance(node, ast.Call):
self._check_call(node)
elif isinstance(node, (ast.Import, ast.ImportFrom)):
self._check_import(node)
elif isinstance(node, ast.FunctionDef):
self._check_function_def(node)
elif isinstance(node, ast.ClassDef):
self._check_class_def(node)
elif isinstance(node, ast.Assign):
self._check_assign(node)
def _check_keyword(self, keyword, node):
"""Проверяет ключевые слова."""
for severity, keywords in self.SECURITY_KEYWORDS.items():
for item in keywords:
if item["keyword"] == keyword:
issue_key = (
item["keyword"],
node.lineno,
node.col_offset,
)
if issue_key not in self.reported_issues:
self.results[severity].append(
{
"keyword": item["keyword"],
"description": item["description"],
"relevance": item["relevance"],
"line": node.lineno,
"col": node.col_offset,
}
)
self.reported_issues.add(issue_key)
def _check_call(self, node):
"""Анализирует вызовы функций."""
if isinstance(node.func, ast.Name):
self._check_keyword(node.func.id, node)
elif isinstance(node.func, ast.Attribute):
full_attr = ""
if isinstance(node.func.value, ast.Name):
full_attr = node.func.value.id + "." + node.func.attr
self._check_keyword(full_attr, node)
else:
self._check_keyword(node.func.attr, node)
elif isinstance(node.func, ast.Subscript):
if isinstance(node.func.value, ast.Attribute):
full_attr = ""
if isinstance(node.func.value.value, ast.Name):
full_attr = node.func.value.value.id + "." + node.func.value.attr
self._check_keyword(full_attr, node)
def _check_function_def(self, node):
self._check_keyword(node.name, node)
def _check_class_def(self, node):
self._check_keyword(node.name, node)
def _check_import(self, node):
"""Анализирует импорты."""
if isinstance(node, ast.Import):
for alias in node.names:
self._check_keyword(alias.name, node)
elif isinstance(node, ast.ImportFrom):
self._check_keyword(node.module, node)
for alias in node.names:
self._check_keyword(alias.name, node)
def _check_assign(self, node):
"""Анализирует присваивания."""
for target in node.targets:
if isinstance(target, ast.Name):
self._check_keyword(target.id, node)
def _heuristic_analysis(self, code: str, tree: ast.AST):
"""
Эвристический анализ для обнаружения подозрительного кода.
"""
self._check_obfuscation(code, tree)
self._check_dynamic_code_generation(code, tree)
self._check_url_patterns(code)
self._check_api_abuse(tree)
self._check_reverse_shell(code)
self._check_file_operations(code)
def _check_obfuscation(self, code: str, tree: ast.AST):
"""Обнаружение обфускации кода."""
if len(re.findall(r"[A-Za-z0-9+/]{30,}", code)) > 2:
issue_key = ("Base64", 1, 1)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": "Base64",
"description": "Подозрительные строки Base64",
"relevance": "Средняя",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
if "zlib.decompress" in code:
issue_key = ("zlib.decompress", 1, 1)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": "zlib.decompress",
"description": "Использование zlib декомпрессии",
"relevance": "Средняя",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
for node in ast.walk(tree):
if isinstance(node, (ast.Call)):
if isinstance(node.func, ast.Name) and node.func.id in ("eval", "exec"):
if len(node.args) > 0 and isinstance(node.args[0], ast.Str):
obfuscated_string = node.args[0].s
if (
len(re.findall(r"[A-Za-z0-9+/]{30,}", obfuscated_string))
> 0
):
issue_key = (
"eval/exec+Base64",
node.lineno,
node.col_offset,
)
if issue_key not in self.reported_issues:
self.results["critical"].append(
{
"keyword": "eval/exec+Base64",
"description": "eval/exec с обфускацией Base64",
"relevance": "Критическая",
"line": node.lineno,
"col": node.col_offset,
}
)
self.reported_issues.add(issue_key)
elif isinstance(node.func, ast.Name) and node.func.id in (
"eval",
"exec",
):
if len(node.args) > 0 and isinstance(node.args[0], ast.Name):
issue_key = ("eval/exec+Variable", node.lineno, node.col_offset)
if issue_key not in self.reported_issues:
self.results["critical"].append(
{
"keyword": "eval/exec+Variable",
"description": "eval/exec с переменной",
"relevance": "Критическая",
"line": node.lineno,
"col": node.col_offset,
}
)
self.reported_issues.add(issue_key)
hash_functions = ["md5", "sha1", "sha256", "sha512"]
for hash_func in hash_functions:
if f"hashlib.{hash_func}" in code:
issue_key = (f"hashlib.{hash_func}", 1, 1)
if issue_key not in self.reported_issues:
self.results["info"].append(
{
"keyword": f"hashlib.{hash_func}",
"description": f"Использование {hash_func} хеширования",
"relevance": "Низкая",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
if any(
x in code
for x in [
"hashlib.md5(password.encode()).hexdigest()",
"hashlib.sha256(password.encode()).hexdigest()",
]
):
issue_key = ("Weak Hashing", 1, 1)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": "Weak Hashing",
"description": "Использование хеширования без соли",
"relevance": "Средняя",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
def _check_dynamic_code_generation(self, code, tree: ast.AST):
"""Обнаружение динамической генерации кода."""
if "compile(" in code:
issue_key = ("compile", 1, 1)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": "compile",
"description": "Использование compile() для генерации кода",
"relevance": "Средняя",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
for node in ast.walk(tree):
if (
isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "type"
):
if (
len(node.args) == 3
and isinstance(node.args[0], ast.Str)
and isinstance(node.args[1], ast.Tuple)
and isinstance(node.args[2], ast.Dict)
):
issue_key = ("type() class", node.lineno, node.col_offset)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": "type() class",
"description": "Динамическое создание классов через type()",
"relevance": "Средняя",
"line": node.lineno,
"col": node.col_offset,
}
)
self.reported_issues.add(issue_key)
def _check_url_patterns(self, code: str):
"""Обнаружение подозрительных URL-паттернов."""
short_url_domains = [
"bit.ly",
"goo.gl",
"t.co",
"tinyurl.com",
"is.gd",
"ow.ly",
"github.com",
"raw.githubusercontent.com",
]
for domain in short_url_domains:
if domain in code:
issue_key = (f"Short URL ({domain})", 1, 1)
if issue_key not in self.reported_issues:
line = self._get_line_from_code(1)
match = re.search(r"(https?://\S+)", line)
url = (
match.group(1)
if match
else f"Не удалось извлечь URL ({domain})"
)
self.results["warning"].append(
{
"keyword": f"Short URL ({domain})",
"description": f"Обнаружен сокращенный URL: {url}",
"relevance": "Низкая",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
webhook_patterns = ["discord.com/api/webhooks", "api.telegram.org/bot"]
for pattern in webhook_patterns:
if pattern in code:
issue_key = (f"Webhook ({pattern})", 1, 1)
if issue_key not in self.reported_issues:
line = self._get_line_from_code(1)
match = re.search(pattern, line)
url = (
match.group(0)
if match
else f"Не удалось извлечь Webhook ({pattern})"
)
self.results["critical"].append(
{
"keyword": f"Webhook ({pattern})",
"description": f"Обнаружен Webhook: {url}",
"relevance": "Критическая",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
def _check_api_abuse(self, tree: ast.AST):
"""Обнаружение потенциального злоупотребления Telegram API."""
send_methods = ["send_message", "send_file", "send_photo"]
for node in ast.walk(tree):
if isinstance(node, ast.For):
for send_method in send_methods:
if send_method in astor.to_source(node):
issue_key = (
f"Mass {send_method}",
node.lineno,
node.col_offset,
)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": f"Mass {send_method}",
"description": f"Подозрение на массовую рассылку ({send_method})",
"relevance": "Средняя",
"line": node.lineno,
"col": node.col_offset,
}
)
self.reported_issues.add(issue_key)
if "time.sleep(" in astor.to_source(tree):
sleep_calls = re.findall(r"time\.sleep\((.*?)\)", astor.to_source(tree))
for sleep_time in sleep_calls:
try:
sleep_value = float(sleep_time)
if sleep_value < 1:
issue_key = ("Short Sleep Time", 1, 1)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": "Short Sleep Time",
"description": "Обнаружена короткая задержка (менее 1 секунды)",
"relevance": "Средняя",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
except ValueError:
pass
def _check_reverse_shell(self, code: str):
"""Обнаружение попыток создания обратного шелла."""
try:
reverse_shell_patterns = [
r"socket\.socket\(\s*socket\.AF_INET",
r"os\.dup2\(",
r"subprocess\.Popen\(\s*\[.+?\]\s*,\s*shell=True",
r"/bin/bash -i",
r"/bin/sh -i",
r"nc -e /bin/bash",
r"nc -e /bin/sh",
r"> /dev/tcp/",
r"python -c 'import socket,subprocess,os;s=socket.socket",
r"python3 -c 'import socket,subprocess,os;s=socket.socket",
]
for pattern in reverse_shell_patterns:
if re.search(pattern, code):
issue_key = ("Reverse Shell", 1, 1)
if issue_key not in self.reported_issues:
self.results["critical"].append(
{
"keyword": "Reverse Shell",
"description": "Обнаружена попытка создания обратного шелла",
"relevance": "Критическая",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
except Exception as e:
logger.error(f"Error in _check_reverse_shell: {e}")
def _check_file_operations(self, code: str):
"""Обнаружение потенциально опасных операций с файлами."""
dangerous_file_paths = [
"/etc/passwd",
"/etc/shadow",
"/etc/hosts",
"/etc/sudoers",
]
for file_path in dangerous_file_paths:
if file_path in code:
issue_key = ("File Override", 1, 1)
if issue_key not in self.reported_issues:
self.results["critical"].append(
{
"keyword": "File Override",
"description": f"Попытка записи в критический файл: {file_path}",
"relevance": "Критическая",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
if "shutil.rmtree" in code:
issue_key = ("Recursive Delete", 1, 1)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": "Recursive Delete",
"description": "Обнаружено рекурсивное удаление каталога",
"relevance": "Средняя",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
executable_extensions = [".py", ".sh", ".bat", ".exe"]
for ext in executable_extensions:
if f"open(..., '{ext}'" in code or f"open(... + '{ext}'" in code:
issue_key = ("Executable File Creation", 1, 1)
if issue_key not in self.reported_issues:
self.results["warning"].append(
{
"keyword": "Executable File Creation",
"description": f"Обнаружено создание файла с расширением {ext}",
"relevance": "Средняя",
"line": 1,
"col": 1,
}
)
self.reported_issues.add(issue_key)
def _format_report(self, strings: dict, original_code: str) -> str:
"""Форматирует отчет об анализе."""
report = strings["report_header"]
if self.is_decoded:
report += "<b>⚠️ Код был расшифрован перед анализом.</b>\n\n"
else:
report += "<b>⚠️ Анализ проводился над исходным кодом, расшифровка не удалась.</b>\n\n"
total_issues = 0
for severity, issues in self.results.items():
if issues:
report += strings[f"{severity}_header"]
total_issues += len(issues)
for issue in issues:
report += strings["issue_format"].format(
keyword=issue["keyword"],
description=issue["description"],
relevance=issue["relevance"],
line=issue["line"],
col=issue["col"],
)
report += "\n"
if total_issues == 0:
report += strings["no_issues"]
else:
report += strings["report_footer"].format(count=total_issues)
return report
@loader.tds
class UserbotAvast(loader.Module):
"""A module for checking modules for security."""
strings = {
"name": "UserbotAvast",
"cfg_ai_enabled": "Включить анализ с помощью AI (g4f)",
"cfg_lingva_url": "Анализирует Python-код модуля на предмет потенциальных угроз безопасности, включая обфускацию и эвристические признаки.",
"report_header": "<b>🛡️ Отчет об анализе безопасности модуля:</b>\n\n",
"critical_header": "<b>🔴 Критические угрозы:</b>\n",
"warning_header": "<b>🟠 Предупреждения:</b>\n",
"info_header": "<b>🔵 Информация:</b>\n",
"issue_format": " - ⚠️ <code>{keyword}</code>: {description} (Важность: {relevance}, Строка: {line}, Позиция: {col})\n",
"no_issues": "Не обнаружено проблем безопасности.\n",
"report_footer": "\nВсего обнаружено {count} проблем.\n",
"syntax_error": "❌ Ошибка синтаксиса в коде: {error}\n",
"loading": "⏳ Запуск анализатора безопасности...",
"no_module": "⚠️ Не удалось получить код модуля. Убедитесь, что ссылка верна или прикрепите файл к сообщению.",
"decoding_error": "⚠️ Обнаружен зашифрованный код, но не удалось его расшифровать.",
}
strings_ru = {
"cfg_ai_enabled": "Включить анализ с помощью AI (g4f)",
"cfg_lingva_url": "Анализирует Python-код модуля на предмет потенциальных угроз безопасности, включая обфускацию и эвристические признаки.",
"report_header": "<b>🛡️ Отчет об анализе безопасности модуля:</b>\n\n",
"critical_header": "<b>🔴 Критические угрозы:</b>\n",
"warning_header": "<b>🟠 Предупреждения:</b>\n",
"info_header": "<b>🔵 Информация:</b>\n",
"issue_format": " - ⚠️ <code>{keyword}</code>: {description} (Важность: {relevance}, Строка: {line}, Позиция: {col})\n",
"no_issues": "Не обнаружено проблем безопасности.\n",
"report_footer": "\nВсего обнаружено {count} проблем.\n",
"syntax_error": "❌ Ошибка синтаксиса в коде: {error}\n",
"loading": "⏳ Запуск анализатора безопасности...",
"no_module": "⚠️ Не удалось получить код модуля. Убедитесь, что ссылка верна или прикрепите файл к сообщению.",
"decoding_error": "⚠️ Обнаружен зашифрованный код, но не удалось его расшифровать.",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"ai_enabled",
False,
lambda: self.strings["cfg_ai_enabled"],
validator=loader.validators.Boolean(),
),
)
async def client_ready(self, client, db):
"""Вызывается при готовности клиента."""
self.client = client
self.db = db
self.security_analyzer = SecurityAnalyzer(self.config["ai_enabled"])
@loader.unrestricted
@loader.ratelimit
async def checkmodcmd(self, message):
"""
[module_link] или [reply file] или [send file] - выполняет проверку модуля на безопасность.
"""
await utils.answer(message, self.strings["loading"])
args = utils.get_args_raw(message)
code = None
if args:
code = await self._get_code_from_url(args)
if not code:
code = await self._get_code_from_message(message)
if not code:
await utils.answer(message, self.strings["no_module"])
return
try:
result = await self.security_analyzer.analyze(code, self.strings)
await utils.answer(message, result)
except Exception as e:
logger.exception("Error during analysis")
await utils.answer(message, f"An error occurred during analysis: {e}")
async def _get_code_from_url(self, url: str) -> str or None:
"""Получает код модуля по URL."""
try:
response = await utils.run_sync(requests.get, url)
response.raise_for_status()
return response.text
except requests.exceptions.RequestException as e:
logger.error(f"Ошибка при получении кода из URL: {e}")
return None
async def _get_code_from_message(self, message) -> str or None:
"""Получает код модуля из прикрепленного файла или ответа на сообщение."""
try:
if message.media:
code = (await self.client.download_file(message.media, bytes)).decode(
"utf-8"
)
return code
reply = await message.get_reply_message()
if reply and reply.media:
code = (await self.client.download_file(reply.media, bytes)).decode(
"utf-8"
)
return code
except Exception as e:
logger.error(f"Ошибка при получении кода из сообщения: {e}")
return None

View File

@@ -0,0 +1,105 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Video2GIF
# Description: Converts video to GIF
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Video2GIF
# scope: Video2GIF 0.0.1
# requires: moviepy
# ---------------------------------------------------------------------------------
import os
import subprocess
from .. import loader, utils
@loader.tds
class Video2GIF(loader.Module):
"""Converts video to GIF"""
strings = {
"name": "Video2GIF",
"conversion_success": "🎉 The conversion is completed!",
"conversion_error": "❌ An error occurred when converting video to GIF.",
"not_video": "⚠️ Please reply to the message with the video or send the video in one message.",
"loading": "⏳ Conversion is underway",
}
strings_ru = {
"conversion_success": "🎉 Преобразование завершено!",
"conversion_error": "❌ Произошла ошибка при преобразовании видео в GIF.",
"not_video": "⚠️ Пожалуйста, ответьте на сообщение с видео или отправьте видео одним сообщением.",
"loading": "⏳ Идет преобразование",
}
@loader.command(
ru_doc="[reply | в одном сообщении с видео] — конвертирует видео в GIF.",
en_doc="[reply | in one message with video] — Converts video to GIF.",
)
async def gifc(self, message):
video = await self.get_video_from_message(message)
if not video:
await utils.answer(message, self.strings["not_video"])
return
await utils.answer(message, self.strings["loading"])
video_path = await self.client.download_media(video)
gif_path = f"{os.path.splitext(video_path)[0]}.gif"
try:
self.convert_video_to_gif(video_path, gif_path)
await message.client.send_file(
message.chat_id, gif_path, caption=self.strings["conversion_success"]
)
except Exception as e:
await utils.answer(message, self.strings["conversion_error"])
print(f"Error during conversion: {e}")
finally:
self.cleanup_temp_files(video_path, gif_path)
async def get_video_from_message(self, message):
"""Получает видео из сообщения."""
if reply := await message.get_reply_message():
return reply.video
return message.video
def convert_video_to_gif(self, video_path: str, gif_path: str) -> None:
"""Конвертирует видео в GIF с улучшенными параметрами."""
command = [
"ffmpeg",
"-i",
video_path,
"-vf",
"fps=30,scale=640:-1:flags=lanczos",
"-c:v",
"gif",
gif_path,
]
subprocess.run(command, check=True)
def cleanup_temp_files(self, video_path: str, gif_path: str) -> None:
"""Удаляет временные файлы."""
for temp_file in [video_path, gif_path]:
if os.path.exists(temp_file):
os.remove(temp_file)

View File

@@ -0,0 +1,161 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: VirusTotal
# Description: Checks files for viruses using VirusTotal
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api VirusTotal
# scope: Api VirusTotal 0.0.1
# requires: json aiohttp tempfile
# ---------------------------------------------------------------------------------
import os
import tempfile
import logging
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class VirusTotalMod(loader.Module):
"""Checks files for viruses using VirusTotal."""
strings = {
"name": "VirusTotal",
"no_file": "<emoji document_id=5210952531676504517>🚫</emoji> <b>You haven't selected a file.</b>",
"download": "<emoji document_id=5334677912270415274>😑</emoji> <b>Downloading...</b>",
"scan": "<emoji document_id=5325792861885570739>🫥</emoji> <b>Scanning...</b>",
"link": "🦠 VirusTotal Link",
"no_virus": "✅ File is clean.",
"error": "<emoji document_id=5463193238393274687>⚠️</emoji> Scan error.",
"no_format": "This format is not supported.",
"no_apikey": "<emoji document_id=5260342697075416641>🚫</emoji> You have not specified an API Key",
"config": "Need a token with www.virustotal.com/gui/my-apikey",
"scanning": "<emoji document_id=5325792861885570739>🫥</emoji> <b>Waiting for scan results...</b>",
"getting_upload_url": "<emoji document_id=5325792861885570739>🫥</emoji> <b>Getting upload URL...</b>",
"analysis_failed": "<emoji document_id=5463193238393274687>⚠️</emoji> Analysis failed after multiple retries.",
}
strings_ru = {
"no_file": "<emoji document_id=5210952531676504517>🚫</emoji> </b>Вы не выбрали файл.</b>",
"download": "<emoji document_id=5334677912270415274>😑</emoji> </b>Скачивание...</b>",
"scan": "<emoji document_id=5325792861885570739>🫥</emoji> <b>Сканирую...</b>",
"link": "🦠 Ссылка на VirusTotal",
"no_virus": "✅ Файл чист.",
"error": "<emoji document_id=5463193238393274687>⚠️</emoji> Ошибка сканирования.",
"no_format": "Этот формат не поддерживается.",
"no_apikey": "<emoji document_id=5260342697075416641>🚫</emoji> Вы не указали Api Key",
"config": "Need a token with www.virustotal.com/gui/my-apikey",
"scanning": "<emoji document_id=5325792861885570739>🫥</emoji> <b>Ожидание результатов сканирования...</b>",
"getting_upload_url": "<emoji document_id=5325792861885570739>🫥</emoji> <b>Получение URL для загрузки...</b>",
"analysis_failed": "<emoji document_id=5463193238393274687>⚠️</emoji> Анализ не удался после нескольких попыток.",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"token-vt",
None,
lambda: "Need a token with www.virustotal.com/gui/my-apikey",
validator=loader.validators.Hidden(),
)
)
async def client_ready(self, client, db):
self.hmodslib = await self.import_lib(
"https://raw.githubusercontent.com/C0dwiz/H.Modules/refs/heads/main-fix/HModsLibrary.py"
)
@loader.command(
ru_doc="<ответ на файл> - Проверяет файлы на наличие вирусов с использованием VirusTotal",
en_doc="<file response> - Checks files for viruses using VirusTotal",
)
async def vt(self, message):
if not message.is_reply:
await utils.answer(message, self.strings("no_file"))
return
reply = await message.get_reply_message()
if not reply.document:
await utils.answer(message, self.strings("no_file"))
return
api_key = self.config.get("token-vt")
if not api_key:
await utils.answer(message, self.strings("no_apikey"))
return
file_extension = os.path.splitext(reply.file.name)[1].lower()
allowed_extensions = (".jpg", ".png", ".ico", ".mp3", ".mp4", ".gif", ".txt")
if file_extension in allowed_extensions:
await utils.answer(message, self.strings("no_format"))
return
try:
await utils.answer(message, self.strings("download"))
with tempfile.TemporaryDirectory() as temp_dir:
file_path = os.path.join(temp_dir, reply.file.name)
await reply.download_media(file_path)
file_size = os.path.getsize(file_path)
is_large_file = file_size > 32 * 1024 * 1024
if is_large_file:
await utils.answer(message, self.strings("getting_upload_url"))
await utils.answer(message, self.strings("scan"))
analysis_results = await self.hmodslib.scan_file_virustotal(
file_path, api_key, is_large_file
)
if analysis_results:
formatted_results = self.hmodslib.format_analysis_results(
analysis_results
)
try:
await self.inline.form(
text=formatted_results["text"],
message=message,
reply_markup={
"text": self.strings("link"),
"url": formatted_results["url"],
}
if formatted_results["url"]
else None,
)
except Exception as e:
logger.error(f"Error displaying inline results: {e}")
await utils.answer(
message,
self.strings("error_report").format(
formatted_results["url"]
),
)
else:
await utils.answer(message, self.strings("analysis_failed"))
except Exception as e:
logger.exception("An error occurred during the VT scan process.")
await utils.answer(
message, self.strings("error") + f"\n\n{type(e).__name__}: {str(e)}"
)

View File

@@ -0,0 +1,125 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: VoiceDL
# Description: Voice Downloader module
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: VoiceDL
# scope: VoiceDL 0.0.1
# requires: tempfile
# ---------------------------------------------------------------------------------
import os
import subprocess
import tempfile
import time
from .. import loader, utils
@loader.tds
class VoiceDL(loader.Module):
"""Voice Downloader module"""
strings = {
"name": "VoiceDL",
"download_success": "Voice message downloaded in MP3 format.",
"download_error": "Error downloading voice message.",
"no_voice_message": "Please reply to a voice message.",
"conversion_error": "Error converting to MP3.",
"file_not_found": "File not found.",
"unsupported_format": "The file format is not supported.",
}
strings_ru = {
"download_success": "Голосовое сообщение загружено в формате MP3.",
"download_error": "Ошибка при загрузке голосового сообщения.",
"no_voice_message": "Пожалуйста, ответьте на голосовое сообщение.",
"conversion_error": "Ошибка при конвертации в MP3.",
"file_not_found": "Файл не найден.",
"unsupported_format": "Формат файла не поддерживается.",
}
@loader.command(
ru_doc=" [reply] — загружает выбранное голосовое сообщение в виде файла mp3 и кидает его в чат.",
en_doc=" [reply] — downloads the selected voice message as an MP3 file and sends it in the chat.",
)
async def voicedl(self, message):
reply = await message.get_reply_message()
if reply:
if reply.voice:
try:
with tempfile.NamedTemporaryFile(
delete=False, suffix=".ogg"
) as temp_voice_file:
voice_file_path = temp_voice_file.name
await message.client.download_file(reply.voice, voice_file_path)
timestamp = int(time.time())
mp3_file_path = f"voice_message_{timestamp}.mp3"
await self.convert_to_mp3(voice_file_path, mp3_file_path)
await message.client.send_file(
message.chat.id,
mp3_file_path,
caption=self.strings("download_success"),
)
os.remove(voice_file_path)
os.remove(mp3_file_path)
except FileNotFoundError:
await utils.answer(
message,
self.strings("download_error")
+ " "
+ self.strings("file_not_found"),
)
except subprocess.CalledProcessError as e:
await utils.answer(
message,
self.strings("conversion_error") + f" {e.stderr.decode()}",
)
except Exception as e:
await utils.answer(
message, self.strings("download_error") + f" {str(e)}"
)
else:
await utils.answer(message, self.strings("no_voice_message"))
else:
await utils.answer(message, self.strings("no_voice_message"))
async def convert_to_mp3(self, input_file: str, output_file: str):
"""Convert audio file to MP3 format using FFmpeg."""
command = ["ffmpeg", "-i", input_file, output_file]
process = subprocess.run(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
if process.returncode != 0:
raise subprocess.CalledProcessError(
process.returncode,
command,
output=process.stdout,
stderr=process.stderr,
)

View File

@@ -0,0 +1,333 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Weather
# Description: Advanced weather module with detailed information
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: api Weather
# scope: api Weather 0.0.1
# ---------------------------------------------------------------------------------
import logging
import requests
from datetime import datetime
from typing import Union, Dict, List
from dataclasses import dataclass
from .. import loader, utils
logger = logging.getLogger(__name__)
DEFAULT_FORECAST_DAYS = 3
DEFAULT_HOURLY_INDEX = 4
WEATHER_API_URL = "https://wttr.in/{city}?format=j1&lang=en"
@dataclass
class WeatherCondition:
"""Represents a weather condition with its emoji."""
condition: str
emoji: str
@dataclass
class WindDirection:
"""Represents a wind direction with its description."""
direction: str
description: str
@dataclass
class ForecastDay:
"""Represents a single day's weather forecast."""
date: str
emoji: str
condition: str
temp_min: str
temp_max: str
wind_speed: str
wind_direction: str
WEATHER_EMOJI: List[WeatherCondition] = [
WeatherCondition("clear", "<emoji document_id=5402477260982731644>☀️</emoji>"),
WeatherCondition("sunny", "<emoji document_id=5402477260982731644>☀️</emoji>"),
WeatherCondition(
"partly cloudy", "<emoji document_id=5350424168615649565>⛅️</emoji>"
),
WeatherCondition("cloudy", "☁️<emoji document_id=5208563370218762357>☁️</emoji>"),
WeatherCondition("overcast", "<emoji document_id=5208563370218762357>☁️</emoji>"),
WeatherCondition("mist", "<emoji document_id=5449510395574229527>😶‍🌫️</emoji>"),
WeatherCondition("fog", "<emoji document_id=5449510395574229527>😶‍🌫️</emoji>"),
WeatherCondition("light rain", "<emoji document_id=5283097055852503586>🌦</emoji>"),
WeatherCondition("rain", "<emoji document_id=5283243028905994049>🌧</emoji>"),
WeatherCondition("heavy rain", "<emoji document_id=5282939632416206153>⛈</emoji>"),
WeatherCondition(
"thunderstorm", "<emoji document_id=5282939632416206153>⛈</emoji>"
),
WeatherCondition("snow", "<emoji document_id=5282833267551117457>🌨</emoji>"),
WeatherCondition("heavy snow", "<emoji document_id=5449449325434266744>❄️</emoji>"),
WeatherCondition("sleet", "<emoji document_id=5282833267551117457>🌨</emoji>"),
WeatherCondition("wind", "💨"),
]
WIND_DIRECTIONS: List[WindDirection] = [
WindDirection("N", "⬆️ North"),
WindDirection("NE", "↗️ Northeast"),
WindDirection("E", "➡️ East"),
WindDirection("SE", "↘️ Southeast"),
WindDirection("S", "⬇️ South"),
WindDirection("SW", "↙️ Southwest"),
WindDirection("W", "⬅️ West"),
WindDirection("NW", "↖️ Northwest"),
]
WIND_DIRECTIONS_RU: List[WindDirection] = [
WindDirection("N", "⬆️ Северный"),
WindDirection("NE", "↗️ Северо-восточный"),
WindDirection("E", "➡️ Восточный"),
WindDirection("SE", "↘️ Юго-восточный"),
WindDirection("S", "⬇️ Южный"),
WindDirection("SW", "↙️ Юго-западный"),
WindDirection("W", "⬅️ Западный"),
WindDirection("NW", "↖️ Северо-западный"),
]
@loader.tds
class Weather(loader.Module):
"""Advanced weather module with detailed information"""
strings = {
"name": "Weather",
"no_city": "🚫 <b>Please specify a city</b>",
"invalid_city": "🚫 <b>City not found</b>",
"loading": "🔄 <b>Fetching weather data for {}</b>...",
"error": "<emoji document_id=5980953710157632545>❌</emoji> <b>Error retrieving weather data</b>",
"default_city": "<emoji document_id=5980930633298350051>✅</emoji> Default city set to: <code>{city}</code>",
"weather_text": """<b>{emoji} Weather: {location}</b>
<b>📊 Current conditions:</b>
├ 🌡 Temperature: <code>{temp}°C</code>
├– <i>Feels like:</i> <code>{feels_like}°C</code>
├ 💧 Humidity: <code>{humidity}%</code>
├ 💨 Wind: <code>{wind_speed} km/h</code> {wind_direction}
├ 🌪 Pressure: <code>{pressure} mmHg</code>
├ 👁 Visibility: <code>{visibility} km</code>
└ ☁️ Cloudiness: <code>{clouds}</code>
<b>🌅 Time:</b>
├ 🌅 Sunrise: <code>{sunrise}</code>
├ 🌇 Sunset: <code>{sunset}</code>
└ ⏱ Local time: <code>{local_time}</code>
<b>📅 Forecast for {forecast_days} days:</b>
{forecast}
⏰ Updated: <code>{updated}</code>""",
"forecast_day": """<b>{date}</b> {emoji}
├ 🌡 Temperature: {temp_min}°C ... {temp_max}°C
└ 💨 Wind: {wind_speed} km/h {wind_direction}
""",
}
strings_ru = {
"no_city": "🚫 <b>Пожалуйста, укажите город</b>",
"invalid_city": "🚫 <b>Город не найден</b>",
"loading": "🔄 <b>Получаю метеоданные для {}</b>...",
"default_city": "<emoji document_id=5980930633298350051>✅</emoji> Город по умолчанию установлен: <code>{city}</code>",
"error": "<emoji document_id=5980953710157632545>❌</emoji> <b>Ошибка при получении данных о погоде</b>",
"weather_text": """<b>{emoji} Погода: {location}</b>
<b>📊 Текущие условия:</b>
├ 🌡 Температура: <code>{temp}°C</code>
├– <i>Ощущается как:</i> <code>{feels_like}°C</code>
├ 💧 Влажность: <code>{humidity}%</code>
├ 💨 Ветер: <code>{wind_speed} км/ч</code> {wind_direction}
├ 🌪 Давление: <code>{pressure} мм.рт.ст</code>
├ 👁 Видимость: <code>{visibility} км</code>
└ ☁️ Облачность: <code>{clouds}</code>
<b>🌅 Время:</b>
├ 🌅 Восход: <code>{sunrise}</code>
├ 🌇 Закат: <code>{sunset}</code>
└ ⏱ Местное время: <code>{local_time}</code>
<b>📅 Прогноз на {forecast_days} дня:</b>
{forecast}
⏰ Обновлено: <code>{updated}</code>""",
"forecast_day": """<b>{date}</b> {emoji}
├ 🌡 Температура: {temp_min}°C ... {temp_max}°C
└ 💨 Ветер: {wind_speed} км/ч {wind_direction}
""",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"default_city",
None,
lambda: "Default city for weather command",
),
loader.ConfigValue(
"language",
"ru",
lambda: "Language for weather output (en/ru)",
),
)
def get_weather_emoji(self, condition: str) -> str:
"""Get emoji for weather conditions"""
condition = condition.lower()
for item in WEATHER_EMOJI:
if item.condition in condition:
return item.emoji
return "🌡"
def get_wind_direction(self, direction: str) -> str:
"""Get wind direction description"""
lang = self.config["language"]
directions = WIND_DIRECTIONS_RU if lang == "ru" else WIND_DIRECTIONS
for item in directions:
if item.direction == direction.upper():
return item.description
return direction
async def get_weather_data(self, city: str) -> Union[Dict, None]:
"""Get weather data from wttr.in"""
lang = self.config["language"]
url = WEATHER_API_URL.format(city=city)
if lang == "ru":
url = f"https://wttr.in/{city}?format=j1&lang=ru"
try:
response = await utils.run_sync(requests.get, url)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Failed to fetch weather data for {city}: {e}")
return None
except Exception as e:
logger.exception(f"Error fetching weather data: {e}")
return None
def format_forecast(self, forecast_data: list) -> str:
"""Format weather forecast for multiple days."""
forecast_text = ""
for day in forecast_data:
hourly = day["hourly"][DEFAULT_HOURLY_INDEX]
forecast_day = ForecastDay(
date=day["date"],
emoji=self.get_weather_emoji(hourly["weatherDesc"][0]["value"]),
condition=hourly["weatherDesc"][0]["value"],
temp_min=day["mintempC"],
temp_max=day["maxtempC"],
wind_speed=hourly["windspeedKmph"],
wind_direction=self.get_wind_direction(hourly["winddir16Point"]),
)
forecast_text += self.strings("forecast_day").format(
date=forecast_day.date,
emoji=forecast_day.emoji,
condition=forecast_day.condition,
temp_min=forecast_day.temp_min,
temp_max=forecast_day.temp_max,
wind_speed=forecast_day.wind_speed,
wind_direction=forecast_day.wind_direction,
)
return forecast_text
async def process_weather_data(self, weather_data: Dict) -> str:
"""Process weather data and format the text."""
current = weather_data["current_condition"][0]
forecast = weather_data["weather"]
location = (
f"{weather_data['nearest_area'][0]['areaName'][0]['value']}, "
f"{weather_data['nearest_area'][0]['country'][0]['value']}"
)
forecast_text = self.format_forecast(forecast[:DEFAULT_FORECAST_DAYS])
return self.strings("weather_text").format(
location=location,
emoji=self.get_weather_emoji(current["weatherDesc"][0]["value"]),
temp=current["temp_C"],
feels_like=current["FeelsLikeC"],
humidity=current["humidity"],
wind_speed=current["windspeedKmph"],
wind_direction=self.get_wind_direction(current["winddir16Point"]),
pressure=current["pressure"],
visibility=current["visibility"],
clouds=current["weatherDesc"][0]["value"],
sunrise=forecast[0]["astronomy"][0]["sunrise"],
sunset=forecast[0]["astronomy"][0]["sunset"],
local_time=current["observation_time"],
forecast=forecast_text,
forecast_days=DEFAULT_FORECAST_DAYS,
updated=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
)
@loader.command(
ru_doc="Узнайте погоду для указанного города",
en_doc="Get the weather for the specified city",
)
async def weather(self, message):
city = utils.get_args_raw(message) or self.config["default_city"]
if not city:
await utils.answer(message, self.strings("no_city"))
return
await utils.answer(message, self.strings("loading").format(city))
weather_data = await self.get_weather_data(city)
if not weather_data:
await utils.answer(message, self.strings("error"))
return
try:
weather_text = await self.process_weather_data(weather_data)
await utils.answer(message, weather_text)
except Exception as e:
logger.exception(f"Error processing weather data: {e}")
await utils.answer(message, self.strings("error"))
@loader.command(
ru_doc="Установите город по умолчанию для определения погоды",
en_doc="Set the default city for weather",
)
async def weatherset(self, message):
city = utils.get_args_raw(message)
if not city:
await utils.answer(message, self.strings("no_city"))
return
weather_data = await self.get_weather_data(city)
if not weather_data:
await utils.answer(message, self.strings("invalid_city"))
return
self.config["default_city"] = city
await utils.answer(message, self.strings("default_city").format(city=city))

View File

@@ -0,0 +1,136 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: WindowsKeys
# Description: Provides you Windows activation keys
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: WindowsKeys
# scope: WindowsKeys 0.0.1
# requires: requests
# ---------------------------------------------------------------------------------
import logging
import json
import requests
from .. import loader, utils
logger = logging.getLogger(__name__)
@loader.tds
class WindowsKeys(loader.Module):
"""Provides you Windows activation keys"""
strings = {
"name": "WindowsKeys",
"winkey": "✅ Your key: <code>{}</code>\n\n⚠ Warning! This key is not a pirate key. It is taken from the official Microsoft site and is intended for further activation via KMS-server",
"error": "❌ An error occurred while retrieving the key. Please try again later.",
}
strings_ru = {
"winkey": "✅ Ваш ключ: <code>{}</code>\n\n⚠ Внимание! Указанный ключ не является пиратским. Он взят с официального сайта Microsoft и предназначен для дальнейшей активации посредством KMS-сервера",
"error": "❌ Произошла ошибка при получении ключа. Попробуйте позже.",
}
@loader.command(
ru_doc="Открывает выбор ключа для активации Windows",
en_doc="Opens the Windows activation key selection",
)
async def winkey(self, message):
await self.inline.form(
text="🔓 Выберите версию и издание Windows, для которой вам необходим ключ",
message=message,
reply_markup=[
[
{
"text": "Windows 10/11 Pro",
"callback": self._inline__give_key,
"args": ["win10_11pro"],
}
],
[
{
"text": "Windows 10/11 Enterprise LTSC",
"callback": self._inline__give_key,
"args": ["win10_11enterpriseLTSC"],
}
],
[
{
"text": "Windows 8.1 Pro",
"callback": self._inline__give_key,
"args": ["win8.1pro"],
}
],
[
{
"text": "Windows 8 Pro",
"callback": self._inline__give_key,
"args": ["win8pro"],
}
],
[
{
"text": "Windows 7 Pro",
"callback": self._inline__give_key,
"args": ["win7pro"],
}
],
[
{
"text": "Windows Vista Business",
"callback": self._inline__give_key,
"args": ["winvistabusiness"],
}
],
[
{
"text": "🎈 Закрыть",
"action": "close",
}
],
],
force_me=False,
silent=True,
)
async def _inline__give_key(self, call, winver):
url = "https://raw.githubusercontent.com/C0dwiz/H.Modules/refs/heads/assets/winkeys.json"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
await call.edit(self.strings["winkey"].format(data[winver]))
except requests.exceptions.RequestException as e:
logger.error("Request error: %e", e)
await call.answer(self.strings("error"), show_alert=True)
except json.JSONDecodeError as e:
logger.error("JSON decode error: %e", e)
await call.answer(self.strings("error"), show_alert=True)
except KeyError as e:
logger.error("Key error: %e", e)
await call.answer(self.strings("error"), show_alert=True)
except Exception as e:
logger.exception("An unexpected error occurred: %e", e)
await call.answer(self.strings("error"), show_alert=True)

View File

@@ -0,0 +1 @@

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: animals
# Description: Random cats and dogs
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api animals
# scope: Api animals 0.0.1
# requires: requests
# ---------------------------------------------------------------------------------
import requests
from .. import loader, utils
@loader.tds
class animals(loader.Module):
"""Random cats and dogs"""
strings = {
"name": "animals",
"loading": "<b>Generation is underway</b>",
"done": "<b>Here is your salute</b>",
}
strings_ru = {
"loading": "<b>Генерация идет полным ходом</b>",
"done": "<b>Вот ваш результат</b>",
}
# thanks https://github.com/C0dwiz/H.Modules/pull/1
async def get_photo(self, prefix: str) -> str:
response = requests.get(f"https://api.{prefix}.com/v1/images/search")
return response.json()[0]["url"]
@loader.command(
ru_doc="Файлы случайных фотографий кошек",
en_doc="Random photos of cats files",
)
async def fcatcmd(self, message):
await utils.answer(message, self.strings("loading"))
cat_url = await self.get_photo("thecat")
await utils.answer_file(
message, cat_url, self.strings("done"), force_document=True
)
@loader.command(
ru_doc="Случайные фотографии собачьих файлов",
en_doc="Random photos of dog files",
)
async def fdogcmd(self, message):
await utils.answer(message, self.strings("loading"))
dog_url = await self.get_photo("thedogapi")
await utils.answer_file(
message, dog_url, self.strings("done"), force_document=True
)
@loader.command(
ru_doc="Случайные фотографии кошек",
en_doc="Random photos of cats",
)
async def catcmd(self, message):
await utils.answer(message, self.strings("loading"))
cat_url = await self.get_photo("thecat")
await utils.answer_file(
message, cat_url, self.strings("done"), force_document=False
)
@loader.command(
ru_doc="Случайные фотографии собаки",
en_doc="Random photos of dog",
)
async def dogcmd(self, message):
await utils.answer(message, self.strings("loading"))
dog_url = await self.get_photo("thedogapi")
await utils.answer_file(
message, dog_url, self.strings("done"), force_document=False
)

View File

@@ -0,0 +1,78 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: face
# Description: Random face
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api face
# scope: Api face 0.0.1
# requires: aiohttp
# ---------------------------------------------------------------------------------
import aiohttp
from .. import loader, utils
@loader.tds
class face(loader.Module):
"""random face"""
strings = {
"name": "face",
"loading": (
"<emoji document_id=5348399448017871250>🔍</emoji> I'm looking for you kaomoji"
),
"random_face": (
"<emoji document_id=5208878706717636743>🗿</emoji> Here is your random one kaomoji\n<code>{}</code>"
),
"error": "An error has occurred!",
}
strings_ru = {
"loading": (
"<emoji document_id=5348399448017871250>🔍</emoji> Ищю вам kaomoji"
),
"random_face": (
"<emoji document_id=5208878706717636743>🗿</emoji> Вот ваш рандомный kaomoji\n<code>{}</code>"
),
"error": "Произошла ошибка!",
}
@loader.command(
ru_doc="Рандом kaomoji",
en_doc="Random kaomoji",
)
async def rfacecmd(self, message):
await utils.answer(message, self.strings("loading"))
url = "https://vsecoder.dev/api/faces"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
data = await response.json()
random_face = data["data"]
await utils.answer(
message, self.strings("random_face").format(random_face)
)
else:
await utils.answer(message, self.strings("error"))

View File

@@ -0,0 +1,47 @@
AccountData
AniLibria
animals
AnimeQuotes
Article
ASCIIArt
AutofarmCookies
BirthdayTime
CheckSpamBan
CryptoCurrency
EnvsSH
face
FakeActions
FakeWallet
full
GigaChat
globalrestrict
hikkahost
InlineButton
InlineCoin
InlineHelper
IrisSimpleMod
jacques
KBSwapper
Memes
Music
novoice
nsfwart
numbersapi
PastebinAPI
profile
ReplaceVowels
SafetyMod
search
shortener
SMAcrhiver
TelegramStatusCodes
TempChat
Text2File
Text_Sticker
TikTokDownloader
Video2GIF
VirusTotal
VoiceDL
HAFK
Weather
InfoBannersManager

View File

@@ -0,0 +1,681 @@
# █ █ ▀ █▄▀ ▄▀█ █▀█ ▀
# █▀█ █ █ █ █▀█ █▀▄ █
# © Copyright 2022
# https://t.me/hikariatama
#
# 🔒 Licensed under the GNU AGPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# Some functions took from Hikarichat by Hikariatama
# ---------------------------------------------------------------------------------
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: GlobalRestrict
# Description: Global mutation or ban
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api GlobalRestrict
# scope: Api GlobalRestrict 0.0.1
# ---------------------------------------------------------------------------------
import re
import time
import typing
from telethon.tl.types import (
Channel,
Chat,
Message,
User,
)
from .. import loader, utils
BANNED_RIGHTS = {
"view_messages": False,
"send_messages": False,
"send_media": False,
"send_stickers": False,
"send_gifs": False,
"send_games": False,
"send_inline": False,
"send_polls": False,
"change_info": False,
"invite_users": False,
}
MUTES_RIGHTS = {
"view_messages": True,
"send_messages": False,
"send_media": False,
"send_stickers": False,
"send_gifs": False,
"send_games": False,
"send_inline": False,
"send_polls": False,
"change_info": False,
"invite_users": False,
}
def get_full_name(user: typing.Union[User, Channel]) -> str:
return utils.escape_html(
user.title
if isinstance(user, Channel)
else (
f"{user.first_name} "
+ (user.last_name if getattr(user, "last_name", False) else "")
)
).strip()
@loader.tds
class GlobalRestrict(loader.Module):
"""Global mutation or ban"""
strings = {
"name": "GlobalRestrict",
"no_reason": "Not specified",
"args": (
"<emoji document_id=5300759756669984376>🚫</emoji> <b>Incorrect arguments</b>"
),
"glban": (
'<emoji document_id=5301059317753979286>🖕</emoji> <b><a href="{}">{}</a>'
" has been globally banned.</b>\n<b>Reason: </b><i>{}</i>\n\n{}"
),
"glbanning": (
"<emoji document_id=5301059317753979286>🖕</emoji> <b>Globally banning <a"
' href="{}">{}</a>...</b>'
),
"gunban": (
'<emoji document_id=6334872157947955302>🤗</emoji> <b><a href="{}">{}</a>'
" has been globally unbanned.</b>\n\n{}"
),
"gunbanning": (
"<emoji document_id=6334872157947955302>🤗</emoji> <b>Global unbanning <a"
' href="{}">{}</a>...</b>'
),
"in_n_chats": (
"<emoji document_id=5379568936218009290>👎</emoji> <b>Banned in {}"
" chat(s)</b>"
),
"unbanned_in_n_chats": (
"<emoji document_id=5461129450341014019>✋️</emoji> <b>Unbanned in {}"
" chat(s)</b>"
),
"glmute": (
'<emoji document_id=5301059317753979286>🖕</emoji> <b><a href="{}">{}</a>'
" has been globally muted.</b>\n<b>Reason: </b><i>{}</i>\n\n{}"
),
"glmutes": (
"<emoji document_id=5301059317753979286>🖕</emoji> <b>Global mute <a"
' href="{}">{}</a>...</b>'
),
"gunmute": (
'<emoji document_id=6334872157947955302>🤗</emoji> <b><a href="{}">{}</a>'
" has been globally unmuted.</b>\n\n{}"
),
"gunmutes": (
"<emoji document_id=6334872157947955302>🤗</emoji> <b>Global unmute <a"
' href="{}">{}</a>...</b>'
),
"in_m_chats": (
"<emoji document_id=5379568936218009290>👎</emoji> <b>Muted in {}"
" chat(s)</b>"
),
"unmute_in_n_chats": (
"<emoji document_id=5461129450341014019>✋️</emoji> <b>Unmuted in {}"
" chat(s)</b>"
),
}
strings_ru = {
"no_reason": "Не указана",
"args": (
"<emoji document_id=5300759756669984376>🚫</emoji> <b>Неверные"
" аргументы</b>"
),
"glban": (
'<emoji document_id=5301059317753979286>🖕</emoji> <b><a href="{}">{}</a>'
" был гзабанен.</b>\n<b>Причина: </b><i>{}</i>\n\n{}"
),
"glbanning": (
"<emoji document_id=5301059317753979286>🖕</emoji> <b>Гбан <a"
' href="{}">{}</a>...</b>'
),
"gunban": (
'<emoji document_id=6334872157947955302>🤗</emoji> <b><a href="{}">{}</a>'
" был гразбанен.</b>\n\n{}"
),
"gunbanning": (
"<emoji document_id=6334872157947955302>🤗</emoji> <b>Гразбан <a"
' href="{}">{}</a>...</b>'
),
"in_n_chats": (
"<emoji document_id=5379568936218009290>👎</emoji> <b>Забанил в {}"
" чат(-ах)</b>"
),
"unbanned_in_n_chats": (
"<emoji document_id=5461129450341014019>✋️</emoji> <b>Разбанил in {}"
" чат(-ах)</b>"
),
"glmute": (
'<emoji document_id=5301059317753979286>🖕</emoji> <b><a href="{}">{}</a>'
" был замучен.</b>\n<b>Причина: </b><i>{}</i>\n\n{}"
),
"glmutes": (
"<emoji document_id=5301059317753979286>🖕</emoji> <b>Гмут <a"
' href="{}">{}</a>...</b>'
),
"gunmute": (
'<emoji document_id=6334872157947955302>🤗</emoji> <b><a href="{}">{}</a>'
" был размучен.</b>\n\n{}"
),
"gunmutes": (
"<emoji document_id=6334872157947955302>🤗</emoji> <b>Гразмут <a"
' href="{}">{}</a>...</b>'
),
"in_m_chats": (
"<emoji document_id=5379568936218009290>👎</emoji> <b>Мут в {} чат(-ах)</b>"
),
"unmute_in_n_chats": (
"<emoji document_id=5461129450341014019>✋️</emoji> <b>Размут in {}"
" чат(-ах)</b>"
),
}
def __init__(self):
self._gban_cache = {}
self._gmute_cache = {}
@staticmethod
def convert_time(t: str) -> int:
"""
Tries to export time from text
"""
try:
if not str(t)[:-1].isdigit():
return 0
if "d" in str(t):
t = int(t[:-1]) * 60 * 60 * 24
if "h" in str(t):
t = int(t[:-1]) * 60 * 60
if "m" in str(t):
t = int(t[:-1]) * 60
if "s" in str(t):
t = int(t[:-1])
t = int(re.sub(r"[^0-9]", "", str(t)))
except ValueError:
return 0
return t
async def args_parser(
self,
message: Message,
include_force: bool = False,
include_silent: bool = False,
) -> tuple:
"""Get args from message"""
args = " " + utils.get_args_raw(message)
if include_force and " -f" in args:
force = True
args = args.replace(" -f", "")
else:
force = False
if include_silent and " -s" in args:
silent = True
args = args.replace(" -s", "")
else:
silent = False
args = args.strip()
reply = await message.get_reply_message()
if reply and not args:
return (
(await self._client.get_entity(reply.sender_id)),
0,
utils.escape_html(self.strings("no_reason")).strip(),
*((force,) if include_force else []),
*((silent,) if include_silent else []),
)
try:
a = args.split()[0]
if str(a).isdigit():
a = int(a)
user = await self._client.get_entity(a)
except Exception:
try:
user = await self._client.get_entity(reply.sender_id)
except Exception:
return False
t = ([arg for arg in args.split() if self.convert_time(arg)] or ["0"])[0]
args = args.replace(t, "").replace(" ", " ")
t = self.convert_time(t)
if not reply:
try:
args = " ".join(args.split()[1:])
except Exception:
pass
if time.time() + t >= 2208978000: # 01.01.2040 00:00:00
t = 0
return (
user,
t,
utils.escape_html(args or self.strings("no_reason")).strip(),
*((force,) if include_force else []),
*((silent,) if include_silent else []),
)
async def ban(
self,
chat: typing.Union[Chat, int],
user: typing.Union[User, Channel, int],
period: int = 0,
reason: str = None,
message: typing.Optional[Message] = None,
silent: bool = False,
):
"""Ban user in chat"""
if str(user).isdigit():
user = int(user)
if reason is None:
reason = self.strings("no_reason")
try:
await self.inline.bot.kick_chat_member(
int(f"-100{getattr(chat, 'id', chat)}"),
int(getattr(user, "id", user)),
)
except Exception:
await self._client.edit_permissions(
chat,
user,
until_date=(time.time() + period) if period else 0,
**BANNED_RIGHTS,
)
if silent:
return
async def mute(
self,
chat: typing.Union[Chat, int],
user: typing.Union[User, Channel, int],
period: int = 0,
reason: str = None,
message: typing.Optional[Message] = None,
silent: bool = False,
):
"""Mute user in chat"""
if str(user).isdigit():
user = int(user)
if reason is None:
reason = self.strings("no_reason")
try:
await self.inline.bot.restrict_chat_member(
int(f"-100{getattr(chat, 'id', chat)}"),
int(getattr(user, "id", user)),
)
except Exception:
await self._client.edit_permissions(
chat,
user,
until_date=(time.time() + period) if period else 0,
**MUTES_RIGHTS,
)
if silent:
return
@loader.command(
ru_doc="<реплай | юзер> [причина] [-s] - Забанить пользователя во всех чатах где ты админ",
en_doc="<replay | user> [reason] [-s] - Ban the user in all chats where you are the admin",
)
async def glban(self, message):
reply = await message.get_reply_message()
args = utils.get_args_raw(message)
if not reply and not args:
await utils.answer(message, self.strings("args"))
return
a = await self.args_parser(message, include_silent=True)
if not a:
await utils.answer(message, self.strings("args"))
return
user, t, reason, silent = a
message = await utils.answer(
message,
self.strings("glbanning").format(
utils.get_entity_url(user),
utils.escape_html(get_full_name(user)),
),
)
if not self._gban_cache or self._gban_cache["exp"] < time.time():
self._gban_cache = {
"exp": int(time.time()) + 10 * 60,
"chats": [
chat.entity.id
async for chat in self._client.iter_dialogs()
if (
(
isinstance(chat.entity, Chat)
or (
isinstance(chat.entity, Channel)
and getattr(chat.entity, "megagroup", False)
)
)
and chat.entity.admin_rights
and chat.entity.participants_count > 5
and chat.entity.admin_rights.ban_users
)
],
}
chats = ""
counter = 0
for chat in self._gban_cache["chats"]:
try:
await self.ban(chat, user, 0, reason, silent=True)
except Exception:
pass
else:
chats += '▫️ <b><a href="{}">{}</a></b>\n'.format(
utils.get_entity_url(await self._client.get_entity(chat, exp=0)),
utils.escape_html(
get_full_name(await self._client.get_entity(chat, exp=0))
),
)
counter += 1
await utils.answer(
message,
self.strings("glban").format(
utils.get_entity_url(user),
utils.escape_html(get_full_name(user)),
reason,
self.strings("in_n_chats").format(counter) if silent else chats,
),
)
@loader.command(
ru_doc="<реплай | юзер> [причина] [-s] - Разбанить пользователя во всех где ты админ",
en_doc="<replay | user> [reason] [-s] - To unban the user in all where you are the admin",
)
async def glunban(self, message: Message):
reply = await message.get_reply_message()
args = utils.get_args_raw(message)
if not reply and not args:
await utils.answer(message, self.strings("args"))
return
a = await self.args_parser(message, include_silent=True)
if not a:
await utils.answer(message, self.strings("args"))
return
user, t, reason, silent = a
message = await utils.answer(
message,
self.strings("gunbanning").format(
utils.get_entity_url(user),
utils.escape_html(get_full_name(user)),
),
)
if not self._gban_cache or self._gban_cache["exp"] < time.time():
self._gban_cache = {
"exp": int(time.time()) + 10 * 60,
"chats": [
chat.entity.id
async for chat in self._client.iter_dialogs()
if (
(
isinstance(chat.entity, Chat)
or (
isinstance(chat.entity, Channel)
and getattr(chat.entity, "megagroup", False)
)
)
and chat.entity.admin_rights
and chat.entity.participants_count > 5
and chat.entity.admin_rights.ban_users
)
],
}
chats = ""
counter = 0
for chat in self._gban_cache["chats"]:
try:
await self._client.edit_permissions(
chat,
user,
until_date=0,
**{right: True for right in BANNED_RIGHTS.keys()},
)
except Exception:
pass
else:
chats += '▫️ <b><a href="{}">{}</a></b>\n'.format(
utils.get_entity_url(await self._client.get_entity(chat, exp=0)),
utils.escape_html(
get_full_name(await self._client.get_entity(chat, exp=0))
),
)
counter += 1
await utils.answer(
message,
self.strings("gunban").format(
utils.get_entity_url(user),
utils.escape_html(get_full_name(user)),
(
self.strings("unbanned_in_n_chats").format(counter)
if silent
else chats
),
),
)
@loader.command(
ru_doc="<реплай | юзер> [причина] [-s] - Замутить пользователя во всех чатах где ты админ",
en_doc="<replay | user> [reason] [-s] - To hook up the user in all chats where you are the admin",
)
async def glmute(self, message):
reply = await message.get_reply_message()
args = utils.get_args_raw(message)
if not reply and not args:
await utils.answer(message, self.strings("args"))
return
a = await self.args_parser(message, include_silent=True)
if not a:
await utils.answer(message, self.strings("args"))
return
user, t, reason, silent = a
message = await utils.answer(
message,
self.strings("glmutes").format(
utils.get_entity_url(user),
utils.escape_html(get_full_name(user)),
),
)
if not self._gmute_cache or self._gmute_cache["exp"] < time.time():
self._gmute_cache = {
"exp": int(time.time()) + 10 * 60,
"chats": [
chat.entity.id
async for chat in self._client.iter_dialogs()
if (
(
isinstance(chat.entity, Chat)
or (
isinstance(chat.entity, Channel)
and getattr(chat.entity, "megagroup", False)
)
)
and chat.entity.admin_rights
and chat.entity.participants_count > 5
and chat.entity.admin_rights.ban_users
)
],
}
chats = ""
counter = 0
for chat in self._gmute_cache["chats"]:
try:
await self.mute(chat, user, 0, reason, silent=True)
except Exception:
pass
else:
chats += '▫️ <b><a href="{}">{}</a></b>\n'.format(
utils.get_entity_url(await self._client.get_entity(chat, exp=0)),
utils.escape_html(
get_full_name(await self._client.get_entity(chat, exp=0))
),
)
counter += 1
await utils.answer(
message,
self.strings("glmute").format(
utils.get_entity_url(user),
utils.escape_html(get_full_name(user)),
reason,
self.strings("in_m_chats").format(counter) if silent else chats,
),
)
@loader.command(
ru_doc="<реплай | юзер> [причина] [-s] - Размутит пользователя во всех где ты админ",
en_doc="<replay | user> [reason] [-s] - Will confuse the user in all where you are the admin",
)
async def glunmute(self, message: Message):
reply = await message.get_reply_message()
args = utils.get_args_raw(message)
if not reply and not args:
await utils.answer(message, self.strings("args"))
return
a = await self.args_parser(message, include_silent=True)
if not a:
await utils.answer(message, self.strings("args"))
return
user, t, reason, silent = a
message = await utils.answer(
message,
self.strings("gunmutes").format(
utils.get_entity_url(user),
utils.escape_html(get_full_name(user)),
),
)
if not self._gmute_cache or self._gmute_cache["exp"] < time.time():
self._gmute_cache = {
"exp": int(time.time()) + 10 * 60,
"chats": [
chat.entity.id
async for chat in self._client.iter_dialogs()
if (
(
isinstance(chat.entity, Chat)
or (
isinstance(chat.entity, Channel)
and getattr(chat.entity, "megagroup", False)
)
)
and chat.entity.admin_rights
and chat.entity.participants_count > 5
and chat.entity.admin_rights.ban_users
)
],
}
chats = ""
counter = 0
for chat in self._gmute_cache["chats"]:
try:
await self._client.edit_permissions(
chat,
user,
until_date=0,
**{right: True for right in MUTES_RIGHTS.keys()},
)
except Exception:
pass
else:
chats += '▫️ <b><a href="{}">{}</a></b>\n'.format(
utils.get_entity_url(await self._client.get_entity(chat, exp=0)),
utils.escape_html(
get_full_name(await self._client.get_entity(chat, exp=0))
),
)
counter += 1
await utils.answer(
message,
self.strings("gunmute").format(
utils.get_entity_url(user),
utils.escape_html(get_full_name(user)),
(
self.strings("unmutes_in_n_chats").format(counter)
if silent
else chats
),
),
)

View File

@@ -0,0 +1,314 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: HikkaHost
# Description: Hikkahost manager.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: api HikkaHost
# scope: api HikkaHost 0.0.1
# ---------------------------------------------------------------------------------
import aiohttp
import json
from datetime import datetime, timedelta, timezone
from .. import loader, utils
class HostApi:
"""
A class for interacting with a Host API.
Args:
token (str): The API token.
"""
def __init__(self, token: str):
self.token = token
async def _request(self, path: str, method: str = "GET") -> dict:
"""
Sends a request to the API.
Args:
path (str): The API path.
method (str, optional): The HTTP method. Defaults to "GET".
Returns:
dict: The API response as a dictionary.
"""
url = "http://158.160.84.24:5000" + path
async with aiohttp.ClientSession(trust_env=True) as session:
async with session.request(
method,
url,
headers={
"Content-Type": "application/json",
"token": self.token,
},
ssl=False,
) as response:
return await response.json()
async def stats(self, user_id: int) -> dict:
"""
Gets the host stats.
Args:
user_id (int): The user ID.
Returns:
dict: The host stats.
"""
url = f"/api/host/{user_id}/stats"
return await self._request(url)
async def host_info(self, user_id: int) -> dict:
"""
Gets the host information.
Args:
user_id (int): The user ID.
Returns:
dict: The host information.
"""
url = f"/api/host/{user_id}"
return await self._request(url)
async def status(self, user_id: int) -> dict:
"""
Gets the host status.
Args:
user_id (int): The user ID.
Returns:
dict: The host status.
"""
url = f"/api/host/{user_id}/status"
return await self._request(url)
async def logs(self, user_id: int) -> dict:
"""
Gets the host logs.
Args:
user_id (int): The user ID.
Returns:
dict: The host logs.
"""
url = f"/api/host/{user_id}/logs/all"
return await self._request(url)
async def action(self, user_id: int, action: str = "restart") -> dict:
"""
Performs an action on the host.
Args:
user_id (int): The user ID.
action (str, optional): The action to perform. Defaults to "restart".
Returns:
dict: The action result.
"""
url = f"/api/host/{user_id}?action={action}"
return await self._request(url, method="PUT")
def bytes_to_megabytes(b: int):
"""
Converts bytes to megabytes.
Args:
b (int): The number of bytes.
Returns:
float: The number of megabytes.
"""
return round(b / 1024 / 1024, 1)
@loader.tds
class HikkahostMod(loader.Module):
"""Hikkahost manager."""
MAX_RAM = 750
strings = {
"name": "HikkaHost",
"info": (
"<emoji document_id=5879770735999717115>👤</emoji> <b>Information panel</b>\n\n"
"<emoji document_id=5974526806995242353>🆔</emoji> <b>Server ID:</b> <code>{server_id}</code>\n"
"<emoji document_id=6005570495603282482>🔑</emoji> <b>ID:</b> <code>{id}</code>\n"
"<emoji document_id=5874986954180791957>📶</emoji> <b>Status:</b> <code>{status}</code>\n"
"<emoji document_id=5451646226975955576>⌛️</emoji> <b>Subscription ends:</b> <code>{end_dates}</code> | <code>{days_end} days</code>\n\n"
"<emoji document_id=5877260593903177342>⚙️</emoji> <b>CPU:</b> <code>{cpu_percent} %</code>\n"
"<emoji document_id=5379652232813750191>💾</emoji> <b>RAM:</b> <code>{memory} / {max_ram} MB</code> <b>{ram_percent} %</b>"
),
"logs": (
"<emoji document_id=5188377234380954537>🌘</emoji> <b>Here are your logs</b>"
),
"restart": (
"<emoji document_id=5789886476472815477>✅</emoji> <b>Restart request sent</b>\n"
"This message remains unchanged after the restart"
),
"loading_info": "<emoji document_id=5451646226975955576>⌛️</emoji> Loading...",
"no_apikey": "<emoji document_id=5260342697075416641>🚫</emoji> You have not specified an API Key\nTo get a token.\n\n1. Go to the @hikkahost_bot\n2. Write /token\n3. Paste it into the config",
"condition": "works",
}
strings_ru = {
"info": (
"<emoji document_id=5879770735999717115>👤</emoji> <b>Панель информации</b>\n\n"
"<emoji document_id=5974526806995242353>🆔</emoji> <b>Server ID:</b> <code>{server_id}</code>\n"
"<emoji document_id=6005570495603282482>🔑</emoji> <b>ID:</b> <code>{id}</code>\n"
"<emoji document_id=5874986954180791957>📶</emoji> <b>Статус:</b> <code>{status}</code>\n"
"<emoji document_id=5451646226975955576>⌛️</emoji> <b>Подписка закончится:</b> <code>{end_dates}</code> | <code>{days_end} дней</code>\n\n"
"<emoji document_id=5877260593903177342>⚙️</emoji> <b>CPU:</b> <code>{cpu_percent} %</code>\n"
"<emoji document_id=5379652232813750191>💾</emoji> <b>RAM:</b> <code>{memory} / {max_ram} MB</code> <b>{ram_percent} %</b>"
),
"logs": (
"<emoji document_id=5188377234380954537>🌘</emoji> <b>Вот ваши логи</b>"
),
"restart": (
"<emoji document_id=5789886476472815477>✅</emoji> <b>Запрос на рестарт отправил</b>\n"
"Это сообщение не изменяется после рестарта"
),
"loading_info": "<emoji document_id=5451646226975955576>⌛️</emoji> Загрузка...",
"no_apikey": "<emoji document_id=5260342697075416641>🚫</emoji> Вы не указали Api Key\nЧтобы получить token.\n\n1. Перейдите в бота @hikkahost_bot\n2. Напишите /token\n3. Вставьте его в конфиг",
"condition": "работает",
}
def __init__(self):
self.name = self.strings["name"]
self.config = loader.ModuleConfig(
loader.ConfigValue(
"token",
None,
validator=loader.validators.Hidden(),
),
)
@loader.command(
ru_doc="Статус HikkaHost",
en_doc="Status HikkaHost",
)
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(token)
stats_data = await api.stats(user_id)
host_data = await api.host_info(user_id)
datas = await api.status(user_id)
memory = bytes_to_megabytes(stats_data["stats"]["memory_stats"]["usage"])
cpu_percent = (
round(
(
stats_data["stats"]["cpu_stats"]["cpu_usage"]["total_usage"]
/ stats_data["stats"]["cpu_stats"]["system_cpu_usage"]
)
* 100.0,
2,
)
if stats_data["stats"]["cpu_stats"]["cpu_usage"]["total_usage"]
and stats_data["stats"]["cpu_stats"]["system_cpu_usage"]
else None
)
ram_percent = round(
bytes_to_megabytes(
stats_data["stats"]["memory_stats"]["usage"] / self.MAX_RAM
)
* 100,
2,
)
server_id = host_data["host"]["server_id"]
target_data = datetime.fromisoformat(
host_data["host"]["end_date"].replace("Z", "+00:00")
).replace(tzinfo=timezone.utc)
current_data = datetime.now(timezone.utc)
days_end = (target_data - current_data).days
end_dates = (current_data + timedelta(days=days_end)).strftime("%d-%m-%Y")
if "status" in datas and datas["status"] == "running":
status = self.strings("condition")
await utils.answer(
message,
self.strings("info").format(
server_id=server_id,
id=user_id,
status=status,
end_dates=end_dates,
days_end=days_end,
cpu_percent=cpu_percent,
memory=memory,
max_ram=self.MAX_RAM,
ram_percent=ram_percent,
),
)
@loader.command(
ru_doc="Логи HikkaHost",
en_doc="Logs HikkaHost",
)
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(token)
data = await api.logs(user_id, token)
files_log = data["logs"]
with open("log.txt", "w") as log_file:
json.dump(files_log, log_file)
await utils.answer_file(message, "log.txt", self.strings("logs"))
@loader.command(
ru_doc="Рестарт HikkaHost",
en_doc="Restart HikkaHost",
)
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(token)
data = await api.action(user_id, token)

View File

@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>H:Mods</title>
<link type=text/css href="https://github.com/C0dwiz/H.Modules/raw/assets/style.css" rel="stylesheet" />
</head>
<body>
<div class="container">
<h1>H:Mods modules</h1>
<ul class="module-list">
<li class=module-item><a href="ASCIIArt.py" class=module-link>ASCIIArt.py</a></li>
<li class=module-item><a href="AccountData.py" class=module-link>AccountData.py</a></li>
<li class=module-item><a href="AniLibria.py" class=module-link>AniLibria.py</a></li>
<li class=module-item><a href="AnimeQuotes.py" class=module-link>AnimeQuotes.py</a></li>
<li class=module-item><a href="Article.py" class=module-link>Article.py</a></li>
<li class=module-item><a href="AutofarmCookies.py" class=module-link>AutofarmCookies.py</a></li>
<li class=module-item><a href="BirthdayTime.py" class=module-link>BirthdayTime.py</a></li>
<li class=module-item><a href="CheckSpamBan.py" class=module-link>CheckSpamBan.py</a></li>
<li class=module-item><a href="CryptoCurrency.py" class=module-link>CryptoCurrency.py</a></li>
<li class=module-item><a href="EnvsSH.py" class=module-link>EnvsSH.py</a></li>
<li class=module-item><a href="FakeActions.py" class=module-link>FakeActions.py</a></li>
<li class=module-item><a href="FakeWallet.py" class=module-link>FakeWallet.py</a></li>
<li class=module-item><a href="GigaChat.py" class=module-link>GigaChat.py</a></li>
<li class=module-item><a href="H.py" class=module-link>H.py</a></li>
<li class=module-item><a href="HAFK.py" class=module-link>HAFK.py</a></li>
<li class=module-item><a href="HModsLibrary.py" class=module-link>HModsLibrary.py</a></li>
<li class=module-item><a href="InfoBannersManager.py" class=module-link>InfoBannersManager.py</a></li>
<li class=module-item><a href="InlineButton.py" class=module-link>InlineButton.py</a></li>
<li class=module-item><a href="InlineCoin.py" class=module-link>InlineCoin.py</a></li>
<li class=module-item><a href="InlineHelper.py" class=module-link>InlineHelper.py</a></li>
<li class=module-item><a href="IrisSimpleMod.py" class=module-link>IrisSimpleMod.py</a></li>
<li class=module-item><a href="KBSwapper.py" class=module-link>KBSwapper.py</a></li>
<li class=module-item><a href="Memes.py" class=module-link>Memes.py</a></li>
<li class=module-item><a href="MooFarmRC1.py" class=module-link>MooFarmRC1.py</a></li>
<li class=module-item><a href="Music.py" class=module-link>Music.py</a></li>
<li class=module-item><a href="PastebinAPI.py" class=module-link>PastebinAPI.py</a></li>
<li class=module-item><a href="ReplaceVowels.py" class=module-link>ReplaceVowels.py</a></li>
<li class=module-item><a href="SMAcrhiver.py" class=module-link>SMAcrhiver.py</a></li>
<li class=module-item><a href="SafetyMod.py" class=module-link>SafetyMod.py</a></li>
<li class=module-item><a href="TaskManager.py" class=module-link>TaskManager.py</a></li>
<li class=module-item><a href="TelegramStatusCodes.py" class=module-link>TelegramStatusCodes.py</a></li>
<li class=module-item><a href="TempChat.py" class=module-link>TempChat.py</a></li>
<li class=module-item><a href="Text2File.py" class=module-link>Text2File.py</a></li>
<li class=module-item><a href="Text_Sticker.py" class=module-link>Text_Sticker.py</a></li>
<li class=module-item><a href="TikTokDownloader.py" class=module-link>TikTokDownloader.py</a></li>
<li class=module-item><a href="UserbotAvast.py" class=module-link>UserbotAvast.py</a></li>
<li class=module-item><a href="Video2GIF.py" class=module-link>Video2GIF.py</a></li>
<li class=module-item><a href="VirusTotal.py" class=module-link>VirusTotal.py</a></li>
<li class=module-item><a href="VoiceDL.py" class=module-link>VoiceDL.py</a></li>
<li class=module-item><a href="Weather.py" class=module-link>Weather.py</a></li>
<li class=module-item><a href="WindowsKeys.py" class=module-link>WindowsKeys.py</a></li>
<li class=module-item><a href="animals.py" class=module-link>animals.py</a></li>
<li class=module-item><a href="face.py" class=module-link>face.py</a></li>
<li class=module-item><a href="globalrestrict.py" class=module-link>globalrestrict.py</a></li>
<li class=module-item><a href="hikkahost.py" class=module-link>hikkahost.py</a></li>
<li class=module-item><a href="jacques.py" class=module-link>jacques.py</a></li>
<li class=module-item><a href="novoice.py" class=module-link>novoice.py</a></li>
<li class=module-item><a href="nsfwart.py" class=module-link>nsfwart.py</a></li>
<li class=module-item><a href="numbersapi.py" class=module-link>numbersapi.py</a></li>
<li class=module-item><a href="profile.py" class=module-link>profile.py</a></li>
<li class=module-item><a href="search.py" class=module-link>search.py</a></li>
<li class=module-item><a href="shortener.py" class=module-link>shortener.py</a></li>
</ul>
</div>
</body>
</html>

View File

@@ -0,0 +1,112 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Жаконизатор
# Description: Жаконизатор
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Жаконизатор
# scope: Жаконизатор 0.0.1
# ---------------------------------------------------------------------------------
import aiohttp
import io
from PIL import Image, ImageDraw, ImageFont
from textwrap import wrap
from .. import loader, utils
@loader.tds
class JacquesMod(loader.Module):
"""Жаконизатор"""
strings = {"name": "Жаконизатор", "usage": "Write <code>.help Жаконизатор</code>"}
strings_ru = {"usage": "Напиши <code>.help Жаконизатор</code>"}
def __init__(self):
self.name = self.strings["name"]
self._me = None
self._ratelimit = []
self.config = loader.ModuleConfig(
loader.ConfigValue(
"font",
"https://github.com/Codwizer/ReModules/blob/main/assets/OpenSans-Light.ttf?raw=true",
lambda: "добавьте ссылку на нужный вам шрифт",
),
loader.ConfigValue(
"location",
"center",
"Можно указать left, right или center",
validator=loader.validators.Choice(["left", "right", "center"]),
),
)
@loader.command(
ru_doc="<реплай на сообщение/свой текст>",
en_doc="<reply to the message/your own text>",
)
async def ionicmd(self, message):
reply = await message.get_reply_message()
args = utils.get_args_raw(message)
if not args:
if not reply:
await utils.answer(message, self.strings("usage", message))
return
else:
txt = reply.raw_text
else:
txt = args
async with aiohttp.ClientSession() as session:
async with session.get(self.config["font"]) as font_response:
font_data = await font_response.read()
async with session.get(
"https://raw.githubusercontent.com/Codwizer/ReModules/main/assets/IMG_20231128_152538.jpg"
) as pic_response:
pic_data = await pic_response.read()
img = Image.open(io.BytesIO(pic_data)).convert("RGB")
wrapped_text = "\n".join(wrap(txt, 19)) + "\n"
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(io.BytesIO(font_data), 32, encoding="UTF-8")
text_size = draw.multiline_textsize(wrapped_text, font=font)
imtext = Image.new("RGBA", (text_size[0] + 10, text_size[1] + 10), (0, 0, 0, 0))
draw_imtext = ImageDraw.Draw(imtext)
draw_imtext.multiline_text(
(10, 10), wrapped_text, (0, 0, 0), font=font, align=self.config["location"]
)
imtext.thumbnail((350, 195))
img.paste(imtext, (10, 10), imtext)
out = io.BytesIO()
out.name = "hikka_mods.jpg"
img.save(out)
out.seek(0)
await message.client.send_file(message.to_id, out, reply_to=reply)
await message.delete()

View File

@@ -0,0 +1,160 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: NoVoice
# Description: A module for prohibiting the sending of voice and video messages
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: NoVoice
# scope: NoVoice 0.0.1
# ---------------------------------------------------------------------------------
import logging
from telethon.tl.custom import Message
from .. import loader, utils
logger = logging.INFO(__name__)
@loader.tds
class NoVoiceMod(loader.Module):
"""A module for prohibiting the sending of voice and video messages"""
strings = {
"name": "NoVoice",
"novoice_true": "❌ Voice messages are disabled for all users!",
"novoice_false": "✅ Voice messages are allowed for all users again!",
"novoice_no_args": "Usage: .novoice [on/off]",
"novoiceuser_no_reply": "Usage: .novoiceuser [username/reply]",
"novoiceuser_true": "❌ User {user_id} is now forbidden to send voice messages!",
"novoicerm_no_reply": "Usage: .novoicerm [username/reply]",
"novoicerm_yes": "✅ User {user_id} is now allowed to send voice messages again!",
"novoicerm_no": "⚠️ User {user_id} not found in the banned list.",
"text": "❌ I do not accept voice messages!",
}
strings_ru = {
"novoice_true": "❌ Голосовые сообщения отключены для всех пользователей!",
"novoice_false": "✅ Голосовые сообщения снова разрешены для всех пользователей!",
"novoice_no_args": "Использование: .novoice [on/off]",
"novoiceuser_no_reply": "Использование: .novoiceuser [username/reply]",
"novoiceuser_true": "❌ Пользователю {user_id} запрещено отправлять голосовые сообщения!",
"novoicerm_no_reply": "Использование: .novoicerm [username/reply]",
"novoicerm_yes": "✅ Пользователю {user_id} снова разрешено отправлять голосовые сообщения!",
"novoicerm_no": "⚠ Пользователь {user_id} не найден в списке запрещенных.",
"text": "❌ Я не принимаю голосовые сообщения!",
}
async def client_ready(self, client, db):
self.client = client
self.db = db
self.novoice_global = self.db.get("NoVoice", "global", False)
self.banned_users = self.db.get("NoVoice", "banned_users", {})
@loader.command(
ru_doc="[on/off] — запрещает/разрешает всем пользователям отправку голосовых и видеосообщений.",
en_doc="[on/off] — prohibits/allows all users to send voice and video messages.",
)
async def novoice(self, message):
args = utils.get_args_raw(message)
if args == "on":
self.novoice_global = True
self.db.set("NoVoice", "global", self.novoice_global)
await utils.answer(message, self.strings("novoice_true"))
elif args == "off":
self.novoice_global = False
self.db.set("NoVoice", "global", self.novoice_global)
await utils.answer(message, self.strings("novoice_false"))
else:
await utils.answer(message, self.strings("novoice_no_args"))
@loader.command(
ru_doc="[username/reply] — запрещает пользователю отправку голосовых и видеосообщений.",
en_doc="[username/reply] — prohibits the user from sending voice and video messages.",
)
async def novoiceuser(self, message):
args = utils.get_args_raw(message)
reply = await message.get_reply_message()
if not args and not reply:
return await utils.answer(message, self.strings("novoiceuser_no_reply"))
if reply:
user_id = reply.from_id
else:
user = await self.client.get_entity(args)
user_id = user.id
self.banned_users[user_id] = True
self.db.set("NoVoice", "banned_users", self.banned_users)
await utils.answer(
message, self.strings("novoiceuser_true").format(user_id=user_id)
)
@loader.command(
ru_doc="[username/reply] — разрешает пользователю отправку голосовых и видеосообщений.",
en_doc="[username/reply] — allows the user to send voice and video messages.",
)
async def novoicerm(self, message):
args = utils.get_args_raw(message)
reply = await message.get_reply_message()
if not args and not reply:
return await utils.answer(message, self.strings("novoicerm_no_reply"))
user_id = None
if reply:
user_id = reply.sender_id
else:
try:
user = await self.client.get_entity(args)
user_id = user.id
except Exception as e:
logger.error(f"Failed to get entity for {args}: {e}")
if user_id in self.banned_users:
del self.banned_users[user_id]
self.db.set("NoVoice", "banned_users", self.banned_users)
await utils.answer(
message, self.strings("novoicerm_yes").format(user_id=user_id)
)
else:
await utils.answer(
message, self.strings("novoicerm_no").format(user_id=user_id)
)
async def watcher(self, message: Message):
"""Обрабатывает входящие сообщения"""
if (
isinstance(message, Message)
and not message.out
and message.is_private
and (self.novoice_global or message.sender_id in self.banned_users)
and (message.voice or message.video_note)
):
await message.delete()
await utils.answer(message, self.strings("text"))
logger.debug(
"Deleted voice/video message from user %s in chat %s",
message.sender_id,
message.chat_id,
)

View File

@@ -0,0 +1,97 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: NSFWArt
# Description: Sends cute anime nsfw-art
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api NSFWArt
# scope: Api NSFWArt 0.0.1
# ---------------------------------------------------------------------------------
import functools
import requests
from typing import List
from .. import loader, utils
async def photos(tags: str, subreddit: str, quantity: int) -> List[str]:
ans = (
await utils.run_sync(
requests.get,
f"https://api.lolicon.app/setu/v2?tag={tags}",
json={
"query": (
" query SubredditQuery( $url: String! $filter: SubredditPostFilter"
" $iterator: String ) { getSubreddit(url: $url) { children("
f" limit: {quantity} iterator: $iterator filter: $filter"
" disabledHosts: null ) { iterator items {url subredditTitle"
" isNsfw mediaSources { url } } } } } "
),
"variables": {"url": subreddit, "filter": None, "hostsDown": None},
"authorization": None,
},
)
).json()
return [ans["data"][0]["urls"]["original"]]
@loader.tds
class NSFWArtMod(loader.Module):
"""Sends cute anime nsfw-art"""
strings = {
"name": "NSFWArt",
"sreddit404": "🚫 <b>Subreddit not found</b>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"tags",
"drool",
lambda: "tag: masturbation, drool, completely, sleeping, yuri",
)
)
@loader.command(
ru_doc="Отправьте симпатичный nsfw-арт",
en_doc="Send cute nsfw-art",
)
async def nsfwartcmd(self, message):
tags = self.config["tags"]
subreddit = f"/v2?tag={tags}"
ans = await utils.run_sync(
requests.get, f"https://api.lolicon.app/setu{subreddit}"
)
if ans.status_code != 200:
await utils.answer(message, self.strings("sreddit404", message))
return
await self.inline.gallery(
message=message,
next_handler=functools.partial(
photos, tags, subreddit=subreddit, quantity=15
),
caption=f"<i>{utils.ascii_face()}</i>",
)

View File

@@ -0,0 +1,95 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: NumbersAPI
# Description: Many interesting facts about numbers.
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: NumbersAPI
# scope: NumbersAPI 0.0.1
# ---------------------------------------------------------------------------------
import aiohttp
from datetime import datetime
from .. import loader, utils
async def get_fact_about_number(number, fact_type):
url = f"http://numbersapi.com/{number}/{fact_type}"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
return await response.text()
else:
return "Извините, не удалось получить факт."
async def get_fact_about_date(month, day):
date_str = datetime.now().replace(month=month, day=day).strftime("%m/%d")
url = f"http://numbersapi.com/{date_str}/date"
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
return await response.text()
else:
return "Извините, не удалось получить факт."
@loader.tds
class NumbersAPI(loader.Module):
"""Many interesting facts about numbers."""
strings = {"name": "NumbersAPI"}
@loader.command(
ru_doc="Дает интересный факт про число или дату\nНапример: .num 10 math или .num 01.01 date",
en_doc="Gives an interesting fact about a number or date\nexample: .num 10 math or .num 01.01 date",
)
async def num(self, message):
args = utils.get_args_raw(message).split()
if len(args) < 2:
await utils.answer(message, "Использование: .num <число или дата> <тип>")
return
num_or_date = args[0]
fact_type = args[1]
if "." in num_or_date:
try:
month, day = map(int, num_or_date.split("."))
result = await get_fact_about_date(month, day)
except ValueError:
await utils.answer(
message, "Ошибка: некорректный формат даты. Используйте: месяц.день"
)
return
else:
try:
number = int(num_or_date)
result = await get_fact_about_number(number, fact_type)
except ValueError:
await utils.answer(message, "Ошибка: некорректный ввод числа.")
return
await utils.answer(message, result)

View File

@@ -0,0 +1,97 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Profile
# Description: This module can change your Telegram profile
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Profile
# scope: Profile 0.0.1
# ---------------------------------------------------------------------------------
from telethon.errors.rpcerrorlist import UsernameOccupiedError
from telethon.tl.functions.account import UpdateProfileRequest, UpdateUsernameRequest
from .. import loader, utils
@loader.tds
class ProfileEditorMod(loader.Module):
"""This module can change your Telegram profile."""
strings = {
"name": "Profile",
"error_format": "Incorrect format of args. Try again.",
"done_name": "The new name was successfully unstalled!",
"done_bio": "The new bio was successfully unstaled!",
"done_username": "The new username was succesfully installed!",
"error_occupied": "The new username is already occupied!",
}
strings_ru = {
"error_format": "Неправильный формат аргумента. Попробуйте еще раз.",
"done_name": "Новое имя успешно настроено!",
"done_bio": "Новое био успешно настроено!",
"done_username": "Новое имя пользователя успешно установлено!",
"error_occupied": "Новое имя пользователя уже занято!",
}
@loader.command(
ru_doc="для того, чтобы сменить свое имя/отчество",
en_doc="for change your first/second name",
)
async def namecmd(self, message):
args = utils.get_args_raw(message).split("/")
if len(args) < 1 or len(args) > 2:
return await utils.answer(message, self.strings("error_format"))
firstname = args[0]
lastname = args[1] if len(args) == 2 else ""
await message.client(
UpdateProfileRequest(first_name=firstname, last_name=lastname)
)
await utils.answer(message, self.strings("done_name"))
@loader.command(
ru_doc="чтобы изменить свою биографию",
en_doc="for change your bio",
)
async def aboutcmd(self, message):
args = utils.get_args_raw(message)
if not args:
return await utils.answer(message, self.strings("error_format"))
await message.client(UpdateProfileRequest(about=args))
await utils.answer(message, self.strings("done_bio"))
@loader.command(
ru_doc="для изменения вашего имени пользователя. Введите значение без '@'",
en_doc="for change your username. Enter value without '@'",
)
async def usercmd(self, message):
"""- for change your username. Enter value without "@"."""
args = utils.get_args_raw(message)
if not args:
return await utils.answer(message, self.strings("error_format"))
try:
await message.client(UpdateUsernameRequest(args))
await utils.answer(message, self.strings("done_username"))
except UsernameOccupiedError:
await utils.answer(message, self.strings("error_occupied"))

View File

@@ -0,0 +1,206 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Search
# Description: Search for your question on the Internet
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Api Search
# scope: Api Search 0.0.1
# ---------------------------------------------------------------------------------
from .. import loader, utils
@loader.tds
class Search(loader.Module):
"""Поисковик"""
strings = {
"name": "Search",
"search": "<emoji document_id=5188311512791393083>🌎</emoji><b> I searched for information for you</b>",
"isearch": "🔎<b> I searched for information for you</b> ",
"link": "🗂️ Link to your request",
"close": "❌ Close",
}
strings_ru = {
"search": "<emoji document_id=5188311512791393083>🌎</emoji><b> Я поискал информацию за тебя</b>",
"isearch": "🔎<b> Я поискал информацию за тебя</b> ",
"link": "🗂️ Ссылка на ваш запрос",
"close": "❌ Закрыть",
}
@loader.command(
ru_doc="Поискать в Google",
en_doc="Search on Google",
)
async def google(self, message):
await self.search_engine(message, "https://google.com/search?q=")
@loader.command(
ru_doc="Поискать в Yandex",
en_doc="Search on Yandex",
)
async def yandex(self, message):
await self.search_engine(message, "https://yandex.ru/?q=")
@loader.command(
ru_doc="Поискать в Duckduckgo",
en_doc="Search on Duckduckgo",
)
async def duckduckgo(self, message):
await self.search_engine(message, "https://duckduckgo.com/?q=")
@loader.command(
ru_doc="Поискать в Bing",
en_doc="Search on Bing",
)
async def bing(self, message):
await self.search_engine(message, "https://bing.com/?q=")
@loader.command(
ru_doc="Поискать в You",
en_doc="Search on You",
)
async def you(self, message):
await self.search_engine(message, "https://you.com/?q=")
async def search_engine(self, message, base_url: str) -> None:
"""Searches on a given search engine."""
query = utils.get_args_raw(message)
search_url = f"{base_url}{query}"
await utils.answer(
message, self.strings("search") + f": <a href={search_url}>link</a>"
)
@loader.command(
ru_doc="Поискать в Google инлайн",
en_doc="Search on Google inline",
)
async def igoogle(self, message):
g = utils.get_args_raw(message)
google = f"https://google.com/search?q={g}"
await self.inline.form(
text=self.strings("isearch"),
message=message,
reply_markup=[
[
{
"text": self.strings("link"),
"url": google,
}
],
[{"text": self.strings("close"), "action": "close"}],
],
silent=True,
)
@loader.command(
ru_doc="Поискать в Yandex инлайн",
en_doc="Search on Yandex inline",
)
async def iyandex(self, message):
y = utils.get_args_raw(message)
yandex = f"https://yandex.ru/?q={y}"
await self.inline.form(
text=self.strings("isearch"),
message=message,
reply_markup=[
[
{
"text": self.strings("link"),
"url": yandex,
}
],
[{"text": self.strings("close"), "action": "close"}],
],
silent=True,
)
@loader.command(
ru_doc="Поискать в Duckduckgo инлайн",
en_doc="Search on Duckduckgo inline",
)
async def iduckduckgo(self, message):
d = utils.get_args_raw(message)
duckduckgo = f"https://duckduckgo.com/?q={d}"
await self.inline.form(
text=self.strings("isearch"),
message=message,
reply_markup=[
[
{
"text": self.strings("link"),
"url": duckduckgo,
}
],
[{"text": self.strings("close"), "action": "close"}],
],
silent=True,
)
@loader.command(
ru_doc="Поискать в Bing инлайн",
en_doc="Search on Bing inline",
)
async def ibing(self, message):
b = utils.get_args_raw(message)
bing = f"https://bing.com/?q={b}"
await self.inline.form(
text=self.strings("isearch"),
message=message,
reply_markup=[
[
{
"text": self.strings("link"),
"url": bing,
}
],
[{"text": self.strings("close"), "action": "close"}],
],
silent=True,
)
@loader.command(
ru_doc="Поискать в You инлайн",
en_doc="Search on You inline",
)
async def iyou(self, message):
y = utils.get_args_raw(message)
you = f"https://you.com/?q={y}"
await self.inline.form(
text=self.strings("isearch"),
message=message,
reply_markup=[
[
{
"text": self.strings("link"),
"url": you,
}
],
[{"text": self.strings("close"), "action": "close"}],
],
silent=True,
)
async def close(self, call):
"""Callback button"""
await call.delete()

View File

@@ -0,0 +1,90 @@
# Proprietary License Agreement
# Copyright (c) 2024-29 CodWiz
# Permission is hereby granted to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal and non-commercial purposes, subject to the following conditions:
# 1. The Software may not be modified, altered, or otherwise changed in any way without the explicit written permission of the author.
# 2. Redistribution of the Software, in original or modified form, is strictly prohibited without the explicit written permission of the author.
# 3. 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 non-infringement. In no event shall the author or copyright holder 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.
# 4. Any use of the Software must include the above copyright notice and this permission notice in all copies or substantial portions of the Software.
# 5. By using the Software, you agree to be bound by the terms and conditions of this license.
# For any inquiries or requests for permissions, please contact codwiz@yandex.ru.
# ---------------------------------------------------------------------------------
# Name: Shortener
# Description: shortening the link
# Author: @hikka_mods
# ---------------------------------------------------------------------------------
# meta developer: @hikka_mods
# scope: Shortener
# scope: Shortener 0.0.1
# requires: pyshorteners
# ---------------------------------------------------------------------------------
import pyshorteners
from .. import loader, utils
@loader.tds
class Shortener(loader.Module):
"""Module for working with the api bit.ly"""
strings = {
"name": "Shortener",
"no_api": "<emoji document_id=5854929766146118183>❌</emoji> You have not specified an API token from the site <a href='https://app.bitly.com/settings/api/'>bit.ly</a>",
"statclcmd": "<emoji document_id=5787384838411522455>📊</emoji> <b>Statistics on clicks for this link:</b> {c}",
"shortencmd": "<emoji document_id=5854762571659218443>✅</emoji> <b>Your shortened link is ready:</b> <code>{c}</code>",
}
strings_ru = {
"no_api": "<emoji document_id=5854929766146118183>❌</emoji> Вы не указали api токен с сайта <a href='https://app.bitly.com/settings/api/'>bit.ly</a>",
"statclcmd": "<emoji document_id=5787384838411522455>📊</emoji> <b>Статистика о переходе по этой ссылке:</b> {c}",
"shortencmd": "<emoji document_id=5854762571659218443>✅</emoji> <b>Ваша сокращённая ссылка готова:</b> <code>{c}</code>",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"token",
None,
lambda: "Need a token with https://app.bitly.com/settings/api/",
validator=loader.validators.Hidden(),
)
)
@loader.command(
ru_doc="Сократить ссылку через bit.ly",
en_doc="Shorten the link via bit.ly",
)
async def shortencmd(self, message):
if self.config["token"] is None:
await utils.answer(message, self.strings("no_api"))
return
s = pyshorteners.Shortener(api_key=self.config["token"])
args = utils.get_args_raw(message)
await utils.answer(
message, self.strings("shortencmd").format(c=s.bitly.short(args))
)
@loader.command(
ru_doc="Посмотреть статистику ссылки через bit.ly",
en_doc="View link statistics via bit.ly",
)
async def statclcmd(self, message):
if self.config["token"] is None:
await utils.answer(message, self.strings("no_api"))
return
s = pyshorteners.Shortener(api_key=self.config["token"])
args = utils.get_args_raw(message)
await utils.answer(
message, self.strings("statclcmd").format(c=s.bitly.total_clicks(args))
)

File diff suppressed because it is too large Load Diff

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

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

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

View File

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

View File

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

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

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

View File

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

View File

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

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

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

View File

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

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

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