mirror of
https://github.com/MuRuLOSE/limoka.git
synced 2026-06-18 07:04:19 +02:00
Added and updated repositories 2026-04-12 14:57:04
This commit is contained in:
248
archquise/q.mods/AniLiberty.py
Normal file
248
archquise/q.mods/AniLiberty.py
Normal file
@@ -0,0 +1,248 @@
|
||||
# █▀▀▄ █▀▄▀█ █▀█ █▀▄ █▀
|
||||
# ▀▀▀█ ▄ █ ▀ █ █▄█ █▄▀ ▄█
|
||||
|
||||
# #### Copyright (c) 2026 Archquise #####
|
||||
|
||||
# 💬 Contact: https://t.me/archquise
|
||||
# 🔒 Licensed under the GNU AGPLv3.
|
||||
# 📄 LICENSE: https://raw.githubusercontent.com/archquise/Q.Mods/main/LICENSE
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Name: Aniliberty
|
||||
# Description: Searches and gives random anime on the Aniliberty database.
|
||||
# Author: @quise_m
|
||||
# ---------------------------------------------------------------------------------
|
||||
# meta developer: @quise_m
|
||||
# meta banner: https://raw.githubusercontent.com/archquise/qmods_meta/main/AniLiberty.png
|
||||
# requires: dacite
|
||||
# ruff: noqa: D101
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from json import JSONDecodeError
|
||||
|
||||
import aiohttp
|
||||
from aiogram.types import CallbackQuery, InlineQueryResultPhoto
|
||||
from dacite import from_dict
|
||||
|
||||
from .. import loader
|
||||
from ..inline.types import InlineQuery
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
BASE_API_URL = "https://aniliberty.top/api/v1"
|
||||
|
||||
|
||||
# Датаклассы для парсинга и хранения json
|
||||
@dataclass
|
||||
class Genre:
|
||||
name: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Name:
|
||||
main: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Type:
|
||||
description: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Poster:
|
||||
preview: str
|
||||
thumbnail: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReleaseInfo:
|
||||
id: int
|
||||
genres: list[Genre] | None
|
||||
name: Name
|
||||
is_ongoing: bool
|
||||
type: Type
|
||||
description: str
|
||||
added_in_users_favorites: int
|
||||
alias: str
|
||||
poster: Poster
|
||||
|
||||
|
||||
@loader.tds
|
||||
class AniLibertyMod(loader.Module):
|
||||
"""Ищет и возвращает случайное аниме из базы Aniliberty."""
|
||||
|
||||
strings = { # noqa: RUF012
|
||||
"name": "AniLiberty",
|
||||
"announce": "<b>The announcement</b>:",
|
||||
"ongoing": "<b>Ongoing</b>:",
|
||||
"type": "<b>Type</b>:",
|
||||
"genres": "<b>Genres</b>:",
|
||||
"favorite": "<b>Favourites <3</b>:", # < == <
|
||||
}
|
||||
|
||||
strings_ru = { # noqa: RUF012
|
||||
"announce": "<b>Анонс</b>:",
|
||||
"ongoing": "<b>Онгоинг</b>:",
|
||||
"type": "<b>Тип</b>:",
|
||||
"genres": "<b>Жанры</b>:",
|
||||
"favorite": "<b>Избранное <3</b>:", # < == <
|
||||
"_cls_doc": "Ищет и отправляет случайное аниме из базы AniLiberty",
|
||||
}
|
||||
|
||||
async def client_ready(self, client, db): # noqa: D102, ARG002, ANN001, ANN201
|
||||
self._aioclient = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(20))
|
||||
|
||||
async def search_title(self, query) -> list: # noqa: ANN001
|
||||
"""Search title in the database."""
|
||||
async with self._aioclient.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: str) -> ReleaseInfo | None:
|
||||
"""Get full title information."""
|
||||
async with self._aioclient.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()
|
||||
return from_dict(data_class=ReleaseInfo, data=json_answer)
|
||||
except JSONDecodeError:
|
||||
logger.exception("Ошибка парсинга JSON!")
|
||||
return None
|
||||
|
||||
async def get_random_title(self) -> ReleaseInfo | None:
|
||||
"""Get random title from the database."""
|
||||
async with self._aioclient.get(
|
||||
f"{BASE_API_URL}/anime/releases/random?limit=1&include=id"
|
||||
) as resp:
|
||||
randid = await resp.json()
|
||||
"""
|
||||
Приходится запрашивать по второму кругу, т.к. API в рандомных релизах не отдает жанры, даже если попросить через include
|
||||
"""
|
||||
return await self.get_title(randid[0]["id"])
|
||||
|
||||
@loader.command(
|
||||
ru_doc="Возвращает случайный релиз из базы",
|
||||
en_doc="Returns a random release from the database",
|
||||
)
|
||||
async def arandom(self, message) -> None: # noqa: D102, ANN001
|
||||
anime_release = await self.get_random_title()
|
||||
genres_str = ""
|
||||
for genre in anime_release.genres[:-1]:
|
||||
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']} {anime_release.added_in_users_favorites!s}"
|
||||
|
||||
kb = [
|
||||
[
|
||||
{
|
||||
"text": "Ссылка",
|
||||
"url": f"https://aniliberty.top/anime/releases/release/{anime_release.alias}/episodes",
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
kb.append([{"text": "🔃 Обновить", "callback": self.inline__update}])
|
||||
kb.append([{"text": "🚫 Закрыть", "callback": self.inline__close}])
|
||||
|
||||
await self.inline.form(
|
||||
text=text,
|
||||
photo=f"https://aniliberty.top{anime_release.poster.preview}",
|
||||
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: # noqa: D102
|
||||
text = query.args
|
||||
|
||||
if not text:
|
||||
return
|
||||
|
||||
anime_releases = await self.search_title(text)
|
||||
|
||||
inline_query = []
|
||||
for anime_release in anime_releases:
|
||||
"""
|
||||
Приходится запрашивать по второму кругу, т.к. API в поиске не отдает жанры, даже если попросить через include
|
||||
"""
|
||||
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 += 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['type']} {anime_release.type.description}\n"
|
||||
f"{self.strings['genres']} {genres_str}\n\n"
|
||||
f"<code>{anime_release.description}</code>\n\n"
|
||||
f"{self.strings['favorite']} {anime_release.added_in_users_favorites}"
|
||||
)
|
||||
|
||||
inline_query.append(
|
||||
InlineQueryResultPhoto(
|
||||
id=str(anime_release.id),
|
||||
title=anime_release.name.main,
|
||||
description=anime_release.type.description,
|
||||
caption=release_text,
|
||||
thumbnail_url=f"https://aniliberty.top{anime_release.poster.thumbnail}",
|
||||
photo_url=f"https://aniliberty.top{anime_release.poster.preview}",
|
||||
parse_mode="html",
|
||||
),
|
||||
)
|
||||
method = query.answer(inline_query, cache_time=0)
|
||||
await method.as_(self.inline.bot)
|
||||
|
||||
async def inline__close(self, call: CallbackQuery) -> None: # noqa: D102
|
||||
await call.delete()
|
||||
|
||||
async def inline__update(self, call: CallbackQuery) -> None: # noqa: D102
|
||||
anime_release = await self.get_random_title()
|
||||
genres_str = ""
|
||||
for genre in anime_release.genres[:-1]:
|
||||
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']} {anime_release.added_in_users_favorites!s}"
|
||||
|
||||
kb = [
|
||||
[
|
||||
{
|
||||
"text": "Ссылка",
|
||||
"url": f"https://aniliberty.top/anime/releases/release/{anime_release.alias}/episodes",
|
||||
},
|
||||
],
|
||||
]
|
||||
kb.append([{"text": "🔃 Обновить", "callback": self.inline__update}])
|
||||
kb.append([{"text": "🚫 Закрыть", "callback": self.inline__close}])
|
||||
|
||||
await call.edit(
|
||||
text=text,
|
||||
photo=f"https://aniliberty.top{anime_release.poster.preview}",
|
||||
reply_markup=kb,
|
||||
)
|
||||
Reference in New Issue
Block a user