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