from __future__ import annotations import logging from typing import Literal from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.constants import ParseMode from telegram.ext import ( ApplicationBuilder, CallbackQueryHandler, CommandHandler, ContextTypes, Defaults, ) from .boinc import BoincClient from .config import Settings from .ui import format_dashboard, format_tasks_section logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) View = Literal["dashboard", "active", "queued", "completed"] def build_keyboard() -> InlineKeyboardMarkup: return InlineKeyboardMarkup( [ [ InlineKeyboardButton("Обновить", callback_data="dashboard"), InlineKeyboardButton("Активные", callback_data="active"), ], [ InlineKeyboardButton("Очередь", callback_data="queued"), InlineKeyboardButton("Готовы к отправке", callback_data="completed"), ], ] ) async def render_view(view: View, client: BoincClient) -> str: snapshot = await client.fetch_snapshot() if view == "dashboard": return format_dashboard(snapshot) if view == "active": return format_tasks_section("Активные задачи", snapshot.active) if view == "queued": return format_tasks_section("Ожидают запуска", snapshot.queued) return format_tasks_section("Готовы к отправке", snapshot.completed) async def start_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: client: BoincClient = context.bot_data["boinc_client"] text = await render_view("dashboard", client) await update.message.reply_text(text, reply_markup=build_keyboard(), parse_mode=ParseMode.HTML) async def callback_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: query = update.callback_query await query.answer() client: BoincClient = context.bot_data["boinc_client"] view: View = query.data if query.data in {"dashboard", "active", "queued", "completed"} else "dashboard" try: text = await render_view(view, client) except Exception as exc: # broad catch to show meaningful message in chat logger.exception("Error rendering view %s", view) text = f"Не удалось получить данные: {exc}" await query.edit_message_text(text=text, reply_markup=build_keyboard(), parse_mode=ParseMode.HTML) def main() -> None: settings = Settings.from_env() client = BoincClient( host=settings.boinc_host, port=settings.boinc_port, password=settings.boinc_password, boinccmd_path=settings.boinccmd_path, sample_output=settings.sample_output, ) defaults = Defaults(parse_mode=ParseMode.HTML) application = ApplicationBuilder().token(settings.telegram_token).defaults(defaults).build() application.bot_data["boinc_client"] = client application.add_handler(CommandHandler("start", start_handler)) application.add_handler(CommandHandler("status", start_handler)) application.add_handler(CallbackQueryHandler(callback_handler)) logger.info("Starting Telegram bot...") application.run_polling() if __name__ == "__main__": main()