Add BOINC Telegram bot, CI, and deploy compose
All checks were successful
publish-images / build (push) Successful in 1m3s
All checks were successful
publish-images / build (push) Successful in 1m3s
This commit is contained in:
106
bot/main.py
Normal file
106
bot/main.py
Normal file
@@ -0,0 +1,106 @@
|
||||
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,
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
application = (
|
||||
ApplicationBuilder()
|
||||
.token(settings.telegram_token)
|
||||
.parse_mode(ParseMode.HTML)
|
||||
.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())
|
||||
|
||||
Reference in New Issue
Block a user