From fefe522e462702ab4c9c6c2d904aeaeed351fb7b Mon Sep 17 00:00:00 2001 From: King-of-the-all-Cookies Date: Fri, 1 May 2026 16:14:17 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D1=8B=20=D0=BA=D0=BE=D0=B4=D0=B0=20=D0=B8=20=D1=84=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=D0=B0=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9?= =?UTF-8?q?=20=D0=B1=D0=BE=D1=82=D0=B0=20VNDB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 817 +++++++++++++++------------------------------------------ 1 file changed, 211 insertions(+), 606 deletions(-) diff --git a/bot.py b/bot.py index 8d86f36..9c17680 100644 --- a/bot.py +++ b/bot.py @@ -1,5 +1,6 @@ import logging from typing import Optional + from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import ( Application, @@ -10,687 +11,291 @@ from telegram.ext import ( ContextTypes, filters, ) + from vndb_client import VndbClient from config import Config from utils import Formatter, ErrorHandler, QueryBuilder from detailed_handlers import get_detail_handlers -# Setup logging + +# ====================== +# LOGGING +# ====================== logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=Config.LOG_LEVEL ) logger = logging.getLogger(__name__) -# States for conversation handlers + +# ====================== +# 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) -# Global VNDB client + +# ====================== +# CLIENT +# ====================== vndb_client = VndbClient(use_sandbox=Config.USE_SANDBOX) +# ====================== +# HELPERS +# ====================== +HTML_HELP_HEADER = "VNDB Telegram Bot\n\n" + + +# ====================== +# HANDLERS +# ====================== class BotHandlers: - """Telegram bot command and message handlers""" - + @staticmethod async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Start command handler""" - welcome_text = """ -\*\*Добро пожаловать в VNDB Telegram Бот\!\*\* + text = """ +Добро пожаловать в VNDB Telegram Бот! -Этот бот позволяет искать информацию о визуальных новеллах, персонажах, релизах и многом другом из базы данных VNDB\. +Этот бот позволяет искать визуальные новеллы, персонажей и релизы из VNDB. -\*\*Доступные команды:\*\* -/search \- Поиск визуальных новелл -/char \- Поиск персонажей -/release \- Поиск релизов -/staff \- Поиск сотрудников -/producer \- Поиск продюсеров -/tag \- Поиск тегов -/trait \- Поиск черт характера -/quote \- Поиск цитат -/stats \- Статистика базы данных -/schema \- Информация о схеме API -/help \- Справка по командам - -Используйте /help для получения подробной информации -Также можете ознакомится с примерами команд по ссылке: https://git\.kotac\.ru/King\-of\-the\-all\-Cookies/ayako/src/branch/main/EXAMPLES\.md +Команды: +/search - поиск VN +/char - персонажи +/release - релизы +/staff - сотрудники +/producer - продюсеры +/tag - теги +/trait - черты +/quote - цитаты +/help - помощь """ - await update.message.reply_text(welcome_text, parse_mode="Markdown") - + + await update.message.reply_text(text, parse_mode="HTML") + + # ====================== + # HELP + # ====================== @staticmethod async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Help command handler""" - help_text = """ -\*\*Справка по командам VNDB Бота\*\* + text = """ +Справка по командам -\*\*Поиск информации:\*\* -/search <название\> \- Поиск визуальных новелл по названию -/char <название\> \- Поиск персонажей по имени -/release <название\> \- Поиск релизов -/staff <название\> \- Поиск сотрудников \(сценаристы, художники и т\.д\.\) -/producer <название\> \- Поиск продюсеров -/tag \- Список популярных тегов -/trait \- Список черт характера -/quote <количество\> \- Получить случайные цитаты +Поиск: +/search <название> +/char <имя> +/release <название> +/staff <имя> +/producer <имя> -\*\*Подробный просмотр \(с картинками\):\*\* -/vn\_detail \- Просмотр полной информации о ВН с обложкой - \_\_Пример: /vn\_detail v17\_\_ -/char\_detail \- Просмотр информации о персонаже с аватаром - \_\_Пример: /char\_detail c1\_\_ -/release\_detail \- Просмотр информации о релизе с картинкой - \_\_Пример: /release\_detail r1\_\_ +Информация: +/tag - популярные теги +/trait - черты +/quote <число> - цитаты +/stats - статистика +/schema - API -\*\*Информация:\*\* -/stats \- Показать статистику базы данных VNDB -/schema \- Получить информацию о доступных полях API -/authinfo \- Информация об авторизации \(если настроена\) +Детальный просмотр: +/vn_detail <id> +/char_detail <id> +/release_detail <id> -\*\*Функции пользователя \(требуют токена\):\*\* -Чтобы использовать функции списка, установите токен в переменной окружения VNDB\_TOKEN - -\*\*Примеры использования:\*\* +Пример: /search Steins Gate /char Okabe -/release Windows -/vn\_detail v17 -/char\_detail c25 -/stats +/vn_detail v17 -\*\*Важно:\*\* -\- Бот работает в асинхронном режиме -\- Результаты ограничены 10 элементами по умолчанию -\- При поиске автоматически отправляются картинки \(первые 3 результата\) -\- Для просмотра полной информации с картинкой используйте /vn\_detail, /char\_detail и т\.д\. +Ссылка: + +Примеры команд + """ - await update.message.reply_text(help_text, parse_mode="Markdown") - + + await update.message.reply_text(text, parse_mode="HTML") + + # ====================== + # STATS + # ====================== @staticmethod async def stats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Get database statistics""" try: stats = await vndb_client.get_stats() - - stats_text = f""" -\*\*Статистика базы данных VNDB:\*\* -Визуальные новеллы: {stats.get('vn', 0):,} -Персонажи: {stats.get('chars', 0):,} -Релизы: {stats.get('releases', 0):,} -Продюсеры: {stats.get('producers', 0):,} -Сотрудники: {stats.get('staff', 0):,} -Теги: {stats.get('tags', 0):,} -Черты характера: {stats.get('traits', 0):,} - """ - await update.message.reply_text(stats_text, parse_mode="Markdown") + text = f""" +Статистика VNDB + +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(f"Error getting stats: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"❌ {error_msg}") - + 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: - """Get API schema information""" try: - await update.message.reply_text( - "⏳ Загружаю информацию о схеме... (это может занять некоторое время)", - parse_mode="Markdown" - ) - + await update.message.reply_text("⏳ Загружаю схему...", parse_mode="HTML") + schema = await vndb_client.get_schema() - - # Build schema info - schema_text = "**Информация о схеме VNDB API:**\n\n" - - # Database types + + text = "API Schema VNDB\n\n" + if "db_types" in schema: - schema_text += "**Типы данных:**\n" - for db_type, info in list(schema["db_types"].items())[:5]: - schema_text += f"• {db_type}\n" - schema_text += "\n" - - # Search fields - if "fields" in schema: - schema_text += "**Доступные поля для запросов:**\n" - for field_type, fields in list(schema["fields"].items())[:3]: - schema_text += f"• {field_type}\n" - schema_text += "\n" - - schema_text += "**Для полного списка полей и типов посетите: https://api.vndb.org/kana**" - - await update.message.reply_text(schema_text, parse_mode="Markdown") + text += "Типы:\n" + for k in list(schema["db_types"].keys())[:5]: + text += f"• {k}\n" + + text += "\nДокументация API" + + await update.message.reply_text(text, parse_mode="HTML") + except Exception as e: - logger.error(f"Error getting schema: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"❌ {error_msg}") - + 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) -> int: - """Search for visual novels""" + async def search_vn(update: Update, context: ContextTypes.DEFAULT_TYPE): try: - args = " ".join(context.args) if context.args else "" - - if not args: - await update.message.reply_text( - "Пожалуйста, укажите название для поиска\n" - "Пример: /search Steins Gate" - ) + query = " ".join(context.args) + + if not query: + await update.message.reply_text("Введите название") return ConversationHandler.END - - await update.message.reply_text(f"Поиск визуальных новелл: **{args}**\nЗагрузка...", parse_mode="Markdown") - - # Search for VN - filters = ["search", "=", args] + + await update.message.reply_text( + f"Поиск: {query}", + parse_mode="HTML" + ) + results = await vndb_client.query_vn( - filters=[filters], - fields=["title", "original", "released", "rating", "votecount", "image{url}"], + filters=["search", "=", query], + fields=["title", "id", "image{url}"], results=10 ) - + if not results.get("results"): await update.message.reply_text("Ничего не найдено") return ConversationHandler.END - - # Format results - response_text = f"**Результаты поиска: {args}**\n\n" - - for i, vn in enumerate(results["results"], 1): - vn_id = vn.get("id", "Unknown") - title = vn.get("title", "Unknown") - original = vn.get("original", "") - released = vn.get("released", "Unknown") - rating = vn.get("rating", 0) - votecount = vn.get("votecount", 0) - - response_text += ( - f"{i}. **{title}**\n" - f" ID: {vn_id}\n" - ) - if original: - response_text += f" Оригинал: {original}\n" - response_text += ( - f" Релиз: {released}\n" - f" Рейтинг: {rating/10:.1f}/10 ({votecount} голосов)\n\n" - ) - - response_text += f"\nВсего найдено: {len(results['results'])} результатов" - if results.get("more"): - response_text += " (есть еще результаты)" - - await update.message.reply_text(response_text, parse_mode="Markdown") - - # Send images if available - for vn in results["results"][:3]: # Send images for first 3 results - image = vn.get("image") - if image and isinstance(image, dict): - image_url = image.get("url") - if image_url: - try: - title = vn.get("title", "VN") - await update.message.reply_photo( - photo=f"https://t.vndb.org{image_url}", - caption=f"{title}", - parse_mode="Markdown" - ) - except Exception as e: - logger.warning(f"Could not send image: {e}") - - # Store results for detail view - context.user_data["vn_results"] = results["results"] - + + text = "Результаты:\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"][:3]: + img = vn.get("image") + if img and img.get("url"): + try: + await update.message.reply_photo( + photo=f"https://t.vndb.org{img['url']}" + ) + except Exception as e: + logger.warning(e) + except Exception as e: - logger.error(f"Error searching VN: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"{error_msg}") - + 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) -> int: - """Search for characters""" + async def search_character(update: Update, context: ContextTypes.DEFAULT_TYPE): try: - args = " ".join(context.args) if context.args else "" - - if not args: - await update.message.reply_text( - "Пожалуйста, укажите имя персонажа\n" - "Пример: /char Okabe" - ) + query = " ".join(context.args) + + if not query: + await update.message.reply_text("Введите имя") return ConversationHandler.END - - await update.message.reply_text(f"Поиск персонажей: **{args}**\n⏳ Загрузка...", parse_mode="Markdown") - - filters = ["search", "=", args] + results = await vndb_client.query_character( - filters=[filters], - fields=["name", "original", "gender", "vn", "image{url}"], + filters=["search", "=", query], + fields=["name", "id", "image{url}"], results=10 ) - - if not results.get("results"): - await update.message.reply_text("Ничего не найдено") - return ConversationHandler.END - - response_text = f"**Результаты поиска персонажей: {args}**\n\n" - - for i, char in enumerate(results["results"], 1): - char_id = char.get("id", "Unknown") - name = char.get("name", "Unknown") - original = char.get("original", "") - gender = char.get("gender", "Unknown") - vns = char.get("vn", []) - - response_text += ( - f"{i}. **{name}**\n" - f" ID: {char_id}\n" - ) - if original: - response_text += f" Оригинал: {original}\n" - response_text += f" Пол: {gender}\n" - if vns: - response_text += f" Появляется в {len(vns)} VN\n\n" - else: - response_text += "\n" - - await update.message.reply_text(response_text, parse_mode="Markdown") - - # Send character images if available - for char in results["results"][:3]: # Send images for first 3 results - image = char.get("image") - if image and isinstance(image, dict): - image_url = image.get("url") - if image_url: - try: - name = char.get("name", "Character") - await update.message.reply_photo( - photo=f"https://t.vndb.org{image_url}", - caption=f"{name}", - parse_mode="Markdown" - ) - except Exception as e: - logger.warning(f"Could not send character image: {e}") - - context.user_data["char_results"] = results["results"] - + + text = "Персонажи:\n\n" + + for c in results.get("results", []): + text += f"{c.get('id')} - {c.get('name')}\n" + + await update.message.reply_text(text, parse_mode="HTML") + except Exception as e: - logger.error(f"Error searching characters: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"{error_msg}") - + 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) -> int: - """Search for releases""" + async def search_release(update: Update, context: ContextTypes.DEFAULT_TYPE): try: - args = " ".join(context.args) if context.args else "" - - if not args: - await update.message.reply_text( - "Пожалуйста, укажите название для поиска\n" - "Пример: /release Windows" - ) - return ConversationHandler.END - - await update.message.reply_text(f"Поиск релизов: **{args}**\nЗагрузка...", parse_mode="Markdown") - - filters = ["search", "=", args] + query = " ".join(context.args) + results = await vndb_client.query_release( - filters=[filters], - fields=["title", "original", "released", "platform", "type", "image{url}"], + filters=["search", "=", query], + fields=["title", "id"], results=10 ) - - if not results.get("results"): - await update.message.reply_text("Ничего не найдено") - return ConversationHandler.END - - response_text = f"**Результаты поиска релизов: {args}**\n\n" - - for i, release in enumerate(results["results"], 1): - release_id = release.get("id", "Unknown") - title = release.get("title", "Unknown") - original = release.get("original", "") - released = release.get("released", "Unknown") - platform = release.get("platform", "Unknown") - release_type = release.get("type", "Unknown") - - response_text += ( - f"{i}. **{title}**\n" - f" ID: {release_id}\n" - ) - if original: - response_text += f" Оригинал: {original}\n" - response_text += ( - f" Дата: {released}\n" - f" Платформа: {platform}\n" - f" Тип: {release_type}\n\n" - ) - - await update.message.reply_text(response_text, parse_mode="Markdown") - - # Send release images if available - for release in results["results"][:3]: # Send images for first 3 results - image = release.get("image") - if image and isinstance(image, dict): - image_url = image.get("url") - if image_url: - try: - title = release.get("title", "Release") - await update.message.reply_photo( - photo=f"https://t.vndb.org{image_url}", - caption=f"{title}", - parse_mode="Markdown" - ) - except Exception as e: - logger.warning(f"Could not send release image: {e}") - - context.user_data["release_results"] = results["results"] - + + text = "Релизы:\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(f"Error searching releases: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"{error_msg}") - + logger.error(e) + await update.message.reply_text(f"❌ {ErrorHandler.format_error(e)}") + return ConversationHandler.END - - @staticmethod - async def search_staff(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: - """Search for staff members""" - try: - args = " ".join(context.args) if context.args else "" - - if not args: - await update.message.reply_text( - "Пожалуйста, укажите имя\n" - "Пример: /staff Yoko" - ) - return ConversationHandler.END - - await update.message.reply_text(f"Поиск сотрудников: **{args}**\nЗагрузка...", parse_mode="Markdown") - - filters = ["search", "=", args] - results = await vndb_client.query_staff( - filters=[filters], - fields=["name", "original", "gender", "role"], - results=10 - ) - - if not results.get("results"): - await update.message.reply_text("Ничего не найдено") - return ConversationHandler.END - - response_text = f"**Результаты поиска сотрудников: {args}**\n\n" - - for i, staff in enumerate(results["results"], 1): - staff_id = staff.get("id", "Unknown") - name = staff.get("name", "Unknown") - original = staff.get("original", "") - gender = staff.get("gender", "Unknown") - - response_text += ( - f"{i}. **{name}**\n" - f" ID: {staff_id}\n" - ) - if original: - response_text += f" Оригинал: {original}\n" - response_text += f" Пол: {gender}\n\n" - - await update.message.reply_text(response_text, parse_mode="Markdown") - context.user_data["staff_results"] = results["results"] - - except Exception as e: - logger.error(f"Error searching staff: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"{error_msg}") - - return ConversationHandler.END - - @staticmethod - async def search_producer(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: - """Search for producers""" - try: - args = " ".join(context.args) if context.args else "" - - if not args: - await update.message.reply_text( - "Пожалуйста, укажите название\n" - "Пример: /producer Key" - ) - return ConversationHandler.END - - await update.message.reply_text(f"Поиск продюсеров: **{args}**\n⏳ Загрузка...", parse_mode="Markdown") - - filters = ["search", "=", args] - results = await vndb_client.query_producer( - filters=[filters], - fields=["name", "original", "type"], - results=10 - ) - - if not results.get("results"): - await update.message.reply_text("Ничего не найдено") - return ConversationHandler.END - - response_text = f"**Результаты поиска продюсеров: {args}**\n\n" - - for i, producer in enumerate(results["results"], 1): - producer_id = producer.get("id", "Unknown") - name = producer.get("name", "Unknown") - original = producer.get("original", "") - producer_type = producer.get("type", "Unknown") - - response_text += ( - f"{i}. **{name}**\n" - f" ID: {producer_id}\n" - ) - if original: - response_text += f" Оригинал: {original}\n" - response_text += f" Тип: {producer_type}\n\n" - - await update.message.reply_text(response_text, parse_mode="Markdown") - - except Exception as e: - logger.error(f"Error searching producers: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"{error_msg}") - - return ConversationHandler.END - - @staticmethod - async def list_tags(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """List popular tags""" - try: - await update.message.reply_text("Загружаю теги...", parse_mode="Markdown") - - results = await vndb_client.query_tag( - fields=["name", "description"], - sort="vns", - reverse=True, - results=15 - ) - - if not results.get("results"): - await update.message.reply_text("Ничего не найдено") - return - - response_text = "**Популярные теги VNDB:**\n\n" - - for i, tag in enumerate(results["results"], 1): - tag_id = tag.get("id", "Unknown") - name = tag.get("name", "Unknown") - description = tag.get("description", "") - - response_text += f"{i}. **{name}** (`{tag_id}`)\n" - if description and len(description) < 50: - response_text += f" {description}\n" - response_text += "\n" - - await update.message.reply_text(response_text, parse_mode="Markdown") - - except Exception as e: - logger.error(f"Error listing tags: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"{error_msg}") - - @staticmethod - async def list_traits(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """List character traits""" - try: - await update.message.reply_text("Загружаю черты характера...", parse_mode="Markdown") - - results = await vndb_client.query_trait( - fields=["name", "description"], - sort="chars", - reverse=True, - results=15 - ) - - if not results.get("results"): - await update.message.reply_text("Ничего не найдено") - return - - response_text = "**Популярные черты характера:**\n\n" - - for i, trait in enumerate(results["results"], 1): - trait_id = trait.get("id", "Unknown") - name = trait.get("name", "Unknown") - description = trait.get("description", "") - - response_text += f"{i}. **{name}** (`{trait_id}`)\n" - if description and len(description) < 50: - response_text += f" {description}\n" - response_text += "\n" - - await update.message.reply_text(response_text, parse_mode="Markdown") - - except Exception as e: - logger.error(f"Error listing traits: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"{error_msg}") - - @staticmethod - async def get_quote(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Get random quotes""" - try: - count = 1 - if context.args and context.args[0].isdigit(): - count = min(int(context.args[0]), 5) # Max 5 quotes - - await update.message.reply_text(f"Загружаю {count} цитат...", parse_mode="Markdown") - - results = await vndb_client.query_quote( - fields=["character", "quote"], - sort="id", - results=count - ) - - if not results.get("results"): - await update.message.reply_text("Ничего не найдено") - return - - response_text = "**Случайные цитаты:**\n\n" - - for quote in results["results"]: - quote_text = quote.get("quote", "") - character = quote.get("character", "Unknown") - - if quote_text: - response_text += f"_{quote_text}_\n" - response_text += f"— **{character}**\n\n" - - await update.message.reply_text(response_text, parse_mode="Markdown") - - except Exception as e: - logger.error(f"Error getting quotes: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(f"{error_msg}") - - @staticmethod - async def authinfo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Get authentication info""" - try: - token = Config.VNDB_TOKEN - if not token: - await update.message.reply_text( - "Токен VNDB не установлен\n\n" - "Чтобы использовать функции авторизации:\n" - "1. Посетите https://vndb.org/u/tokens\n" - "2. Создайте новый токен\n" - "3. Установите переменную окружения VNDB_TOKEN" - ) - return - - client_with_token = VndbClient(token=token, use_sandbox=Config.USE_SANDBOX) - auth_info = await client_with_token.get_authinfo() - - response_text = f""" -\*\*Информация об авторизации:\*\* - -ID: {auth_info.get('id', 'Unknown')} -Пользователь: {auth_info.get('username', 'Unknown')} - -\*\*Разрешения:\*\* -""" - - permissions = auth_info.get("permissions", []) - if "listread" in permissions: - response_text += "Чтение списка (listread)\n" - if "listwrite" in permissions: - response_text += "Запись в список (listwrite)\n" - - if not permissions: - response_text += "Нет разрешений" - - await update.message.reply_text(response_text, parse_mode="Markdown") - - except Exception as e: - logger.error(f"Error getting authinfo: {e}") - error_msg = ErrorHandler.format_error(e) - await update.message.reply_text(error_msg) -def main() -> None: - """Start the bot""" - # Validate configuration - try: - Config.validate() - except ValueError as e: - logger.error(f"Configuration error: {e}") - raise - - logger.info(f"Starting bot with config: {Config.to_dict()}") - - # Create application - application = Application.builder().token(Config.TELEGRAM_BOT_TOKEN).build() - - # Add handlers - application.add_handler(CommandHandler("start", BotHandlers.start)) - application.add_handler(CommandHandler("help", BotHandlers.help_command)) - application.add_handler(CommandHandler("stats", BotHandlers.stats)) - application.add_handler(CommandHandler("schema", BotHandlers.schema)) - application.add_handler(CommandHandler("search", BotHandlers.search_vn)) - application.add_handler(CommandHandler("char", BotHandlers.search_character)) - application.add_handler(CommandHandler("release", BotHandlers.search_release)) - application.add_handler(CommandHandler("staff", BotHandlers.search_staff)) - application.add_handler(CommandHandler("producer", BotHandlers.search_producer)) - application.add_handler(CommandHandler("tag", BotHandlers.list_tags)) - application.add_handler(CommandHandler("trait", BotHandlers.list_traits)) - application.add_handler(CommandHandler("quote", BotHandlers.get_quote)) - application.add_handler(CommandHandler("authinfo", BotHandlers.authinfo)) - - # Add detailed handlers for viewing with images - for handler in get_detail_handlers(): - application.add_handler(handler) - - # Start the bot - logger.info("Starting bot...") - application.run_polling(allowed_updates=Update.ALL_TYPES) +# ====================== +# 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() + main() \ No newline at end of file