mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-17 14:54:18 +02:00
Added and updated repositories 2026-03-11 01:21:45
This commit is contained in:
@@ -26,8 +26,10 @@
|
||||
# scope: Api AccountData 0.0.1
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
|
||||
@@ -35,6 +37,7 @@ from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AccountData(loader.Module):
|
||||
"""Find out the approximate date of registration of the telegram account"""
|
||||
@@ -45,9 +48,21 @@ class AccountData(loader.Module):
|
||||
"api_token",
|
||||
"7518491974:1ea2284eec9dc40a9838cfbcb48a2b36",
|
||||
"API token for datereg.pro",
|
||||
validator=loader.validators.String(),
|
||||
validator=loader.validators.Hidden(),
|
||||
)
|
||||
)
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=15)
|
||||
)
|
||||
return self._session
|
||||
|
||||
async def on_unload(self):
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
strings = {
|
||||
"name": "AccountData",
|
||||
@@ -64,15 +79,16 @@ class AccountData(loader.Module):
|
||||
"no_reply": "<emoji document_id=6030512294109122096>💬</emoji> Вы не ответили на сообщение пользователя",
|
||||
}
|
||||
|
||||
async def get_creation_date(self, user_id: int) -> str:
|
||||
api_token = self.config.get("api_token", "")
|
||||
async def get_creation_date(self, user_id: int) -> dict:
|
||||
api_token = self.config["api_token"]
|
||||
if not api_token:
|
||||
return {"error": "API token not configured"}
|
||||
|
||||
|
||||
url = "https://api.datereg.pro/api/v1/users/getCreationDateFast"
|
||||
params = {"token": api_token, "user_id": user_id}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
session = await self._get_session()
|
||||
try:
|
||||
async with session.get(url, params=params) as response:
|
||||
if response.status == 200:
|
||||
json_response = await response.json()
|
||||
@@ -80,11 +96,15 @@ class AccountData(loader.Module):
|
||||
return {
|
||||
"creation_date": json_response["creation_date"],
|
||||
"accuracy_percent": json_response["accuracy_percent"],
|
||||
} # type: ignore
|
||||
}
|
||||
else:
|
||||
return {"error": json_response["error"]["message"]} # type: ignore
|
||||
return {"error": json_response["error"]["message"]}
|
||||
else:
|
||||
return {"error": f"HTTP {response.status}"} # type: ignore
|
||||
return {"error": f"HTTP {response.status}"}
|
||||
except asyncio.TimeoutError:
|
||||
return {"error": "Request timed out"}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Узнать примерную дату регистрации Telergam-аккаунта",
|
||||
@@ -93,17 +113,17 @@ class AccountData(loader.Module):
|
||||
async def accdata(self, message):
|
||||
if reply := await message.get_reply_message():
|
||||
result = await self.get_creation_date(user_id=reply.sender.id)
|
||||
|
||||
|
||||
if "error" in result or not result.get("creation_date"):
|
||||
error_msg = result.get("error", "Unknown error occurred")
|
||||
await utils.answer(message, f"Ошибка: {error_msg}")
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
month, year = map(int, result['creation_date'].split('.'))
|
||||
month, year = map(int, result["creation_date"].split("."))
|
||||
date_object = datetime(year, month, 1)
|
||||
formatted = date_object.strftime('%B %Y')
|
||||
|
||||
formatted = date_object.strftime("%B %Y")
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
f"{self.strings('date_text').format(data=formatted, accuracy=result['accuracy_percent'])}\n\n{self.strings('date_text_ps')}",
|
||||
|
||||
@@ -43,26 +43,35 @@ from ..inline.types import InlineQuery
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
BASE_API_URL = "https://aniliberty.top/api/v1"
|
||||
BASE_API_URL = "https://aniliberty.top/api/v1"
|
||||
|
||||
|
||||
# Датаклассы для парсинга и хранения json
|
||||
@dataclass
|
||||
class Genre:
|
||||
name: str
|
||||
@dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Name:
|
||||
main: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Type:
|
||||
description: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Poster:
|
||||
preview: str
|
||||
thumbnail: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReleaseInfo:
|
||||
id: int
|
||||
genres: Optional[list[Genre]]
|
||||
genres: Optional[list[Genre]]
|
||||
name: Name
|
||||
is_ongoing: bool
|
||||
type: Type
|
||||
@@ -71,6 +80,7 @@ class ReleaseInfo:
|
||||
alias: str
|
||||
poster: Poster
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AniLibertyMod(loader.Module):
|
||||
"""Ищет и возвращает случайное аниме из базы Aniliberty"""
|
||||
@@ -92,36 +102,53 @@ class AniLibertyMod(loader.Module):
|
||||
"favorite": "<b>Избранное <3</b>:", # < == <
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=15)
|
||||
)
|
||||
return self._session
|
||||
|
||||
async def on_unload(self):
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
async def search_title(self, query):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f'{BASE_API_URL}/app/search/releases?query={query}&include=id%2Cname.main%2Cis_ongoing%2Ctype.description%2Cdescription%2Cadded_in_users_favorites%2Calias%2Cposter.preview%2Cposter.thumbnail') as resp:
|
||||
json_answer = await resp.json()
|
||||
results = []
|
||||
for i in json_answer:
|
||||
obj = from_dict(data_class=ReleaseInfo, data=i)
|
||||
results.append(obj)
|
||||
return results
|
||||
|
||||
session = await self._get_session()
|
||||
async with session.get(
|
||||
f"{BASE_API_URL}/app/search/releases?query={query}&include=id%2Cname.main%2Cis_ongoing%2Ctype.description%2Cdescription%2Cadded_in_users_favorites%2Calias%2Cposter.preview%2Cposter.thumbnail"
|
||||
) as resp:
|
||||
json_answer = await resp.json()
|
||||
results = []
|
||||
for i in json_answer:
|
||||
obj = from_dict(data_class=ReleaseInfo, data=i)
|
||||
results.append(obj)
|
||||
return results
|
||||
|
||||
async def get_title(self, release_id):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f'{BASE_API_URL}/anime/releases/{release_id}?include=id%2Cgenres.name%2Cname.main%2Cis_ongoing%2Ctype.description%2Cdescription%2Cadded_in_users_favorites%2Calias%2Cposter.preview%2Cposter.thumbnail') as resp:
|
||||
try:
|
||||
json_answer = await resp.json()
|
||||
data = from_dict(data_class=ReleaseInfo, data=json_answer)
|
||||
return data
|
||||
except JSONDecodeError:
|
||||
logger.error("Ошибка парсинга JSON!")
|
||||
session = await self._get_session()
|
||||
async with session.get(
|
||||
f"{BASE_API_URL}/anime/releases/{release_id}?include=id%2Cgenres.name%2Cname.main%2Cis_ongoing%2Ctype.description%2Cdescription%2Cadded_in_users_favorites%2Calias%2Cposter.preview%2Cposter.thumbnail"
|
||||
) as resp:
|
||||
try:
|
||||
json_answer = await resp.json()
|
||||
data = from_dict(data_class=ReleaseInfo, data=json_answer)
|
||||
return data
|
||||
except JSONDecodeError:
|
||||
logger.error("Ошибка парсинга JSON!")
|
||||
|
||||
async def get_random_title(self):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f'{BASE_API_URL}/anime/releases/random?limit=1&include=id') as resp:
|
||||
randid = await resp.json()
|
||||
"""
|
||||
Приходится запрашивать по второму кругу, т.к. API в рандомных релизах не отдает жанры, даже если попросить через include
|
||||
"""
|
||||
data = await self.get_title(randid[0]['id'])
|
||||
return data
|
||||
|
||||
session = await self._get_session()
|
||||
async with session.get(
|
||||
f"{BASE_API_URL}/anime/releases/random?limit=1&include=id"
|
||||
) as resp:
|
||||
randid = await resp.json()
|
||||
data = await self.get_title(randid[0]["id"])
|
||||
return data
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Возвращает случайный релиз из базы",
|
||||
en_doc="Returns a random release from the database",
|
||||
@@ -130,17 +157,18 @@ class AniLibertyMod(loader.Module):
|
||||
anime_release = await self.get_random_title()
|
||||
genres_str = ""
|
||||
for genre in anime_release.genres[:-1]:
|
||||
genres_str += f'{genre.name}, '
|
||||
genres_str += f"{genre.name}, "
|
||||
genres_str += anime_release.genres[-1].name
|
||||
|
||||
|
||||
text = f"{anime_release.name.main} \n"
|
||||
text += f"{self.strings['ongoing']} {'Да' if anime_release.is_ongoing else 'Нет'}\n\n"
|
||||
text += f"{self.strings['type']} {anime_release.type.description}\n"
|
||||
text += f"{self.strings['genres']} {genres_str}\n\n"
|
||||
|
||||
|
||||
text += f"<code>{anime_release.description}</code>\n\n"
|
||||
text += f"{self.strings['favorite']} {str(anime_release.added_in_users_favorites)}"
|
||||
text += (
|
||||
f"{self.strings['favorite']} {str(anime_release.added_in_users_favorites)}"
|
||||
)
|
||||
|
||||
kb = [
|
||||
[
|
||||
@@ -179,14 +207,14 @@ class AniLibertyMod(loader.Module):
|
||||
"""
|
||||
Приходится запрашивать по второму кругу, т.к. API в поиске не отдает жанры, даже если попросить через include
|
||||
"""
|
||||
release_genres = await self.get_title(anime_release.id)
|
||||
release_genres = await self.get_title(anime_release.id)
|
||||
genres_str = ""
|
||||
for genre in release_genres.genres[:-1]:
|
||||
genres_str += f'{genre.name}, '
|
||||
genres_str += f"{genre.name}, "
|
||||
genres_str += release_genres.genres[-1].name
|
||||
release_text = (
|
||||
f"{anime_release.name.main}\n"
|
||||
f"{self.strings['ongoing']} {"Да" if anime_release.is_ongoing else "Нет"}\n\n"
|
||||
f"{self.strings['ongoing']} {'Да' if anime_release.is_ongoing else 'Нет'}\n\n"
|
||||
f"{self.strings['type']} {anime_release.type.description}\n"
|
||||
f"{self.strings['genres']} {genres_str}\n\n"
|
||||
f"<code>{anime_release.description}</code>\n\n"
|
||||
@@ -214,16 +242,18 @@ class AniLibertyMod(loader.Module):
|
||||
anime_release = await self.get_random_title()
|
||||
genres_str = ""
|
||||
for genre in anime_release.genres[:-1]:
|
||||
genres_str += f'{genre.name}, '
|
||||
genres_str += f"{genre.name}, "
|
||||
genres_str += anime_release.genres[-1].name
|
||||
|
||||
text = f"{anime_release.name.main} \n"
|
||||
text += f"{self.strings['ongoing']} {"Да" if anime_release.is_ongoing else "Нет"}\n\n"
|
||||
text += f"{self.strings['ongoing']} {'Да' if anime_release.is_ongoing else 'Нет'}\n\n"
|
||||
text += f"{self.strings['type']} {anime_release.type.description}\n"
|
||||
text += f"{self.strings['genres']} {genres_str}\n\n"
|
||||
|
||||
text += f"<code>{anime_release.description}</code>\n\n"
|
||||
text += f"{self.strings['favorite']} {str(anime_release.added_in_users_favorites)}"
|
||||
text += (
|
||||
f"{self.strings['favorite']} {str(anime_release.added_in_users_favorites)}"
|
||||
)
|
||||
|
||||
kb = [
|
||||
[
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
|
||||
@@ -35,6 +36,7 @@ from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AnimeQuotesMod(loader.Module):
|
||||
"""A module for sending random quotes from anime"""
|
||||
@@ -58,6 +60,20 @@ class AnimeQuotesMod(loader.Module):
|
||||
"error": "<b>Не удалось получить цитату. Попробуйте позже!</b>",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=15)
|
||||
)
|
||||
return self._session
|
||||
|
||||
async def on_unload(self):
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Получить случайную цитату из аниме",
|
||||
en_doc="Get a random quote from the anime",
|
||||
@@ -66,19 +82,19 @@ class AnimeQuotesMod(loader.Module):
|
||||
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()
|
||||
session = await self._get_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_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)
|
||||
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"])
|
||||
await utils.answer(message, self.strings("error"))
|
||||
|
||||
@@ -29,6 +29,7 @@ import aiohttp
|
||||
import aiofiles
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from .. import loader, utils
|
||||
from telethon.types import MessageMediaDocument
|
||||
@@ -53,19 +54,33 @@ class CodeShareMod(loader.Module):
|
||||
"link_ready": "<emoji document_id=5854762571659218443>✅</emoji> <b>Код загружен! Ссылка:</b> <code>{}</code>",
|
||||
}
|
||||
|
||||
async def upload_to_kmi(self, content: str) -> str:
|
||||
def __init__(self):
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=15)
|
||||
)
|
||||
return self._session
|
||||
|
||||
async def on_unload(self):
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
async def upload_to_kmi(self, content: str) -> Optional[str]:
|
||||
url = "https://kmi.aeza.net"
|
||||
data = aiohttp.FormData()
|
||||
data.add_field("kmi", content)
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, data=data) as response:
|
||||
if response.status == 200:
|
||||
link = await response.text()
|
||||
return link
|
||||
else:
|
||||
logger.error(f"Error occurred! Status code: {response.status}")
|
||||
return
|
||||
session = await self._get_session()
|
||||
async with session.post(url, data=data) as response:
|
||||
if response.status == 200:
|
||||
link = await response.text()
|
||||
return link
|
||||
else:
|
||||
logger.error(f"Error occurred! Status code: {response.status}")
|
||||
return None
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Загрузка кода на сайт",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
|
||||
@@ -34,6 +35,7 @@ from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class CryptoCurrencyMod(loader.Module):
|
||||
"""Module for displaying current cryptocurrency exchange rates."""
|
||||
@@ -49,12 +51,26 @@ class CryptoCurrencyMod(loader.Module):
|
||||
"coin_not_found": "Криптовалюта '{query}' не найдена.",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=15)
|
||||
)
|
||||
return self._session
|
||||
|
||||
async def on_unload(self):
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
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()
|
||||
session = await self._get_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."""
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Name: FolderAutoRead
|
||||
# Description: Automatically reads chats in selected folders
|
||||
# Description: Automatically reads chats in selected folders
|
||||
# Author: @hikka_mods
|
||||
# ---------------------------------------------------------------------------------
|
||||
# meta developer: @hikka_mods
|
||||
@@ -41,13 +41,13 @@ class FolderAutoReadMod(loader.Module):
|
||||
"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_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>"
|
||||
"addfolder": "<emoji document_id=5278227821364275264>📁</emoji> <b>Folder is successfully added to the tracking list!</b>",
|
||||
}
|
||||
|
||||
strings_ru = {
|
||||
@@ -59,79 +59,96 @@ class FolderAutoReadMod(loader.Module):
|
||||
"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>"
|
||||
"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", [])
|
||||
self.tracked_folders = self.pointer("tracked_folders", [])
|
||||
|
||||
async def on_unload(self):
|
||||
self.tracked_folders = []
|
||||
self.set("tracked_folders", [])
|
||||
async def _read_peers(self, peers):
|
||||
for peer in peers:
|
||||
try:
|
||||
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)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug(f"Failed to read peer {peer}: {e}")
|
||||
|
||||
@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)):
|
||||
all_folders = await self._client(
|
||||
functions.messages.GetDialogFiltersRequest()
|
||||
)
|
||||
for folder_name in 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
|
||||
(
|
||||
f
|
||||
for f in all_folders.filters
|
||||
if isinstance(f, DialogFilter) and f.title.text == folder_name
|
||||
),
|
||||
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))
|
||||
if match is None:
|
||||
continue
|
||||
await self._read_peers(match.pinned_peers)
|
||||
await self._read_peers(match.include_peers)
|
||||
|
||||
@loader.command()
|
||||
@loader.command(
|
||||
ru_doc="Добавить папку в список отслеживания",
|
||||
en_doc="Add folder to the tracking list",
|
||||
)
|
||||
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
|
||||
all_folders = await self._client(
|
||||
functions.messages.GetDialogFiltersRequest()
|
||||
)
|
||||
if match and match not in self.tracked_folders:
|
||||
match = next(
|
||||
(
|
||||
f
|
||||
for f in all_folders.filters
|
||||
if isinstance(f, DialogFilter) and f.title.text == arg
|
||||
),
|
||||
None,
|
||||
)
|
||||
if match and arg 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()
|
||||
await utils.answer(message, self.strings("addfolder"))
|
||||
else:
|
||||
await utils.answer(message, self.strings("not_exists_or_already_added"))
|
||||
else:
|
||||
await utils.answer(message, self.strings("wrong_args"))
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Удалить папку из списка отслеживания",
|
||||
en_doc="Delete folder from the tracking list",
|
||||
)
|
||||
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'])
|
||||
await utils.answer(message, self.strings("delfolder"))
|
||||
else:
|
||||
await utils.answer(message, self.strings["wrong_args"])
|
||||
|
||||
@loader.command()
|
||||
await utils.answer(message, self.strings("wrong_args"))
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Список отслеживаемых папок",
|
||||
en_doc="List tracked folders",
|
||||
)
|
||||
async def listfolders(self, message):
|
||||
await utils.answer(message, self.strings["listfolders"] + "\n".join(
|
||||
f"• {folder}" for folder in self.tracked_folders
|
||||
))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("listfolders")
|
||||
+ "\n".join(f"• {folder}" for folder in self.tracked_folders),
|
||||
)
|
||||
|
||||
@@ -465,6 +465,7 @@ class AutoFarmbotMod(loader.Module):
|
||||
:return:
|
||||
"""
|
||||
self.client = client
|
||||
self.tg_id = (await client.get_me()).id
|
||||
self.db = 0
|
||||
self.redis = await aioredis.from_url(
|
||||
self.config["config_redis_cloud_link"],
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
import re
|
||||
@@ -37,6 +38,7 @@ from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class face(loader.Module):
|
||||
"""random face"""
|
||||
@@ -62,6 +64,20 @@ class face(loader.Module):
|
||||
"error": "Произошла ошибка!",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=15)
|
||||
)
|
||||
return self._session
|
||||
|
||||
async def on_unload(self):
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Рандом kaomoji",
|
||||
en_doc="Random kaomoji",
|
||||
@@ -71,14 +87,14 @@ class face(loader.Module):
|
||||
|
||||
url = "https://files.archquise.ru/kaomoji.txt"
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as response:
|
||||
if response.status == 200:
|
||||
data = await response.text()
|
||||
kaomoji_list = [s.strip() for s in re.split(r'[\t\r\n]+', data) if s.strip()]
|
||||
kaomoji = random.choice(kaomoji_list)
|
||||
await utils.answer(
|
||||
message, self.strings("random_face").format(kaomoji)
|
||||
)
|
||||
else:
|
||||
await utils.answer(message, self.strings("error"))
|
||||
session = await self._get_session()
|
||||
async with session.get(url) as response:
|
||||
if response.status == 200:
|
||||
data = await response.text()
|
||||
kaomoji_list = [
|
||||
s.strip() for s in re.split(r"[\t\r\n]+", data) if s.strip()
|
||||
]
|
||||
kaomoji = random.choice(kaomoji_list)
|
||||
await utils.answer(message, self.strings("random_face").format(kaomoji))
|
||||
else:
|
||||
await utils.answer(message, self.strings("error"))
|
||||
|
||||
@@ -676,7 +676,7 @@ class GlobalRestrict(loader.Module):
|
||||
utils.get_entity_url(user),
|
||||
utils.escape_html(get_full_name(user)),
|
||||
(
|
||||
self.strings("unmutes_in_n_chats").format(counter)
|
||||
self.strings("unmute_in_n_chats").format(counter)
|
||||
if silent
|
||||
else chats
|
||||
),
|
||||
|
||||
@@ -36,6 +36,7 @@ from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HostApi:
|
||||
"""
|
||||
A class for interacting with a Host API.
|
||||
@@ -58,8 +59,11 @@ class HostApi:
|
||||
Returns:
|
||||
dict: The API response as a dictionary.
|
||||
"""
|
||||
url = "http://api.hikka.host" + path
|
||||
async with aiohttp.ClientSession(trust_env=True) as session:
|
||||
url = "https://api.hikka.host" + path
|
||||
async with aiohttp.ClientSession(
|
||||
trust_env=True,
|
||||
timeout=aiohttp.ClientTimeout(total=15),
|
||||
) as session:
|
||||
async with session.request(
|
||||
method,
|
||||
url,
|
||||
@@ -67,7 +71,6 @@ class HostApi:
|
||||
"Content-Type": "application/json",
|
||||
"token": self.token,
|
||||
},
|
||||
ssl=False,
|
||||
) as response:
|
||||
return await response.json()
|
||||
|
||||
@@ -290,14 +293,20 @@ class HikkahostMod(loader.Module):
|
||||
token = self.config["token"]
|
||||
user_id = token.split(":")[0]
|
||||
api = HostApi(token)
|
||||
data = await api.logs(user_id, token)
|
||||
data = await api.logs(user_id)
|
||||
|
||||
files_log = data["logs"]
|
||||
|
||||
with open("log.txt", "w") as log_file:
|
||||
json.dump(files_log, log_file)
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
await utils.answer_file(message, "log.txt", self.strings("logs"))
|
||||
fd, tmp_path = tempfile.mkstemp(suffix=".txt", prefix="hikkahost_log_")
|
||||
try:
|
||||
with os.fdopen(fd, "w") as log_file:
|
||||
json.dump(files_log, log_file)
|
||||
await utils.answer_file(message, tmp_path, self.strings("logs"))
|
||||
finally:
|
||||
os.unlink(tmp_path)
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Рестарт HikkaHost",
|
||||
@@ -314,4 +323,4 @@ class HikkahostMod(loader.Module):
|
||||
user_id = token.split(":")[0]
|
||||
api = HostApi(token)
|
||||
|
||||
await api.action(user_id, token)
|
||||
await api.action(user_id)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
import io
|
||||
import logging
|
||||
from textwrap import wrap
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
@@ -38,6 +39,7 @@ from .. import loader, utils
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@loader.tds
|
||||
class JacquesMod(loader.Module):
|
||||
"""Жаконизатор"""
|
||||
@@ -50,6 +52,7 @@ class JacquesMod(loader.Module):
|
||||
self.name = self.strings["name"]
|
||||
self._me = None
|
||||
self._ratelimit = []
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
self.config = loader.ModuleConfig(
|
||||
loader.ConfigValue(
|
||||
"font",
|
||||
@@ -64,6 +67,17 @@ class JacquesMod(loader.Module):
|
||||
),
|
||||
)
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=30)
|
||||
)
|
||||
return self._session
|
||||
|
||||
async def on_unload(self):
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
@loader.command(
|
||||
ru_doc="<реплай на сообщение/свой текст>",
|
||||
en_doc="<reply to the message/your own text>",
|
||||
@@ -81,14 +95,14 @@ class JacquesMod(loader.Module):
|
||||
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()
|
||||
session = await self._get_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()
|
||||
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")
|
||||
|
||||
@@ -96,7 +110,8 @@ class JacquesMod(loader.Module):
|
||||
draw = ImageDraw.Draw(img)
|
||||
font = ImageFont.truetype(io.BytesIO(font_data), 32, encoding="UTF-8")
|
||||
|
||||
text_size = draw.multiline_textsize(wrapped_text, font=font)
|
||||
text_bbox = draw.multiline_textbbox((0, 0), wrapped_text, font=font)
|
||||
text_size = (text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1])
|
||||
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(
|
||||
|
||||
@@ -114,13 +114,13 @@ class NumbersAPI(loader.Module):
|
||||
|
||||
async def _get_number_fact(self, number: int, fact_type: str) -> str:
|
||||
"""Get fact about number"""
|
||||
url = f"http://numbersapi.com/{number}/{fact_type}"
|
||||
url = f"https://numbersapi.com/{number}/{fact_type}"
|
||||
return await self._fetch_fact(url)
|
||||
|
||||
async def _get_date_fact(self, month: int, day: int) -> str:
|
||||
"""Get fact about date"""
|
||||
date_str = f"{month:02d}/{day:02d}"
|
||||
url = f"http://numbersapi.com/{date_str}/date"
|
||||
url = f"https://numbersapi.com/{date_str}/date"
|
||||
return await self._fetch_fact(url)
|
||||
|
||||
@loader.command(
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
import logging
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
|
||||
from .. import loader, utils
|
||||
@@ -69,6 +71,18 @@ class Shortener(loader.Module):
|
||||
validator=loader.validators.Hidden(),
|
||||
)
|
||||
)
|
||||
self._session: Optional[aiohttp.ClientSession] = None
|
||||
|
||||
async def _get_session(self) -> aiohttp.ClientSession:
|
||||
if self._session is None or self._session.closed:
|
||||
self._session = aiohttp.ClientSession(
|
||||
timeout=aiohttp.ClientTimeout(total=15)
|
||||
)
|
||||
return self._session
|
||||
|
||||
async def on_unload(self):
|
||||
if self._session and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
def _validate_url(self, url: str) -> bool:
|
||||
"""Validate URL format"""
|
||||
@@ -87,27 +101,32 @@ class Shortener(loader.Module):
|
||||
|
||||
return url_pattern.match(url) is not None
|
||||
|
||||
async def shorten_url(self, url: str, token: str) -> str:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post("https://api-ssl.bitly.com/v4/shorten", json={'long_url': url}, headers={"Authorization": f"Bearer {token}"}) as resp:
|
||||
if resp.status == 201:
|
||||
json_response = await resp.json()
|
||||
return json_response['link']
|
||||
else:
|
||||
logger.error(f"Error occurred! Status code: {resp.status}")
|
||||
return
|
||||
|
||||
async def get_bitlink_stats(self, bitlink: str, token: str) -> str:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(f"https://api-ssl.bitly.com/v4/bitlinks/{bitlink}/clicks/summary", headers={"Authorization": f"Bearer {token}"}) as resp:
|
||||
if resp.status == 200:
|
||||
json_response = await resp.json()
|
||||
return json_response['total_clicks']
|
||||
else:
|
||||
logger.error(f"Error occurred! Status code: {resp.status}")
|
||||
return
|
||||
|
||||
async def shorten_url(self, url: str, token: str) -> Optional[str]:
|
||||
session = await self._get_session()
|
||||
async with session.post(
|
||||
"https://api-ssl.bitly.com/v4/shorten",
|
||||
json={"long_url": url},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
) as resp:
|
||||
if resp.status == 201:
|
||||
json_response = await resp.json()
|
||||
return json_response["link"]
|
||||
else:
|
||||
logger.error(f"Error occurred! Status code: {resp.status}")
|
||||
return None
|
||||
|
||||
async def get_bitlink_stats(self, bitlink: str, token: str) -> Optional[int]:
|
||||
session = await self._get_session()
|
||||
async with session.get(
|
||||
f"https://api-ssl.bitly.com/v4/bitlinks/{bitlink}/clicks/summary",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
) as resp:
|
||||
if resp.status == 200:
|
||||
json_response = await resp.json()
|
||||
return json_response["total_clicks"]
|
||||
else:
|
||||
logger.error(f"Error occurred! Status code: {resp.status}")
|
||||
return None
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Сократить ссылку через bit.ly (ссылка с https://)",
|
||||
@@ -129,7 +148,13 @@ class Shortener(loader.Module):
|
||||
return
|
||||
|
||||
try:
|
||||
short_url = await self.shorten_url(url=args, token=self.config['token'])
|
||||
short_url = await self.shorten_url(url=args, token=self.config["token"])
|
||||
if short_url is None:
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("api_error").format(error="Failed to shorten URL"),
|
||||
)
|
||||
return
|
||||
await utils.answer(message, self.strings("shortencmd").format(c=short_url))
|
||||
except Exception as e:
|
||||
logger.error(f"Error shortening URL: {e}")
|
||||
@@ -155,7 +180,17 @@ class Shortener(loader.Module):
|
||||
await utils.answer(message, self.strings("invalid_url"))
|
||||
return
|
||||
else:
|
||||
clicks = await self.get_bitlink_stats(bitlink=args, token=self.config['token'])
|
||||
clicks = await self.get_bitlink_stats(
|
||||
bitlink=args, token=self.config["token"]
|
||||
)
|
||||
if clicks is None:
|
||||
await utils.answer(
|
||||
message,
|
||||
self.strings("api_error").format(
|
||||
error="Failed to get statistics"
|
||||
),
|
||||
)
|
||||
return
|
||||
await utils.answer(message, self.strings("statclcmd").format(c=clicks))
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting statistics: {e}")
|
||||
|
||||
Reference in New Issue
Block a user