From 27d262ddb54c36ee3f9445f3d96280e578585197 Mon Sep 17 00:00:00 2001 From: King-of-the-all-Cookies Date: Fri, 1 May 2026 18:20:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20=D0=B1=D0=BE=D1=82=D0=B0=20VNDB:=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B8=D0=BD=D0=B8=D1=86=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20aiogram,=20=D1=83=D0=BB=D1=83=D1=87=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA,=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD?= =?UTF-8?q?=D0=B4=20=D0=B8=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B5=D0=B9=20=D0=B2=20requirements.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 158 ++++++++++++++++++++++++++++----------------- docker-compose.yml | 2 - requirements.txt | 4 +- 3 files changed, 99 insertions(+), 65 deletions(-) diff --git a/bot.py b/bot.py index c8804aa..8f215ad 100644 --- a/bot.py +++ b/bot.py @@ -1,105 +1,141 @@ import os import logging import httpx -from aiogram import Bot, Dispatcher, types, F +from aiogram import Bot, Dispatcher, types from aiogram.filters import Command -from aiogram.utils.markdown import hbold, hlink +from aiogram.client.default import DefaultBotProperties +from aiogram.enums import ParseMode # Настройки TOKEN = os.getenv("TELEGRAM_TOKEN") API_URL = os.getenv("VNDB_API_URL", "https://api.vndb.org/kana") -# Логирование logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) -bot = Bot(token=TOKEN, parse_mode="HTML") +# Исправленная инициализация для aiogram 3.7.0+ +bot = Bot( + token=TOKEN, + default=DefaultBotProperties(parse_mode=ParseMode.HTML) +) dp = Dispatcher() -# Хелпер для запросов к VNDB async def fetch_vndb(endpoint: str, filters: list, fields: str): + """Универсальный и безопасный клиент для VNDB API""" payload = {"filters": filters, "fields": fields} async with httpx.AsyncClient(timeout=10.0) as client: try: response = await client.post(f"{API_URL}/{endpoint}", json=payload) - if response.status_code != 200: - logger.error(f"VNDB Error {response.status_code}: {response.text}") - return None - return response.json().get("results", []) + if response.status_code == 200: + return response.json().get("results", []) + logger.error(f"VNDB API Error {response.status_code}: {response.text}") + return None except Exception as e: - logger.error(f"Connection error: {e}") + logger.error(f"Request failed: {e}") return None -# Команды /start и /help @dp.message(Command("start", "help")) -async def cmd_start(message: types.Message): +async def cmd_help(message: types.Message): help_text = ( - "🤖 VNDB Bot\n\n" + "🤖 VNDB Bot v2.0\n\n" "Поиск:\n" - "/search [name] - поиск новеллы\n" - "/char [name] - поиск персонажа\n" - "/release [name] - поиск релизов\n\n" + "/search <name> — поиск VN\n" + "/char <name> — поиск персонажа\n" + "/release <name> — поиск релиза\n\n" "Инфо по ID:\n" - "/vn [id] (напр. v17)\n" - "/char_id [id] (напр. c1)\n" - "/rel_id [id] (напр. r1)" + "/vn <id> (напр. v17)\n" + "/char_id <id> (напр. c1)\n" + "/rel_id <id> (напр. r1)" ) await message.answer(help_text) -# Обработчик поиска VN +# --- SEARCH HANDLERS --- + @dp.message(Command("search")) async def search_vn(message: types.Message): query = message.extract_args() - if not query: - return await message.answer("Введите название для поиска. Пример: /search Steins;Gate") - - res = await fetch_vndb("vn", ["search", "=", query], "title, released, rating") - if not res: - return await message.answer("❌ Ничего не найдено или ошибка API") - - out = [f"🔍 Результаты поиска:"] - for item in res[:10]: - rating = f"⭐ {item['rating']/10}" if item.get('rating') else "No rating" - out.append(f"• {item['title']} (ID: {item['id']}) | {rating}") + if not query: return await message.answer("Пример: /search Steins;Gate") + res = await fetch_vndb("vn", ["search", "=", query], "title, rating") + if not res: return await message.answer("❌ Ничего не найдено.") + + out = ["🔍 VN Results:"] + for i in res[:10]: + out.append(f"• {i['title']} (ID: {i['id']})") await message.answer("\n".join(out)) -# Обработчик деталей VN -@dp.message(Command("vn")) -async def details_vn(message: types.Message): - vn_id = message.extract_args() - if not vn_id: return await message.answer("Укажите ID. Пример: /vn v11") - - res = await fetch_vndb("vn", ["id", "=", vn_id], "id, title, original, released, rating, votecount") - if not res: return await message.answer("❌ VN не найдена") - - v = res[0] - text = ( - f"📖 {v['title']}\n" - f"Original: {v.get('original', 'N/A')}\n" - f"Released: {v.get('released', 'N/A')}\n" - f"Rating: {v.get('rating', 'N/A') if v.get('rating') else 'N/A'} ({v.get('votecount', 0)} votes)\n" - f"Link: https://vndb.org/{v['id']}" - ) - await message.answer(text) - -# Обработчик персонажей @dp.message(Command("char")) async def search_char(message: types.Message): query = message.extract_args() if not query: return await message.answer("Пример: /char Kurisu") - res = await fetch_vndb("character", ["search", "=", query], "id, name, original") - if not res: return await message.answer("❌ Персонаж не найден") + res = await fetch_vndb("character", ["search", "=", query], "name") + if not res: return await message.answer("❌ Персонаж не найден.") - out = [f"👤 Персонажи:"] - for c in res[:10]: - out.append(f"• {c['name']} ({c['id']})") + out = ["👤 Characters:"] + for i in res[:10]: + out.append(f"• {i['name']} (ID: {i['id']})") await message.answer("\n".join(out)) -# Запуск +@dp.message(Command("release")) +async def search_release(message: types.Message): + query = message.extract_args() + if not query: return await message.answer("Пример: /release Chaos;Head") + + res = await fetch_vndb("release", ["search", "=", query], "title") + if not res: return await message.answer("❌ Релиз не найден.") + + out = ["💿 Releases:"] + for i in res[:10]: + out.append(f"• {i['title']} (ID: {i['id']})") + await message.answer("\n".join(out)) + +# --- DETAIL HANDLERS --- + +@dp.message(Command("vn")) +async def detail_vn(message: types.Message): + vid = message.extract_args() + if not vid: return await message.answer("Введите ID (например v17)") + + res = await fetch_vndb("vn", ["id", "=", vid], "id, title, original, released, rating, votecount") + if not res: return await message.answer("❌ VN не найдена.") + + v = res[0] + rating = f"{v['rating']/10} ⭐" if v.get('rating') else "N/A" + text = (f"📖 {v['title']}\n" + f"Original: {v.get('original', 'N/A')}\n" + f"Released: {v.get('released', 'N/A')}\n" + f"Rating: {rating} ({v.get('votecount', 0)} votes)\n" + f"https://vndb.org/{v['id']}") + await message.answer(text) + +@dp.message(Command("char_id")) +async def detail_char(message: types.Message): + cid = message.extract_args() + if not cid: return await message.answer("Введите ID (например c1)") + + res = await fetch_vndb("character", ["id", "=", cid], "id, name, original") + if not res: return await message.answer("❌ Персонаж не найден.") + + c = res[0] + text = (f"👤 {c['name']}\n" + f"Original: {c.get('original', 'N/A')}\n" + f"https://vndb.org/{c['id']}") + await message.answer(text) + +@dp.message(Command("rel_id")) +async def detail_rel(message: types.Message): + rid = message.extract_args() + if not rid: return await message.answer("Введите ID (например r1)") + + res = await fetch_vndb("release", ["id", "=", rid], "id, title, released") + if not res: return await message.answer("❌ Релиз не найден.") + + r = res[0] + text = (f"💿 {r['title']}\n" + f"Released: {r.get('released', 'N/A')}\n" + f"https://vndb.org/{r['id']}") + await message.answer(text) + if __name__ == "__main__": - if not TOKEN: - print("Error: TELEGRAM_TOKEN not set!") - else: - dp.run_polling(bot) \ No newline at end of file + dp.run_polling(bot) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 50fc2dd..542ba30 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: bot: build: . diff --git a/requirements.txt b/requirements.txt index 5bf55f8..d216bd4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -aiogram==3.10.0 -httpx==0.28.1 \ No newline at end of file +aiogram>=3.10.0 +httpx>=0.27.0 \ No newline at end of file