Files
ayako/bot.py

297 lines
8.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
from typing import Optional
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (
Application,
CommandHandler,
MessageHandler,
CallbackQueryHandler,
ConversationHandler,
ContextTypes,
filters,
)
from vndb_client import VndbClient
from config import Config
from utils import Formatter, ErrorHandler, QueryBuilder
from detailed_handlers import get_detail_handlers
# ======================
# LOGGING
# ======================
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
level=Config.LOG_LEVEL
)
logger = logging.getLogger(__name__)
# ======================
# STATES
# ======================
SEARCH_VN, SELECT_VN, VN_DETAILS = range(3)
SEARCH_CHARACTER, SELECT_CHARACTER = range(2)
SEARCH_RELEASE, SELECT_RELEASE = range(2)
SEARCH_STAFF, SELECT_STAFF = range(2)
# ======================
# CLIENT
# ======================
vndb_client = VndbClient(use_sandbox=Config.USE_SANDBOX)
# ======================
# HELPERS
# ======================
HTML_HELP_HEADER = "<b>VNDB Telegram Bot</b>\n\n"
# ======================
# HANDLERS
# ======================
class BotHandlers:
@staticmethod
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
text = """
<b>Добро пожаловать в VNDB Telegram Бот!</b>
Этот бот позволяет искать визуальные новеллы, персонажей и релизы из VNDB.
<b>Команды:</b>
/search - поиск VN
/char - персонажи
/release - релизы
/staff - сотрудники
/producer - продюсеры
/tag - теги
/trait - черты
/quote - цитаты
/help - помощь
"""
await update.message.reply_text(text, parse_mode="HTML")
# ======================
# HELP
# ======================
@staticmethod
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
text = """
<b>Справка по командам</b>
<b>Поиск:</b>
/search &lt;название&gt;
/char &lt;имя&gt;
/release &lt;название&gt;
/staff &lt;имя&gt;
/producer &lt;имя&gt;
<b>Информация:</b>
/tag - популярные теги
/trait - черты
/quote &lt;число&gt; - цитаты
/stats - статистика
/schema - API
<b>Детальный просмотр:</b>
/vn_detail &lt;id&gt;
/char_detail &lt;id&gt;
/release_detail &lt;id&gt;
<b>Пример:</b>
/search Steins Gate
/char Okabe
/vn_detail v17
<b>Ссылка:</b>
<a href="https://git.kotac.ru/King-of-the-all-Cookies/ayako/src/branch/main/EXAMPLES.md">
Примеры команд
</a>
"""
await update.message.reply_text(text, parse_mode="HTML")
# ======================
# STATS
# ======================
@staticmethod
async def stats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
try:
stats = await vndb_client.get_stats()
text = f"""
<b>Статистика VNDB</b>
VN: {stats.get('vn', 0)}
Characters: {stats.get('chars', 0)}
Releases: {stats.get('releases', 0)}
Producers: {stats.get('producers', 0)}
Staff: {stats.get('staff', 0)}
"""
await update.message.reply_text(text, parse_mode="HTML")
except Exception as e:
logger.error(e)
await update.message.reply_text(f"{ErrorHandler.format_error(e)}")
# ======================
# SCHEMA
# ======================
@staticmethod
async def schema(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
try:
await update.message.reply_text("⏳ Загружаю схему...", parse_mode="HTML")
schema = await vndb_client.get_schema()
text = "<b>API Schema VNDB</b>\n\n"
if "db_types" in schema:
text += "<b>Типы:</b>\n"
for k in list(schema["db_types"].keys())[:5]:
text += f"{k}\n"
text += "\n<a href='https://api.vndb.org/kana'>Документация API</a>"
await update.message.reply_text(text, parse_mode="HTML")
except Exception as e:
logger.error(e)
await update.message.reply_text(f"{ErrorHandler.format_error(e)}")
# ======================
# SEARCH VN
# ======================
@staticmethod
async def search_vn(update: Update, context: ContextTypes.DEFAULT_TYPE):
try:
query = " ".join(context.args)
if not query:
await update.message.reply_text("Введите название")
return ConversationHandler.END
await update.message.reply_text(
f"<b>Поиск:</b> {query}",
parse_mode="HTML"
)
results = await vndb_client.query_vn(
filters=["search", "=", query.strip()],
fields=["id", "title"],
results=10
)
if not results.get("results"):
await update.message.reply_text("Ничего не найдено")
return ConversationHandler.END
text = "<b>Результаты:</b>\n\n"
for vn in results["results"]:
text += f"{vn.get('id')} - {vn.get('title')}\n"
await update.message.reply_text(text, parse_mode="HTML")
# images
for vn in results["results"]:
text += f"{vn.get('id')} - {vn.get('title')}\n"
# 👇 ДОБАВЬ ССЫЛКУ НА VNDB
text += f"https://vndb.org/{vn.get('id')}\n\n"
except Exception as e:
logger.error(e)
await update.message.reply_text(f"{ErrorHandler.format_error(e)}")
return ConversationHandler.END
# ======================
# CHARACTER
# ======================
@staticmethod
async def search_character(update: Update, context: ContextTypes.DEFAULT_TYPE):
try:
query = " ".join(context.args)
if not query:
await update.message.reply_text("Введите имя")
return ConversationHandler.END
results = await vndb_client.query_character(
filters=["search", "=", query.strip()],
fields=["id", "name", "original"],
results=10
)
text = "<b>Персонажи:</b>\n\n"
for c in results.get("results", []):
text += f"{c.get('id')} - {c.get('name')} ({c.get('original', '')})\n"
await update.message.reply_text(text, parse_mode="HTML")
except Exception as e:
logger.error(e)
await update.message.reply_text(f"{ErrorHandler.format_error(e)}")
return ConversationHandler.END
# ======================
# RELEASE
# ======================
@staticmethod
async def search_release(update: Update, context: ContextTypes.DEFAULT_TYPE):
try:
query = " ".join(context.args)
results = await vndb_client.query_release(
filters=["search", "=", query.strip()], # ✔️ важно
fields=["id", "title"],
results=10
)
text = "<b>Релизы:</b>\n\n"
for r in results.get("results", []):
text += f"{r.get('id')} - {r.get('title')}\n"
await update.message.reply_text(text, parse_mode="HTML")
except Exception as e:
logger.error(e)
await update.message.reply_text(f"{ErrorHandler.format_error(e)}")
return ConversationHandler.END
# ======================
# MAIN
# ======================
def main():
Config.validate()
app = Application.builder().token(Config.TELEGRAM_BOT_TOKEN).build()
app.add_handler(CommandHandler("start", BotHandlers.start))
app.add_handler(CommandHandler("help", BotHandlers.help_command))
app.add_handler(CommandHandler("stats", BotHandlers.stats))
app.add_handler(CommandHandler("schema", BotHandlers.schema))
app.add_handler(CommandHandler("search", BotHandlers.search_vn))
app.add_handler(CommandHandler("char", BotHandlers.search_character))
app.add_handler(CommandHandler("release", BotHandlers.search_release))
for h in get_detail_handlers():
app.add_handler(h)
logger.info("Bot started")
app.run_polling()
if __name__ == "__main__":
main()