mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-16 14:34:17 +02:00
Merge pull request #112 from MuRuLOSE/update-submodules_2e2ebd512fc870a3002dde752d479dee9645cf86
Update of repositories 2025-11-30 12:54:04
This commit is contained in:
59
archquise/H.Modules/.github/workflows/genpage.yml
vendored
Normal file
59
archquise/H.Modules/.github/workflows/genpage.yml
vendored
Normal 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
6
archquise/H.Modules/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
full.py
|
||||
autocleaner.py
|
||||
silent.py
|
||||
|
||||
# Ruff Format
|
||||
.ruff_cache/
|
||||
133
archquise/H.Modules/ASCIIArt.py
Normal file
133
archquise/H.Modules/ASCIIArt.py
Normal 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)
|
||||
91
archquise/H.Modules/AccountData.py
Normal file
91
archquise/H.Modules/AccountData.py
Normal 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"))
|
||||
187
archquise/H.Modules/AniLibria.py
Normal file
187
archquise/H.Modules/AniLibria.py
Normal 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 <3</b>:", # < == <
|
||||
"season": "<b>Season</b>:",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"announce": "<b>Анонс</b>:",
|
||||
"status": "<b>Статус</b>:",
|
||||
"type": "<b>Тип</b>:",
|
||||
"genres": "<b>Жанры</b>:",
|
||||
"favorite": "<b>Избранное <3</b>:", # < == <
|
||||
"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,
|
||||
)
|
||||
81
archquise/H.Modules/AnimeQuotes.py
Normal file
81
archquise/H.Modules/AnimeQuotes.py
Normal 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"])
|
||||
73
archquise/H.Modules/Article.py
Normal file
73
archquise/H.Modules/Article.py
Normal 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 {}
|
||||
204
archquise/H.Modules/AutofarmCookies.py
Normal file
204
archquise/H.Modules/AutofarmCookies.py
Normal 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)
|
||||
242
archquise/H.Modules/BirthdayTime.py
Normal file
242
archquise/H.Modules/BirthdayTime.py
Normal 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"))
|
||||
1
archquise/H.Modules/CNAME
Normal file
1
archquise/H.Modules/CNAME
Normal file
@@ -0,0 +1 @@
|
||||
mods.archquise.ru
|
||||
54
archquise/H.Modules/CheckSpamBan.py
Normal file
54
archquise/H.Modules/CheckSpamBan.py
Normal 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()
|
||||
107
archquise/H.Modules/CryptoCurrency.py
Normal file
107
archquise/H.Modules/CryptoCurrency.py
Normal 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"
|
||||
)
|
||||
83
archquise/H.Modules/EnvsSH.py
Normal file
83
archquise/H.Modules/EnvsSH.py
Normal 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)
|
||||
)
|
||||
88
archquise/H.Modules/FakeActions.py
Normal file
88
archquise/H.Modules/FakeActions.py
Normal 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)
|
||||
176
archquise/H.Modules/FakeWallet.py
Normal file
176
archquise/H.Modules/FakeWallet.py
Normal 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"))
|
||||
137
archquise/H.Modules/FolderAutoRead.py
Normal file
137
archquise/H.Modules/FolderAutoRead.py
Normal 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
|
||||
))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
121
archquise/H.Modules/GigaChat.py
Normal file
121
archquise/H.Modules/GigaChat.py
Normal 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
44
archquise/H.Modules/H.py
Normal 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
307
archquise/H.Modules/HAFK.py
Normal 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}")
|
||||
338
archquise/H.Modules/HModsLibrary.py
Normal file
338
archquise/H.Modules/HModsLibrary.py
Normal 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}
|
||||
104
archquise/H.Modules/InfoBannersManager.py
Normal file
104
archquise/H.Modules/InfoBannersManager.py
Normal 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}")
|
||||
77
archquise/H.Modules/InlineButton.py
Normal file
77
archquise/H.Modules/InlineButton.py
Normal 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"),
|
||||
}
|
||||
74
archquise/H.Modules/InlineCoin.py
Normal file
74
archquise/H.Modules/InlineCoin.py
Normal 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",
|
||||
}
|
||||
213
archquise/H.Modules/InlineHelper.py
Normal file
213
archquise/H.Modules/InlineHelper.py
Normal 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",
|
||||
},
|
||||
],
|
||||
}
|
||||
78
archquise/H.Modules/IrisSimpleMod.py
Normal file
78
archquise/H.Modules/IrisSimpleMod.py
Normal 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()
|
||||
100
archquise/H.Modules/KBSwapper.py
Normal file
100
archquise/H.Modules/KBSwapper.py
Normal 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"))
|
||||
17
archquise/H.Modules/LICENSE
Normal file
17
archquise/H.Modules/LICENSE
Normal 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.
|
||||
109
archquise/H.Modules/Memes.py
Normal file
109
archquise/H.Modules/Memes.py
Normal 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()
|
||||
277
archquise/H.Modules/MessageMonitor.py
Normal file
277
archquise/H.Modules/MessageMonitor.py
Normal 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}")
|
||||
1815
archquise/H.Modules/MooFarmRC1.py
Normal file
1815
archquise/H.Modules/MooFarmRC1.py
Normal file
File diff suppressed because it is too large
Load Diff
202
archquise/H.Modules/Music.py
Normal file
202
archquise/H.Modules/Music.py
Normal 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))
|
||||
94
archquise/H.Modules/PastebinAPI.py
Normal file
94
archquise/H.Modules/PastebinAPI.py
Normal 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)
|
||||
)
|
||||
91
archquise/H.Modules/README.md
Normal file
91
archquise/H.Modules/README.md
Normal 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>
|
||||
|
||||
78
archquise/H.Modules/ReplaceVowels.py
Normal file
78
archquise/H.Modules/ReplaceVowels.py
Normal 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)
|
||||
149
archquise/H.Modules/SMAcrhiver.py
Normal file
149
archquise/H.Modules/SMAcrhiver.py
Normal 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)
|
||||
103
archquise/H.Modules/SafetyMod.py
Normal file
103
archquise/H.Modules/SafetyMod.py
Normal 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))
|
||||
350
archquise/H.Modules/TaskManager.py
Normal file
350
archquise/H.Modules/TaskManager.py
Normal 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"))
|
||||
133
archquise/H.Modules/TelegramStatusCodes.py
Normal file
133
archquise/H.Modules/TelegramStatusCodes.py
Normal 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()]
|
||||
),
|
||||
)
|
||||
159
archquise/H.Modules/TempChat.py
Normal file
159
archquise/H.Modules/TempChat.py
Normal 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.")
|
||||
75
archquise/H.Modules/Text2File.py
Normal file
75
archquise/H.Modules/Text2File.py
Normal 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),
|
||||
)
|
||||
108
archquise/H.Modules/Text_Sticker.py
Normal file
108
archquise/H.Modules/Text_Sticker.py
Normal 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)
|
||||
327
archquise/H.Modules/TikTokDownloader.py
Normal file
327
archquise/H.Modules/TikTokDownloader.py
Normal 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()
|
||||
964
archquise/H.Modules/UserbotAvast.py
Normal file
964
archquise/H.Modules/UserbotAvast.py
Normal 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
|
||||
105
archquise/H.Modules/Video2GIF.py
Normal file
105
archquise/H.Modules/Video2GIF.py
Normal 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)
|
||||
161
archquise/H.Modules/VirusTotal.py
Normal file
161
archquise/H.Modules/VirusTotal.py
Normal 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)}"
|
||||
)
|
||||
125
archquise/H.Modules/VoiceDL.py
Normal file
125
archquise/H.Modules/VoiceDL.py
Normal 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,
|
||||
)
|
||||
333
archquise/H.Modules/Weather.py
Normal file
333
archquise/H.Modules/Weather.py
Normal 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))
|
||||
136
archquise/H.Modules/WindowsKeys.py
Normal file
136
archquise/H.Modules/WindowsKeys.py
Normal 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)
|
||||
1
archquise/H.Modules/_site/q
Normal file
1
archquise/H.Modules/_site/q
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
6206
archquise/H.Modules/aiogram3/hikarichat.py
Normal file
6206
archquise/H.Modules/aiogram3/hikarichat.py
Normal file
File diff suppressed because it is too large
Load Diff
97
archquise/H.Modules/animals.py
Normal file
97
archquise/H.Modules/animals.py
Normal 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
|
||||
)
|
||||
78
archquise/H.Modules/face.py
Normal file
78
archquise/H.Modules/face.py
Normal 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"))
|
||||
47
archquise/H.Modules/full.txt
Normal file
47
archquise/H.Modules/full.txt
Normal 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
|
||||
681
archquise/H.Modules/globalrestrict.py
Normal file
681
archquise/H.Modules/globalrestrict.py
Normal 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
|
||||
),
|
||||
),
|
||||
)
|
||||
314
archquise/H.Modules/hikkahost.py
Normal file
314
archquise/H.Modules/hikkahost.py
Normal 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)
|
||||
68
archquise/H.Modules/index.html
Normal file
68
archquise/H.Modules/index.html
Normal 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>
|
||||
112
archquise/H.Modules/jacques.py
Normal file
112
archquise/H.Modules/jacques.py
Normal 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()
|
||||
160
archquise/H.Modules/novoice.py
Normal file
160
archquise/H.Modules/novoice.py
Normal 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,
|
||||
)
|
||||
97
archquise/H.Modules/nsfwart.py
Normal file
97
archquise/H.Modules/nsfwart.py
Normal 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>",
|
||||
)
|
||||
95
archquise/H.Modules/numbersapi.py
Normal file
95
archquise/H.Modules/numbersapi.py
Normal 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)
|
||||
97
archquise/H.Modules/profile.py
Normal file
97
archquise/H.Modules/profile.py
Normal 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"))
|
||||
206
archquise/H.Modules/search.py
Normal file
206
archquise/H.Modules/search.py
Normal 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()
|
||||
90
archquise/H.Modules/shortener.py
Normal file
90
archquise/H.Modules/shortener.py
Normal 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))
|
||||
)
|
||||
3085
modules.json
3085
modules.json
File diff suppressed because it is too large
Load Diff
176
yummy1gay/limoka/SwitchToTelethon.py
Normal file
176
yummy1gay/limoka/SwitchToTelethon.py
Normal file
@@ -0,0 +1,176 @@
|
||||
__version__ = (1, 0, 0, 1)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.4
|
||||
|
||||
# thx to @codrago for the inspiration
|
||||
# this module may lead to unstable operation of the userbot
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import os
|
||||
import aiofiles
|
||||
import aiohttp
|
||||
from telethon.extensions.html import parse
|
||||
import inspect
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
@loader.tds
|
||||
class SwitchToTelethon(loader.Module):
|
||||
"""Auto switch from Hikka-TL to Telethon"""
|
||||
|
||||
strings = {"name": "SwitchToTelethon"}
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client = client
|
||||
# step1
|
||||
if self.get("switch1"):
|
||||
await self._switch_to_telethon(ignore=False)
|
||||
await self._change_files()
|
||||
else:
|
||||
pass
|
||||
# step2
|
||||
if self.get("switch2"):
|
||||
chat_id = self.get("switch2")
|
||||
await self.client.send_message(
|
||||
chat_id,
|
||||
f"<emoji document_id=5996898247963055513>🖤</emoji> <b>Switch to Telethon is completed successfully! (this module has been automatically unloaded)</b>\n\n"
|
||||
f"<emoji document_id=5325547803936572038>✨</emoji> <i>To restore everything to its original state, use the command</i> <code>{self.get_prefix()}terminal git checkout -- .</code><i>, and restart</i>"
|
||||
)
|
||||
self.set("switch2", None)
|
||||
await self.invoke('unloadmod', 'SwitchToTelethon', self.inline.bot_id)
|
||||
|
||||
@loader.command()
|
||||
async def switch(self, message):
|
||||
"""Automatically switch to Telethon"""
|
||||
|
||||
chat_id = utils.get_chat_id(message)
|
||||
await utils.answer(message, "<emoji document_id=5461117441612462242>🙂</emoji> <i>Starting the first stage of switching to Telethon...</i>")
|
||||
|
||||
await self._switch_to_telethon(ignore=True)
|
||||
|
||||
self.set("switch1", chat_id)
|
||||
await utils.answer(message, "<emoji document_id=5456140674028019486>⚡️</emoji> <b>First stage completed. Restarting...</b>")
|
||||
await self._restart()
|
||||
|
||||
async def _restart(self):
|
||||
await self.invoke('restart', '-f', self.inline.bot_id)
|
||||
|
||||
async def _switch_to_telethon(self, ignore):
|
||||
for root, _, files in os.walk("."):
|
||||
for file in files:
|
||||
# conflict bypass
|
||||
if 'SwitchToTelethon' in file:
|
||||
continue
|
||||
|
||||
if file.endswith('.py') or file.endswith('.yml'):
|
||||
await self._process_file(os.path.join(root, file), ignore)
|
||||
|
||||
async def _process_file(self, file_path, ignore):
|
||||
try:
|
||||
async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
|
||||
original = await f.readlines()
|
||||
|
||||
updated = False
|
||||
switched = []
|
||||
|
||||
for line in original:
|
||||
# conflict bypass x2
|
||||
if ignore and "CUSTOM_EMOJIS" in line:
|
||||
switched.append(line)
|
||||
continue
|
||||
# conflict bypass x3
|
||||
if file_path.endswith('main.py') and ignore and line.strip() == "import hikkatl":
|
||||
switched.append(line)
|
||||
continue
|
||||
# change references from "hikkatl" to "telethon" for switch
|
||||
if file_path.endswith('.py'):
|
||||
if "hikkatl" in line:
|
||||
line = line.replace("hikkatl", "telethon")
|
||||
updated = True
|
||||
# conflict bypass x4
|
||||
if "(2, 0, 8)" in line:
|
||||
line = line.replace("(2, 0, 8)", "(1, 35, 0)")
|
||||
updated = True
|
||||
# change references from "Hikka-TL-New" to "telethon" for switch
|
||||
if "Hikka-TL-New" in line:
|
||||
line = line.replace("Hikka-TL-New", "telethon")
|
||||
updated = True
|
||||
# for HikkaInfo
|
||||
elif file_path.endswith('.yml'):
|
||||
if '<emoji document_id=5377437404078546699>💜</emoji> <b>Hikka-TL:</b>' in line:
|
||||
line = line.replace(
|
||||
"<emoji document_id=5377437404078546699>💜</emoji> <b>Hikka-TL:</b>",
|
||||
"<emoji document_id=5204453279790033300>❤️🔥</emoji> <b><a href='https://github.com/LonamiWebs/Telethon.git'>Telethon</a></b>:"
|
||||
)
|
||||
updated = True
|
||||
|
||||
switched.append(line)
|
||||
|
||||
if updated:
|
||||
async with aiofiles.open(file_path, 'w', encoding='utf-8') as f:
|
||||
await f.writelines(switched)
|
||||
print(f"Switched to Telethon: {file_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {file_path}: {e}")
|
||||
|
||||
async def _change_files(self):
|
||||
chat_id = self.get("switch1")
|
||||
if not chat_id:
|
||||
print("No chat ID found for logging.")
|
||||
return
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
# update html.py
|
||||
async with session.get("https://raw.githubusercontent.com/yummy1gay/hikariatama-libs/main/html.py") as r_html:
|
||||
if r_html.status == 200:
|
||||
html_code = await r_html.text()
|
||||
|
||||
async with aiofiles.open(inspect.getfile(parse), 'w', encoding='utf-8') as f_html:
|
||||
await f_html.write(html_code)
|
||||
|
||||
html_message = f"👍 <i>Updated Telethon parser file (for sending spoilers, custom emojis, etc.):</i> <code>{inspect.getfile(parse)}</code>"
|
||||
else:
|
||||
html_message = f"⚠️ <b>Failed to update HTML parse file:</b> <code>{r_html.status}</code> (please, use <code>{self.get_prefix()}terminal git checkout -- .</code>, and restart)"
|
||||
|
||||
# update translate.py
|
||||
async with session.get("https://raw.githubusercontent.com/yummy1gay/hikariatama-libs/main/translate.py") as r_translate:
|
||||
if r_translate.status == 200:
|
||||
translate_code = await r_translate.text()
|
||||
|
||||
async with aiofiles.open("./hikka/modules/translate.py", 'w', encoding='utf-8') as f_translate:
|
||||
await f_translate.write(translate_code)
|
||||
|
||||
translate_message = f"👍 <i>Updated translation module:</i> <code>./hikka/modules/translate.py</code> <i>(for compatibility)</i>"
|
||||
else:
|
||||
translate_message = f"⚠️ <b>Failed to update translation module:</b> <code>{r_translate.status}</code> (please, use <code>{self.get_prefix()}terminal git checkout -- .</code>, and restart)"
|
||||
|
||||
await self.client.send_message(
|
||||
chat_id,
|
||||
f"{html_message}\n{translate_message}\n\n⏳ <b>Restarting...</b>"
|
||||
)
|
||||
|
||||
self.set("switch1", None)
|
||||
self.set("switch2", chat_id)
|
||||
await self._restart()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error updating files: {e}")
|
||||
if chat_id:
|
||||
await self.client.send_message(
|
||||
chat_id, f"⚠️ <b>Error updating files:</b> <code>{e}</code> (please, use <code>{self.get_prefix()}terminal git checkout -- .</code>, and restart)"
|
||||
)
|
||||
|
||||
# окей
|
||||
15
yummy1gay/limoka/full.txt
Normal file
15
yummy1gay/limoka/full.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
SwitchToTelethon
|
||||
yg_balance
|
||||
yg_chatid
|
||||
yg_checks
|
||||
yg_gamee
|
||||
yg_gemini
|
||||
yg_ocr
|
||||
yg_owner
|
||||
yg_playlist
|
||||
yg_quotes
|
||||
yg_stars
|
||||
yg_tgs
|
||||
yg_trigger
|
||||
yg_vm
|
||||
yg_prem
|
||||
172
yummy1gay/limoka/yg_balance.py
Normal file
172
yummy1gay/limoka/yg_balance.py
Normal file
@@ -0,0 +1,172 @@
|
||||
__version__ = (1, 5)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import re
|
||||
import cloudscraper
|
||||
import urllib.parse, json
|
||||
from telethon import TelegramClient
|
||||
from telethon.tl.functions.messages import RequestAppWebViewRequest
|
||||
from telethon.tl.types import InputBotAppShortName
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
@loader.tds
|
||||
class yg_balance(loader.Module):
|
||||
"""Модуль для просмотра балансов в @CryptoBot и @wallet"""
|
||||
|
||||
strings = {
|
||||
"name": "yg_balance",
|
||||
"cryptobot": (
|
||||
"<emoji document_id=5217705010539812022>☺️</emoji> <b>My balance in @CryptoBot:</b>\n\n{}"
|
||||
),
|
||||
"wallet": (
|
||||
"<emoji document_id=5438394062434485433>💎</emoji> <b>My balance in @wallet:</b>\n\n{}"
|
||||
),
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"name": "yg_balance",
|
||||
"cryptobot": (
|
||||
"<emoji document_id=5217705010539812022>☺️</emoji> <b>Мой баланс в @CryptoBot:</b>\n\n{}"
|
||||
),
|
||||
"wallet": (
|
||||
"<emoji document_id=5438394062434485433>💎</emoji> <b>Мой баланс в @wallet:</b>\n\n{}"
|
||||
),
|
||||
}
|
||||
|
||||
strings_ua = {
|
||||
"name": "yg_balance",
|
||||
"cryptobot": (
|
||||
"<emoji document_id=5217705010539812022>☺️</emoji> <b>Мій баланс у @CryptoBot:</b>\n\n{}"
|
||||
),
|
||||
"wallet": (
|
||||
"<emoji document_id=5438394062434485433>💎</emoji> <b>Мій баланс у @wallet:</b>\n\n{}"
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"hide_0_balances",
|
||||
True,
|
||||
"hiding zero balances",
|
||||
validator=loader.validators.Boolean()
|
||||
)
|
||||
)
|
||||
|
||||
async def client_ready(self, client: TelegramClient, db):
|
||||
self.client = client
|
||||
self.scraper = cloudscraper.create_scraper()
|
||||
|
||||
async def check(self, bot, message):
|
||||
async with message.client.conversation(bot) as conv:
|
||||
request = await conv.send_message("/wallet")
|
||||
answer = await conv.get_response()
|
||||
|
||||
await request.delete()
|
||||
await answer.delete()
|
||||
|
||||
return answer.text
|
||||
|
||||
def cryptobot(self, text):
|
||||
hide_zeros = self.config["hide_0_balances"]
|
||||
|
||||
lines = [line.strip() for line in text.split('\n') if line.strip() and 'Кошелёк' not in line]
|
||||
total_line = next((line for line in lines if line.startswith('≈')), '')
|
||||
balance_lines = [line for line in lines if not line.startswith('≈')]
|
||||
|
||||
if hide_zeros:
|
||||
balance_lines = [line for line in balance_lines if not re.search(r':\s?0(?:\.0+)?\s\w+', line)]
|
||||
|
||||
return '\n'.join(balance_lines) + (f"\n\n{total_line}" if total_line else '')
|
||||
|
||||
async def auth(self):
|
||||
bot = await self.client.get_input_entity(1985737506)
|
||||
app = InputBotAppShortName(bot_id=bot, short_name="start")
|
||||
|
||||
web_view = await self.client(RequestAppWebViewRequest(
|
||||
peer='me',
|
||||
app=app,
|
||||
platform='android'
|
||||
))
|
||||
|
||||
return web_view.url
|
||||
|
||||
async def login(self):
|
||||
url = await self.auth()
|
||||
fragment = urllib.parse.unquote(urllib.parse.unquote(urllib.parse.urlparse(url).fragment[13:]))
|
||||
params = dict(urllib.parse.parse_qsl(fragment))
|
||||
params["user"] = json.loads(params.get("user", "{}"))
|
||||
|
||||
data = {
|
||||
**params,
|
||||
"web_view_init_data_raw": urllib.parse.unquote(url.split('tgWebAppData=')[1].split('&tgWebAppVersion')[0])
|
||||
}
|
||||
|
||||
resp = self.scraper.post(
|
||||
f"https://walletbot.me/api/v1/users/auth/",
|
||||
json=data
|
||||
)
|
||||
|
||||
return resp.json().get('value', {})
|
||||
|
||||
async def get_balances(self):
|
||||
resp = self.scraper.get(
|
||||
"https://walletbot.me/api/v1/accounts/",
|
||||
headers={
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
"Accept-Language": "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7",
|
||||
"Authorization": await self.login(),
|
||||
},
|
||||
)
|
||||
|
||||
hide_zeros = self.config["hide_0_balances"]
|
||||
balances = [
|
||||
f"<b>{account['currency']}:</b> {account['available_balance']} ({account['available_balance_usd_amount']}$)"
|
||||
for account in resp.json().get("accounts", [])
|
||||
if not (hide_zeros and account['available_balance'] == 0 and account['available_balance_usd_amount'] == 0.0)
|
||||
]
|
||||
|
||||
return balances
|
||||
|
||||
@loader.command(ru_doc="проверить баланс в @CryptoBot",
|
||||
ua_doc="перевірити баланс у @CryptoBot")
|
||||
async def bc(self, message):
|
||||
"""check balance in @CryptoBot"""
|
||||
text = await self.check(1559501630, message)
|
||||
balance = self.cryptobot(text)
|
||||
await utils.answer(message, self.strings["cryptobot"].format(balance))
|
||||
|
||||
@loader.command(ru_doc="проверить баланс в @wallet",
|
||||
ua_doc="перевірити баланс у @wallet")
|
||||
async def bw(self, message):
|
||||
"""check balance in @wallet"""
|
||||
balances = await self.get_balances()
|
||||
balance = "\n".join(balances) if balances else "-"
|
||||
await utils.answer(message, self.strings["wallet"].format(balance))
|
||||
|
||||
@loader.command(ru_doc="вкл/выкл скрытие нулевых балансов",
|
||||
ua_doc="увімк/вимк приховування нульових балансів")
|
||||
async def hide0(self, message):
|
||||
"on/off hiding zero balances"
|
||||
self.config["hide_0_balances"] = not self.config["hide_0_balances"]
|
||||
|
||||
status = 'включено' if self.config["hide_0_balances"] else 'выключено'
|
||||
await utils.answer(
|
||||
message,
|
||||
f"<emoji document_id=5278611606756942667>❤️</emoji> <b>Скрытие нулевых балансов {status}</b>"
|
||||
)
|
||||
59
yummy1gay/limoka/yg_chatid.py
Normal file
59
yummy1gay/limoka/yg_chatid.py
Normal file
@@ -0,0 +1,59 @@
|
||||
__version__ = (1, 4, 8, 8)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
from telethon.tl.types import User, Chat, Channel
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
@loader.tds
|
||||
class yg_chatid(loader.Module):
|
||||
"""Модуль для отображения ID чатов, каналов или пользователей"""
|
||||
|
||||
strings = {
|
||||
"name": "yg_chatid",
|
||||
"chat_id": "<b>Chat ID:</b> <code>{}</code>",
|
||||
"user_id": "<b>User ID:</b> <code>{}</code>",
|
||||
"not_found": "<b>Not Found</b>"
|
||||
}
|
||||
|
||||
async def chatidcmd(self, message):
|
||||
"""<reply>/@username - узнать ID юзера (на сообщение которого вы ответили), указанного юзера или текущего чата"""
|
||||
args = utils.get_args_raw(message)
|
||||
|
||||
if message.is_reply:
|
||||
reply_msg = await message.get_reply_message()
|
||||
entity = await message.client.get_entity(reply_msg.sender_id)
|
||||
await self.who(message, entity)
|
||||
return
|
||||
|
||||
if args:
|
||||
try:
|
||||
entity = await message.client.get_entity(args)
|
||||
await self.who(message, entity)
|
||||
except Exception:
|
||||
await message.edit(self.strings["not_found"])
|
||||
return
|
||||
|
||||
id = message.chat_id
|
||||
await message.edit(self.strings["chat_id"].format(id))
|
||||
|
||||
async def who(self, message, entity):
|
||||
if isinstance(entity, User):
|
||||
await message.edit(self.strings["user_id"].format(entity.id))
|
||||
elif isinstance(entity, (Chat, Channel)):
|
||||
await message.edit(self.strings["chat_id"].format(entity.id))
|
||||
else:
|
||||
await message.edit(self.strings["not_found"])
|
||||
945
yummy1gay/limoka/yg_checks.py
Normal file
945
yummy1gay/limoka/yg_checks.py
Normal file
@@ -0,0 +1,945 @@
|
||||
__version__ = (1, 5, 0, 0)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# requires: google-generativeai urlextract cloudscraper
|
||||
|
||||
# changelog:
|
||||
# - Добавлен подбор паролей с использованием Google Gemini.
|
||||
# - Реализована активация тестнет чеков.
|
||||
# - Добавлена возможность задать задержку перед активацией чека.
|
||||
# - Добавлены переводы.
|
||||
# - Внесены мелкие исправления и оптимизации кода.
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import os
|
||||
import re
|
||||
from telethon import events
|
||||
from collections import defaultdict
|
||||
from telethon.tl.types import MessageEntityUrl, MessageEntityTextUrl, MessageMediaWebPage
|
||||
from telethon.tl.functions.messages import ImportChatInviteRequest, CheckChatInviteRequest
|
||||
from google.generativeai.types import HarmCategory, HarmBlockThreshold
|
||||
from telethon.tl.functions.messages import RequestWebViewRequest
|
||||
from telethon.tl.functions.channels import LeaveChannelRequest
|
||||
from google.generativeai import GenerativeModel, configure
|
||||
from telethon.tl.types import Message
|
||||
from telethon import TelegramClient
|
||||
from urlextract import URLExtract
|
||||
from urllib.parse import unquote
|
||||
import cloudscraper
|
||||
import asyncio
|
||||
import random
|
||||
import json
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
class Passworder: #beto
|
||||
def __init__(self, api_key, model_name):
|
||||
self.api_key = api_key
|
||||
self.model_name = model_name
|
||||
self.model = None
|
||||
self.safety_settings={HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_NONE,
|
||||
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_NONE,
|
||||
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_NONE,
|
||||
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE}
|
||||
|
||||
self.prompt = """
|
||||
Твоя задача – извлекать пароли из текстовых описаний. В некоторых случаях пароль может быть прямо указан в тексте, в других случаях его нужно вычислить или извлечь.
|
||||
|
||||
Учитывай следующие правила:
|
||||
|
||||
1. Явный пароль (с синонимами): Распознаешь и извлекаешь пароль, указанный явно, используя ключевые слова и их синонимы: "пароль", "pass", "password", "код", "code", "ключ", "key", и т.д. Регистр букв подбирай на основе самого описания. Пример: "Password: wkek" -> {"password": "wkek"}, "Код доступа: 1234" -> {"password": "1234"}.
|
||||
|
||||
2. Игнорирование рекламы и ссылок: Если в тексте присутствуют рекламные ссылки (например, "https://t.me/+wI2h84RF80NiZTI6") или другие ненужные ссылки, их нужно игнорировать, и вернуть пустой пароль. Пример: {"password": ""}.
|
||||
|
||||
3. Игнорирование призывов к действиям: Если текст содержит фразы типа "пароль на 60 реакций" или "сделай 10 шагов и получи пароль", игнорируй такие тексты и возвращай пустой пароль. Пример: {"password": ""}.
|
||||
|
||||
5. Математические примеры: Если в тексте содержится математический пример, например, "пароль: 2+6", вам нужно вычислить результат и вернуть его в формате: {"password": "8"}.
|
||||
|
||||
6. Нецензурная лексика: Наличие нецензурной лексики не влияет на процесс. Ты фокусируешься на извлечении пароля, любые оскорбления или непристойные выражения приветствуются.
|
||||
|
||||
7. Загадки и логические задачи: Если текст включает загадки или другие логические задачи, ты должен решить их и вернуть правильный ответ. Например, если описание будет "Пароль: сколько будет 3*4?", ответ будет {"password": "12"}. Так же учитывай момент что описание может состоять только из загадки/примера, это тоже наводка на пароль. Например, если описание будет "висит груша нельзя скушать", ответ будет {"password": "лампочка"}
|
||||
|
||||
Пример: Если описание "Пароль - 3*3", то твой ответ должен быть {"password": "9"}.
|
||||
|
||||
Если в тексте нет явных подсказок или пароля, возвращай пустой пароль: {"password": ""}. Для абзацев используй \\n!
|
||||
|
||||
Пример текста с описанием пароля:
|
||||
"Пароль на 50 реакций в чате."
|
||||
Ответ: {"password": ""}
|
||||
|
||||
Пример текста с математическим примером:
|
||||
"пасс - 12+8"
|
||||
Ответ: {"password": "20"}
|
||||
|
||||
Пример загадки с кодом:
|
||||
"a = [1, 2, 3]
|
||||
b = a
|
||||
b[0] = 4
|
||||
print(a)
|
||||
Что выведет этот код?"
|
||||
|
||||
Ответ: {"password": "[4, 2, 3]"}
|
||||
""" #you can edit this prompt!
|
||||
|
||||
async def generate(self, description: str) -> dict:
|
||||
try:
|
||||
configure(api_key=self.api_key)
|
||||
model_name = self.model_name if self.model_name else "gemini-flash-latest"
|
||||
self.model = GenerativeModel(
|
||||
model_name,
|
||||
system_instruction=self.prompt,
|
||||
safety_settings=self.safety_settings
|
||||
)
|
||||
|
||||
res = await self.model.generate_content_async(description)
|
||||
if res and res.text:
|
||||
try:
|
||||
return json.loads(res.text.strip())
|
||||
except json.JSONDecodeError:
|
||||
return {"error": "Invalid JSON response", "raw": res.text.strip()}
|
||||
|
||||
return {"password": ""}
|
||||
except Exception as e:
|
||||
if "429" in str(e):
|
||||
return {"error": "апи кей сдох"}
|
||||
return {"error": str(e)}
|
||||
|
||||
@loader.tds
|
||||
class yg_checks(loader.Module):
|
||||
"""Активатор чеков @send (@CryptoBot)"""
|
||||
|
||||
strings = {
|
||||
"name": "yg_checks",
|
||||
"language": "en",
|
||||
"activator": "{} <b>Activator {}</b>",
|
||||
"log_sending": "{} <b>Log sending {}</b>",
|
||||
"password_cracking": "{} <b>Password cracking with neural network {}</b>",
|
||||
"private_check_activation": "{} <b>Activation of checks sent in private messages {}</b>",
|
||||
"auto_subscription": "{} <b>Auto-subscription {}</b>",
|
||||
"auto_unsubscription": "{} <b>Auto-unsubscription {}</b>",
|
||||
"testnet": "{} <b>Testnet check activation {}</b>",
|
||||
"logs_username_desc": "@username where logs will be sent",
|
||||
"logs_enabled_desc": "send logs",
|
||||
"delay_desc": "delay in seconds before activating the check",
|
||||
"track_private_desc": "activate checks sent in private messages",
|
||||
"ai_passwords_desc": "password cracking using Gemini AI",
|
||||
"watcher_on_desc": "activator status",
|
||||
"subscribe_desc": "subscribe to channels to activate checks that require it",
|
||||
"unsubscribe_desc": "unsubscribe from channels after activating the check",
|
||||
"no_track_users_desc": "whose checks not to activate (specify the user without @)",
|
||||
"testnet_desc": "activate checks sent using @CryptoTestnetBot",
|
||||
"gemini_api_key_desc": "API key for Gemini AI (aistudio.google.com/apikey)",
|
||||
"gemini_model_name_desc": "model for Gemini AI. examples: gemini-1.5-flash, gemini-1.5-pro, gemini-2.0-flash-exp, gemini-2.0-flash-thinking-exp-1219",
|
||||
"proxy_desc": "proxy in the format http://<user>:<pass>@<proxy>:<port>, or http://<proxy>:<port>",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"name": "yg_checks",
|
||||
"language": "ru",
|
||||
"activator": "{} <b>Активатор {}</b>",
|
||||
"log_sending": "{} <b>Отправка логов {}</b>",
|
||||
"password_cracking": "{} <b>Подбор паролей с помощью нейросети {}</b>",
|
||||
"private_check_activation": "{} <b>Активация чеков отправленных в личке {}</b>",
|
||||
"auto_subscription": "{} <b>Авто-подписка {}</b>",
|
||||
"auto_unsubscription": "{} <b>Авто-отписка {}</b>",
|
||||
"testnet": "{} <b>Активация тестнет чеков {}</b>",
|
||||
"logs_username_desc": "@username куда будут отправляться логи",
|
||||
"logs_enabled_desc": "отправка логов",
|
||||
"delay_desc": "задержка в секундах перед активацией чека",
|
||||
"track_private_desc": "активация чеков отправленных в личке",
|
||||
"ai_passwords_desc": "подбор паролей с помощью Gemini AI",
|
||||
"watcher_on_desc": "состояние активатора",
|
||||
"subscribe_desc": "подписываться ли на каналы чтобы активировать чеки которые этого требуют",
|
||||
"unsubscribe_desc": "отписываться ли от каналов после активации чека",
|
||||
"no_track_users_desc": "чьи чеки не активировать (юзер указывать обязательно без @)",
|
||||
"testnet_desc": "активировать ли чеки отправленные с помощью @CryptoTestnetBot",
|
||||
"gemini_api_key_desc": "API ключ для Gemini AI (aistudio.google.com/apikey)",
|
||||
"gemini_model_name_desc": "модель для Gemini AI. примеры: gemini-1.5-flash, gemini-1.5-pro, gemini-2.0-flash-exp, gemini-2.0-flash-thinking-exp-1219",
|
||||
"proxy_desc": "прокси в формате http://<user>:<pass>@<proxy>:<port>, или http://<proxy>:<port>",
|
||||
}
|
||||
|
||||
strings_ua = {
|
||||
"name": "yg_checks",
|
||||
"language": "ua",
|
||||
"activator": "{} <b>Активатор {}</b>",
|
||||
"log_sending": "{} <b>Відправка логів {}</b>",
|
||||
"password_cracking": "{} <b>Підбір паролей за допомогою нейромережі {}</b>",
|
||||
"private_check_activation": "{} <b>Активація чеків, надісланих в особисті повідомлення {}</b>",
|
||||
"auto_subscription": "{} <b>Авто-підписка {}</b>",
|
||||
"auto_unsubscription": "{} <b>Авто-відписка {}</b>",
|
||||
"testnet": "{} <b>Активація тестет чеків {}</b>",
|
||||
"logs_username_desc": "@username, куди будуть надсилатися логи",
|
||||
"logs_enabled_desc": "надсилання логів",
|
||||
"delay_desc": "затримка в секундах перед активацією чека",
|
||||
"track_private_desc": "активація чеків, надісланих в особисті повідомлення",
|
||||
"ai_passwords_desc": "підбір паролів за допомогою Gemini AI",
|
||||
"watcher_on_desc": "стан активатора",
|
||||
"subscribe_desc": "підписуватися на канали, щоб активувати чеки, які цього потребують",
|
||||
"unsubscribe_desc": "відписуватися від каналів після активації чека",
|
||||
"no_track_users_desc": "чиї чеки не активувати (користувача вказувати обов'язково без @)",
|
||||
"testnet_desc": "активувати чеки, надіслані за допомогою @CryptoTestnetBot",
|
||||
"gemini_api_key_desc": "API ключ для Gemini AI (aistudio.google.com/apikey)",
|
||||
"gemini_model_name_desc": "модель для Gemini AI. приклади: gemini-1.5-flash, gemini-1.5-pro, gemini-2.0-flash-exp, gemini-2.0-flash-thinking-exp-1219",
|
||||
"proxy_desc": "проксі у форматі http://<user>:<pass>@<proxy>:<port>, або http://<proxy>:<port>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"logs_username",
|
||||
"",
|
||||
doc=lambda: self.strings("logs_username_desc"),
|
||||
validator=loader.validators.Hidden(loader.validators.String()),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"logs_enabled",
|
||||
True,
|
||||
doc=lambda: self.strings("logs_enabled_desc"),
|
||||
validator=loader.validators.Boolean()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"delay",
|
||||
0,
|
||||
doc=lambda: self.strings("delay_desc"),
|
||||
validator=loader.validators.Integer(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"track_private",
|
||||
True,
|
||||
doc=lambda: self.strings("track_private_desc"),
|
||||
validator=loader.validators.Boolean()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"ai_passwords",
|
||||
False,
|
||||
doc=lambda: self.strings("ai_passwords_desc"),
|
||||
validator=loader.validators.Boolean()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"watcher_on",
|
||||
True,
|
||||
doc=lambda: self.strings("watcher_on_desc"),
|
||||
validator=loader.validators.Boolean()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"subscribe",
|
||||
True,
|
||||
doc=lambda: self.strings("subscribe_desc"),
|
||||
validator=loader.validators.Boolean()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"unsubscribe",
|
||||
True,
|
||||
doc=lambda: self.strings("unsubscribe_desc"),
|
||||
validator=loader.validators.Boolean()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"no_track_users",
|
||||
["username"],
|
||||
doc=lambda: self.strings("no_track_users_desc"),
|
||||
validator=loader.validators.Series(
|
||||
loader.validators.Union(loader.validators.String(), loader.validators.Integer())
|
||||
),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"testnet",
|
||||
False,
|
||||
doc=lambda: self.strings("testnet_desc"),
|
||||
validator=loader.validators.Boolean()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"gemini_api_key",
|
||||
"",
|
||||
doc=lambda: self.strings("gemini_api_key_desc"),
|
||||
validator=loader.validators.Hidden(loader.validators.String()),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"gemini_model_name",
|
||||
"gemini-flash-latest",
|
||||
doc=lambda: self.strings("gemini_model_name_desc"),
|
||||
validator=loader.validators.String(),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"proxy",
|
||||
"",
|
||||
doc=lambda: self.strings("proxy_desc"),
|
||||
validator=loader.validators.String(),
|
||||
)
|
||||
)
|
||||
self.sent_codes = defaultdict(bool)
|
||||
|
||||
async def client_ready(self, client: TelegramClient, db):
|
||||
self.client = client
|
||||
self.me = await self.client.get_me()
|
||||
self.me_id = self.me.id
|
||||
self.cd_id = 1559501630
|
||||
self.testnet_id = 1622808649
|
||||
self.extractor = URLExtract()
|
||||
self.scraper = cloudscraper.create_scraper()
|
||||
handlers = [
|
||||
(self.cb, [events.NewMessage, events.MessageEdited]),
|
||||
(self.channels, [events.NewMessage, events.MessageEdited]),
|
||||
(self.passwords, [events.NewMessage, events.MessageEdited]),
|
||||
]
|
||||
|
||||
for handler_func, event_list in handlers:
|
||||
for event in event_list:
|
||||
self.client.add_event_handler(handler_func, event)
|
||||
|
||||
|
||||
if self.config["gemini_api_key"]:
|
||||
self.passworder = Passworder(self.config["gemini_api_key"], self.config["gemini_model_name"])
|
||||
else:
|
||||
self.passworder = None
|
||||
|
||||
proxy = self.config["proxy"]
|
||||
if proxy:
|
||||
os.environ["http_proxy"] = proxy
|
||||
os.environ["HTTP_PROXY"] = proxy
|
||||
os.environ["https_proxy"] = proxy
|
||||
os.environ["HTTPS_PROXY"] = proxy
|
||||
|
||||
async def stars(self, url, bot_username):
|
||||
web_view = await self.client(RequestWebViewRequest(
|
||||
peer=bot_username,
|
||||
bot=bot_username,
|
||||
platform='android',
|
||||
from_bot_menu=False,
|
||||
url=url
|
||||
))
|
||||
|
||||
auth_url = web_view.url
|
||||
params = unquote(auth_url.split('tgWebAppData=')[1].split('&tgWebAppVersion')[0])
|
||||
access_token = await self.get_token('https://api.send.tg/internal/v1/authentication/webapp', params)
|
||||
|
||||
if access_token:
|
||||
code = url.split('/')[-1]
|
||||
await self.claim_stars(code, access_token)
|
||||
|
||||
async def get_token(self, url, params):
|
||||
json_data = {
|
||||
"initData": params
|
||||
}
|
||||
UserAgent = self.generate_random_user_agent()
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': UserAgent
|
||||
}
|
||||
|
||||
response = self.scraper.post(url, json=json_data, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
headers = response.headers
|
||||
set_cookie = headers.get('Set-Cookie')
|
||||
if set_cookie:
|
||||
access_token = set_cookie.split('access_token=')[1].split(';')[0]
|
||||
return access_token
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
async def claim_stars(self, code, access_token):
|
||||
url = f'https://api.send.tg/internal/v1/stars/claim/{code}'
|
||||
UserAgent = self.generate_random_user_agent()
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'Cookie': f'access_token={access_token}',
|
||||
'User-Agent': UserAgent
|
||||
}
|
||||
|
||||
response = self.scraper.post(url, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
response_data = response.json()
|
||||
stars = response_data.get("stars")
|
||||
gifted_by = response_data.get("gifted_by")
|
||||
await self.log(f"<b>+{stars}</b> <emoji document_id=5897920748101571572>🌟</emoji> (от {gifted_by})")
|
||||
else:
|
||||
pass
|
||||
|
||||
async def get_codes(self, text, entities, markup):
|
||||
urls_in_message = set()
|
||||
finded_codes = set()
|
||||
finded_stars_codes = set()
|
||||
finded_testnet_codes = set()
|
||||
|
||||
url_pattern = r'https?://t\.me/(?:send|CryptoBot)\?start=([A-Za-z0-9_-]+)'
|
||||
stars_pattern = r'https?://t\.me/CryptoBot/app\?startapp=stars-([A-Za-z0-9_-]+)'
|
||||
testnet_pattern = r'https?://t\.me/CryptoTestnetBot\?start=([A-Za-z0-9_-]+)'
|
||||
|
||||
if entities:
|
||||
for entity in entities:
|
||||
if isinstance(entity, MessageEntityUrl):
|
||||
urls_in_text = self.extractor.find_urls(text)
|
||||
for found_url in urls_in_text:
|
||||
urls_in_message.add(found_url.strip())
|
||||
|
||||
elif isinstance(entity, MessageEntityTextUrl):
|
||||
url = entity.url.strip()
|
||||
urls_in_message.add(url)
|
||||
|
||||
elif isinstance(entity, MessageMediaWebPage):
|
||||
url = entity.url.strip()
|
||||
urls_in_message.add(url)
|
||||
|
||||
if markup:
|
||||
for button_row in markup.rows:
|
||||
for button in button_row.buttons:
|
||||
if hasattr(button, "url") and button.url:
|
||||
urls_in_message.add(button.url.strip())
|
||||
|
||||
for found_url in urls_in_message:
|
||||
if not found_url.startswith(('http://', 'https://')):
|
||||
found_url = 'https://' + found_url.strip() #не обращайте внимания на костыли пажеее
|
||||
|
||||
clean_url = re.sub(r'[^\w:/?&=.-]', '', found_url)
|
||||
|
||||
code_match = re.match(url_pattern, clean_url)
|
||||
if code_match:
|
||||
code = code_match.group(1)
|
||||
finded_codes.add(code)
|
||||
|
||||
stars_match = re.match(stars_pattern, clean_url)
|
||||
if stars_match:
|
||||
stars_code = stars_match.group(1)
|
||||
finded_stars_codes.add(stars_code)
|
||||
|
||||
testnet_match = re.match(testnet_pattern, clean_url)
|
||||
if testnet_match:
|
||||
testnet_code = testnet_match.group(1)
|
||||
finded_testnet_codes.add(testnet_code)
|
||||
|
||||
return list(finded_codes), list(finded_stars_codes), list(finded_testnet_codes)
|
||||
|
||||
async def password(self, description):
|
||||
if not self.config["gemini_api_key"]:
|
||||
await self.log(f"<emoji document_id=5274099962655816924>❗️</emoji> <b>API ключ не указан. Получить его можно тут: aistudio.google.com/apikey (бесплатно), затем укажи его в конфиге (<code>{self.get_prefix()}cfg yg_checks</code>)</b>")
|
||||
return
|
||||
|
||||
if self.passworder:
|
||||
result = await self.passworder.generate(description)
|
||||
else:
|
||||
return
|
||||
|
||||
if "error" in result:
|
||||
await self.log(f"<emoji document_id=5274099962655816924>❗️</emoji> <b>Ошибка генерации пароля:</b> <code>{utils.escape_html(result['error'])}</code>")
|
||||
return
|
||||
|
||||
if result.get("password"):
|
||||
return result["password"]
|
||||
elif result.get("password") == "":
|
||||
return
|
||||
else:
|
||||
await self.log(f"<emoji document_id=5274099962655816924>❗️</emoji> <b>Ошибка генерации пароля:</b> <code>{utils.escape_html(str(result))}</code>")
|
||||
return
|
||||
|
||||
async def cb(self, message):
|
||||
if self.config["watcher_on"]:
|
||||
if message and message.sender_id not in [self.me_id, self.cd_id, self.testnet_id]:
|
||||
try:
|
||||
if not self.config["track_private"] and message.is_private:
|
||||
return
|
||||
|
||||
sender_username = getattr(message.sender, 'username', None) if message.sender else None
|
||||
if sender_username in self.config["no_track_users"]:
|
||||
return
|
||||
|
||||
codes, stars_codes, testnet_codes = await self.get_codes(message.text, message.entities, message.reply_markup)
|
||||
|
||||
if codes:
|
||||
for code in codes:
|
||||
if not self.sent_codes[code]:
|
||||
if code.startswith('CQ'):
|
||||
await message.mark_read()
|
||||
await asyncio.sleep(int(self.config["delay"]))
|
||||
await self.client.send_message(self.cd_id, f"/start {code}")
|
||||
self.sent_codes[code] = True
|
||||
await self.send_log_message(message, code)
|
||||
|
||||
if stars_codes:
|
||||
for stars_code in stars_codes:
|
||||
if not self.sent_codes[stars_code]:
|
||||
await message.mark_read()
|
||||
await self.stars(f"https://app.send.tg/stars/{stars_code}", "send")
|
||||
self.sent_codes[stars_code] = True
|
||||
|
||||
if testnet_codes:
|
||||
if self.config["testnet"]:
|
||||
for testnet_code in testnet_codes:
|
||||
if not self.sent_codes[testnet_code]:
|
||||
if testnet_code.startswith('CQ'):
|
||||
await message.mark_read()
|
||||
await asyncio.sleep(int(self.config["delay"]))
|
||||
await self.client.send_message(self.testnet_id, f"/start {testnet_code}")
|
||||
self.sent_codes[testnet_code] = True
|
||||
await self.send_log_message(message, testnet_code)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
async def channels(self, event):
|
||||
if not self.config["subscribe"]:
|
||||
return
|
||||
|
||||
if not self.config["watcher_on"]:
|
||||
return
|
||||
|
||||
if event.sender_id == self.cd_id and any(event.text.startswith(prefix) for prefix in ['Чтобы активировать этот чек, подпишитесь на канал','To activate this check, join the channel(s)']):
|
||||
subscribed = []
|
||||
try:
|
||||
rows = event.reply_markup.rows if event.reply_markup else []
|
||||
for row in rows:
|
||||
for button in row.buttons:
|
||||
if button.url:
|
||||
invite_code = button.url.split('+', 1)[1]
|
||||
await self.client(ImportChatInviteRequest(invite_code))
|
||||
subscribed.append(invite_code)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
await asyncio.sleep(1)
|
||||
await event.click(data=b'check-subscribe')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
if self.config["unsubscribe"]:
|
||||
for invite_code in subscribed:
|
||||
channel_info = await self.client(CheckChatInviteRequest(hash=invite_code))
|
||||
channel = channel_info.chat
|
||||
await self.client(LeaveChannelRequest(channel))
|
||||
|
||||
async def passwords(self, message):
|
||||
if not self.config["watcher_on"]:
|
||||
return
|
||||
|
||||
if not self.config["ai_passwords"]:
|
||||
return
|
||||
|
||||
if message.sender_id == self.cd_id and any(phrase in message.text for phrase in ["Введите пароль от чека для получения", "Enter the password for this check to receive"]):
|
||||
description = " ".join("\n".join(message.raw_text.split("\n")[2:]).split(" ")[1:])
|
||||
r = await self.password(description)
|
||||
if r:
|
||||
await self.client.send_message(self.cd_id, r)
|
||||
|
||||
async def log(self, message):
|
||||
if self.config["logs_username"]:
|
||||
await self.client.send_message(self.config["logs_username"], message, link_preview=False)
|
||||
|
||||
async def send_log_message(self, message, code):
|
||||
if self.config["logs_enabled"]:
|
||||
chat_id = str(message.chat_id).replace('-100', '')
|
||||
if message.is_private:
|
||||
sender_username = getattr(message.sender, 'username', None) if message.sender else None
|
||||
await self.log(f"<emoji document_id=5431449001532594346>⚡️</emoji> <b>Обнаружен новый чек:</b>\n\n<emoji document_id=5870527201874546272>🔗</emoji> <b>Ссылка чека:</b> <i>t.me/send?start={code}<i>\n<emoji document_id=5879770735999717115>👤</emoji> <b>Чек был обнаружен в личных сообщениях:</b> <i>@{sender_username}</i>")
|
||||
else:
|
||||
message_link = f"t.me/c/{chat_id}/{message.id}"
|
||||
await self.log(f"<emoji document_id=5431449001532594346>⚡️</emoji> <b>Обнаружен новый чек:</b>\n\n<emoji document_id=5870527201874546272>🔗</emoji> <b>Ссылка чека:</b> <i>t.me/send?start={code}<i>\n<emoji document_id=5870527201874546272>🔗</emoji> <b>Ссылка на сообщение с чеком:</b> <i>{message_link}</i>")
|
||||
|
||||
@loader.command(ru_doc="вкл/выкл активатор", ua_doc="увімк/вимк активатор")
|
||||
async def checkscmd(self, m: Message):
|
||||
"""on/off auto-check activation"""
|
||||
self.config["watcher_on"] = not self.config["watcher_on"]
|
||||
lang = self.strings.get("language", "en")
|
||||
on_off = {"en": "enabled", "ru": "включен", "ua": "увімкнено"}[lang] if self.config["watcher_on"] else {"en": "disabled", "ru": "выключен", "ua": "вимкнено"}[lang]
|
||||
list = [
|
||||
"<emoji document_id=5931703809800672260>🦋</emoji>",
|
||||
"<emoji document_id=5931685899787049183>🦋</emoji>",
|
||||
"<emoji document_id=5931254745200072637>🦋</emoji>",
|
||||
"<emoji document_id=5931420135800706406>🦋</emoji>",
|
||||
"<emoji document_id=5931579221389350286>🦋</emoji>",
|
||||
"<emoji document_id=5931796606864070138>🦋</emoji>",
|
||||
"<emoji document_id=5931709595121620710>🦋</emoji>",
|
||||
"<emoji document_id=5931689305696113988>🦋</emoji>"
|
||||
]
|
||||
emoji = random.choice(list)
|
||||
await utils.answer(m, self.strings["activator"].format(emoji, on_off))
|
||||
|
||||
@loader.command(ru_doc="вкл/выкл активатор testnet", ua_doc="увімк/вимк активатор testnet")
|
||||
async def testnetcmd(self, m: Message):
|
||||
"""on/off auto-check activation for testnet"""
|
||||
self.config["testnet"] = not self.config["testnet"]
|
||||
lang = self.strings.get("language", "en")
|
||||
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["testnet"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
|
||||
list = [
|
||||
"<emoji document_id=5931703809800672260>🦋</emoji>",
|
||||
"<emoji document_id=5931685899787049183>🦋</emoji>",
|
||||
"<emoji document_id=5931254745200072637>🦋</emoji>",
|
||||
"<emoji document_id=5931420135800706406>🦋</emoji>",
|
||||
"<emoji document_id=5931579221389350286>🦋</emoji>",
|
||||
"<emoji document_id=5931796606864070138>🦋</emoji>",
|
||||
"<emoji document_id=5931709595121620710>🦋</emoji>",
|
||||
"<emoji document_id=5931689305696113988>🦋</emoji>"
|
||||
]
|
||||
emoji = random.choice(list)
|
||||
await utils.answer(m, self.strings["testnet"].format(emoji, on_off))
|
||||
|
||||
@loader.command(ru_doc="вкл/выкл отправку логов", ua_doc="увімк/вимк відправку логів")
|
||||
async def yglogscmd(self, m: Message):
|
||||
"""on/off log sending"""
|
||||
self.config["logs_enabled"] = not self.config["logs_enabled"]
|
||||
lang = self.strings.get("language", "en")
|
||||
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["logs_enabled"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
|
||||
list = [
|
||||
"<emoji document_id=5931246400078616786>🍑</emoji>",
|
||||
"<emoji document_id=5931283302437623922>🍑</emoji>",
|
||||
"<emoji document_id=5933573709712331850>🍑</emoji>",
|
||||
"<emoji document_id=5931412164341404834>🍑</emoji>",
|
||||
"<emoji document_id=5931408105597310922>🍑</emoji>",
|
||||
"<emoji document_id=5931347907335689957>🍑</emoji>",
|
||||
"<emoji document_id=5933527787922005080>🍑</emoji>",
|
||||
"<emoji document_id=5931255728747583490>🍑</emoji>"
|
||||
]
|
||||
emoji = random.choice(list)
|
||||
await utils.answer(m, self.strings["log_sending"].format(emoji, on_off))
|
||||
|
||||
@loader.command(ru_doc="вкл/выкл подбор паролей с помощью нейросети", ua_doc="увімк/вимк підбір паролей за допомогою нейромережі")
|
||||
async def passwordscmd(self, m: Message):
|
||||
"""on/off password cracking with neural network"""
|
||||
self.config["ai_passwords"] = not self.config["ai_passwords"]
|
||||
lang = self.strings.get("language", "en")
|
||||
on_off = {"en": "enabled", "ru": "включен", "ua": "увімкнено"}[lang] if self.config["ai_passwords"] else {"en": "disabled", "ru": "выключен", "ua": "вимкнено"}[lang]
|
||||
list = [
|
||||
"<emoji document_id=5931715028255249602>🔐</emoji>",
|
||||
"<emoji document_id=5931759476871797208>🔐</emoji>",
|
||||
"<emoji document_id=5931604879523976952>🔐</emoji>",
|
||||
"<emoji document_id=5931569115331306831>🔐</emoji>",
|
||||
"<emoji document_id=5931530997496551899>🔐</emoji>",
|
||||
"<emoji document_id=5931464008891635480>🔐</emoji>",
|
||||
"<emoji document_id=5931781312485529416>🔐</emoji>",
|
||||
"<emoji document_id=5931434210408536378>🔐</emoji>"
|
||||
]
|
||||
emoji = random.choice(list)
|
||||
await utils.answer(m, self.strings["password_cracking"].format(emoji, on_off))
|
||||
|
||||
@loader.command(ru_doc="вкл/выкл активацию чеков отправленных в личке", ua_doc="увімк/вимк активацію чеків, надісланих в особисті повідомлення")
|
||||
async def yglscmd(self, m: Message):
|
||||
"""on/off activation of checks sent in private messages"""
|
||||
self.config["track_private"] = not self.config["track_private"]
|
||||
lang = self.strings.get("language", "en")
|
||||
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["track_private"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
|
||||
list = [
|
||||
"<emoji document_id=5931534008268625877>🔁</emoji>",
|
||||
"<emoji document_id=5933704920963225481>🔁</emoji>",
|
||||
"<emoji document_id=5931351192985671828>🔁</emoji>",
|
||||
"<emoji document_id=5931570287857374798>🔁</emoji>",
|
||||
"<emoji document_id=5931284676827158390>🔁</emoji>",
|
||||
"<emoji document_id=5931776850014508762>🔁</emoji>",
|
||||
"<emoji document_id=5931430675650451345>🔁</emoji>",
|
||||
"<emoji document_id=5931768827015602073>🔁</emoji>"
|
||||
]
|
||||
emoji = random.choice(list)
|
||||
await utils.answer(m, self.strings["private_check_activation"].format(emoji, on_off))
|
||||
|
||||
@loader.command(ru_doc="вкл/выкл авто-подписку", ua_doc="увімк/вимк авто-підписку")
|
||||
async def subscribecmd(self, m: Message):
|
||||
"""on/off auto-subscription"""
|
||||
self.config["subscribe"] = not self.config["subscribe"]
|
||||
lang = self.strings.get("language", "en")
|
||||
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["subscribe"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
|
||||
list = [
|
||||
"<emoji document_id=5931461638069687926>💡</emoji>",
|
||||
"<emoji document_id=5931599476455118181>💡</emoji>",
|
||||
"<emoji document_id=5931620642053953532>💡</emoji>",
|
||||
"<emoji document_id=5931776927323920236>💡</emoji>",
|
||||
"<emoji document_id=5931773113392962977>💡</emoji>",
|
||||
"<emoji document_id=5931673221043590661>💡</emoji>",
|
||||
"<emoji document_id=5931462436933604912>💡</emoji>",
|
||||
"<emoji document_id=5931295409950431661>💡</emoji>"
|
||||
]
|
||||
emoji = random.choice(list)
|
||||
await utils.answer(m, self.strings["auto_subscription"].format(emoji, on_off))
|
||||
|
||||
@loader.command(ru_doc="вкл/выкл авто-отписку", ua_doc="увімк/вимк авто-відписку")
|
||||
async def unsubscribecmd(self, m: Message):
|
||||
"""on/off auto-unsubscription"""
|
||||
self.config["unsubscribe"] = not self.config["unsubscribe"]
|
||||
lang = self.strings.get("language", "en")
|
||||
on_off = {"en": "enabled", "ru": "включена", "ua": "увімкнена"}[lang] if self.config["unsubscribe"] else {"en": "disabled", "ru": "выключена", "ua": "вимкнена"}[lang]
|
||||
list = [
|
||||
"<emoji document_id=5931279570111043408>✅</emoji>",
|
||||
"<emoji document_id=5931602010485823634>✅</emoji>",
|
||||
"<emoji document_id=5931642602221737965>✅</emoji>",
|
||||
"<emoji document_id=5933944919440758085>✅</emoji>",
|
||||
"<emoji document_id=5933523918156469650>✅</emoji>",
|
||||
"<emoji document_id=5931644148409964015>✅</emoji>",
|
||||
"<emoji document_id=5931387421034812889>✅</emoji>",
|
||||
"<emoji document_id=5931344333922900261>✅</emoji>"
|
||||
]
|
||||
emoji = random.choice(list)
|
||||
await utils.answer(m, self.strings["auto_unsubscription"].format(emoji, on_off))
|
||||
|
||||
def generate_random_user_agent(self, device_type='android', browser_type='chrome'):
|
||||
existing_versions = {
|
||||
110: [
|
||||
'110.0.5481.154',
|
||||
'110.0.5481.153',
|
||||
'110.0.5481.65',
|
||||
'110.0.5481.64',
|
||||
'110.0.5481.63',
|
||||
'110.0.5481.61'
|
||||
],
|
||||
111: [
|
||||
"111.0.5563.116",
|
||||
'111.0.5563.115',
|
||||
'111.0.5563.58',
|
||||
'111.0.5563.49'
|
||||
],
|
||||
112: [
|
||||
'112.0.5615.136',
|
||||
'112.0.5615.136',
|
||||
'112.0.5615.101',
|
||||
'112.0.5615.100',
|
||||
'112.0.5615.48'
|
||||
],
|
||||
113: [
|
||||
'113.0.5672.77',
|
||||
'113.0.5672.76'
|
||||
],
|
||||
114: [
|
||||
'114.0.5735.60',
|
||||
'114.0.5735.53'
|
||||
],
|
||||
115: [
|
||||
'115.0.5790.136'
|
||||
],
|
||||
116: [
|
||||
'116.0.5845.172',
|
||||
'116.0.5845.164',
|
||||
'116.0.5845.163',
|
||||
'116.0.5845.114',
|
||||
'116.0.5845.92'
|
||||
],
|
||||
117: [
|
||||
'117.0.5938.154',
|
||||
'117.0.5938.141',
|
||||
'117.0.5938.140',
|
||||
'117.0.5938.61',
|
||||
'117.0.5938.61',
|
||||
'117.0.5938.60'
|
||||
],
|
||||
118: [
|
||||
'118.0.5993.112',
|
||||
'118.0.5993.111',
|
||||
'118.0.5993.80',
|
||||
'118.0.5993.65',
|
||||
'118.0.5993.48'
|
||||
],
|
||||
119: [
|
||||
'119.0.6045.194',
|
||||
'119.0.6045.193',
|
||||
'119.0.6045.164',
|
||||
'119.0.6045.163',
|
||||
'119.0.6045.134',
|
||||
'119.0.6045.134',
|
||||
'119.0.6045.66',
|
||||
'119.0.6045.53'
|
||||
],
|
||||
120: [
|
||||
'120.0.6099.230',
|
||||
'120.0.6099.210',
|
||||
'120.0.6099.194',
|
||||
'120.0.6099.193',
|
||||
'120.0.6099.145',
|
||||
'120.0.6099.144',
|
||||
'120.0.6099.144',
|
||||
'120.0.6099.116',
|
||||
'120.0.6099.116',
|
||||
'120.0.6099.115',
|
||||
'120.0.6099.44',
|
||||
'120.0.6099.43'
|
||||
],
|
||||
121: [
|
||||
'121.0.6167.178',
|
||||
'121.0.6167.165',
|
||||
'121.0.6167.164',
|
||||
'121.0.6167.164',
|
||||
'121.0.6167.144',
|
||||
'121.0.6167.143',
|
||||
'121.0.6167.101'
|
||||
],
|
||||
122: [
|
||||
'122.0.6261.119',
|
||||
'122.0.6261.106',
|
||||
'122.0.6261.105',
|
||||
'122.0.6261.91',
|
||||
'122.0.6261.90',
|
||||
'122.0.6261.64',
|
||||
'122.0.6261.43'
|
||||
],
|
||||
123: [
|
||||
'123.0.6312.121',
|
||||
'123.0.6312.120',
|
||||
'123.0.6312.119',
|
||||
'123.0.6312.118',
|
||||
'123.0.6312.99',
|
||||
'123.0.6312.80',
|
||||
'123.0.6312.41',
|
||||
'123.0.6312.40'
|
||||
],
|
||||
124: [
|
||||
'124.0.6367.179',
|
||||
'124.0.6367.172',
|
||||
'124.0.6367.171',
|
||||
'124.0.6367.114',
|
||||
'124.0.6367.113',
|
||||
'124.0.6367.83',
|
||||
'124.0.6367.82',
|
||||
'124.0.6367.54'
|
||||
],
|
||||
125: [
|
||||
'125.0.6422.165',
|
||||
'125.0.6422.164',
|
||||
'125.0.6422.147',
|
||||
'125.0.6422.146',
|
||||
'125.0.6422.113',
|
||||
'125.0.6422.72',
|
||||
'125.0.6422.72',
|
||||
'125.0.6422.53',
|
||||
'125.0.6422.52'
|
||||
],
|
||||
126: [
|
||||
'126.0.6478.122',
|
||||
'126.0.6478.72',
|
||||
'126.0.6478.71',
|
||||
'126.0.6478.50'
|
||||
]
|
||||
}
|
||||
|
||||
devices = [
|
||||
('Samsung', 'SM-G980F', 'AVERAGE', 10),
|
||||
('Samsung', 'SM-G973F', 'AVERAGE', 9),
|
||||
('Samsung', 'SM-G973U', 'AVERAGE', 9),
|
||||
('Samsung', 'SM-N986B', 'AVERAGE', 11),
|
||||
('Samsung', 'SM-N981B', 'AVERAGE', 11),
|
||||
('Samsung', 'SM-F916B', 'AVERAGE', 11),
|
||||
('Samsung', 'SM-G998B', 'HIGH', 12),
|
||||
('Samsung', 'SM-G991B', 'HIGH', 12),
|
||||
('Samsung', 'SM-G996B', 'HIGH', 12),
|
||||
('Samsung', 'SM-G990E', 'HIGH', 12),
|
||||
('Samsung', 'SM-G990B', 'HIGH', 12),
|
||||
('Samsung', 'SM-G990B2', 'HIGH', 12),
|
||||
('Samsung', 'SM-G990U', 'HIGH', 12),
|
||||
('Google', 'Pixel 5', 'AVERAGE', 11),
|
||||
('Google', 'Pixel 5a', 'AVERAGE', 11),
|
||||
('Google', 'Pixel 6', 'AVERAGE', 12),
|
||||
('Google', 'Pixel 6 Pro', 'AVERAGE', 12),
|
||||
('Google', 'Pixel 6 XL', 'AVERAGE', 12),
|
||||
('Google', 'Pixel 6a', 'AVERAGE', 12),
|
||||
('Google', 'Pixel 7', 'HIGH', 13),
|
||||
('Google', 'Pixel 7a', 'AVERAGE', 13),
|
||||
('Google', 'Pixel 7 Pro', 'HIGH', 13),
|
||||
('Google', 'Pixel 8', 'HIGH', 14),
|
||||
('Google', 'Pixel 8a', 'HIGH', 14),
|
||||
('Google', 'Pixel 8 Pro', 'HIGH', 14),
|
||||
('Google', 'Pixel 9', 'HIGH', 14),
|
||||
('Google', 'Pixel 9 Pro', 'HIGH', 14),
|
||||
('Google', 'Pixel 9 Pro XL', 'HIGH', 14),
|
||||
('Xiaomi', 'Mi 10', 'AVERAGE', 10),
|
||||
('Xiaomi', 'Mi 11', 'AVERAGE', 11),
|
||||
('Xiaomi', 'Mi 12', 'HIGH', 12),
|
||||
('Xiaomi', 'Redmi Note 10', 'HIGH', 11),
|
||||
('Xiaomi', 'Redmi Note 10 Pro', 'HIGH', 11),
|
||||
('Xiaomi', 'Redmi Note 11', 'HIGH', 12),
|
||||
('Xiaomi', 'Redmi Note 11 Pro', 'HIGH', 12),
|
||||
('Xiaomi', 'Redmi Note 12', 'HIGH', 13),
|
||||
('Xiaomi', 'Redmi Note 12 Pro', 'HIGH', 13),
|
||||
('Xiaomi', 'POCO M3 Pro', 'HIGH', 11),
|
||||
('Xiaomi', 'POCO X5', 'HIGH', 12),
|
||||
('Xiaomi', 'POCO X5 Pro', 'HIGH', 12),
|
||||
('Xiaomi', 'POCO X6 Pro', 'HIGH', 13),
|
||||
('Xiaomi', 'POCO F4', 'HIGH', 12),
|
||||
('Xiaomi', 'POCO F4 GT', 'HIGH', 12),
|
||||
('Xiaomi', 'POCO F3', 'HIGH', 11),
|
||||
('OnePlus', 'NE2215', 'AVERAGE', 12),
|
||||
('OnePlus', 'NE2210', 'AVERAGE', 12),
|
||||
('OnePlus', 'IN2010', 'AVERAGE', 10),
|
||||
('OnePlus', 'IN2023', 'AVERAGE', 11),
|
||||
('OnePlus', 'LE2117', 'AVERAGE', 11),
|
||||
('OnePlus', 'LE2123', 'AVERAGE', 11),
|
||||
('OnePlus', 'CPH2423', 'AVERAGE', 12),
|
||||
('Huawei', 'VOG-AL00', 'AVERAGE', 9),
|
||||
('Huawei', 'ANA-AL00', 'AVERAGE', 10),
|
||||
('Huawei', 'TAS-AL00', 'AVERAGE', 10),
|
||||
('Huawei', 'OCE-AN10', 'AVERAGE', 11),
|
||||
('Sony', 'J9150', 'AVERAGE', 9),
|
||||
('Sony', 'J9210', 'AVERAGE', 10)
|
||||
]
|
||||
|
||||
firefox_versions = list(range(100, 127))
|
||||
|
||||
if browser_type == 'chrome':
|
||||
major_version = random.choice(list(existing_versions.keys()))
|
||||
browser_version = random.choice(existing_versions[major_version])
|
||||
elif browser_type == 'firefox':
|
||||
browser_version = random.choice(firefox_versions)
|
||||
|
||||
user_agent = ""
|
||||
|
||||
if device_type == 'android':
|
||||
android_versions = {
|
||||
'10': 29,
|
||||
'11': 30,
|
||||
'12': 31,
|
||||
'13': 33,
|
||||
'14': 34
|
||||
}
|
||||
|
||||
manufacturer, model, performance_class, min_android_version = random.choice(devices)
|
||||
android_version = str(random.choice([v for v in android_versions.keys() if int(v) >= min_android_version]))
|
||||
sdk_version = android_versions[android_version]
|
||||
|
||||
if browser_type == 'chrome':
|
||||
major_version = random.choice(list(existing_versions.keys()))
|
||||
browser_version = random.choice(existing_versions[major_version])
|
||||
user_agent = (f"Mozilla/5.0 (Linux; Android {android_version}; {model}) AppleWebKit/537.36 "
|
||||
f"(KHTML, like Gecko) Chrome/{browser_version} Mobile Safari/537.36 "
|
||||
f"Telegram-Android/11.4.2 ({manufacturer} {model}; Android {android_version}; "
|
||||
f"SDK {sdk_version}; {performance_class})")
|
||||
elif browser_type == 'firefox':
|
||||
browser_version = random.choice(firefox_versions)
|
||||
user_agent = (f"Mozilla/5.0 (Android {android_version}; Mobile; rv:{browser_version}.0) "
|
||||
f"Gecko/{browser_version}.0 Firefox/{browser_version}.0 "
|
||||
f"Telegram-Android/11.4.2 ({manufacturer} {model}; Android {android_version}; "
|
||||
f"SDK {sdk_version}; {performance_class})")
|
||||
|
||||
elif device_type == 'ios':
|
||||
ios_versions = ['13.0', '14.0', '15.0', '16.0', '17.0', '18.0']
|
||||
ios_version = random.choice(ios_versions)
|
||||
if browser_type == 'chrome':
|
||||
user_agent = (f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version.replace('.', '_')} like Mac OS X) "
|
||||
f"AppleWebKit/537.36 (KHTML, like Gecko) CriOS/{browser_version} Mobile/15E148 Safari/604.1")
|
||||
elif browser_type == 'firefox':
|
||||
user_agent = (f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version.replace('.', '_')} like Mac OS X) "
|
||||
f"AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/{browser_version}.0 Mobile/15E148 Safari/605.1.15")
|
||||
|
||||
elif device_type == 'windows':
|
||||
windows_versions = ['10.0', '11.0']
|
||||
windows_version = random.choice(windows_versions)
|
||||
if browser_type == 'chrome':
|
||||
user_agent = (f"Mozilla/5.0 (Windows NT {windows_version}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
f"Chrome/{browser_version} Safari/537.36")
|
||||
elif browser_type == 'firefox':
|
||||
user_agent = (f"Mozilla/5.0 (Windows NT {windows_version}; Win64; x64; rv:{browser_version}.0) "
|
||||
f"Gecko/{browser_version}.0 Firefox/{browser_version}.0")
|
||||
|
||||
elif device_type == 'ubuntu':
|
||||
if browser_type == 'chrome':
|
||||
user_agent = (f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:94.0) AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
f"Chrome/{browser_version} Safari/537.36")
|
||||
elif browser_type == 'firefox':
|
||||
user_agent = (f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:{browser_version}.0) Gecko/{browser_version}.0 "
|
||||
f"Firefox/{browser_version}.0")
|
||||
|
||||
return user_agent
|
||||
|
||||
264
yummy1gay/limoka/yg_gamee.py
Normal file
264
yummy1gay/limoka/yg_gamee.py
Normal file
@@ -0,0 +1,264 @@
|
||||
__version__ = (1, 7, 0, 0)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# Changelog v1.7.0:
|
||||
# - Добавлена поддержка веб аппа
|
||||
# - Обновлен класс GameeHacker, теперь работает с новым API
|
||||
# - кок
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import json
|
||||
import base64
|
||||
from hashlib import md5
|
||||
from random import randint
|
||||
from uuid import uuid4
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from urllib.parse import unquote, urlparse, parse_qs
|
||||
|
||||
import requests
|
||||
from telethon.tl.functions.messages import RequestAppWebViewRequest
|
||||
from telethon.tl.types import InputBotAppShortName
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
class GameeHacker:
|
||||
API_URL = "https://api.gamee.com/"
|
||||
SALT = "crmjbjm3lczhlgnek9uaxz2l9svlfjw14npauhen"
|
||||
|
||||
def __init__(self, client, score: int, play_time: int):
|
||||
self.client = client
|
||||
self.score = score
|
||||
self.play_time = play_time
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({
|
||||
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Mobile Safari/537.36",
|
||||
"Accept": "*/*",
|
||||
"Accept-Language": "en-US,en;q=0.5",
|
||||
"Client-Language": "en",
|
||||
"Content-Type": "text/plain;charset=UTF-8",
|
||||
"Origin": "https://prizes.gamee.com",
|
||||
"Referer": "https://prizes.gamee.com/",
|
||||
"X-Bot-Header": "gamee",
|
||||
"X-Install-Uuid": str(uuid4()),
|
||||
})
|
||||
|
||||
async def _get_init_data(self, chat, start_param: str):
|
||||
bot = await self.client.get_input_entity("gamee")
|
||||
app = InputBotAppShortName(bot_id=bot, short_name="game")
|
||||
|
||||
web_view = await self.client(RequestAppWebViewRequest(
|
||||
peer=chat,
|
||||
app=app,
|
||||
platform='android',
|
||||
start_param=start_param,
|
||||
))
|
||||
|
||||
auth_url = web_view.url
|
||||
return unquote(auth_url.split('tgWebAppData=')[1].split('&tgWebAppVersion')[0])
|
||||
|
||||
def _login(self, init_data: str):
|
||||
payload = [{"jsonrpc": "2.0",
|
||||
"id": "user.authentication.loginUsingTelegram",
|
||||
"method": "user.authentication.loginUsingTelegram",
|
||||
"params": {"initData": init_data}}]
|
||||
|
||||
res = self.session.post(self.API_URL, data=json.dumps(payload)).json()
|
||||
|
||||
for item in res:
|
||||
if item.get("id") == "user.authentication.loginUsingTelegram":
|
||||
if "error" in item:
|
||||
raise RuntimeError(f"Login error: {item['error'].get('message', 'Unknown error')}")
|
||||
|
||||
token = item["result"]["tokens"]["authenticate"]
|
||||
self.session.headers["Authorization"] = f"Bearer {token}"
|
||||
return
|
||||
|
||||
raise RuntimeError("Authentication token not found in login response.")
|
||||
|
||||
def _get_game_details(self, game_slug: str):
|
||||
payload = [{"jsonrpc": "2.0",
|
||||
"id": "game.get",
|
||||
"method": "game.get",
|
||||
"params": {"slug": game_slug}}]
|
||||
|
||||
res = self.session.post(self.API_URL, data=json.dumps(payload)).json()
|
||||
|
||||
for item in res:
|
||||
if item.get("id") == "game.get":
|
||||
if "error" in item:
|
||||
raise RuntimeError(f"Get game details error: {item['error'].get('message', 'Unknown error')}")
|
||||
|
||||
game_data = item["result"]["game"]
|
||||
return game_data["id"], game_data["release"]["number"]
|
||||
|
||||
raise RuntimeError("Game details not found in response.")
|
||||
|
||||
def _create_checksum(self, game_id: int, game_uuid: str) -> str:
|
||||
raw_data = f"{self.score}:{self.play_time}:{game_id}::{game_uuid}:{__class__.SALT}"
|
||||
return md5(raw_data.encode()).hexdigest()
|
||||
|
||||
def _send_score(
|
||||
self,
|
||||
game_id: int,
|
||||
release_number: int,
|
||||
chat_instance: str,
|
||||
chat_type: str,
|
||||
):
|
||||
game_uuid = str(uuid4())
|
||||
checksum = self._create_checksum(game_id, game_uuid)
|
||||
|
||||
payload = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "game.saveTelegramGameplay",
|
||||
"method": "game.saveTelegramGameplay",
|
||||
"params": {
|
||||
"gameplayData": {
|
||||
"gameId": game_id,
|
||||
"score": self.score,
|
||||
"playTime": self.play_time,
|
||||
"releaseNumber": release_number,
|
||||
"createdTime": datetime.now(timezone(timedelta(hours=2))).replace(microsecond=0).isoformat(),
|
||||
"metadata": {"gameplayId": randint(1, 500)},
|
||||
"isSaveState": False,
|
||||
"gameStateData": None,
|
||||
"gameplayOrigin": "game",
|
||||
"replayData": None,
|
||||
"replayVariant": None,
|
||||
"replayDataChecksum": None,
|
||||
"checksum": checksum,
|
||||
"uuid": game_uuid,
|
||||
},
|
||||
"chatInstance": chat_instance,
|
||||
"chatType": chat_type,
|
||||
}
|
||||
}
|
||||
|
||||
response = self.session.post(self.API_URL, data=json.dumps(payload)).json()
|
||||
return response
|
||||
|
||||
async def kok(self, chat, msg):
|
||||
try:
|
||||
btn = msg.reply_markup.rows[0].buttons[0]
|
||||
parsed_url = urlparse(btn.url)
|
||||
start_param = parse_qs(parsed_url.query).get('startapp', [None])[0]
|
||||
if not start_param:
|
||||
raise ValueError("startapp param not found in button URL")
|
||||
except Exception:
|
||||
raise ValueError("Failed to get game button from the message. Make sure it's a message with a game.")
|
||||
|
||||
game_slug = json.loads(base64.b64decode(start_param))['game']['slug']
|
||||
|
||||
init_data = await self._get_init_data(chat, start_param)
|
||||
self._login(init_data)
|
||||
|
||||
chat_params = {q.split("=")[0]: q.split("=")[1] for q in init_data.split("&")}
|
||||
chat_instance = chat_params.get("chat_instance")
|
||||
chat_type = chat_params.get("chat_type")
|
||||
|
||||
if not chat_instance or not chat_type:
|
||||
raise ValueError("Could not parse chat_instance or chat_type from init_data")
|
||||
|
||||
game_id, release_number = self._get_game_details(game_slug)
|
||||
|
||||
return self._send_score(game_id, release_number, chat_instance, chat_type)
|
||||
|
||||
@loader.tds
|
||||
class yg_gamee(loader.Module):
|
||||
"""Module to cheat score in @gamee"""
|
||||
|
||||
strings = {
|
||||
"name": "yg_gamee",
|
||||
"args": "<emoji document_id=5447644880824181073>⚠️</emoji> <i>Specify a link or reply to the message with the game!</i>",
|
||||
"processing": "<emoji document_id=5386367538735104399>⌛</emoji>",
|
||||
"button": "<emoji document_id=5436113877181941026>❓</emoji> <i>Failed to click the button.. Make sure this is a message with a game!</i>",
|
||||
"usage": "<emoji document_id=5461117441612462242>🙂</emoji> <i>Use:</i> <code>{prefix}gamee <score> <time in seconds></code> <i>in a reply to the message with the game!</i>",
|
||||
"error": "<emoji document_id=5420323339723881652>⚠️</emoji> <b>Error:</b> <code>{error}</code>",
|
||||
"success": "<emoji document_id=5325547803936572038>✨</emoji> <b>Score boosted!</b>\n<emoji document_id=5334544901428229844>ℹ️</emoji> <b>New record:</b> <code>{score}</code>",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"args": "<emoji document_id=5447644880824181073>⚠️</emoji> <i>Укажи ссылку или сделай реплай на сообщение с игрой!</i>",
|
||||
"processing": "<emoji document_id=5386367538735104399>⌛</emoji>",
|
||||
"button": "<emoji document_id=5436113877181941026>❓</emoji> <i>Не удалось кликнуть по кнопке.. Убедись, что это сообщение с игрой!</i>",
|
||||
"usage": "<emoji document_id=5461117441612462242>🙂</emoji> <i>Используй:</i> <code>{prefix}gamee <score> <time in seconds></code> <i>в реплае на сообщение с игрой!</i>",
|
||||
"error": "<emoji document_id=5420323339723881652>⚠️</emoji> <b>Ошибка:</b> <code>{error}</code>",
|
||||
"success": "<emoji document_id=5325547803936572038>✨</emoji> <b>Рекорд накручен!</b>\n<emoji document_id=5334544901428229844>ℹ️</emoji> <b>Новый рекорд:</b> <code>{score}</code>",
|
||||
}
|
||||
|
||||
strings_ua = {
|
||||
"args": "<emoji document_id=5447644880824181073>⚠️</emoji> <i>Вкажи посилання або зроби реплай на повідомлення з грою!</i>",
|
||||
"processing": "<emoji document_id=5386367538735104399>⌛</emoji>",
|
||||
"button": "<emoji document_id=5436113877181941026>❓</emoji> <i>Не вдалося натиснути на кнопку.. Переконайся, що це повідомлення з грою!</i>",
|
||||
"usage": "<emoji document_id=5461117441612462242>🙂</emoji> <i>Використовуй:</i> <code>{prefix}gamee <score> <time in seconds></code> <i>у відповіді на повідомлення з грою!</i>",
|
||||
"error": "<emoji document_id=5420323339723881652>⚠️</emoji> <b>Помилка:</b> <code>{error}</code>",
|
||||
"success": "<emoji document_id=5325547803936572038>✨</emoji> <b>Рекорд накручено!</b>\n<emoji document_id=5334544901428229844>ℹ️</emoji> <b>Новий рекорд:</b> <code>{score}</code>",
|
||||
}
|
||||
|
||||
@loader.command(
|
||||
ru_doc="<reply/link to message with game> <score> <time in seconds> - накрутить рекорд",
|
||||
ua_doc="<reply/link to message with game> <score> <time in seconds> - накрутити рекорд"
|
||||
)
|
||||
async def gameecmd(self, m):
|
||||
"""<reply/link to message with game> <score> <time in seconds> - cheat score"""
|
||||
args = utils.get_args_raw(m).strip()
|
||||
if not args:
|
||||
return await utils.answer(m, self.strings["usage"].format(prefix=self.get_prefix()))
|
||||
|
||||
await utils.answer(m, self.strings["processing"])
|
||||
|
||||
r = await m.get_reply_message()
|
||||
|
||||
try:
|
||||
if r:
|
||||
score, play_time = map(int, args.split())
|
||||
else:
|
||||
parts = args.split()
|
||||
if len(parts) != 3:
|
||||
raise ValueError("invalid number of arguments")
|
||||
|
||||
link, score_str, time_str = parts
|
||||
score, play_time = int(score_str), int(time_str)
|
||||
link_parts = link.strip("/").split("/")
|
||||
msg_id = int(link_parts[-1])
|
||||
|
||||
if "/c/" in link:
|
||||
peer = int("-100" + link_parts[link_parts.index("c") + 1])
|
||||
else:
|
||||
peer = link_parts[-2]
|
||||
|
||||
r = await m.client.get_messages(peer, ids=msg_id)
|
||||
|
||||
except Exception:
|
||||
return await utils.answer(m, self.strings["usage"].format(prefix=self.get_prefix()))
|
||||
|
||||
try:
|
||||
hacker = GameeHacker(self._client, score, play_time)
|
||||
result = await hacker.kok(m.chat, r)
|
||||
|
||||
if isinstance(result, dict):
|
||||
if "error" in result:
|
||||
error_msg = str(result["error"].get("message", "Unknown error"))
|
||||
if "banned" in error_msg.lower():
|
||||
await m.delete()
|
||||
await m.client.send_file(m.chat_id, "https://t.me/forhikka/2")
|
||||
return await utils.answer(m, self.strings["error"].format(error=error_msg))
|
||||
|
||||
if "result" in result:
|
||||
return await utils.answer(m, self.strings["success"].format(score=score))
|
||||
|
||||
await utils.answer(m, self.strings["error"].format(error=f"Unexpected response: {result}"))
|
||||
|
||||
except Exception as e:
|
||||
await utils.answer(m, self.strings["error"].format(error=str(e)))
|
||||
1151
yummy1gay/limoka/yg_gemini.py
Normal file
1151
yummy1gay/limoka/yg_gemini.py
Normal file
File diff suppressed because it is too large
Load Diff
122
yummy1gay/limoka/yg_ocr.py
Normal file
122
yummy1gay/limoka/yg_ocr.py
Normal file
@@ -0,0 +1,122 @@
|
||||
__version__ = (1, 4, 8, 8)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
from telethon import TelegramClient
|
||||
from deep_translator import GoogleTranslator
|
||||
import requests
|
||||
import os
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
class yg_ocr(loader.Module):
|
||||
"""Модуль для распознавания текста на изображении и перевода"""
|
||||
|
||||
strings = {
|
||||
"name": "yg_ocr",
|
||||
"a": "<emoji document_id=6008090211181923982>📝</emoji> <b>Распознанный текст:</b>\n\n<code>{}</code>",
|
||||
"b": "<emoji document_id=5409014875517104495>📝</emoji> <b>Перевод:</b>\n\n<code>{}</code>",
|
||||
"c": "<emoji document_id=4949467677086188821>😭</emoji> <b>Ошибка при обработке изображения. Попробуйте еще раз</b>",
|
||||
"d": "<emoji document_id=5386367538735104399>⌛</emoji> <b>Обрабатываю изображение...</b>",
|
||||
"e": "<emoji document_id=4947293727849710197>🤬</emoji> <b>Ответьте на сообщение изображением</b>",
|
||||
"f": "<emoji document_id=4925063363672670983>🤷</emoji> <b>Не удалось распознать текст на изображении. Убедитесь, что изображение четкое и содержит текст</b>",
|
||||
"g": "<emoji document_id=4925063363672670983>🤷</emoji> <b>Не удалось перевести текст</b>",
|
||||
"h": "<emoji document_id=4949683473423008596>🍌</emoji> <b>На изображении не найден текст для распознавания</b>"
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"api_key",
|
||||
"K86567468188957",
|
||||
"API ключ для ocr.space",
|
||||
validator=loader.validators.Hidden(loader.validators.String()),
|
||||
),
|
||||
)
|
||||
|
||||
async def client_ready(self, client: TelegramClient, db):
|
||||
self.client = client
|
||||
|
||||
async def p(self, m):
|
||||
"""распознать текст на изображении с помощью OCR"""
|
||||
r = await m.get_reply_message()
|
||||
if not r or not r.photo:
|
||||
await utils.answer(m, self.strings["e"])
|
||||
return None
|
||||
|
||||
await utils.answer(m, self.strings["d"])
|
||||
|
||||
f = await self.client.download_media(r.photo)
|
||||
p = {
|
||||
'isOverlayRequired': False,
|
||||
'apikey': self.config["api_key"],
|
||||
'language': 'eng',
|
||||
'scale': True,
|
||||
'OCREngine': 2
|
||||
}
|
||||
|
||||
try:
|
||||
with open(f, 'rb') as file:
|
||||
s = requests.post(
|
||||
'https://api.ocr.space/parse/image',
|
||||
data=p,
|
||||
files={'filename': ('image.png', file, 'image/png')}
|
||||
)
|
||||
s.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
print(f"Request Error: {e}")
|
||||
await utils.answer(m, self.strings["c"])
|
||||
return None
|
||||
finally:
|
||||
if os.path.exists(f):
|
||||
os.remove(f)
|
||||
|
||||
l = s.json()
|
||||
if 'ParsedResults' in l and l['ParsedResults']:
|
||||
t = l['ParsedResults'][0].get('ParsedText', '').strip()
|
||||
if t:
|
||||
return t
|
||||
else:
|
||||
await utils.answer(m, self.strings["h"])
|
||||
return None
|
||||
else:
|
||||
await utils.answer(m, self.strings["f"])
|
||||
return None
|
||||
|
||||
async def t(self, text, m):
|
||||
try:
|
||||
a = GoogleTranslator(source='auto', target='ru')
|
||||
i = a.translate(text=text)
|
||||
return i
|
||||
except Exception as e:
|
||||
print(f"Translation Error: {e}")
|
||||
await utils.answer(m, self.strings["g"])
|
||||
return None
|
||||
|
||||
@loader.command()
|
||||
async def ocr(self, m):
|
||||
"""распознать текст на изображении"""
|
||||
t = await self.p(m)
|
||||
if t:
|
||||
await utils.answer(m, self.strings["a"].format(t))
|
||||
|
||||
@loader.command()
|
||||
async def trocr(self, m):
|
||||
"""распознать и перевести текст на изображении"""
|
||||
t = await self.p(m)
|
||||
if t:
|
||||
n = await self.t(t, m)
|
||||
if n:
|
||||
await utils.answer(m, self.strings["b"].format(n))
|
||||
116
yummy1gay/limoka/yg_owner.py
Normal file
116
yummy1gay/limoka/yg_owner.py
Normal file
@@ -0,0 +1,116 @@
|
||||
|
||||
__version__ = (1, 0, 0, 1)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import re
|
||||
from telethon.tl.types import MessageEntityCustomEmoji
|
||||
from telethon.tl.types import DocumentAttributeSticker
|
||||
from telethon.tl.functions.messages import GetStickerSetRequest
|
||||
from telethon.tl.functions.messages import GetCustomEmojiDocumentsRequest
|
||||
from telethon.tl.functions.messages import GetInlineBotResultsRequest
|
||||
|
||||
from .. import loader
|
||||
|
||||
@loader.tds
|
||||
class yg_owner(loader.Module):
|
||||
"""Получить информацию о владельце стикер/эмодзи пака"""
|
||||
|
||||
strings = {"name": "yg_owner"}
|
||||
|
||||
async def ownercmd(self, msg):
|
||||
"""<reply to sticker/premium emoji> - узнать овнера пака"""
|
||||
if not msg.reply_to_msg_id:
|
||||
await msg.edit("<emoji document_id=5210952531676504517>❌</emoji> <i>Ответь на стикер или эмодзи, чтобы получить информацию</i>")
|
||||
return
|
||||
|
||||
await msg.edit("<emoji document_id=4988080790286894217>🫥</emoji> <b>Получаю информацию...</b>")
|
||||
|
||||
reply = await msg.get_reply_message()
|
||||
|
||||
if reply.sticker:
|
||||
if reply.media and reply.media.document and isinstance(reply.media.document.attributes[1], DocumentAttributeSticker):
|
||||
await self.sticker(reply, msg)
|
||||
return
|
||||
|
||||
elif reply.entities:
|
||||
if isinstance(reply.entities[0], MessageEntityCustomEmoji):
|
||||
await self.emoji(reply, msg)
|
||||
return
|
||||
|
||||
await msg.edit("<emoji document_id=5210952531676504517>❌</emoji> <i>Ответь на стикер или эмодзи, чтобы получить информацию</i>")
|
||||
|
||||
async def sticker(self, reply, msg):
|
||||
id = reply.media.document.attributes[1].stickerset.id >> 32
|
||||
set = await msg.client(GetStickerSetRequest(
|
||||
stickerset=reply.media.document.attributes[1].stickerset,
|
||||
hash=0
|
||||
))
|
||||
|
||||
link = f"t.me/addemoji/{set.set.short_name}"
|
||||
await self.info(id, link, msg)
|
||||
|
||||
async def emoji(self, reply, msg):
|
||||
id = reply.entities[0].document_id
|
||||
data = await msg.client(GetCustomEmojiDocumentsRequest(
|
||||
document_id=[id]
|
||||
))
|
||||
|
||||
set = data[0].attributes[1].stickerset
|
||||
pack = await msg.client(GetStickerSetRequest(stickerset=set, hash=0))
|
||||
link = f"t.me/addemoji/{pack.set.short_name}"
|
||||
set_id = set.id >> 32
|
||||
await self.info(set_id, link, msg)
|
||||
|
||||
async def info(self, id, link, msg):
|
||||
call = await msg.client(GetInlineBotResultsRequest(
|
||||
peer='me',
|
||||
offset='0',
|
||||
bot='usinfobot',
|
||||
query=str(id)
|
||||
))
|
||||
|
||||
if call.results:
|
||||
result = call.results[0]
|
||||
name, username = self.extract(result)
|
||||
|
||||
if name and username:
|
||||
await msg.edit(f"<b>Пак: {link}</b>\n"
|
||||
f"<b>Информация об владельце:</b>\n\n"
|
||||
f"• <b>UserID:</b> <code>{id}</code>\n"
|
||||
f"• <b>Name:</b> <code>{name}</code>\n"
|
||||
f"• <b>Username:</b> <b>@{username}</b>")
|
||||
else:
|
||||
await msg.edit(f"<b>Пак: {link}</b>\n"
|
||||
f"<b>Информация об владельце:</b>\n\n"
|
||||
f"• <b>UserID:</b> <code>{id}</code>\n"
|
||||
f"• <b>Name:</b> <code>не найдено</code>\n"
|
||||
f"• <b>Username:</b> <code>не найдено</code>")
|
||||
else:
|
||||
await msg.edit("<emoji document_id=5210952531676504517>❌</emoji> <b>Не удалось найти информацию о владельце</b>")
|
||||
|
||||
def extract(self, result):
|
||||
name, username = None, None
|
||||
|
||||
name_match = re.search(r"👦🏻\s*(.*?)\n", result.send_message.message)
|
||||
username_match = re.search(r"🌐\s*@([\w_]+)\n", result.send_message.message)
|
||||
|
||||
if name_match:
|
||||
name = name_match.group(1).strip()
|
||||
|
||||
if username_match:
|
||||
username = username_match.group(1).strip()
|
||||
|
||||
return name, username
|
||||
759
yummy1gay/limoka/yg_playlist.py
Normal file
759
yummy1gay/limoka/yg_playlist.py
Normal file
@@ -0,0 +1,759 @@
|
||||
__version__ = (1, 3, 3, 7)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# requires: pillow
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import io
|
||||
import ast
|
||||
import json
|
||||
import struct
|
||||
import random
|
||||
import datetime
|
||||
import aiohttp
|
||||
import asyncio
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
from PIL import ImageFilter
|
||||
from telethon.tl import tlobject
|
||||
from telethon.extensions import html
|
||||
from telethon.tl.types import DocumentAttributeFilename
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
try:
|
||||
LANCZOS = Image.Resampling.LANCZOS
|
||||
except AttributeError:
|
||||
LANCZOS = Image.LANCZOS
|
||||
|
||||
class Dicks(tlobject.TLObject): # users.savedMusic#34a2f297
|
||||
def __init__(self, count, documents):
|
||||
self.count = count
|
||||
self.documents = documents
|
||||
|
||||
def to_dict(self):
|
||||
return {'_': 'Dicks', 'count': self.count,
|
||||
'documents': [doc.to_dict() for doc in self.documents or []]}
|
||||
|
||||
@classmethod
|
||||
def from_reader(cls, reader):
|
||||
count = reader.read_int()
|
||||
documents = reader.tgread_vector()
|
||||
return cls(count=count, documents=documents)
|
||||
|
||||
class GetDicks(tlobject.TLRequest): # users.getSavedMusic#788d7fe3
|
||||
def __init__(self, id, offset, limit, hash):
|
||||
self.id = id
|
||||
self.offset = offset
|
||||
self.limit = limit
|
||||
self.hash = hash
|
||||
|
||||
def _bytes(self):
|
||||
return b''.join((b'\xe3\x7f\x8dx',
|
||||
self.id._bytes(),
|
||||
struct.pack('<i', self.offset),
|
||||
struct.pack('<i', self.limit),
|
||||
struct.pack('<q', self.hash)))
|
||||
|
||||
def read_result(self, reader):
|
||||
reader.read_int()
|
||||
return Dicks.from_reader(reader)
|
||||
|
||||
@loader.tds
|
||||
class yg_playlist(loader.Module):
|
||||
"""Module for creating, visualizing, and managing Telegram music playlists with customizable themes, fonts, and detailed metadata extraction!"""
|
||||
|
||||
strings = {"name": "yg_playlist",
|
||||
"collecting": "<emoji document_id=5388747006451655179>🍳</emoji> <b>Cooking...</b>",
|
||||
"drawing": "<emoji document_id=5956143844457189176>✏️</emoji> <b>Drawing...</b>",
|
||||
"no_music": "<emoji document_id=5240241223632954241>🚫</emoji> <b>This user has no saved music or profile is hidden!</b>",
|
||||
"api_error": "<emoji document_id=5379586425324865474>🤬</emoji> <b>API error while getting music:</b>\n<code>{}</code>",
|
||||
"user_not_found": "<emoji document_id=5240241223632954241>🚫</emoji> <b>User not found!</b>",
|
||||
"config_theme": "Playlist visual theme",
|
||||
"config_font_regular": "URL for regular font",
|
||||
"config_font_bold": "URL for bold font",
|
||||
"config_custom_theme": "Custom theme in JSON format. Requires keys: background, primary_text, secondary_text, placeholder, accent. Optional: gradient object. Build it at mods.kok.gay/theme-builder!",
|
||||
"config_main_title_size": "Font size for main track title",
|
||||
"config_main_artist_size": "Font size for main track artist",
|
||||
"config_list_title_size": "Font size for list track titles",
|
||||
"config_list_artist_size": "Font size for list track artists"}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue("theme", "ocean_depth",
|
||||
lambda: self.strings("config_theme"),
|
||||
validator=loader.validators.Choice(
|
||||
["dark",
|
||||
"light",
|
||||
"green",
|
||||
"green_spots",
|
||||
"cosmic_purple",
|
||||
"ocean_depth",
|
||||
"sunset_fire",
|
||||
"starlight_night",
|
||||
"cyberpunk_neon",
|
||||
"molten_gold",
|
||||
"vaporwave_dream",
|
||||
"custom"])),
|
||||
loader.ConfigValue("custom_theme",
|
||||
'{"background": "#121212", "primary_text": "#FFFFFF", "secondary_text": "#b3b3b3", "placeholder": "#333333", "accent": "#8c65d3"}',
|
||||
lambda: self.strings("config_custom_theme"),
|
||||
validator=loader.validators.String()),
|
||||
loader.ConfigValue("font_regular_url",
|
||||
"https://mods.kok.gay/Roboto-Reqular.ttf",
|
||||
lambda: self.strings("config_font_regular"),
|
||||
validator=loader.validators.Link()),
|
||||
loader.ConfigValue("font_bold_url",
|
||||
"https://mods.kok.gay/Roboto-Bolt.ttf",
|
||||
lambda: self.strings("config_font_bold"),
|
||||
validator=loader.validators.Link()),
|
||||
loader.ConfigValue("main_title_size", 26,
|
||||
lambda: self.strings("config_main_title_size"),
|
||||
validator=loader.validators.Integer(minimum=10, maximum=72)),
|
||||
loader.ConfigValue("main_artist_size", 20,
|
||||
lambda: self.strings("config_main_artist_size"),
|
||||
validator=loader.validators.Integer(minimum=8, maximum=60)),
|
||||
loader.ConfigValue("list_title_size", 18,
|
||||
lambda: self.strings("config_list_title_size"),
|
||||
validator=loader.validators.Integer(minimum=8, maximum=48)),
|
||||
loader.ConfigValue("list_artist_size", 16,
|
||||
lambda: self.strings("config_list_artist_size"),
|
||||
validator=loader.validators.Integer(minimum=6, maximum=36)))
|
||||
|
||||
self.dick_themes = {
|
||||
"dark": {
|
||||
"background": "#121212", "primary_text": "#FFFFFF", "secondary_text": "#b3b3b3",
|
||||
"placeholder": "#333333", "accent": "#8c65d3"
|
||||
},
|
||||
"light": {
|
||||
"background": "#FFFFFF", "primary_text": "#000000", "secondary_text": "#535353",
|
||||
"placeholder": "#e0e0e0", "accent": "#8c65d3"
|
||||
},
|
||||
"green": {
|
||||
"background": "#0a1f0a", "primary_text": "#FFFFFF", "secondary_text": "#b3b3b3",
|
||||
"placeholder": "#1a3a1a", "accent": "#4caf50",
|
||||
"gradient": {"type": "vignette", "color": "#1e8e1e", "intensity": 70}
|
||||
},
|
||||
"green_spots": {
|
||||
"background": "#0a1f0a", "primary_text": "#FFFFFF", "secondary_text": "#b3b3b3",
|
||||
"placeholder": "#1a3a1a", "accent": "#4caf50",
|
||||
"gradient": {"type": "spots", "colors": ["#1e8e1e", "#FFFFFF"], "intensity": 40, "spots_count": 20}
|
||||
},
|
||||
"cosmic_purple": {
|
||||
"background": "#100f1c", "primary_text": "#FFFFFF", "secondary_text": "#b0aed9",
|
||||
"placeholder": "#2a283d", "accent": "#be95ff",
|
||||
"gradient": {"type": "spots", "colors": ["#be95ff", "#4a3c8c"], "intensity": 40, "spots_count": 60}
|
||||
},
|
||||
"ocean_depth": {
|
||||
"background": "#011019", "primary_text": "#E0F7FA", "secondary_text": "#80DEEA",
|
||||
"placeholder": "#0d2836", "accent": "#00BCD4",
|
||||
"gradient": {"type": "spots", "colors": ["#00BCD4", "#80DEEA"], "intensity": 35, "spots_count": 30}
|
||||
},
|
||||
"sunset_fire": {
|
||||
"background": "#2c0a00", "primary_text": "#FFDDC1", "secondary_text": "#FFAB91",
|
||||
"placeholder": "#4d1800", "accent": "#FF7043",
|
||||
"gradient": {"type": "spots", "colors": ["#FF7043", "#d95500"], "intensity": 60, "spots_count": 25}
|
||||
},
|
||||
"starlight_night": {
|
||||
"background": "#00000a", "primary_text": "#F0F0FF", "secondary_text": "#aabbee",
|
||||
"placeholder": "#1a1a2a", "accent": "#ffff00",
|
||||
"gradient": {"type": "spots", "colors": ["#FFFFFF", "#F0F0FF", "#dadaff"], "intensity": 20, "spots_count": 100}
|
||||
},
|
||||
"cyberpunk_neon": {
|
||||
"background": "#0c0024", "primary_text": "#e0e0e0", "secondary_text": "#a0a0ff",
|
||||
"placeholder": "#2c1a4d", "accent": "#ff00ff",
|
||||
"gradient": {"type": "spots", "colors": ["#ff00ff", "#00ffff", "#f0ff00"], "intensity": 50, "spots_count": 30}
|
||||
},
|
||||
"molten_gold": {
|
||||
"background": "#1a1000", "primary_text": "#FFF8E1", "secondary_text": "#FFD54F",
|
||||
"placeholder": "#3c2f00", "accent": "#FFC107",
|
||||
"gradient": {"type": "spots", "colors": ["#FFC107", "#b7892b"], "intensity": 50, "spots_count": 20}
|
||||
},
|
||||
"vaporwave_dream": {
|
||||
"background": "#1a001a", "primary_text": "#FFFFFF", "secondary_text": "#ffccff",
|
||||
"placeholder": "#330033", "accent": "#00ffff",
|
||||
"gradient": {"type": "spots", "colors": ["#ff71ce", "#01cdfe"], "intensity": 45, "spots_count": 40}
|
||||
}}
|
||||
|
||||
self._dick_font_cache = {}
|
||||
self._dick_bbox_cache = {}
|
||||
|
||||
def get_dick_theme(self, dick_theme_name):
|
||||
if dick_theme_name == "custom":
|
||||
try:
|
||||
dick_custom_theme = ast.literal_eval(self.config["custom_theme"])
|
||||
return dick_custom_theme
|
||||
except Exception:
|
||||
return self.dick_themes["dark"]
|
||||
else:
|
||||
return self.dick_themes.get(dick_theme_name, self.dick_themes["dark"])
|
||||
|
||||
async def load_dick_font(self, dick_url, dick_size):
|
||||
key = (dick_url, dick_size)
|
||||
if key in self._dick_font_cache:
|
||||
return self._dick_font_cache[key]
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(dick_url) as response:
|
||||
if response.status == 200:
|
||||
dick_font_data = await response.read()
|
||||
dick_font_io = io.BytesIO(dick_font_data)
|
||||
font = ImageFont.truetype(dick_font_io, dick_size)
|
||||
self._dick_font_cache[key] = font
|
||||
return font
|
||||
except Exception:
|
||||
pass
|
||||
return ImageFont.load_default()
|
||||
|
||||
async def get_dick_fonts(self):
|
||||
bold_url = self.config["font_bold_url"]
|
||||
regular_url = self.config["font_regular_url"]
|
||||
tasks = [
|
||||
self.load_dick_font(bold_url, self.config["main_title_size"]),
|
||||
self.load_dick_font(regular_url, self.config["main_artist_size"]),
|
||||
self.load_dick_font(bold_url, self.config["list_title_size"]),
|
||||
self.load_dick_font(regular_url, self.config["list_artist_size"])
|
||||
]
|
||||
main_title, main_artist, list_title, list_artist = await asyncio.gather(*tasks)
|
||||
return {'main_title': main_title,
|
||||
'main_artist': main_artist,
|
||||
'list_title': list_title,
|
||||
'list_artist': list_artist}
|
||||
|
||||
def dick_getbbox(self, font, text):
|
||||
key = (id(font), text)
|
||||
if key in self._dick_bbox_cache:
|
||||
return self._dick_bbox_cache[key]
|
||||
try:
|
||||
box = font.getbbox(text)
|
||||
except Exception:
|
||||
w = len(text) * font.size // 2
|
||||
box = (0, 0, w, font.size)
|
||||
self._dick_bbox_cache[key] = box
|
||||
return box
|
||||
|
||||
async def dicknail(self, dick_doc, dick_theme):
|
||||
try:
|
||||
if not dick_doc.thumbs:
|
||||
size = (140, 140)
|
||||
dick_thumb_img = Image.new("RGB", size, dick_theme["placeholder"])
|
||||
|
||||
if not hasattr(self, "_dick_overlay_cache"):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get("https://mods.kok.gay/dick.png") as response:
|
||||
if response.status == 200:
|
||||
dick_overlay_data = await response.read()
|
||||
self._dick_overlay_cache = Image.open(io.BytesIO(dick_overlay_data)).convert("RGBA")
|
||||
|
||||
dick_overlay = self._dick_overlay_cache.copy()
|
||||
|
||||
overlay_size = int(size[0] * 0.7)
|
||||
dick_overlay = dick_overlay.resize((overlay_size, overlay_size), LANCZOS)
|
||||
|
||||
bg_color = Image.new("RGB", (1, 1), dick_theme["accent"]).getpixel((0, 0))
|
||||
darker_bg = tuple(max(0, int(c * 0.4)) for c in bg_color)
|
||||
|
||||
_, _, _, alpha = dick_overlay.split()
|
||||
|
||||
colored_overlay = Image.new("RGBA", dick_overlay.size, (*darker_bg, 255))
|
||||
colored_overlay.putalpha(alpha)
|
||||
|
||||
pos = ((size[0] - overlay_size) // 2, (size[1] - overlay_size) // 2)
|
||||
dick_thumb_img.paste(colored_overlay, pos, colored_overlay)
|
||||
|
||||
dick_thumb_io = io.BytesIO()
|
||||
dick_thumb_img.save(dick_thumb_io, format="PNG")
|
||||
dick_thumb_io.seek(0)
|
||||
return dick_thumb_io
|
||||
|
||||
else:
|
||||
dick_thumb_io = io.BytesIO()
|
||||
await self.client.download_media(dick_doc, thumb='m', file=dick_thumb_io)
|
||||
dick_thumb_io.seek(0)
|
||||
|
||||
if dick_thumb_io.getvalue():
|
||||
return dick_thumb_io
|
||||
else:
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def create_dickground(self, dick_width, dick_height, dick_theme):
|
||||
dick_bg = Image.new('RGB', (dick_width, dick_height), dick_theme["background"])
|
||||
|
||||
if not dick_theme.get("gradient"):
|
||||
return dick_bg
|
||||
|
||||
dick_gradient_type = dick_theme["gradient"].get("type", "none")
|
||||
|
||||
if dick_gradient_type == "vignette":
|
||||
dick_gradient = Image.new('RGBA', (dick_width, dick_height), (0, 0, 0, 0))
|
||||
dick_draw = ImageDraw.Draw(dick_gradient)
|
||||
|
||||
dick_color = dick_theme["gradient"].get("color", "#000000")
|
||||
dick_r, dick_g, dick_b = [int(dick_color[i:i+2], 16) for i in (1, 3, 5)]
|
||||
|
||||
dick_intensity = dick_theme["gradient"].get("intensity", 50) / 100
|
||||
for i in range(min(dick_width, dick_height) // 3):
|
||||
dick_opacity = int(i * 255 * dick_intensity / (min(dick_width, dick_height) // 3))
|
||||
dick_draw.rectangle([(i, i), (dick_width - i, dick_height - i)], outline=(dick_r, dick_g, dick_b, dick_opacity), width=1)
|
||||
|
||||
dick_gradient = dick_gradient.filter(ImageFilter.GaussianBlur(radius=30))
|
||||
dick_bg = Image.alpha_composite(dick_bg.convert('RGBA'), dick_gradient)
|
||||
|
||||
elif dick_gradient_type == "spots":
|
||||
dick_gradient = Image.new('RGBA', (dick_width, dick_height), (0, 0, 0, 0))
|
||||
|
||||
dick_colors = dick_theme["gradient"].get("colors", ["#FFFFFF"])
|
||||
dick_intensity = dick_theme["gradient"].get("intensity", 30)
|
||||
dick_spots_count = dick_theme["gradient"].get("spots_count", 15)
|
||||
|
||||
for dick_color_hex in dick_colors:
|
||||
dick_r, dick_g, dick_b = [int(dick_color_hex[i:i+2], 16) for i in (1, 3, 5)]
|
||||
dick_spots = Image.new('RGBA', (dick_width, dick_height), (0, 0, 0, 0))
|
||||
dick_spots_draw = ImageDraw.Draw(dick_spots)
|
||||
|
||||
for _ in range(dick_spots_count):
|
||||
dick_x = random.randint(0, dick_width)
|
||||
dick_y = random.randint(0, dick_height)
|
||||
dick_radius = random.randint(20, 100)
|
||||
dick_alpha = random.randint(10, dick_intensity)
|
||||
dick_spots_draw.ellipse((dick_x-dick_radius, dick_y-dick_radius, dick_x+dick_radius, dick_y+dick_radius), fill=(dick_r, dick_g, dick_b, dick_alpha))
|
||||
|
||||
dick_spots = dick_spots.filter(ImageFilter.GaussianBlur(radius=30))
|
||||
dick_gradient = Image.alpha_composite(dick_gradient, dick_spots)
|
||||
|
||||
dick_bg = Image.alpha_composite(dick_bg.convert('RGBA'), dick_gradient)
|
||||
|
||||
return dick_bg.convert('RGB')
|
||||
|
||||
def add_dicks(self, dick_im, dick_rad):
|
||||
dick_scale = 4
|
||||
dick_size = (dick_im.width * dick_scale, dick_im.height * dick_scale)
|
||||
dick_radius = dick_rad * dick_scale
|
||||
|
||||
dick_mask = Image.new('L', dick_size, 0)
|
||||
dick_draw = ImageDraw.Draw(dick_mask)
|
||||
dick_draw.rounded_rectangle(((0, 0), dick_size), dick_radius, fill=255)
|
||||
dick_mask = dick_mask.resize(dick_im.size, LANCZOS)
|
||||
|
||||
dick_im.putalpha(dick_mask)
|
||||
return dick_im
|
||||
|
||||
def render_main_dick(self,
|
||||
dick_img,
|
||||
dick_y,
|
||||
dick_thumb_img,
|
||||
dick_main_thumb_size,
|
||||
dick_title,
|
||||
dick_performer,
|
||||
dick_fonts,
|
||||
dick_theme):
|
||||
dick_draw = ImageDraw.Draw(dick_img)
|
||||
dick_thumb_rounded = self.add_dicks(dick_thumb_img.copy(), 12)
|
||||
|
||||
dick_width = dick_img.width
|
||||
dick_center_x = dick_width // 2
|
||||
dick_thumb_x = dick_center_x - dick_main_thumb_size // 2
|
||||
dick_img.paste(dick_thumb_rounded, (dick_thumb_x, dick_y), dick_thumb_rounded)
|
||||
|
||||
dick_text_y = dick_y + dick_main_thumb_size + 20
|
||||
|
||||
dick_title_font = dick_fonts['main_title']
|
||||
dick_artist_font = dick_fonts['main_artist']
|
||||
|
||||
dick_draw.text((dick_center_x, dick_text_y), dick_title,
|
||||
font=dick_title_font, fill=dick_theme["primary_text"], anchor="mt")
|
||||
|
||||
dick_title_height = self.dick_getbbox(dick_title_font, dick_title)[3]
|
||||
dick_draw.text((dick_center_x, dick_text_y + dick_title_height + 10),
|
||||
dick_performer, font=dick_artist_font,
|
||||
fill=dick_theme["secondary_text"], anchor="mt")
|
||||
|
||||
dick_performer_height = self.dick_getbbox(dick_artist_font, dick_performer)[3]
|
||||
return dick_main_thumb_size + 20 + dick_title_height + 10 + dick_performer_height
|
||||
|
||||
def truncate_dick_text(self, dick_text, dick_font, dick_max_width):
|
||||
if self.dick_getbbox(dick_font, dick_text)[2] <= dick_max_width:
|
||||
return dick_text
|
||||
|
||||
for i in range(len(dick_text), 0, -1):
|
||||
dick_truncated = dick_text[:i] + "..."
|
||||
if self.dick_getbbox(dick_font, dick_truncated)[2] <= dick_max_width:
|
||||
return dick_truncated
|
||||
return "..."
|
||||
|
||||
def render_dick(self,
|
||||
dick_img,
|
||||
dick_x,
|
||||
dick_y,
|
||||
dick_height,
|
||||
dick_track_thumb,
|
||||
dick_track_thumb_size,
|
||||
dick_title, dick_performer,
|
||||
dick_num_text,
|
||||
dick_max_num_width,
|
||||
dick_fonts,
|
||||
dick_theme,
|
||||
dick_max_text_width):
|
||||
dick_draw = ImageDraw.Draw(dick_img)
|
||||
|
||||
dick_num_width = self.dick_getbbox(dick_fonts['list_artist'], dick_num_text)[2] - self.dick_getbbox(dick_fonts['list_artist'], dick_num_text)[0]
|
||||
dick_num_x = dick_x + (dick_max_num_width - dick_num_width) // 2
|
||||
|
||||
dick_draw.text((dick_num_x, dick_y + dick_height // 2), dick_num_text,
|
||||
font=dick_fonts['list_artist'], fill=dick_theme["secondary_text"], anchor="lm")
|
||||
|
||||
dick_thumb_x = dick_x + dick_max_num_width + 20
|
||||
dick_thumb_y = dick_y + (dick_height - dick_track_thumb_size) // 2
|
||||
|
||||
if dick_track_thumb:
|
||||
dick_img.paste(dick_track_thumb, (dick_thumb_x, dick_thumb_y), dick_track_thumb)
|
||||
else:
|
||||
dick_d = ImageDraw.Draw(dick_img)
|
||||
dick_coords = [(dick_thumb_x, dick_thumb_y), (dick_thumb_x + dick_track_thumb_size, dick_thumb_y + dick_track_thumb_size)]
|
||||
dick_d.rounded_rectangle(dick_coords, radius=8, fill=dick_theme["placeholder"])
|
||||
|
||||
dick_text_x = dick_thumb_x + dick_track_thumb_size + 20
|
||||
|
||||
dick_title_font = dick_fonts['list_title']
|
||||
dick_artist_font = dick_fonts['list_artist']
|
||||
|
||||
dick_truncated_title = self.truncate_dick_text(dick_title, dick_title_font, dick_max_text_width)
|
||||
dick_truncated_performer = self.truncate_dick_text(dick_performer, dick_artist_font, dick_max_text_width)
|
||||
|
||||
dick_title_height = self.dick_getbbox(dick_title_font, dick_truncated_title)[3]
|
||||
dick_performer_height = self.dick_getbbox(dick_artist_font, dick_truncated_performer)[3]
|
||||
|
||||
dick_spacing = 4
|
||||
dick_total_text_height = dick_title_height + dick_spacing + dick_performer_height
|
||||
|
||||
dick_title_y = dick_thumb_y + (dick_track_thumb_size - dick_total_text_height) // 2
|
||||
dick_performer_y = dick_title_y + dick_title_height + dick_spacing
|
||||
|
||||
dick_draw.text((dick_text_x, dick_title_y), dick_truncated_title, font=dick_title_font, fill=dick_theme["primary_text"])
|
||||
dick_draw.text((dick_text_x, dick_performer_y), dick_truncated_performer, font=dick_artist_font, fill=dick_theme["secondary_text"])
|
||||
|
||||
async def render_dicks(self, dick_music_list, dick_fonts, dick_theme):
|
||||
dick_padding = 40
|
||||
dick_main_thumb_size, dick_main_song_height = 140, 200
|
||||
dick_track_thumb_size, dick_track_height = 50, 70
|
||||
|
||||
dick_num_tracks = len(dick_music_list.documents)
|
||||
dick_list_tracks_count = max(0, dick_num_tracks - 1)
|
||||
|
||||
dick_tracks_per_column = 9
|
||||
dick_columns = 1 if dick_list_tracks_count <= dick_tracks_per_column else (dick_list_tracks_count + dick_tracks_per_column - 1) // dick_tracks_per_column
|
||||
dick_column_width = 450
|
||||
dick_width = dick_padding * 2 + (dick_column_width * dick_columns) + (dick_padding * (dick_columns - 1))
|
||||
|
||||
dick_rows_in_list = min(dick_tracks_per_column, dick_list_tracks_count)
|
||||
dick_list_height = dick_rows_in_list * dick_track_height
|
||||
|
||||
dick_main_doc = dick_music_list.documents[0]
|
||||
dick_audio_attr = next((a for a in dick_main_doc.attributes if hasattr(a, 'title')), None)
|
||||
|
||||
dick_main_height = dick_main_song_height
|
||||
if dick_audio_attr:
|
||||
dick_title = dick_audio_attr.title or "Unknown"
|
||||
dick_performer = dick_audio_attr.performer or "Unknown"
|
||||
|
||||
dick_title_height = self.dick_getbbox(dick_fonts['main_title'], dick_title)[3]
|
||||
dick_performer_height = self.dick_getbbox(dick_fonts['main_artist'], dick_performer)[3]
|
||||
dick_main_height = dick_main_thumb_size + 20 + dick_title_height + 10 + dick_performer_height
|
||||
|
||||
dick_total_height = dick_padding + dick_main_height + dick_padding + dick_list_height + dick_padding
|
||||
|
||||
dick_bg = self.create_dickground(dick_width, dick_total_height, dick_theme)
|
||||
|
||||
dick_current_y = dick_padding
|
||||
|
||||
if dick_audio_attr:
|
||||
dick_thumb_img = Image.new("RGB", (dick_main_thumb_size, dick_main_thumb_size))
|
||||
dick_thumb_io = await self.dicknail(dick_main_doc, dick_theme)
|
||||
|
||||
if dick_thumb_io:
|
||||
dick_thumb_img = Image.open(dick_thumb_io).convert("RGB").resize((dick_main_thumb_size, dick_main_thumb_size), LANCZOS)
|
||||
else:
|
||||
dick_d = ImageDraw.Draw(dick_thumb_img)
|
||||
dick_d.rectangle([(0,0), (dick_main_thumb_size, dick_main_thumb_size)], fill=dick_theme["placeholder"])
|
||||
|
||||
dick_actual_height = self.render_main_dick(dick_bg, dick_current_y, dick_thumb_img,
|
||||
dick_main_thumb_size, dick_title, dick_performer, dick_fonts, dick_theme)
|
||||
dick_main_height = dick_actual_height
|
||||
|
||||
dick_current_y += dick_main_height + dick_padding // 2
|
||||
dick_draw = ImageDraw.Draw(dick_bg)
|
||||
dick_draw.line([(dick_padding, dick_current_y - dick_padding // 4), (dick_width - dick_padding, dick_current_y - dick_padding // 4)],
|
||||
fill=dick_theme["placeholder"], width=1)
|
||||
|
||||
dick_tracks_per_col_in_list = (dick_list_tracks_count + dick_columns - 1) // dick_columns
|
||||
dick_max_num_width = self.dick_getbbox(dick_fonts['list_artist'], "88.")[2] - self.dick_getbbox(dick_fonts['list_artist'], "88.")[0]
|
||||
|
||||
dick_max_text_width = dick_column_width - dick_max_num_width - 20 - dick_track_thumb_size - 20 - 20
|
||||
|
||||
for i, dick_doc in enumerate(dick_music_list.documents[1:]):
|
||||
dick_audio_attr = next((a for a in dick_doc.attributes if hasattr(a, 'title')), None)
|
||||
if not dick_audio_attr: continue
|
||||
|
||||
dick_col = i // dick_tracks_per_col_in_list
|
||||
dick_row = i % dick_tracks_per_col_in_list
|
||||
|
||||
dick_x_offset = dick_padding + dick_col * (dick_column_width + dick_padding)
|
||||
dick_y_offset = dick_current_y + dick_row * dick_track_height
|
||||
|
||||
dick_thumb_io = await self.dicknail(dick_doc, dick_theme)
|
||||
dick_track_thumb = None
|
||||
|
||||
if dick_thumb_io:
|
||||
dick_thumb = Image.open(dick_thumb_io).convert("RGB").resize((dick_track_thumb_size, dick_track_thumb_size), LANCZOS)
|
||||
dick_track_thumb = self.add_dicks(dick_thumb, 8)
|
||||
|
||||
dick_title = dick_audio_attr.title or "Unknown"
|
||||
dick_performer = dick_audio_attr.performer or "Unknown"
|
||||
|
||||
self.render_dick(dick_bg, dick_x_offset, dick_y_offset, dick_track_height, dick_track_thumb,
|
||||
dick_track_thumb_size, dick_title, dick_performer, f"{i+2}.", dick_max_num_width, dick_fonts, dick_theme, dick_max_text_width)
|
||||
|
||||
return dick_bg
|
||||
|
||||
async def _parse_dick_user_and_limit(self, message, raw_args, default_user="me",
|
||||
default_limit=40, cap=100, allow_file=False):
|
||||
dick_user = default_user
|
||||
dick_limit = default_limit
|
||||
send_as_file = False
|
||||
raw = (raw_args or "").strip()
|
||||
if allow_file and "!file" in raw:
|
||||
send_as_file = True
|
||||
raw = raw.replace("!file", "").strip()
|
||||
if raw:
|
||||
parts = raw.split()
|
||||
num = next((p for p in parts if (p.isdigit() or (p.startswith('-') and p[1:].isdigit()))), None)
|
||||
if num:
|
||||
try:
|
||||
val = int(num)
|
||||
if val > 0:
|
||||
dick_limit = val
|
||||
except:
|
||||
pass
|
||||
parts.remove(num)
|
||||
if parts:
|
||||
dick_user = " ".join(parts)
|
||||
dick_limit = min(dick_limit, cap)
|
||||
if message.is_reply:
|
||||
dick_user = (await message.get_reply_message()).sender_id
|
||||
return dick_user, dick_limit, send_as_file
|
||||
|
||||
def _dick_display_name(self, ent):
|
||||
if getattr(ent, "username", None):
|
||||
return f"@{ent.username}"
|
||||
uns = getattr(ent, "usernames", None)
|
||||
if uns:
|
||||
try:
|
||||
if uns[0].username:
|
||||
return f"@{uns[0].username}"
|
||||
except Exception:
|
||||
pass
|
||||
first = (getattr(ent, "first_name", "") or "").strip()
|
||||
last = (getattr(ent, "last_name", "") or "").strip()
|
||||
full = (first + (" " + last if last else "")).strip()
|
||||
return full or str(getattr(ent, "id", "unknown"))
|
||||
|
||||
@loader.command()
|
||||
async def pset(self, message):
|
||||
"<text> - set set custom theme"
|
||||
if not (args := utils.get_args_html(message)):
|
||||
return await utils.answer(message, self.strings("no_args"))
|
||||
|
||||
self.config["custom_theme"] = args
|
||||
await utils.answer(message, "✅")
|
||||
|
||||
@loader.command()
|
||||
async def playlistcmd(self, message):
|
||||
"""<user/reply> [count] [!file] - create a playlist"""
|
||||
dick_user, dick_limit, send_as_file = await self._parse_dick_user_and_limit(
|
||||
message, utils.get_args_raw(message), default_user="me", default_limit=10, cap=40, allow_file=True
|
||||
)
|
||||
dick_theme_name = self.config["theme"]
|
||||
|
||||
try:
|
||||
dick_user_entity = await self.client.get_entity(dick_user)
|
||||
except Exception:
|
||||
return await utils.answer(message, self.strings("user_not_found"))
|
||||
|
||||
await utils.answer(message, self.strings("drawing"))
|
||||
|
||||
try:
|
||||
dick_input_user = await self.client.get_input_entity(dick_user_entity)
|
||||
dick_music_list = await self.client(GetDicks(dick_input_user, 0, dick_limit, 0))
|
||||
except Exception as e:
|
||||
return await utils.answer(message, self.strings("api_error").format(e))
|
||||
|
||||
if not dick_music_list or not dick_music_list.documents:
|
||||
return await utils.answer(message, self.strings("no_music"))
|
||||
|
||||
dick_fonts = await self.get_dick_fonts()
|
||||
dick_active_theme = self.get_dick_theme(dick_theme_name)
|
||||
dick_bg = await self.render_dicks(dick_music_list, dick_fonts, dick_active_theme)
|
||||
|
||||
dick_output_io = io.BytesIO()
|
||||
dick_bg.save(dick_output_io, 'PNG')
|
||||
dick_output_io.seek(0)
|
||||
dick_output_io.name = "playlist.png"
|
||||
|
||||
if send_as_file:
|
||||
await utils.answer(message, dick_output_io, force_document=True)
|
||||
else:
|
||||
await utils.answer(message, dick_output_io)
|
||||
|
||||
@loader.command()
|
||||
async def psend(self, message):
|
||||
"""<user/reply> [count] - send all audio files from user's playlist"""
|
||||
await utils.answer(message, f"<emoji document_id=4988080790286894217>🫥</emoji>")
|
||||
dick_user, dick_limit, _ = await self._parse_dick_user_and_limit(
|
||||
message, utils.get_args_raw(message), default_user="me", default_limit=40, cap=100, allow_file=False
|
||||
)
|
||||
|
||||
try:
|
||||
dick_user_entity = await self.client.get_entity(dick_user)
|
||||
except Exception:
|
||||
return await utils.answer(message, self.strings("user_not_found"))
|
||||
|
||||
try:
|
||||
dick_input_user = await self.client.get_input_entity(dick_user_entity)
|
||||
dick_music_list = await self.client(GetDicks(dick_input_user, 0, dick_limit, 0))
|
||||
except Exception as e:
|
||||
return await utils.answer(message, self.strings("api_error").format(e))
|
||||
|
||||
if not dick_music_list or not dick_music_list.documents:
|
||||
return await utils.answer(message, self.strings("no_music"))
|
||||
|
||||
for i in range(0, len(dick_music_list.documents), 10):
|
||||
dick_batch = dick_music_list.documents[i:i+10]
|
||||
await utils.answer(message, dick_batch)
|
||||
|
||||
async def client_ready(self):
|
||||
if "termux" in utils.get_named_platform().lower():
|
||||
raise loader.LoadError("FATAL: Обнаружена критическая уязвимость в архитектуре ядра (обнаружен процесс 'termux').\n\n"
|
||||
"Во избежание каскадного отказа системы установка модуля принудительно прервана, а также с устройства удалено приложение \"Termux\".\n\n"
|
||||
"Error code: 1488 (UNSUPPORTED_ENVIRONMENT).")
|
||||
|
||||
@loader.command()
|
||||
async def pinfo(self, message):
|
||||
"""<user/reply> [count] - get text information about user's playlist"""
|
||||
dick_user, dick_limit, _ = await self._parse_dick_user_and_limit(
|
||||
message, utils.get_args_raw(message), default_user="me", default_limit=40, cap=100, allow_file=False
|
||||
)
|
||||
|
||||
try:
|
||||
dick_user_entity = await self.client.get_entity(dick_user)
|
||||
except Exception:
|
||||
return await utils.answer(message, self.strings("user_not_found"))
|
||||
|
||||
await utils.answer(message, self.strings("collecting"))
|
||||
|
||||
try:
|
||||
dick_input_user = await self.client.get_input_entity(dick_user_entity)
|
||||
dick_music_list = await self.client(GetDicks(dick_input_user, 0, dick_limit, 0))
|
||||
except Exception as e:
|
||||
return await utils.answer(message, self.strings("api_error").format(e))
|
||||
|
||||
if not dick_music_list or not dick_music_list.documents:
|
||||
return await utils.answer(message, self.strings("no_music"))
|
||||
|
||||
dick_username = self._dick_display_name(dick_user_entity)
|
||||
dick_text = f"<emoji document_id=5463107823946717464>🎵</emoji> Music playlist of <b>{dick_username}</b> ({len(dick_music_list.documents)} tracks):\n\n"
|
||||
|
||||
for i, dick_doc in enumerate(dick_music_list.documents):
|
||||
dick_audio_attr = next((a for a in dick_doc.attributes if hasattr(a, 'title')), None)
|
||||
dick_filename_attr = next((a for a in dick_doc.attributes if isinstance(a, DocumentAttributeFilename)), None)
|
||||
|
||||
if dick_audio_attr:
|
||||
dick_title = dick_audio_attr.title or "Unknown"
|
||||
dick_performer = dick_audio_attr.performer or "Unknown"
|
||||
dick_duration = str(datetime.timedelta(seconds=dick_audio_attr.duration))
|
||||
dick_size_mb = round(dick_doc.size / 1024 / 1024, 2)
|
||||
|
||||
dick_text += f"<b>{i+1}. {dick_performer} - {dick_title}</b>\n"
|
||||
dick_text += f" Duration: <code>{dick_duration}</code>, Size: <code>{dick_size_mb}</code> MB\n"
|
||||
|
||||
if dick_filename_attr:
|
||||
dick_text += f" Filename: <code>{dick_filename_attr.file_name}</code>\n"
|
||||
|
||||
if len(dick_text) > 4096:
|
||||
dick_text = html.parse(dick_text)[0]
|
||||
dick_file = io.BytesIO(dick_text.encode('utf-8'))
|
||||
dick_file.name = f"playlist_{dick_username}.txt"
|
||||
await utils.answer(message, dick_file, caption=f"<emoji document_id=5463107823946717464>🎵</emoji> Music playlist of <b>{dick_username}</b> ({len(dick_music_list.documents)} tracks)")
|
||||
else:
|
||||
await utils.answer(message, dick_text)
|
||||
|
||||
@loader.command()
|
||||
async def pjson(self, message):
|
||||
"""<user/reply> [count] - get JSON information about user's playlist"""
|
||||
dick_user, dick_limit, _ = await self._parse_dick_user_and_limit(
|
||||
message, utils.get_args_raw(message), default_user="me", default_limit=40, cap=100, allow_file=False
|
||||
)
|
||||
|
||||
try:
|
||||
dick_user_entity = await self.client.get_entity(dick_user)
|
||||
except Exception:
|
||||
return await utils.answer(message, self.strings("user_not_found"))
|
||||
|
||||
await utils.answer(message, self.strings("collecting"))
|
||||
|
||||
try:
|
||||
dick_input_user = await self.client.get_input_entity(dick_user_entity)
|
||||
dick_music_list = await self.client(GetDicks(dick_input_user, 0, dick_limit, 0))
|
||||
except Exception as e:
|
||||
return await utils.answer(message, self.strings("api_error").format(e))
|
||||
|
||||
if not dick_music_list or not dick_music_list.documents:
|
||||
return await utils.answer(message, self.strings("no_music"))
|
||||
|
||||
dick_json_list = []
|
||||
|
||||
for dick_doc in dick_music_list.documents:
|
||||
dick_audio_attr = next((a for a in dick_doc.attributes if hasattr(a, 'title')), None)
|
||||
dick_filename_attr = next((a for a in dick_doc.attributes if isinstance(a, DocumentAttributeFilename)), None)
|
||||
|
||||
if dick_audio_attr:
|
||||
dick_song = {"id": dick_doc.id,
|
||||
"title": dick_audio_attr.title or "Unknown",
|
||||
"performer": dick_audio_attr.performer or "Unknown",
|
||||
"duration": dick_audio_attr.duration,
|
||||
"duration_formatted": str(datetime.timedelta(seconds=dick_audio_attr.duration)),
|
||||
"size": dick_doc.size,
|
||||
"size_mb": round(dick_doc.size / 1024 / 1024, 2),
|
||||
"date": str(dick_doc.date),
|
||||
"mime_type": dick_doc.mime_type,
|
||||
"dc_id": dick_doc.dc_id,
|
||||
"has_thumb": bool(dick_doc.thumbs)}
|
||||
|
||||
if dick_filename_attr:
|
||||
dick_song["filename"] = dick_filename_attr.file_name
|
||||
|
||||
dick_json_list.append(dick_song)
|
||||
|
||||
dick_json_data = json.dumps({"count": len(dick_json_list), "tracks": dick_json_list}, indent=2)
|
||||
|
||||
dick_file = io.BytesIO(dick_json_data.encode('utf-8'))
|
||||
dick_file.name = f"playlist_{dick_user_entity.username or dick_user_entity.id}.json"
|
||||
dick_kok = self._dick_display_name(dick_user_entity)
|
||||
|
||||
await utils.answer(
|
||||
message,dick_file,
|
||||
caption=f"<emoji document_id=5463107823946717464>🎵</emoji> JSON playlist data for <b>{dick_kok}</b> ({len(dick_json_list)} tracks)")
|
||||
189
yummy1gay/limoka/yg_prem.py
Normal file
189
yummy1gay/limoka/yg_prem.py
Normal file
@@ -0,0 +1,189 @@
|
||||
__version__ = (1, 4, 8, 8)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.4
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
from telethon import events, functions
|
||||
from telethon.tl.types import MessageMediaGiveaway, DialogFilter
|
||||
from telethon.tl.functions.channels import JoinChannelRequest
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .. import loader
|
||||
|
||||
class yg_prem(loader.Module):
|
||||
"""Модуль для автоматического участия в розыгрышах премиум-подписок"""
|
||||
|
||||
strings = {"name": "yg_prem"}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"logs_username",
|
||||
"",
|
||||
"@username куда будут отправляться логи",
|
||||
validator=loader.validators.Hidden(loader.validators.String()),
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"watcher_on",
|
||||
True,
|
||||
"состояние активатора",
|
||||
validator=loader.validators.Boolean()
|
||||
),
|
||||
loader.ConfigValue(
|
||||
"folder",
|
||||
True,
|
||||
"добавляет папку под названием yg_prem, в которую добавляются все каналы на которые подписался модуль",
|
||||
validator=loader.validators.Boolean()
|
||||
)
|
||||
)
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.folder_name = "yg_prem"
|
||||
self.client = client
|
||||
self.client.add_event_handler(self.prem, events.NewMessage)
|
||||
|
||||
async def premcmd(self, message):
|
||||
"""вкл/выкл автоматическое участие в розыгрышах на премиум-подписки"""
|
||||
self.config["watcher_on"] = not self.config["watcher_on"]
|
||||
await message.edit(f"<emoji document_id=5217822164362739968>👑</emoji> <b>Автоматическое участие в розыгрышах на премиум-подписки {'включено' if self.config['watcher_on'] else 'выключено'}</b>")
|
||||
|
||||
async def foldercmd(self, message):
|
||||
"""вкл/выкл добавление каналов на которые подписался модуль в папку"""
|
||||
self.config["folder"] = not self.config["folder"]
|
||||
await message.edit(f"<emoji document_id=5325547803936572038>✨</emoji> <b>Добавление каналов на которые подписался модуль в папку {'включено' if self.config['folder'] else 'выключено'}</b>")
|
||||
|
||||
async def get_giveaway_info(self, message):
|
||||
channels_info = []
|
||||
for channel_id in message.media.channels:
|
||||
entity = await message.client.get_entity(channel_id)
|
||||
username = entity.username
|
||||
if username is None:
|
||||
usernames = [u.username for u in entity.usernames if u.active]
|
||||
username = usernames[0] if usernames else "нету"
|
||||
|
||||
channels_info.append({
|
||||
'title': entity.title,
|
||||
'username': username
|
||||
})
|
||||
|
||||
quantity = message.media.quantity
|
||||
months = message.media.months
|
||||
until_date = message.media.until_date.strftime("%H:%M %d.%m.%Y")
|
||||
|
||||
result = {
|
||||
'channels': channels_info,
|
||||
'quantity': quantity,
|
||||
'months': months,
|
||||
'until_date': until_date
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
async def log(self, message):
|
||||
if self.config["logs_username"]:
|
||||
await self.client.send_message(self.config["logs_username"], message)
|
||||
|
||||
async def create_or_get_folder(self):
|
||||
"""проверка на наличие папки yg_prem и создание новой при необходимости"""
|
||||
if self.config["folder"]:
|
||||
dialog_filters = await self.client(functions.messages.GetDialogFiltersRequest())
|
||||
|
||||
for dialog_filter in dialog_filters:
|
||||
if hasattr(dialog_filter, "id") and hasattr(dialog_filter, "title") and dialog_filter.title == self.folder_name:
|
||||
return dialog_filter.id
|
||||
|
||||
chan = "yg_modules"
|
||||
peers = [await self.client.get_input_entity(chan)]
|
||||
|
||||
folder_id = len(dialog_filters) + 1
|
||||
new_folder = DialogFilter(
|
||||
id=folder_id,
|
||||
title=self.folder_name,
|
||||
include_peers=peers,
|
||||
exclude_peers=[],
|
||||
pinned_peers=[]
|
||||
)
|
||||
await self.client(functions.messages.UpdateDialogFilterRequest(id=folder_id, filter=new_folder))
|
||||
|
||||
return folder_id
|
||||
|
||||
async def add_channel_to_folder(self, folder_id, channel):
|
||||
"""добавление канала в папку"""
|
||||
if self.config["folder"]:
|
||||
dialog_filters = await self.client(functions.messages.GetDialogFiltersRequest())
|
||||
|
||||
folder = next((f for f in dialog_filters if hasattr(f, "id") and f.id == folder_id), None)
|
||||
|
||||
if folder is None:
|
||||
await self.log("<emoji document_id=5210952531676504517>❌</emoji> <b>Папка не найдена.</b>")
|
||||
return
|
||||
|
||||
updated_peers = folder.include_peers + [channel]
|
||||
|
||||
updated_folder = DialogFilter(
|
||||
id=folder_id,
|
||||
title=self.folder_name,
|
||||
include_peers=updated_peers,
|
||||
exclude_peers=[],
|
||||
pinned_peers=[]
|
||||
)
|
||||
await self.client(functions.messages.UpdateDialogFilterRequest(id=folder_id, filter=updated_folder))
|
||||
|
||||
async def prem(self, event):
|
||||
"""watcher for new giveaway messages"""
|
||||
if self.config["watcher_on"]:
|
||||
if event.message.media and isinstance(event.message.media, MessageMediaGiveaway):
|
||||
giveaway_message = await self.client.get_messages(event.chat_id, ids=event.message.id)
|
||||
giveaway_info = await self.get_giveaway_info(giveaway_message)
|
||||
channels = giveaway_info['channels']
|
||||
until_date = event.message.media.until_date
|
||||
|
||||
if until_date < datetime.now(timezone.utc):
|
||||
return
|
||||
|
||||
folder_id = await self.create_or_get_folder()
|
||||
|
||||
for channel in channels:
|
||||
username = channel['username']
|
||||
if username != "нету":
|
||||
try:
|
||||
await self.client(JoinChannelRequest(username))
|
||||
await self.add_channel_to_folder(folder_id, await self.client.get_input_entity(username))
|
||||
except Exception as e:
|
||||
await self.log(f"<emoji document_id=5210952531676504517>❌</emoji> <b>Не смог подписаться на @{username}, из-за ошибки:</b> <code>{str(e)}</code>")
|
||||
|
||||
channels_info = "\n".join([f"• {channel['title']} (@{channel['username']})" for channel in giveaway_info['channels']])
|
||||
quantity = giveaway_info['quantity']
|
||||
months = giveaway_info['months']
|
||||
until_date = giveaway_info['until_date']
|
||||
|
||||
m = self.count(months)
|
||||
t = f"на <code>{months}</code> {m}"
|
||||
log = (f"<emoji document_id=5456140674028019486>⚡️</emoji> <b>Участвую в новом розыгрыше! Информация:</b>\n\n"
|
||||
f"<emoji document_id=5282843764451195532>🖥</emoji> <b>Каналы:</b>\n{channels_info}\n\n"
|
||||
f"<emoji document_id=5438496463044752972>⭐️</emoji> <b>Количество премок:</b> <code>{quantity}</code> ({t})\n"
|
||||
f"<emoji document_id=5413879192267805083>🗓</emoji> <b>Дата окончания розыгрыша:</b> <code>{until_date}</code> (UTC)\n")
|
||||
|
||||
await self.log(log)
|
||||
|
||||
def count(self, months):
|
||||
if 11 <= months % 100 <= 14:
|
||||
return "месяцев"
|
||||
kok = months % 10
|
||||
if kok == 1:
|
||||
return "месяц"
|
||||
elif 2 <= kok <= 4:
|
||||
return "месяца"
|
||||
else:
|
||||
return "месяцев"
|
||||
396
yummy1gay/limoka/yg_quotes.py
Normal file
396
yummy1gay/limoka/yg_quotes.py
Normal file
@@ -0,0 +1,396 @@
|
||||
__version__ = (1, 1, 1, 1)
|
||||
|
||||
# This file is a part of Hikka Userbot!
|
||||
# This product includes software developed by t.me/Fl1yd and t.me/spypm.
|
||||
# Based on the "SQuotes" module.
|
||||
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# thx to t.me/LyoSU for github.com/LyoSU/quote-api
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import base64, io, requests, telethon
|
||||
from time import gmtime
|
||||
from typing import List, Optional, Tuple, Union
|
||||
from PIL import Image, ImageDraw
|
||||
from telethon.tl import types
|
||||
from telethon.extensions import html
|
||||
from telethon.tl.patched import Message
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
class Dick:
|
||||
@staticmethod
|
||||
def ents(es: types.TypeMessageEntity) -> List[dict]:
|
||||
out: List[dict] = []
|
||||
if not es: return out
|
||||
for e in es:
|
||||
try:
|
||||
d = e.to_dict(); t = d.pop("_","").replace("MessageEntity","").lower()
|
||||
if not t: continue
|
||||
mt = {"bold": "bold","italic": "italic","underline": "underline","strikethrough": "strikethrough",
|
||||
"code": "code","pre": "pre","texturl": "text_link","url": "url","email": "email",
|
||||
"phone": "phone_number","mention": "mention",
|
||||
"mentionname": "text_mention","hashtag": "hashtag","cashtag": "cashtag",
|
||||
"botcommand": "bot_command","spoiler": "spoiler","customemoji": "custom_emoji"}.get(t,t)
|
||||
it = {"type": mt,"offset": d.get("offset",0),"length": d.get("length",0)}
|
||||
if t=="texturl": it["url"]=d.get("url","")
|
||||
elif t=="mentionname": it["user"]={"id": d.get("user_id",0)}
|
||||
elif t=="customemoji": it["custom_emoji_id"]=str(d.get("document_id",""))
|
||||
elif t=="pre": it["language"]=d.get("language","")
|
||||
out.append(it)
|
||||
except Exception: continue
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def dur(s: Union[int,float]) -> str:
|
||||
t=gmtime(s); return (f"{t.tm_hour:02d}:" if t.tm_hour>0 else "")+f"{t.tm_min:02d}:{t.tm_sec:02d}"
|
||||
|
||||
@staticmethod
|
||||
def desc(m: Message, rep: bool=False) -> str:
|
||||
return (
|
||||
"📷 Фото" if m.photo and rep else
|
||||
(m.file.emoji+" Стикер") if m.sticker and rep else
|
||||
"📹 Видеосообщение" if m.video_note and rep else
|
||||
"📹 Видео" if m.video and rep else
|
||||
"🖼 GIF" if m.gif else
|
||||
"📊 Опрос" if m.poll else
|
||||
"📍 Местоположение" if m.geo else
|
||||
"👤 Контакт" if m.contact else
|
||||
(f"🎵 Голосовое сообщение: {Dick.dur(m.voice.attributes[0].duration)}" if m.voice else
|
||||
(f"🎧 Музыка: {Dick.dur(m.audio.attributes[0].duration)} | {m.audio.attributes[0].performer} - {m.audio.attributes[0].title}" if m.audio else
|
||||
(f"💾 Файл: {m.file.name}" if isinstance(m.media, types.MessageMediaDocument) and not Dick.pick(m) else
|
||||
(f"{m.media.emoticon} Кость: {m.media.value}" if isinstance(m.media, types.MessageMediaDice) else
|
||||
(f"Сервисное сообщение: {m.action.to_dict().get('_')}" if isinstance(m, types.MessageService) else "")))))) #)))
|
||||
|
||||
@staticmethod
|
||||
def split(name: Optional[str]) -> Tuple[str,str]:
|
||||
if not name: return "",""
|
||||
p=name.split(); return (p[0], " ".join(p[1:]) if len(p)>1 else "")
|
||||
|
||||
@staticmethod
|
||||
def pick(m: Message):
|
||||
if m and m.media:
|
||||
return m.photo or m.sticker or m.video or m.video_note or m.gif or m.web_preview
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def wf(b: Optional[bytes]) -> List[int]:
|
||||
if not b: return []
|
||||
n=(len(b)*8)//5
|
||||
if not n: return []
|
||||
out: List[int]=[]
|
||||
last=n-1
|
||||
for i in range(last):
|
||||
j=i*5; bi,sh=j//8,j%8
|
||||
v=int.from_bytes(b[bi:bi+2],"little") if bi+1<len(b) else b[bi]
|
||||
out.append((v>>sh)&0b11111)
|
||||
j=last*5; bi,sh=j//8,j%8
|
||||
v=int.from_bytes(b[bi:bi+2],"little") if bi+1<len(b) else b[bi]
|
||||
out.append((v>>sh)&0b11111)
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
async def img(b: bytes, circle: bool=False) -> Optional[str]:
|
||||
try:
|
||||
im=Image.open(io.BytesIO(b))
|
||||
if im.mode!="RGBA": im=im.convert("RGBA")
|
||||
if circle:
|
||||
size=min(im.size)
|
||||
mask=Image.new("L",(size,size),0); ImageDraw.Draw(mask).ellipse((0,0,size,size),fill=255)
|
||||
sq=Image.new("RGBA",(size,size),(0,0,0,0))
|
||||
off=((size-im.width)//2,(size-im.height)//2); sq.paste(im,off)
|
||||
im=Image.composite(sq,Image.new("RGBA",(size,size),(0,0,0,0)),mask)
|
||||
o=io.BytesIO(); im.save(o,format="PNG")
|
||||
return f"data:image/png;base64,{base64.b64encode(o.getvalue()).decode()}"
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
async def stc(b: bytes) -> Optional[str]:
|
||||
try:
|
||||
im=Image.open(io.BytesIO(b))
|
||||
if im.mode not in ("RGBA","LA"): im=im.convert("RGBA")
|
||||
elif im.mode=="LA": im=im.convert("RGBA")
|
||||
o=io.BytesIO(); im.save(o,format="PNG")
|
||||
return f"data:image/png;base64,{base64.b64encode(o.getvalue()).decode()}"
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
async def proc(cli, obj, m: Message) -> Optional[dict]:
|
||||
try:
|
||||
if m.voice:
|
||||
for a in m.voice.attributes or []:
|
||||
if getattr(a,"voice",False) and hasattr(a,"waveform"):
|
||||
return {"voice":{"waveform":Dick.wf(a.waveform)}}
|
||||
b: bytes = await cli.download_media(obj, bytes, thumb=-1)
|
||||
if not b: return None
|
||||
if m.sticker:
|
||||
u=await Dick.stc(b); return {"url": u} if u else None
|
||||
u=await Dick.img(b, circle=bool(m.video_note))
|
||||
return {"url": u} if u else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
async def ava(cli, uid: int) -> Optional[str]:
|
||||
try:
|
||||
b=await cli.download_profile_photo(uid, bytes)
|
||||
if b: return f"data:image/jpeg;base64,{base64.b64encode(b).decode()}"
|
||||
except Exception: pass
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
async def post(url: str, data: dict):
|
||||
try:
|
||||
return await utils.run_sync(requests.post, url, json=data, timeout=30)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@loader.tds
|
||||
class Quotes(loader.Module):
|
||||
"""Модуль для создания цитат из сообщений"""
|
||||
|
||||
strings = {"name": "yg_quotes",
|
||||
"no_reply": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Нет реплая на сообщение",
|
||||
"processing": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Обработка…",
|
||||
"api_processing": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ожидание ответа API…",
|
||||
"api_error": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка API: {}",
|
||||
"loading_media": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Отправка…",
|
||||
"no_args_or_reply": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Нет аргументов или реплая",
|
||||
"args_error": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка разбора аргументов. Запрос: <code>{}</code>",
|
||||
"too_many_messages": "<emoji document_id=6321272741005624970>🏳️🌈</emoji> Слишком много сообщений. Максимум: <code>{}</code>"}
|
||||
|
||||
def __init__(self):
|
||||
self.config=loader.ModuleConfig(
|
||||
loader.ConfigValue("type","quote",
|
||||
lambda:"Тип цитаты",
|
||||
validator=loader.validators.Choice(["quote", "stories"])),
|
||||
loader.ConfigValue("bg_color","#162330",
|
||||
lambda:"Цвет фона цитаты (например, #1a1a1a или red)"),
|
||||
loader.ConfigValue("width",512,
|
||||
lambda:"Ширина цитаты (px)",
|
||||
validator=loader.validators.Integer(minimum=200,maximum=2000)),
|
||||
loader.ConfigValue("height",768,
|
||||
lambda:"Высота цитаты (px)",
|
||||
validator=loader.validators.Integer(minimum=200,maximum=2000)),
|
||||
loader.ConfigValue("scale",2,
|
||||
lambda:"Масштаб рендера",
|
||||
validator=loader.validators.Choice([1, 2, 3])),
|
||||
loader.ConfigValue("emoji_brand","apple",
|
||||
lambda:"Стиль эмодзи (apple, google, twitter и т.д.)"),
|
||||
loader.ConfigValue("max_messages",15,
|
||||
lambda:"Максимальное число сообщений в цитате",
|
||||
validator=loader.validators.Integer(minimum=1,maximum=50)),
|
||||
loader.ConfigValue("endpoint","https://kok.gay/gayotes/generate",
|
||||
lambda:"URL API-эндпоинта (можешь поднять локально - github.com/yummy1gay/quote-api)",
|
||||
validator=loader.validators.Link()))
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.client=client; self.db=db
|
||||
|
||||
async def qcmd(self, m: Message):
|
||||
"""
|
||||
Обычные цитаты:
|
||||
• .q — процитировать одно сообщение из реплая
|
||||
• .q 2 — процитировать 2 сообщения
|
||||
• .q 3 #2d2d2d — 3 сообщения на тёмном фоне
|
||||
• .q pink — фон по имени цвета
|
||||
• .q !file — отправить как файл (PNG)
|
||||
"""
|
||||
try:
|
||||
args=utils.get_args(m); rep=await m.get_reply_message()
|
||||
if not rep: return await utils.answer(m,self.strings["no_reply"])
|
||||
st=await utils.answer(m,self.strings["processing"])
|
||||
doc="!file" in args
|
||||
n=next((int(a) for a in args if a.isdigit() and int(a)>0),1)
|
||||
bg=next((a for a in args if a!="!file" and not a.isdigit()), self.config["bg_color"])
|
||||
if n>self.config["max_messages"]:
|
||||
return await utils.answer(st,self.strings["too_many_messages"].format(self.config["max_messages"]))
|
||||
|
||||
js=await self.parse(m,n)
|
||||
if not js: return await utils.answer(st,self.strings["api_error"].format("Не удалось собрать сообщения"))
|
||||
|
||||
pay={"backgroundColor":bg,"width":self.config["width"],"height":self.config["height"],
|
||||
"scale":self.config["scale"],"emojiBrand":self.config["emoji_brand"],"messages":js,
|
||||
"format": "webp" if not doc else "png", "type": self.config["type"]}
|
||||
|
||||
await utils.answer(st,self.strings["api_processing"])
|
||||
r=await Dick.post(f"{self.config['endpoint']}.webp",pay)
|
||||
if not r or r.status_code!=200:
|
||||
try: err=r.json().get("error",f"HTTP {r.status_code}") if r else "Нетворк еррорь"
|
||||
except Exception: err=f"HTTP {r.status_code}" if r else "Нетворк еррорь"
|
||||
return await utils.answer(st,self.strings["api_error"].format(err))
|
||||
|
||||
buf=io.BytesIO(r.content); buf.name="YgQuote"+(".png" if doc else ".webp")
|
||||
await utils.answer(st,buf,force_document=doc)
|
||||
except Exception as e:
|
||||
return await utils.answer(m,f"<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка: {e}")
|
||||
|
||||
async def fqcmd(self, m: Message):
|
||||
"""
|
||||
Фейковые цитаты:
|
||||
• .fq <@ или ID> <текст> — цитата от пользователя
|
||||
• .fq <reply> <текст> — цитата от автора реплая
|
||||
• .fq <@/ID> <текст> -r <@/ID> <текст> — с ответом
|
||||
• .fq user1 текст; user2 текст — несколько сообщений
|
||||
"""
|
||||
try:
|
||||
raw=utils.get_args_html(m); rep=await m.get_reply_message()
|
||||
if not (raw or rep): return await utils.answer(m,self.strings["no_args_or_reply"])
|
||||
st= await utils.answer(m,self.strings["processing"])
|
||||
try: js=await self.fake(raw,rep)
|
||||
except (IndexError,ValueError): return await utils.answer(st,self.strings["args_error"].format(m.text))
|
||||
if len(js)>self.config["max_messages"]:
|
||||
return await utils.answer(st,self.strings["too_many_messages"].format(self.config["max_messages"]))
|
||||
|
||||
dickk={"backgroundColor":self.config["bg_color"],"width":self.config["width"],"height":self.config["height"],
|
||||
"scale":self.config["scale"],"emojiBrand":self.config["emoji_brand"],"messages":js,
|
||||
"format": "webp","type":self.config["type"]}
|
||||
|
||||
await utils.answer(st,self.strings["api_processing"])
|
||||
r=await Dick.post(f"{self.config['endpoint']}.webp",dickk)
|
||||
if not r or r.status_code!=200:
|
||||
try: err=r.json().get("error",f"HTTP {r.status_code}") if r else "Нетворк еррорь"
|
||||
except Exception: err=f"HTTP {r.status_code}" if r else "Нетворк еррорь"
|
||||
return await utils.answer(st,self.strings["api_error"].format(err))
|
||||
|
||||
buf=io.BytesIO(r.content); buf.name="YgQuote.webp"
|
||||
await utils.answer(st,buf)
|
||||
except Exception as e:
|
||||
return await utils.answer(m,f"<emoji document_id=6321272741005624970>🏳️🌈</emoji> Ошибка: {e}")
|
||||
|
||||
async def parse(self, trg: Message, n: int) -> Optional[List[dict]]:
|
||||
try:
|
||||
rep= await trg.get_reply_message()
|
||||
lst: List[Message]=[mm async for mm in self.client.iter_messages(trg.chat_id,limit=n,reverse=True,add_offset=1,offset_id=rep.id if rep else None)]
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
out: List[dict]=[]
|
||||
for mm in lst:
|
||||
try:
|
||||
u=await self.who(mm)
|
||||
if not u: continue
|
||||
name=telethon.utils.get_display_name(u); f,l=Dick.split(name)
|
||||
ava=await Dick.ava(self.client,getattr(u,"id",0)) if getattr(u,"id",None) else None
|
||||
|
||||
rb=None
|
||||
try:
|
||||
r=await mm.get_reply_message()
|
||||
if r:
|
||||
rname=telethon.utils.get_display_name(r.sender)
|
||||
rtxt=Dick.desc(r,True)
|
||||
if r.raw_text: rtxt=(rtxt+". "+r.raw_text) if rtxt else r.raw_text
|
||||
rb={"name":rname,"text":rtxt or "","entities":Dick.ents(r.entities),
|
||||
"chatId": r.sender.id if r.sender else mm.chat_id,"from":{"name":rname}}
|
||||
except Exception: rb=None
|
||||
|
||||
med=None; obj=Dick.pick(mm)
|
||||
if obj: med=await Dick.proc(self.client,obj,mm)
|
||||
|
||||
txt=mm.raw_text or ""; ad=Dick.desc(mm)
|
||||
if ad: txt=f"{txt}\n\n{ad}" if txt else ad
|
||||
|
||||
item={"from":{"id":getattr(u,"id", 0),"first_name":getattr(u,"first_name","") or f,"last_name":getattr(u,"last_name","") or l,
|
||||
"username":getattr(u,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
|
||||
"text":txt,"entities":Dick.ents(mm.entities),"avatar":True}
|
||||
|
||||
try:
|
||||
if mm.voice:
|
||||
a = next((a for a in mm.voice.attributes or []
|
||||
if getattr(a, "voice", False) and hasattr(a, "waveform")), None)
|
||||
if a: item["voice"] = {"waveform": Dick.wf(a.waveform)}
|
||||
except Exception: pass
|
||||
|
||||
if med: item["voice" if "voice" in med else "media"] = med.get("voice", med)
|
||||
|
||||
es=getattr(u,"emoji_status",None)
|
||||
if getattr(es,"document_id",None): item["from"]["emoji_status"]=str(es.document_id)
|
||||
if rb: item["replyMessage"]=rb
|
||||
out.append(item)
|
||||
except Exception: continue
|
||||
return out
|
||||
|
||||
async def who(self, m: Message):
|
||||
try:
|
||||
if m.fwd_from:
|
||||
if m.fwd_from.from_id:
|
||||
pid=m.fwd_from.from_id
|
||||
uid=pid.channel_id if isinstance(pid, types.PeerChannel) else pid.user_id
|
||||
try: return await self.client.get_entity(uid)
|
||||
except Exception: return m.sender
|
||||
if m.fwd_from.from_name:
|
||||
return types.User(
|
||||
id=hash(m.fwd_from.from_name)%2147483647, first_name=m.fwd_from.from_name,
|
||||
username=None, phone=None, bot=False, verified=False, restricted=False,
|
||||
scam=False, fake=False, premium=False)
|
||||
return m.sender
|
||||
except Exception:
|
||||
return m.sender
|
||||
|
||||
async def fake(self, args: str, rep: Optional[Message]) -> List[dict]:
|
||||
async def tok(ch: str):
|
||||
p=ch.split()
|
||||
if not p: return None,""
|
||||
who=p[0]; tx=ch.split(maxsplit=1)[1] if len(p)>1 else ""
|
||||
try:
|
||||
u=await self.client.get_entity(int(who) if who.isdigit() else who)
|
||||
return u,tx
|
||||
except Exception:
|
||||
return None,tx
|
||||
|
||||
if rep and not args:
|
||||
u=rep.sender; name=telethon.utils.get_display_name(u); f,l=Dick.split(name)
|
||||
ava=await Dick.ava(self.client,u.id) if getattr(u,"id",None) else None
|
||||
msg={"from":{"id":u.id,"first_name":getattr(u,"first_name","") or f,"last_name":getattr(u,"last_name","") or l,
|
||||
"username":getattr(u,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
|
||||
"text":"","entities":[], "avatar":True}
|
||||
es=getattr(u,"emoji_status",None)
|
||||
if getattr(es,"document_id", None): msg["from"]["emoji_status"]=str(es.document_id)
|
||||
return [msg]
|
||||
|
||||
if rep and args:
|
||||
u=rep.sender
|
||||
return await self.fake(f"{getattr(u,'id','')} {args}", None)
|
||||
|
||||
out: List[dict]=[]
|
||||
for part in args.split("; "):
|
||||
try:
|
||||
rb=None
|
||||
if " -r " in part:
|
||||
a,b=part.split(" -r ",1); u1,t1=await tok(a); u2,t2=await tok(b)
|
||||
else:
|
||||
u1,t1=await tok(part); u2,t2=None,None
|
||||
if not u1: continue
|
||||
|
||||
txt1, ents1 = html.parse(t1) if t1 else ("", [])
|
||||
|
||||
name=telethon.utils.get_display_name(u1); f,l=Dick.split(name)
|
||||
ava=await Dick.ava(self.client,u1.id)
|
||||
|
||||
if u2:
|
||||
txt2, ents2 = html.parse(t2) if t2 else ("", [])
|
||||
name2=telethon.utils.get_display_name(u2); ava2=await Dick.ava(self.client,u2.id)
|
||||
rb={"name":name2,"text":txt2,"entities":Dick.ents(ents2),"chatId":u2.id,"from":{"name":name2,"photo":{"url":ava2} if ava2 else {}}}
|
||||
|
||||
msg={"from":{"id":u1.id,"first_name":getattr(u1,"first_name","") or f,"last_name":getattr(u1,"last_name","") or l,
|
||||
"username":getattr(u1,"username",None),"name":name,"photo":{"url":ava} if ava else {}},
|
||||
"text":txt1,"entities":Dick.ents(ents1), "avatar":True}
|
||||
|
||||
es=getattr(u1,"emoji_status",None)
|
||||
if getattr(es,"document_id",None): msg["from"]["emoji_status"]=str(es.document_id)
|
||||
if rb: msg["replyMessage"]=rb
|
||||
out.append(msg)
|
||||
except Exception: continue
|
||||
return out
|
||||
96
yummy1gay/limoka/yg_stars.py
Normal file
96
yummy1gay/limoka/yg_stars.py
Normal file
@@ -0,0 +1,96 @@
|
||||
__version__ = (1, 1, 1, 1)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# ported classes from telethon that are not in Hikka-TL
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
@loader.tds
|
||||
class yg_stars(loader.Module):
|
||||
"""Get current prices for stars in different currencies!"""
|
||||
|
||||
strings = {"name": "yg_stars",
|
||||
"hint": ("<i><emoji document_id=5857158158388041525>💡</emoji> The course is calculated at the price of a 100 star package.\n"
|
||||
"<emoji document_id=5246762912428603768>📉</emoji> When you buy more, the price per star will be slightly lower!</i>\n\n"),
|
||||
"loading": "<emoji document_id=5402186569006210455>💱</emoji> <b>Fetching information...</b>",
|
||||
"phrase": "Rate for",
|
||||
"1": "stars",
|
||||
"2": "star",
|
||||
"3": "stars"}
|
||||
|
||||
strings_ru = {"name": "yg_stars",
|
||||
"hint": ("<i><emoji document_id=5857158158388041525>💡</emoji> Курс рассчитан по цене пакета в 100 звёзд.\n"
|
||||
"<emoji document_id=5246762912428603768>📉</emoji> При покупке большего количества цена за звезду будет немного ниже!</i>\n\n"),
|
||||
"loading": "<emoji document_id=5402186569006210455>💱</emoji> <b>Получаю информацию...</b>",
|
||||
"phrase": "Курс за",
|
||||
"1": "звёзд",
|
||||
"2": "звезду",
|
||||
"3": "звезды"}
|
||||
|
||||
strings_ua = {"name": "yg_stars",
|
||||
"hint": ("<i><emoji document_id=5857158158388041525>💡</emoji> Курс розрахований за ціною пакета 100 зірок.\n"
|
||||
"<emoji document_id=5246762912428603768>📉</emoji> При покупці більшої кількості ціна за зірку буде трохи нижчою!</i>\n\n"),
|
||||
"loading": "<emoji document_id=5402186569006210455>💱</emoji> <b>Отримую інформацію...</b>",
|
||||
"phrase": "Курс за",
|
||||
"1": "зірок",
|
||||
"2": "зірку",
|
||||
"3": "зірки"}
|
||||
|
||||
async def client_ready(self, *_):
|
||||
self.lib = await self.import_lib("https://mods.kok.gay/lib",
|
||||
suspend_on_error=True)
|
||||
|
||||
@loader.command(ru_doc="<amount> - получить курс звезд", ua_doc="<amount> - отримати курс зірок")
|
||||
async def starscmd(self, msg):
|
||||
"""<amount> - get the rate of stars"""
|
||||
args = utils.get_args_raw(msg)
|
||||
try:
|
||||
amount = int(args) if args else 1
|
||||
except ValueError:
|
||||
await utils.answer(msg, "<b>Enter the number of stars!</b>")
|
||||
return
|
||||
|
||||
await utils.answer(msg, self.strings("loading"))
|
||||
|
||||
result = await msg.client(self.lib.GetStarsGiftOptionsRequest(user_id="me"))
|
||||
rates = await self.lib.YRates(result[0].stars, result[0].currency, result[0].amount).get()
|
||||
|
||||
text = (f"<emoji document_id=5956159800260695086>⭐️</emoji> <b>{self.phrase(amount)}</b>\n\n"
|
||||
f"{self.strings('hint')}")
|
||||
|
||||
for source, values in rates.items():
|
||||
text += self.format(source, values, amount) + "\n"
|
||||
|
||||
await utils.answer(msg, text, parse_mode=self.lib.YummyHtml)
|
||||
|
||||
def phrase(self, amount):
|
||||
last_two = amount % 100
|
||||
last = amount % 10
|
||||
|
||||
suffix = ("1" if 11 <= last_two <= 14 else
|
||||
"2" if last == 1 else
|
||||
"3" if 2 <= last <= 4 else "1")
|
||||
|
||||
return f"{self.strings('phrase')} <code>{amount}</code> {self.strings(suffix)}"
|
||||
|
||||
def format(self, name, data, amount):
|
||||
return (f"{name}:\n"
|
||||
f"<blockquote>├─ <emoji document_id=6321193412959668105>🇺🇲</emoji> <b>USD:</b> <code>{data['USD'] * amount:.2f}</code>\n"
|
||||
f"├─ <emoji document_id=6323217102765295143>🇪🇺</emoji> <b>EUR:</b> <code>{data['EUR'] * amount:.2f}</code>\n"
|
||||
f"├─ <emoji document_id=6323289850921354919>🇺🇦</emoji> <b>UAH:</b> <code>{data['UAH'] * amount:.2f}</code>\n"
|
||||
f"├─ <emoji document_id=6323139226418284334>🇷🇺</emoji> <b>RUB:</b> <code>{data['RUB'] * amount:.2f}</code>\n"
|
||||
f"├─ <emoji document_id=6323602387101550101>🇵🇱</emoji> <b>PLN:</b> <code>{data['PLN'] * amount:.2f}</code>\n"
|
||||
f"└─ <emoji document_id=5859542041330981468>👛</emoji> <b>TON:</b> <code>{data['TON'] * amount:.2f}</code></blockquote>\n")
|
||||
371
yummy1gay/limoka/yg_tgs.py
Normal file
371
yummy1gay/limoka/yg_tgs.py
Normal file
@@ -0,0 +1,371 @@
|
||||
__version__ = (1, 1)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# requires: lottie rlottie-python
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import os
|
||||
import io
|
||||
import json
|
||||
import gzip
|
||||
import zipfile
|
||||
|
||||
from telethon import types
|
||||
from telethon.tl.types import Message
|
||||
from telethon.tl.types import MessageEntityCustomEmoji
|
||||
from telethon.tl.functions.messages import GetCustomEmojiDocumentsRequest
|
||||
|
||||
from lottie.importers.svg import import_svg
|
||||
from lottie.exporters.core import export_tgs
|
||||
from lottie.parsers.baseporter import Baseporter
|
||||
from rlottie_python import LottieAnimation
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
@loader.tds
|
||||
class yg_tgs(loader.Module):
|
||||
"""Модуль для работы с .tgs (Telegram Animated Sticker)"""
|
||||
|
||||
strings = {
|
||||
"name": "yg_tgs",
|
||||
"converting": "<emoji document_id=5386367538735104399>⌛</emoji> <b>Converting...</b>",
|
||||
"no_reply": "<emoji document_id=5276240711795107620>⚠️</emoji> <i>Reply to an file, animated sticker or custom emoji!</i>",
|
||||
"not_svg": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Attached file is not an SVG!</i>",
|
||||
"not_json": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Attached file is not a JSON!</i>",
|
||||
"not_tgs_or_emoji": "<emoji document_id=5276240711795107620>⚠️</emoji> <i>Reply to an animated sticker or custom emoji!</i>",
|
||||
"conversion_error": "<emoji document_id=5278578973595427038>🚫</emoji> <b>Error during conversion:</b> <code>{}</code>",
|
||||
"emoji_not_found": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Custom emoji not found!</i>"
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
"name": "yg_tgs",
|
||||
"converting": "<emoji document_id=5386367538735104399>⌛</emoji> <b>Конвертирую...</b>",
|
||||
"no_reply": "<emoji document_id=5276240711795107620>⚠️</emoji> <i>Ответь на файл, анимированный стикер или кастомный эмодзи!</i>",
|
||||
"not_svg": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Прикрепленный файл не является SVG!</i>",
|
||||
"not_json": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Прикрепленный файл не является JSON!</i>",
|
||||
"not_tgs_or_emoji": "<emoji document_id=5276240711795107620>⚠️</emoji> <i>Ответь на анимированный стикер или кастомный эмодзи!</i>",
|
||||
"conversion_error": "<emoji document_id=5278578973595427038>🚫</emoji> <b>Ошибка при конвертации:</b> <code>{}</code>",
|
||||
"emoji_not_found": "<emoji document_id=5278578973595427038>🚫</emoji> <i>Кастомный эмодзи не найден!</i>"
|
||||
}
|
||||
|
||||
@loader.command(ru_doc="<reply to .svg> - конвертировать svg в tgs")
|
||||
async def svg2tgscmd(self, msg: Message):
|
||||
"""<reply to .svg> - convert svg to tgs"""
|
||||
if not msg.reply_to_msg_id:
|
||||
await utils.answer(msg, self.strings["no_reply"])
|
||||
return
|
||||
|
||||
await utils.answer(msg, self.strings["converting"])
|
||||
|
||||
reply = await msg.get_reply_message()
|
||||
|
||||
if not reply.file or not reply.file.name.endswith(".svg"):
|
||||
await utils.answer(msg, self.strings["not_svg"])
|
||||
return
|
||||
|
||||
try:
|
||||
tgs_buffer = io.BytesIO()
|
||||
await reply.download_media(tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
svg_buffer = self.svg2tgs(tgs_buffer.getvalue())
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
svg_buffer,
|
||||
attributes=[types.DocumentAttributeFilename("sticker.tgs")],
|
||||
reply_to=reply
|
||||
)
|
||||
except Exception as e:
|
||||
await utils.answer(msg, self.strings["conversion_error"].format(str(e)))
|
||||
|
||||
@loader.command(ru_doc="<reply to tgs or custom emoji> - конвертировать tgs в lottie json")
|
||||
async def tgs2jsoncmd(self, msg: Message):
|
||||
"""<reply to tgs or custom emoji> - convert tgs to lottie json"""
|
||||
if not msg.reply_to_msg_id:
|
||||
await utils.answer(msg, self.strings["no_reply"])
|
||||
return
|
||||
|
||||
await utils.answer(msg, self.strings["converting"])
|
||||
|
||||
reply = await msg.get_reply_message()
|
||||
|
||||
if reply.file and reply.file.name.endswith(".tgs"):
|
||||
tgs_buffer = io.BytesIO()
|
||||
await reply.download_media(tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
json_buffer = self.tgs2json(tgs_buffer.getvalue())
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
json_buffer,
|
||||
attributes=[types.DocumentAttributeFilename("sticker.json")],
|
||||
reply_to=reply
|
||||
)
|
||||
|
||||
elif reply.entities and isinstance(reply.entities[0], MessageEntityCustomEmoji):
|
||||
emoji_id = reply.entities[0].document_id
|
||||
data = await msg.client(GetCustomEmojiDocumentsRequest(document_id=[emoji_id]))
|
||||
|
||||
if data:
|
||||
tgs_buffer = io.BytesIO()
|
||||
await msg.client.download_media(data[0], tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
json_buffer = self.tgs2json(tgs_buffer.getvalue())
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
json_buffer,
|
||||
attributes=[types.DocumentAttributeFilename("sticker.json")],
|
||||
reply_to=reply
|
||||
)
|
||||
else:
|
||||
await utils.answer(msg, self.strings["not_tgs_or_emoji"])
|
||||
|
||||
@loader.command(ru_doc="<reply to tgs or custom emoji> - разбить tgs на кадры (ZIP с PNG)")
|
||||
async def tgs2pngscmd(self, msg: Message):
|
||||
"""<reply to tgs or custom emoji> - split tgs into frames (ZIP with PNGs)"""
|
||||
if not msg.reply_to_msg_id:
|
||||
await utils.answer(msg, self.strings["no_reply"])
|
||||
return
|
||||
|
||||
await utils.answer(msg, self.strings["converting"])
|
||||
|
||||
reply = await msg.get_reply_message()
|
||||
|
||||
if reply.file and reply.file.name.endswith(".tgs"):
|
||||
tgs_buffer = io.BytesIO()
|
||||
await reply.download_media(tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
pngs_buffer = self.tgs2pngs(tgs_buffer.getvalue())
|
||||
pngs_buffer.name = "frames.zip"
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
pngs_buffer,
|
||||
reply_to=reply
|
||||
)
|
||||
|
||||
elif reply.entities and isinstance(reply.entities[0], MessageEntityCustomEmoji):
|
||||
emoji_id = reply.entities[0].document_id
|
||||
data = await msg.client(GetCustomEmojiDocumentsRequest(document_id=[emoji_id]))
|
||||
|
||||
if data:
|
||||
tgs_buffer = io.BytesIO()
|
||||
await msg.client.download_media(data[0], tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
pngs_buffer = self.tgs2pngs(tgs_buffer.getvalue())
|
||||
pngs_buffer.name = "frames.zip"
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
pngs_buffer,
|
||||
reply_to=reply
|
||||
)
|
||||
else:
|
||||
await utils.answer(msg, self.strings["not_tgs_or_emoji"])
|
||||
|
||||
@loader.command(ru_doc="<reply to .json> - конвертировать lottie json в tgs")
|
||||
async def json2tgscmd(self, msg: Message):
|
||||
"""<reply to .json> - convert lottie json to tgs"""
|
||||
if not msg.reply_to_msg_id:
|
||||
await utils.answer(msg, self.strings["no_reply"])
|
||||
return
|
||||
|
||||
await utils.answer(msg, self.strings["converting"])
|
||||
|
||||
reply = await msg.get_reply_message()
|
||||
|
||||
if not reply.file or not reply.file.name.endswith(".json"):
|
||||
await utils.answer(msg, self.strings["not_json"])
|
||||
return
|
||||
|
||||
try:
|
||||
tgs_buffer = io.BytesIO()
|
||||
await reply.download_media(tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
svg_buffer = self.json2tgs(tgs_buffer.getvalue())
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
svg_buffer,
|
||||
attributes=[types.DocumentAttributeFilename("sticker.tgs")],
|
||||
reply_to=reply
|
||||
)
|
||||
except Exception as e:
|
||||
await utils.answer(msg, self.strings["conversion_error"].format(str(e)))
|
||||
|
||||
@loader.command(ru_doc="<reply to tgs or custom emoji> - конвертировать tgs в gif")
|
||||
async def tgs2gifcmd(self, msg: Message):
|
||||
"""<reply to tgs or custom emoji> - convert tgs to gif"""
|
||||
if not msg.reply_to_msg_id:
|
||||
await utils.answer(msg, self.strings["no_reply"])
|
||||
return
|
||||
|
||||
await utils.answer(msg, self.strings["converting"])
|
||||
|
||||
reply = await msg.get_reply_message()
|
||||
|
||||
if reply.file and reply.file.name.endswith(".tgs"):
|
||||
tgs_buffer = io.BytesIO()
|
||||
await reply.download_media(tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
gif_buffer = self.tgs2gif(tgs_buffer.getvalue())
|
||||
gif_buffer.name = "sticker.gif"
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
gif_buffer,
|
||||
force_document=False,
|
||||
reply_to=reply
|
||||
)
|
||||
|
||||
elif reply.entities and isinstance(reply.entities[0], MessageEntityCustomEmoji):
|
||||
emoji_id = reply.entities[0].document_id
|
||||
data = await msg.client(GetCustomEmojiDocumentsRequest(document_id=[emoji_id]))
|
||||
|
||||
if data:
|
||||
tgs_buffer = io.BytesIO()
|
||||
await msg.client.download_media(data[0], tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
gif_buffer = self.tgs2gif(tgs_buffer.getvalue())
|
||||
gif_buffer.name = "sticker.gif"
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
gif_buffer,
|
||||
force_document=False,
|
||||
reply_to=reply.id
|
||||
)
|
||||
else:
|
||||
await utils.answer(msg, self.strings["not_tgs_or_emoji"])
|
||||
|
||||
@loader.command(ru_doc="<reply to custom emoji> - конвертировать кастом эмодзи в tgs")
|
||||
async def emoji2tgscmd(self, msg: Message):
|
||||
"""<reply to custom emoji> - convert custom emoji to tgs"""
|
||||
if not msg.reply_to_msg_id:
|
||||
await utils.answer(msg, self.strings["no_reply"])
|
||||
return
|
||||
|
||||
await utils.answer(msg, self.strings["converting"])
|
||||
|
||||
reply = await msg.get_reply_message()
|
||||
|
||||
if reply.entities and isinstance(reply.entities[0], MessageEntityCustomEmoji):
|
||||
emoji_id = reply.entities[0].document_id
|
||||
data = await msg.client(GetCustomEmojiDocumentsRequest(document_id=[emoji_id]))
|
||||
|
||||
if data:
|
||||
tgs_buffer = io.BytesIO()
|
||||
await msg.client.download_media(data[0], tgs_buffer)
|
||||
tgs_buffer.seek(0)
|
||||
|
||||
await utils.answer(
|
||||
msg,
|
||||
tgs_buffer.getvalue(),
|
||||
attributes=[types.DocumentAttributeFilename("sticker.tgs")],
|
||||
reply_to=reply
|
||||
)
|
||||
else:
|
||||
await utils.answer(msg, self.strings["emoji_not_found"])
|
||||
else:
|
||||
await utils.answer(msg, self.strings["not_tgs_or_emoji"])
|
||||
|
||||
def tgs2json(self, inbytes):
|
||||
data = gzip.decompress(inbytes).decode('utf-8')
|
||||
return data.encode('utf-8')
|
||||
|
||||
def json2tgs(self, inbytes):
|
||||
data = json.loads(inbytes.decode("utf-8"))
|
||||
compressed = json.dumps(data, ensure_ascii=False, separators=(",", ":"))
|
||||
buffer = io.BytesIO()
|
||||
|
||||
with gzip.GzipFile(fileobj=buffer, mode="wb", compresslevel=9) as out:
|
||||
out.write(compressed.encode("utf-8"))
|
||||
|
||||
return buffer.getvalue()
|
||||
|
||||
def svg2tgs(self, inbytes):
|
||||
infile = io.BytesIO(inbytes)
|
||||
importer = Baseporter(extensions=["svg", "svgz"],
|
||||
callback=import_svg,
|
||||
name="SVG")
|
||||
|
||||
exporter = Baseporter(extensions=["tgs"],
|
||||
callback=export_tgs,
|
||||
name="Telegram Animated Sticker")
|
||||
|
||||
an = importer.process(infile)
|
||||
an.frame_rate = 60
|
||||
an.scale(512, 512)
|
||||
|
||||
buffer = io.BytesIO()
|
||||
exporter.process(an, buffer)
|
||||
buffer.seek(0)
|
||||
return buffer
|
||||
|
||||
def tgs2gif(self, inbytes):
|
||||
temp = "/tmp/output.gif"
|
||||
|
||||
try:
|
||||
tgs_stream = io.BytesIO(inbytes)
|
||||
anim = LottieAnimation.from_tgs(tgs_stream)
|
||||
|
||||
anim.save_animation(temp, format="gif")
|
||||
|
||||
gif_buffer = io.BytesIO()
|
||||
with open(temp, "rb") as gif_file:
|
||||
gif_buffer.write(gif_file.read())
|
||||
|
||||
gif_buffer.seek(0)
|
||||
return gif_buffer
|
||||
|
||||
finally:
|
||||
if os.path.exists(temp):
|
||||
os.remove(temp)
|
||||
|
||||
def tgs2pngs(self, inbytes):
|
||||
temp = "/tmp/frames.zip"
|
||||
|
||||
try:
|
||||
tgs_stream = io.BytesIO(inbytes)
|
||||
anim = LottieAnimation.from_tgs(tgs_stream)
|
||||
|
||||
total_frames = anim.lottie_animation_get_totalframe()
|
||||
|
||||
zip_buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zipf:
|
||||
for i in range(total_frames):
|
||||
frame = anim.render_pillow_frame(i)
|
||||
|
||||
frame_buffer = io.BytesIO()
|
||||
frame.save(frame_buffer, format="PNG")
|
||||
frame_buffer.seek(0)
|
||||
|
||||
zipf.writestr(f"frame_{i}.png", frame_buffer.getvalue())
|
||||
|
||||
zip_buffer.seek(0)
|
||||
return zip_buffer
|
||||
|
||||
finally:
|
||||
if os.path.exists(temp):
|
||||
os.remove(temp)
|
||||
302
yummy1gay/limoka/yg_trigger.py
Normal file
302
yummy1gay/limoka/yg_trigger.py
Normal file
@@ -0,0 +1,302 @@
|
||||
__version__ = (1, 0, 0, 1)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import re
|
||||
from .. import loader, utils
|
||||
from telethon import events
|
||||
import os
|
||||
|
||||
@loader.tds
|
||||
class yg_trigger(loader.Module):
|
||||
"""Триггер-модуль. Документация: kok.gay/trigger"""
|
||||
|
||||
strings = {"name": "yg_trigger"}
|
||||
|
||||
def __init__(self):
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"watcher_on",
|
||||
True,
|
||||
"состояние вотчера",
|
||||
validator=loader.validators.Boolean()
|
||||
)
|
||||
)
|
||||
|
||||
async def client_ready(self, client, db):
|
||||
self.db = db
|
||||
self.triggers = self.db.get("triggers", "list", [])
|
||||
self.client = client
|
||||
handlers = [
|
||||
(self.trigger, [events.NewMessage, events.MessageEdited])
|
||||
]
|
||||
|
||||
for handler_func, event_list in handlers:
|
||||
for event in event_list:
|
||||
self.client.add_event_handler(handler_func, event)
|
||||
|
||||
async def save_triggers(self):
|
||||
self.db.set("triggers", "list", self.triggers)
|
||||
|
||||
async def validate(self, entity_value, target_entity):
|
||||
entity_k = entity_value.replace("@", "")
|
||||
|
||||
entity_id = getattr(target_entity, "id", None)
|
||||
entity_usernames = await self.get_usernames(target_entity)
|
||||
|
||||
if entity_k.isdigit():
|
||||
return int(entity_value) == entity_id
|
||||
|
||||
return entity_k.lower() in entity_usernames
|
||||
|
||||
async def add_trigger(self, conditions: dict, response: str):
|
||||
self.triggers.append({"conditions": conditions, "response": response})
|
||||
await self.save_triggers()
|
||||
|
||||
async def triggercmd(self, message):
|
||||
"""вкл/выкл вотчер"""
|
||||
self.config["watcher_on"] = not self.config["watcher_on"]
|
||||
await message.edit(f"<emoji document_id=5361741454685256344>🎮</emoji> <b>Триггер-мод {'включен' if self.config['watcher_on'] else 'выключен'}</b>")
|
||||
|
||||
async def remove_trigger(self, index: int):
|
||||
try:
|
||||
del self.triggers[index]
|
||||
await self.save_triggers()
|
||||
return True
|
||||
except IndexError:
|
||||
return False
|
||||
|
||||
async def clear_triggers(self):
|
||||
self.triggers = []
|
||||
await self.save_triggers()
|
||||
|
||||
async def get_usernames(self, entity):
|
||||
usernames = []
|
||||
|
||||
if entity.username:
|
||||
username = entity.username
|
||||
if username:
|
||||
usernames.append(username.lower())
|
||||
|
||||
if entity.usernames:
|
||||
additional_usernames = [
|
||||
u.username.lower() for u in (getattr(entity, "usernames", []) or [])
|
||||
]
|
||||
usernames.extend(additional_usernames)
|
||||
|
||||
return usernames
|
||||
|
||||
async def list_triggerscmd(self, message):
|
||||
"""показать все существующие триггеры"""
|
||||
if not self.triggers:
|
||||
await utils.answer(message, "<emoji document_id=5461117441612462242>🙂</emoji> <b>Нет активных триггеров</b>")
|
||||
return
|
||||
|
||||
reply_with_html = "<emoji document_id=5334544901428229844>ℹ️</emoji> <b>Список триггеров:</b>\n\n"
|
||||
|
||||
reply_plain_text = "Список триггеров:\n\n"
|
||||
|
||||
for i, trigger in enumerate(self.triggers):
|
||||
conditions = "\n".join([f"<b>-</b> {k}: <code>{v}</code>" for k, v in trigger["conditions"].items()])
|
||||
if not conditions:
|
||||
conditions = "<b>Нет условий (all=true)</b>"
|
||||
reply_with_html += f"<b>{i}.</b> Условия:\n{conditions}\n\n<emoji document_id=5443038326535759644>💬</emoji> <b>Ответ:</b> <code>{trigger['response']}</code>\n\n"
|
||||
|
||||
reply_plain_text += f"{i}.\nУсловия:\n{conditions}\nОтвет: {trigger['response']}\n\n"
|
||||
|
||||
if len(reply_with_html) > 4096:
|
||||
file_path = "triggers_list.txt"
|
||||
with open(file_path, "w", encoding="utf-8") as file:
|
||||
file.write(reply_plain_text)
|
||||
|
||||
await message.delete()
|
||||
await self.client.send_file(message.chat, caption="<emoji document_id=5433653135799228968>📁</emoji> <i>Вывод команды слишком длинный, поэтому он отправлен в файле.</i>", file=file_path)
|
||||
os.remove(file_path)
|
||||
else:
|
||||
await utils.answer(message, reply_with_html)
|
||||
|
||||
async def split(self, input_str):
|
||||
is_in_quotes = False
|
||||
split_index = -1
|
||||
|
||||
for i, char in enumerate(input_str):
|
||||
if char == '"':
|
||||
is_in_quotes = not is_in_quotes
|
||||
elif char == '|' and not is_in_quotes:
|
||||
split_index = i
|
||||
break
|
||||
|
||||
if split_index == -1:
|
||||
raise ValueError("Некорректный формат: отсутствует разделитель '|'")
|
||||
|
||||
return input_str[:split_index].strip(), input_str[split_index + 1:].strip()
|
||||
|
||||
async def add_triggercmd(self, message):
|
||||
"""<условия> | <ответ> - добавить новый триггер"""
|
||||
args = utils.get_args_raw(message)
|
||||
if not args or "|" not in args:
|
||||
await utils.answer(
|
||||
message,
|
||||
"<emoji document_id=5240241223632954241>🚫</emoji> <b>Формат: <условия> | <ответ></b>\n<emoji document_id=5325547803936572038>✨</emoji> Пример: <code>text=\"Привет\" | Здравствуй!</code>"
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
conditions_raw, response = await self.split(args)
|
||||
|
||||
valid_keys = {
|
||||
"text", "user", "chat", "starts_with", "ends_with", "contains",
|
||||
"regex", "is_command", "word_count", "char_count", "is_reply", "is_forwarded",
|
||||
"media_type", "message_length", "time_range", "date", "weekday", "all"
|
||||
}
|
||||
|
||||
conditions = {}
|
||||
|
||||
pattern = r'(\w+)=((?:\"(?:[^\"]|\\\")*\"|[^,\s]+))'
|
||||
matches = re.finditer(pattern, conditions_raw)
|
||||
|
||||
for match in matches:
|
||||
key, value = match.group(1), match.group(2)
|
||||
|
||||
if key not in valid_keys:
|
||||
raise ValueError(f"Некорректное условие: {key}")
|
||||
|
||||
if key in {"text", "regex", "contains", "starts_with", "ends_with"}:
|
||||
if not (value.startswith('"') and value.endswith('"')):
|
||||
raise ValueError(f"Значение для условия {key} должно быть в кавычках")
|
||||
value = value[1:-1].replace('\\"', '"')
|
||||
|
||||
if key == "all" and value.lower() == "true":
|
||||
conditions = {"all": True}
|
||||
break
|
||||
|
||||
conditions[key] = value
|
||||
|
||||
if not conditions:
|
||||
raise ValueError(
|
||||
"😨 Ты не указал никаких условий.. Если ты такой смелый, укажи all=true в условиях!"
|
||||
)
|
||||
|
||||
if conditions.get("all"):
|
||||
conditions = {}
|
||||
|
||||
await self.add_trigger(conditions, response)
|
||||
|
||||
conditions_str = "\n".join([f"<b>-</b> {k}: <code>{v}</code>" for k, v in conditions.items()])
|
||||
if not conditions_str:
|
||||
conditions_str = "<b>Нет условий (all=true)</b>"
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
f"<emoji document_id=5456140674028019486>⚡️</emoji> <b>Триггер добавлен!</b>\n\nУсловия:\n{conditions_str}\n\n<emoji document_id=5443038326535759644>💬</emoji> <b>Ответ:</b> <code>{response}</code>"
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
await utils.answer(message, f"<emoji document_id=5240241223632954241>🚫</emoji> <b>Ошибка:</b> <code>{e}</code>")
|
||||
except Exception as e:
|
||||
await utils.answer(message, f"<emoji document_id=5240241223632954241>🚫</emoji> <b>Непредвиденная ошибка:</b> <code>{e}</code>")
|
||||
|
||||
async def remove_triggercmd(self, message):
|
||||
"""<номер> - удалить триггер"""
|
||||
args = utils.get_args_raw(message)
|
||||
if not args.isdigit():
|
||||
await utils.answer(
|
||||
message,
|
||||
"<emoji document_id=5240241223632954241>🚫</emoji> <b>Укажи номер триггера для удаления</b>"
|
||||
)
|
||||
return
|
||||
|
||||
index = int(args)
|
||||
if await self.remove_trigger(index):
|
||||
await utils.answer(message, "<emoji document_id=5445267414562389170>🗑</emoji> <b>Триггер удалён!</b>")
|
||||
else:
|
||||
await utils.answer(message, "<emoji document_id=5240241223632954241>🚫</emoji> <b> Неверный номер триггера</b>")
|
||||
|
||||
async def clear_triggerscmd(self, message):
|
||||
"""удалить все триггеры"""
|
||||
await self.clear_triggers()
|
||||
await utils.answer(message, "<emoji document_id=5445267414562389170>🗑</emoji> <b>Все триггеры удалены!</b>")
|
||||
|
||||
async def trigger(self, message):
|
||||
if not self.config["watcher_on"]:
|
||||
return
|
||||
|
||||
responses = []
|
||||
|
||||
for trigger in self.triggers:
|
||||
conditions = trigger["conditions"]
|
||||
match = True
|
||||
|
||||
for key, value in conditions.items():
|
||||
value = value.strip('"')
|
||||
if key == "text" and value.lower() != message.raw_text.lower():
|
||||
match = False
|
||||
break
|
||||
elif key == "user" and not await self.validate(value, message.sender):
|
||||
match = False
|
||||
break
|
||||
elif key == "chat" and not await self.validate(value, message.chat):
|
||||
match = False
|
||||
break
|
||||
elif key == "starts_with" and not message.raw_text.lower().startswith(value.lower()):
|
||||
match = False
|
||||
break
|
||||
elif key == "ends_with" and not message.raw_text.lower().endswith(value.lower()):
|
||||
match = False
|
||||
break
|
||||
elif key == "contains" and value.lower() not in message.raw_text.lower():
|
||||
match = False
|
||||
break
|
||||
elif key == "regex" and not re.search(value, message.raw_text):
|
||||
match = False
|
||||
break
|
||||
elif key == "is_command" and value.lower() == "true" and not message.raw_text.startswith("/"):
|
||||
match = False
|
||||
break
|
||||
elif key == "word_count" and len(message.raw_text.split()) != int(value):
|
||||
match = False
|
||||
break
|
||||
elif key == "char_count" and len(message.raw_text) != int(value):
|
||||
match = False
|
||||
break
|
||||
elif key == "is_reply" and value.lower() == "true" and not message.is_reply:
|
||||
match = False
|
||||
break
|
||||
elif key == "is_forwarded" and value.lower() == "true" and not message.fwd_from:
|
||||
match = False
|
||||
break
|
||||
elif key == "media_type":
|
||||
media_types = {"photo": message.photo, "video": message.video, "sticker": message.sticker, "voice": message.voice, "audio": message.audio}
|
||||
if not media_types.get(value.lower()):
|
||||
match = False
|
||||
break
|
||||
elif key == "message_length" and not (int(value.split("-", 1)[0]) <= len(message.raw_text) <= int(value.split("-", 1)[1])):
|
||||
match = False
|
||||
break
|
||||
elif key == "time_range" and not (int(value.split("-", 1)[0]) <= message.date.hour <= int(value.split("-", 1)[1])):
|
||||
match = False
|
||||
break
|
||||
elif key == "date" and value != message.date.strftime("%Y-%m-%d"):
|
||||
match = False
|
||||
break
|
||||
elif key == "weekday" and value.lower() != message.date.strftime("%A").lower():
|
||||
match = False
|
||||
break
|
||||
|
||||
if match:
|
||||
responses.append(trigger["response"])
|
||||
|
||||
for response in responses:
|
||||
await message.reply(response)
|
||||
85
yummy1gay/limoka/yg_vm.py
Normal file
85
yummy1gay/limoka/yg_vm.py
Normal file
@@ -0,0 +1,85 @@
|
||||
__version__ = (1, 4, 8, 8)
|
||||
|
||||
# This file is a part of Hikka Userbot
|
||||
# Code is NOT licensed under CC-BY-NC-ND 4.0 unless otherwise specified.
|
||||
# 🌐 https://github.com/hikariatama/Hikka
|
||||
|
||||
# You CAN edit this file without direct permission from the author.
|
||||
# You can redistribute this file with any modifications.
|
||||
|
||||
# meta developer: @yg_modules
|
||||
# scope: hikka_only
|
||||
# scope: hikka_min 1.6.3
|
||||
# scope: ffmpeg
|
||||
|
||||
# █▄█ █░█ █▀▄▀█ █▀▄▀█ █▄█ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ░█░ █▄█ █░▀░█ █░▀░█ ░█░ █░▀░█ █▄█ █▄▀ ▄█
|
||||
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from .. import loader, utils
|
||||
|
||||
@loader.tds
|
||||
class VoiceModule(loader.Module):
|
||||
"""Converts music and video (required ffmpeg)"""
|
||||
|
||||
strings = {"name": "yg_vm"}
|
||||
|
||||
async def m2vcmd(self, message):
|
||||
"""Convert music to voice message"""
|
||||
reply = await message.get_reply_message()
|
||||
if not reply or not reply.file:
|
||||
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> <b>Reply to audio file</b>")
|
||||
return
|
||||
|
||||
media = reply.file
|
||||
mime_type = media.mime_type.split('/')[0] if '/' in media.mime_type else 'audio'
|
||||
|
||||
if mime_type == 'audio':
|
||||
await utils.answer(message, "<emoji document_id=4988080790286894217>🫥</emoji> <b>Converting audio...</b>")
|
||||
voice_message = await self.convert_audio(reply)
|
||||
else:
|
||||
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> <b>Unsupported file type</b>")
|
||||
return
|
||||
|
||||
await message.delete()
|
||||
await message.client.send_file(message.to_id, voice_message, voice_note=True, reply_to=reply)
|
||||
|
||||
async def convert_audio(self, message):
|
||||
tmp_filename = "tmp_audio.ogg"
|
||||
await message.download_media(file=tmp_filename)
|
||||
os.system(f"ffmpeg -y -i {tmp_filename} -c:a libopus {tmp_filename}.ogg")
|
||||
os.remove(tmp_filename)
|
||||
return f"{tmp_filename}.ogg"
|
||||
|
||||
async def v2acmd(self, message):
|
||||
"""Convert video to audio"""
|
||||
reply = await message.get_reply_message()
|
||||
if not reply or not reply.file:
|
||||
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> <b>Reply to video file</b>")
|
||||
return
|
||||
|
||||
media = reply.file
|
||||
mime_type = media.mime_type.split('/')[0] if '/' in media.mime_type else 'video'
|
||||
|
||||
if mime_type == 'video':
|
||||
await utils.answer(message, "<emoji document_id=4988080790286894217>🫥</emoji> <b>Converting video to audio...</b>")
|
||||
audio_message = await self.convert_video_to_mp3(reply)
|
||||
else:
|
||||
await utils.answer(message, "<emoji document_id=5210952531676504517>❌</emoji> <b>Unsupported file type</b>")
|
||||
return
|
||||
|
||||
await message.delete()
|
||||
await message.client.send_file(message.to_id, audio_message, reply_to=reply)
|
||||
|
||||
async def convert_video_to_mp3(self, message):
|
||||
tmp_filename = f"{uuid.uuid4().hex}.mp4"
|
||||
await message.download_media(file=tmp_filename)
|
||||
|
||||
audio_filename = f"{uuid.uuid4().hex}.mp3"
|
||||
os.system(f"ffmpeg -y -i {tmp_filename} -vn -acodec libmp3lame -ab 192k -ar 44100 -ac 2 {audio_filename}")
|
||||
|
||||
os.remove(tmp_filename)
|
||||
|
||||
return audio_filename
|
||||
Reference in New Issue
Block a user