commit ac0d56d9b9e9758f052990bc6b34c5fb3f7db2dc Author: DerrtSML <93052047+DerrtSML@users.noreply.github.com> Date: Mon Jun 23 15:48:05 2025 +0300 Add files via upload diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c860a82 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# Используем официальный образ Python +FROM python:3.10-slim-buster + +# Устанавливаем рабочую директорию +WORKDIR /app + +# Копируем файл зависимостей и устанавливаем их +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Копируем остальные файлы приложения +COPY . . + +# Команда для запуска приложения +CMD ["python", "bot.py"] \ No newline at end of file diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..d3cbe23 --- /dev/null +++ b/bot.py @@ -0,0 +1,181 @@ +import logging +import os +from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup +from telegram.ext import ( + Application, + CommandHandler, + MessageHandler, + filters, + CallbackQueryHandler, + ContextTypes, +) +from qbittorrent import Client, APIError + +# Настройка логирования +logging.basicConfig( + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + level=logging.INFO +) +logger = logging.getLogger(__name__) + +# --- Конфигурация (будет загружаться из переменных окружения Docker) --- +TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") +QBT_HOST = os.getenv("QBT_HOST") +QBT_PORT = os.getenv("QBT_PORT", "8080") +QBT_USERNAME = os.getenv("QBT_USERNAME") +QBT_PASSWORD = os.getenv("QBT_PASSWORD") + +# Определение доступных директорий (предполагаем, что они известны) +# В реальной системе можно было бы получить список из qBittorrent API +# или настроить их через переменные окружения. +DOWNLOAD_DIRECTORIES = { + "Фильмы": "/share/Data/Felms", + "Сериалы": "/share/Data/Serials", + "Музыка": "/share/Music", + "Другое": "/share/Data/torrents", +} + +# --- Инициализация qBittorrent клиента --- +qb = None + +def init_qbittorrent_client(): + global qb + if not all([QBT_HOST, QBT_USERNAME, QBT_PASSWORD]): + logger.error("QBittorrent credentials are not fully set in environment variables.") + return False + try: + qb = Client(f"http://{QBT_HOST}:{QBT_PORT}/") + qb.login(QBT_USERNAME, QBT_PASSWORD) + logger.info(f"Successfully connected to qBittorrent at {QBT_HOST}:{QBT_PORT}") + return True + except APIError as e: + logger.error(f"Failed to connect or login to qBittorrent: {e}") + qb = None + return False + except Exception as e: + logger.error(f"An unexpected error occurred during qBittorrent connection: {e}") + qb = None + return False + +# --- Обработчики команд --- + +async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + if not init_qbittorrent_client(): + await update.message.reply_text( + "Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера." + ) + return + await update.message.reply_text( + "Привет! Я бот для управления qBittorrent.\n" + "Отправь мне magnet-ссылку или URL torrent-файла, " + "чтобы добавить загрузку.\n" + "Используй /status для просмотра текущих загрузок." + ) + +async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + if not qb: + if not init_qbittorrent_client(): + await update.message.reply_text( + "Не удалось подключиться к qBittorrent. Попробуйте еще раз или проверьте настройки." + ) + return + + try: + torrents = qb.torrents() + if not torrents: + await update.message.reply_text("Нет активных загрузок.") + return + + message = "Текущие загрузки:\n\n" + for t in torrents: + progress = f"{t.progress:.2%}" + size = f"{t.size / (1024*1024*1024):.2f} GB" if t.size else "N/A" + download_speed = f"{t.dlspeed / (1024*1024):.2f} MB/s" + upload_speed = f"{t.upspeed / (1024*1024):.2f} MB/s" + + message += ( + f"📝 Имя: {t.name}\n" + f"📊 Прогресс: {progress}\n" + f"📦 Размер: {size}\n" + f"⬇️ Скорость загрузки: {download_speed}\n" + f"⬆️ Скорость отдачи: {upload_speed}\n" + f"🚦 Статус: {t.state}\n" + f"--- \n" + ) + await update.message.reply_text(message) + + except APIError as e: + logger.error(f"Error fetching torrents: {e}") + await update.message.reply_text(f"Ошибка при получении списка загрузок: {e}") + except Exception as e: + logger.error(f"An unexpected error occurred: {e}") + await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}") + + +async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + if not qb: + if not init_qbittorrent_client(): + await update.message.reply_text( + "Не удалось подключиться к qBittorrent. Попробуйте еще раз или проверьте настройки." + ) + return + + url = update.message.text + context.user_data['download_url'] = url + + keyboard = [] + for name, path in DOWNLOAD_DIRECTORIES.items(): + keyboard.append([InlineKeyboardButton(name, callback_data=f"dir_{path}")]) + reply_markup = InlineKeyboardMarkup(keyboard) + + await update.message.reply_text( + f"Вы хотите загрузить: `{url}`\n" + "Выберите директорию для загрузки:", + reply_markup=reply_markup, + parse_mode='Markdown' + ) + +async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + query = update.callback_query + await query.answer() + + if query.data.startswith("dir_"): + selected_directory = query.data.replace("dir_", "") + download_url = context.user_data.get('download_url') + + if not download_url: + await query.edit_message_text("Ошибка: URL для загрузки не найден. Пожалуйста, отправьте ссылку снова.") + return + + try: + qb.download_from_link(download_url, save_path=selected_directory) + await query.edit_message_text( + f"Загрузка '{download_url}' добавлена в '{selected_directory}'." + ) + logger.info(f"Added download '{download_url}' to '{selected_directory}'") + except APIError as e: + logger.error(f"Error adding download: {e}") + await query.edit_message_text(f"Ошибка при добавлении загрузки: {e}") + except Exception as e: + logger.error(f"An unexpected error occurred while adding download: {e}") + await query.edit_message_text(f"Произошла непредвиденная ошибка при добавлении загрузки: {e}") + + +def main() -> None: + application = Application.builder().token(TELEGRAM_BOT_TOKEN).build() + + application.add_handler(CommandHandler("start", start)) + application.add_handler(CommandHandler("status", status)) + application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_url)) + application.add_handler(CallbackQueryHandler(button_callback)) + + logger.info("Bot started polling...") + application.run_polling(allowed_updates=Update.ALL_TYPES) + +if __name__ == "__main__": + if not TELEGRAM_BOT_TOKEN: + logger.error("TELEGRAM_BOT_TOKEN is not set. Please set the environment variable.") + exit(1) + if not all([QBT_HOST, QBT_USERNAME, QBT_PASSWORD]): + logger.warning("QBittorrent connection details (QBT_HOST, QBT_USERNAME, QBT_PASSWORD) are not fully set. Bot will attempt to connect on first use of qBittorrent related commands.") + main() \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cca928b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' + +services: + qbittorrent_telegram_bot: + container_name: qbittorrent_telegram_bot + build: . + restart: unless-stopped + environment: + # Замените на ваш токен бота + - TELEGRAM_BOT_TOKEN=6239443076:AAFnFHT9VWd5iuEriQZqssEAn2DugbLK0v8 + # Замените на IP или hostname вашего сервера qBittorrent + - QBT_HOST=https://qbittorrent.derrt.site/ + # Порт Web UI qBittorrent (по умолчанию 8080) + - QBT_PORT=443 + # Логин для qBittorrent Web UI + - QBT_USERNAME=Derrt + # Пароль для qBittorrent Web UI + - QBT_PASSWORD=Derrty5Derrt5 + # Если qBittorrent находится на другом хосте или вы используете Docker bridge network, + # убедитесь, что бот может до него достучаться. + # Если qBittorrent также в Docker и в той же сети, можно использовать имя сервиса. + # network_mode: host # Используйте, если хотите, чтобы бот видел хост напрямую \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f98090e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +python-telegram-bot +python-qbt \ No newline at end of file