99 lines
3.4 KiB
Python
99 lines
3.4 KiB
Python
from __future__ import annotations
|
||
|
||
import asyncio
|
||
import logging
|
||
from functools import partial
|
||
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)
|
||
|
||
|
||
async 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...")
|
||
await application.run_polling(close_loop=False)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|