commit b7ba40b4da43ab6393cae8a8a73288908e3cb328 Author: MrZaiko Date: Sun Apr 6 11:38:10 2025 +0200 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cc913b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +.idea +*.log +tmp/ +venv/ +__pycache__/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f6cebc1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Use an official Python base image +FROM python:3.11-slim + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +# Set working directory inside the container +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements and install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy bot source code +COPY . . + +# Run the bot +CMD ["python", "main.py"] diff --git a/api/kuma.py b/api/kuma.py new file mode 100644 index 0000000..39ccb41 --- /dev/null +++ b/api/kuma.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +from uptime_kuma_api import UptimeKumaApi +import json + +class KumaAPI: + def __init__(self, ip, password): + self.api = UptimeKumaApi(ip) + self.api.login('admin', password) + + def get_status(self): + monitors = self.api.get_monitors() + + res = {} + for monitor in monitors: + id = monitor['id'] + status = self.api.get_monitor_status(id) + res[id] = { + "status": status, + "name": monitor["name"], + } + + return res diff --git a/api/qbittorrent.py b/api/qbittorrent.py new file mode 100644 index 0000000..1772316 --- /dev/null +++ b/api/qbittorrent.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +from qbittorrent import Client + +# Replace with your qBittorrent Web UI credentials +qb = Client('http://192.168.1.17:8112') +qb.login('admin', 'tMHNjrJr7nhjyhJrYsahi4anq2h6LJ') + +# Retrieve all active torrents +torrents = qb.torrents(filter='downloading') + +# Print details of each active torrent +for torrent in torrents: + print(f"Name: {torrent['name']}") + print(f"State: {torrent['state']}") + print(f"Progress: {torrent['progress'] * 100:.2f}%") + print(f"Download Speed: {torrent['dlspeed'] / 1024:.2f} KB/s") + print(f"Upload Speed: {torrent['upspeed'] / 1024:.2f} KB/s") + print(f"Size: {torrent['size'] / (1024 * 1024):.2f} MB") + print('-' * 40) diff --git a/api/torrent.py b/api/torrent.py new file mode 100644 index 0000000..62611ae --- /dev/null +++ b/api/torrent.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +from qbittorrent import Client + +class TorrentApi: + def __init__(self, ip, password, username='admin'): + # Initialize the qBittorrent client + self.qb = Client(ip) + self.qb.login(username, password) + + def get_torrents(self): + return self.get_filtered_torrents('all') + + def get_filtered_torrents(self, state): + # Retrieve torrents filtered by the given state + torrents = self.qb.torrents(filter=state) + # Extract relevant information + torrent_list = [] + for torrent in torrents: + torrent_info = { + 'name': torrent['name'], + 'state': torrent['state'], + 'progress': torrent['progress'] * 100, # Convert to percentage + 'eta': torrent['eta'] + } + torrent_list.append(torrent_info) + return torrent_list diff --git a/main.py b/main.py new file mode 100644 index 0000000..8e00f74 --- /dev/null +++ b/main.py @@ -0,0 +1,131 @@ +#!/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 + +TOKEN = "7396669954:AAH8_I0Y-qg3j_LfbUdRTOLPDKh80NdijMo" + +# --- 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')] + ]) + +# --- Command Handlers --- +async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + await update.message.reply_text("Choose an option:", reply_markup=main_menu_keyboard()) + + +def format_torrents(torrents): + if len(torrents) == 0: + return "No torrents." + + text = "" + + i = 0 + for torrent in torrents: + if i > 10: + text += "...\n" + return text + + 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': + torrent_api = context.bot_data.get("torrent_api", {}) + torrents = torrent_api.get_filtered_torrents("downloading") + + text = format_torrents(torrents) + + await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) + + case 'status_active': + torrent_api = context.bot_data.get("torrent_api", {}) + torrents = torrent_api.get_filtered_torrents("active") + + text = format_torrents(torrents) + + await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) + + + case 'status_all': + torrent_api = context.bot_data.get("torrent_api", {}) + torrents = torrent_api.get_filtered_torrents("all") + + text = format_torrents(torrents) + + await query.edit_message_text(text, reply_markup=torrents_menu_keyboard()) + + case 'menu_status': + kuma_api = context.bot_data.get("kuma_api", {}) + monitors = kuma_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(): + # Initiate api + kuma_api = kuma.KumaAPI("http://192.168.1.2:36667", "k!PTfyvoIJho9o*gX6F1") + torrent_api = torrent.TorrentApi("http://192.168.1.17:8112", "tMHNjrJr7nhjyhJrYsahi4anq2h6LJ") + + app = Application.builder().token(TOKEN).build() + app.bot_data["kuma_api"] = kuma_api + app.bot_data["torrent_api"] = torrent_api + + app.add_handler(CommandHandler("start", start)) + app.add_handler(CallbackQueryHandler(handle_menu)) + + print("Bot is running... Press Ctrl+C to stop.") + app.run_polling() + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3420310 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +python-qbittorrent +uptime-kuma-api +python-telegram-bot