diff --git a/Dockerfile b/Dockerfile index f6cebc1..afc9889 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,12 @@ FROM python:3.11-slim ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 +ENV BOT_TOKEN=$BOT_TOKEN +ENV AUTHORIZED_USER_ID=$AUTHORIZED_USER_ID +ENV NTFY_AUTH_HEADER=$NTFY_AUTH_HEADER +ENV KUMA_API_PASSWORD=$KUMA_API_PASSWORD +ENV TORRENT_API_PASSWORD=$TORRENT_API_PASSWORD + # Set working directory inside the container WORKDIR /app diff --git a/main.py b/main.py index 9737e75..e6b2587 100644 --- a/main.py +++ b/main.py @@ -18,6 +18,13 @@ from config import ( TORRENT_API_PASSWORD, ) +from menus import ( + main_menu_keyboard, + torrents_menu_keyboard, + status_menu_keyboard, + handle_menu, +) + NTFY_SERVER = "http://192.168.1.2:54720" DB_PATH = "config/subscriptions.db" @@ -77,130 +84,6 @@ async def list_subs(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text("Subscribed topics:\n" + "\n".join(topics)) -# --- Menu Definitions --- -def main_menu_keyboard(): - return InlineKeyboardMarkup( - [ - [InlineKeyboardButton("Torrents", callback_data="status_downloading")], - [InlineKeyboardButton("Status", callback_data="menu_status")], - ] - ) - - -def torrents_menu_keyboard(): - return InlineKeyboardMarkup( - [ - # First row: Display the status counts for downloading, paused, seeding - [InlineKeyboardButton(f"Downloading", callback_data="status_downloading")], - [InlineKeyboardButton(f"Active", callback_data="status_active")], - [InlineKeyboardButton(f"All", callback_data="status_all")], - # Second row: Back button - [InlineKeyboardButton("πŸ”™ Back", callback_data="menu_main")], - ] - ) - - -def status_menu_keyboard(): - return InlineKeyboardMarkup( - [[InlineKeyboardButton("πŸ”™ Back", callback_data="menu_main")]] - ) - - -def format_torrents(torrents): - if len(torrents) == 0: - return "No torrents." - - text = "" - - i = 0 - for torrent in torrents: - if i > 5: - text += "...\n" - return text - - text += f"Name: {torrent['name']}\n" - text += f"State: {torrent['state']}\n" - text += f"Progress: {torrent['progress']:.2f}%\n" - text += f"ETA: {torrent['eta']}\n" - text += "-" * 20 + "\n" - - text += f"- {torrent['name']} - {torrent['progress']} ({torrent['eta']})\n" - i += 1 - - return text - - -# --- Callback Query Handler --- -async def handle_menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - query = update.callback_query - await query.answer() - - match query.data: - case "menu_main": - await query.edit_message_text( - "Choose an option:", reply_markup=main_menu_keyboard() - ) - - case "status_downloading": - t_api = context.bot_data.get("torrent_api", {}) - torrents = t_api.get_filtered_torrents("downloading") - - if len(torrents) == 0: - text = "No downloading torrents." - else: - text = format_torrents(torrents) - - await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) - - case "status_active": - t_api = context.bot_data.get("torrent_api", {}) - torrents = t_api.get_filtered_torrents("active") - - if len(torrents) == 0: - text = "No active torrents." - else: - text = format_torrents(torrents) - - await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) - - case "status_all": - t_api = context.bot_data.get("torrent_api", {}) - torrents = t_api.get_filtered_torrents("all") - - if len(torrents) == 0: - text = "No torrents." - else: - text = format_torrents(torrents) - - await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) - - case "menu_status": - k_api = context.bot_data.get("kuma_api", {}) - monitors = k_api.get_status() - - up_text, down_text, paused_text = "", "", "" - for _, monitor in monitors.items(): - status = monitor["status"] - - if status == MonitorStatus.UP: - up_text += f" - {monitor['name']}\n" - elif status == MonitorStatus.DOWN: - down_text += f" - {monitor['name']}\n" - else: - paused_text += f" - {monitor['name']}\n" - - status_text = f"πŸ“‘ *Status:*\n\n 🟒 Up:\n{up_text}\nπŸ”΄ Down\n{down_text}\n⏸️ Paused\n{paused_text}" - - await query.edit_message_text( - status_text, reply_markup=status_menu_keyboard() - ) - - case _: - await query.edit_message_text( - "Unknown option selected.", reply_markup=main_menu_keyboard() - ) - - # --- Main Function --- def main(): print("Starting Jarvis...") @@ -213,7 +96,7 @@ def main(): ntfy_api = ntfy.NtfyAPI(DB_PATH, NTFY_SERVER, NTFY_AUTH_HEADER, AUTHORIZED_USER_ID) - app = Application.builder().token(TOKEN).build() + app = Application.builder().token(BOT_TOKEN).build() app.bot_data["kuma_api"] = kuma_api app.bot_data["torrent_api"] = torrent_api app.bot_data["ntfy_api"] = ntfy_api diff --git a/menus.py b/menus.py new file mode 100644 index 0000000..1de5851 --- /dev/null +++ b/menus.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 + +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update +from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes + +from uptime_kuma_api import MonitorStatus +import api.kuma as kuma +import api.torrent as torrent +import api.ntfy as ntfy + + +# --- Menu Definitions --- +def main_menu_keyboard(): + return InlineKeyboardMarkup( + [ + [InlineKeyboardButton("Torrents", callback_data="status_downloading")], + [InlineKeyboardButton("Status", callback_data="menu_status")], + ] + ) + + +def torrents_menu_keyboard(): + return InlineKeyboardMarkup( + [ + # First row: Display the status counts for downloading, paused, seeding + [InlineKeyboardButton(f"Downloading", callback_data="status_downloading")], + [InlineKeyboardButton(f"Active", callback_data="status_active")], + [InlineKeyboardButton(f"All", callback_data="status_all")], + # Second row: Back button + [InlineKeyboardButton("πŸ”™ Back", callback_data="menu_main")], + ] + ) + + +def status_menu_keyboard(): + return InlineKeyboardMarkup( + [[InlineKeyboardButton("πŸ”™ Back", callback_data="menu_main")]] + ) + + +def format_torrents(torrents): + if len(torrents) == 0: + return "No torrents." + + text = "" + + i = 0 + for torrent in torrents: + if i > 5: + text += "...\n" + return text + + text += f"Name: {torrent['name']}\n" + text += f"State: {torrent['state']}\n" + text += f"Progress: {torrent['progress']:.2f}%\n" + text += f"ETA: {torrent['eta']}\n" + text += "-" * 20 + "\n" + + text += f"- {torrent['name']} - {torrent['progress']} ({torrent['eta']})\n" + i += 1 + + return text + + +# --- Callback Query Handler --- +async def handle_menu(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + query = update.callback_query + await query.answer() + + match query.data: + case "menu_main": + await query.edit_message_text( + "Choose an option:", reply_markup=main_menu_keyboard() + ) + + case "status_downloading": + t_api = context.bot_data.get("torrent_api", {}) + torrents = t_api.get_filtered_torrents("downloading") + + if len(torrents) == 0: + text = "No downloading torrents." + else: + text = format_torrents(torrents) + + await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) + + case "status_active": + t_api = context.bot_data.get("torrent_api", {}) + torrents = t_api.get_filtered_torrents("active") + + if len(torrents) == 0: + text = "No active torrents." + else: + text = format_torrents(torrents) + + await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) + + case "status_all": + t_api = context.bot_data.get("torrent_api", {}) + torrents = t_api.get_filtered_torrents("all") + + if len(torrents) == 0: + text = "No torrents." + else: + text = format_torrents(torrents) + + await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) + + case "menu_status": + k_api = context.bot_data.get("kuma_api", {}) + monitors = k_api.get_status() + + up_text, down_text, paused_text = "", "", "" + for _, monitor in monitors.items(): + status = monitor["status"] + + if status == MonitorStatus.UP: + up_text += f" - {monitor['name']}\n" + elif status == MonitorStatus.DOWN: + down_text += f" - {monitor['name']}\n" + else: + paused_text += f" - {monitor['name']}\n" + + status_text = f"πŸ“‘ *Status:*\n\n 🟒 Up:\n{up_text}\nπŸ”΄ Down\n{down_text}\n⏸️ Paused\n{paused_text}" + + await query.edit_message_text( + status_text, reply_markup=status_menu_keyboard() + ) + + case _: + await query.edit_message_text( + "Unknown option selected.", reply_markup=main_menu_keyboard() + )