mirror of
https://github.com/DerrtSML/qbittorent_bot.git
synced 2025-10-26 04:20:08 +03:00
Update bot.py
This commit is contained in:
parent
7e35b1eaf1
commit
3534fc5b79
127
bot.py
127
bot.py
@ -70,7 +70,6 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
"Привет! Я бот для управления qBittorrent.\n"
|
"Привет! Я бот для управления qBittorrent.\n"
|
||||||
"Отправь мне magnet-ссылку или URL torrent-файла, "
|
"Отправь мне magnet-ссылку или URL torrent-файла, "
|
||||||
@ -80,7 +79,7 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
"Используй /help для списка команд."
|
"Используй /help для списка команд."
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Команда /help (без Markdown) ---
|
# --- Команда /help ---
|
||||||
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if update.message is None:
|
if update.message is None:
|
||||||
logger.warning("Received an update without a message object in help handler.")
|
logger.warning("Received an update without a message object in help handler.")
|
||||||
@ -88,20 +87,19 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
|||||||
|
|
||||||
logger.info(f"Received /help command from {update.effective_user.id}")
|
logger.info(f"Received /help command from {update.effective_user.id}")
|
||||||
|
|
||||||
# Здесь Markdown был удален для отладки
|
|
||||||
help_text = (
|
help_text = (
|
||||||
"Вот список доступных команд:\n\n"
|
"Вот список доступных команд:\n\n"
|
||||||
"/start - Начать работу с ботом и проверить подключение к qBittorrent.\n"
|
"**/start** - Начать работу с ботом и проверить подключение к qBittorrent.\n"
|
||||||
"/status - Показать текущий статус всех активных загрузок.\n"
|
"**/status** - Показать текущий статус всех активных загрузок и управлять ими.\n" # Обновил описание
|
||||||
"/stop_torrent - Выбрать и остановить загрузку торрента.\n"
|
"**/stop_torrent** - Выбрать и остановить загрузку торрента (устаревает, используйте /status).\n" # Отметил как устаревшую
|
||||||
"/help - Показать это справочное сообщение.\n\n"
|
"**/help** - Показать это справочное сообщение.\n\n"
|
||||||
"Также вы можете отправить мне magnet-ссылку или URL torrent-файла "
|
"Также вы можете отправить мне *magnet-ссылку* или *URL torrent-файла* "
|
||||||
"для добавления загрузки. Бот предложит выбрать категорию и директорию."
|
"для добавления загрузки. Бот предложит выбрать категорию и директорию."
|
||||||
)
|
)
|
||||||
await update.message.reply_text(help_text) # parse_mode="Markdown" удален
|
await update.message.reply_text(help_text, parse_mode="Markdown")
|
||||||
|
|
||||||
|
|
||||||
# --- Команда /status (без Markdown) ---
|
# --- Команда /status (обновлено для кнопок старт/стоп) ---
|
||||||
async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if update.message is None:
|
if update.message is None:
|
||||||
logger.warning("Received an update without a message object in status handler.")
|
logger.warning("Received an update without a message object in status handler.")
|
||||||
@ -121,7 +119,6 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
await update.message.reply_text("Загрузок не найдено.")
|
await update.message.reply_text("Загрузок не найдено.")
|
||||||
return
|
return
|
||||||
|
|
||||||
status_messages = []
|
|
||||||
for torrent in torrents:
|
for torrent in torrents:
|
||||||
download_speed = torrent.dlspeed / (1024 * 1024) if torrent.dlspeed >= (1024 * 1024) else torrent.dlspeed / 1024
|
download_speed = torrent.dlspeed / (1024 * 1024) if torrent.dlspeed >= (1024 * 1024) else torrent.dlspeed / 1024
|
||||||
upload_speed = torrent.upspeed / (1024 * 1024) if torrent.upspeed >= (1024 * 1024) else torrent.upspeed / 1024
|
upload_speed = torrent.upspeed / (1024 * 1024) if torrent.upspeed >= (1024 * 1024) else torrent.upspeed / 1024
|
||||||
@ -130,7 +127,7 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
up_unit = "MB/s" if torrent.upspeed >= (1024 * 1024) else "KB/s"
|
up_unit = "MB/s" if torrent.upspeed >= (1024 * 1024) else "KB/s"
|
||||||
|
|
||||||
eta_str = ""
|
eta_str = ""
|
||||||
if torrent.eta == 8640000:
|
if torrent.eta == 8640000: # qBittorrent returns 8640000 for "infinite" ETA
|
||||||
eta_str = "∞"
|
eta_str = "∞"
|
||||||
elif torrent.eta > 0:
|
elif torrent.eta > 0:
|
||||||
hours, remainder = divmod(torrent.eta, 3600)
|
hours, remainder = divmod(torrent.eta, 3600)
|
||||||
@ -145,19 +142,39 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
eta_str = "Завершено"
|
eta_str = "Завершено"
|
||||||
|
|
||||||
|
|
||||||
# Здесь Markdown был удален для отладки (включая *)
|
message_text = (
|
||||||
status_messages.append(
|
f"📊 *{torrent.name}*\n"
|
||||||
f"📊 {torrent.name}\n"
|
|
||||||
f" Состояние: {torrent.state}\n"
|
f" Состояние: {torrent.state}\n"
|
||||||
f" Прогресс: {torrent.progress:.2%}\n"
|
f" Прогресс: {torrent.progress:.2%}\n"
|
||||||
f" ⬇️ {download_speed:.2f} {dl_unit} ⬆️ {upload_speed:.2f} {up_unit}\n"
|
f" ⬇️ {download_speed:.2f} {dl_unit} ⬆️ {upload_speed:.2f} {up_unit}\n"
|
||||||
f" ETA: {eta_str}\n"
|
f" ETA: {eta_str}\n"
|
||||||
f" Размер: {(torrent.size / (1024*1024*1024)):.2f} ГБ"
|
f" Размер: {(torrent.size / (1024*1024*1024)):.2f} ГБ"
|
||||||
)
|
)
|
||||||
|
|
||||||
await update.message.reply_text(
|
keyboard = []
|
||||||
"\n\n".join(status_messages)
|
# Проверяем состояние торрента для отображения нужной кнопки
|
||||||
) # parse_mode="Markdown" удален
|
if torrent.state in ['downloading', 'stalledDL', 'uploading', 'checkingQT', 'queuedDL', 'checkingUP', 'pausedDL', 'queuedUP']:
|
||||||
|
# Если торрент активен или на паузе (но не в остановленном состоянии "pausedUP"), предлагаем остановить
|
||||||
|
keyboard.append(InlineKeyboardButton("🔴 Остановить", callback_data=f"stop_hash_{torrent.hash}"))
|
||||||
|
elif torrent.state == 'pausedUP': # Это состояние, когда торрент завершен, но на паузе.
|
||||||
|
keyboard.append(InlineKeyboardButton("▶️ Запустить", callback_data=f"start_hash_{torrent.hash}"))
|
||||||
|
elif torrent.state == 'metaDL': # Метаданные загружаются
|
||||||
|
# Пока не предлагаем кнопок, пока не начнется фактическая загрузка
|
||||||
|
pass # Можно добавить кнопку "Отменить" позже
|
||||||
|
else:
|
||||||
|
# Для других состояний, таких как 'error', 'missingFiles', 'unknown'
|
||||||
|
# или для тех, что не подразумевают активного управления через старт/стоп
|
||||||
|
keyboard.append(InlineKeyboardButton("❓ Неизвестное состояние", callback_data=f"info_hash_{torrent.hash}"))
|
||||||
|
|
||||||
|
|
||||||
|
reply_markup = InlineKeyboardMarkup([keyboard]) if keyboard else None
|
||||||
|
|
||||||
|
# Отправляем каждое сообщение о торренте отдельно с его кнопками
|
||||||
|
await update.message.reply_text(
|
||||||
|
message_text,
|
||||||
|
parse_mode="Markdown",
|
||||||
|
reply_markup=reply_markup
|
||||||
|
)
|
||||||
|
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
logger.error(f"Error getting torrent status: {e}")
|
logger.error(f"Error getting torrent status: {e}")
|
||||||
@ -166,7 +183,9 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
logger.error(f"An unexpected error occurred in status command: {e}")
|
logger.error(f"An unexpected error occurred in status command: {e}")
|
||||||
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
# --- Команда /stop_torrent ---
|
|
||||||
|
# --- Команда /stop_torrent (Теперь это будет устаревшая команда) ---
|
||||||
|
# Ее можно удалить позже, когда все привыкнут к кнопкам в /status
|
||||||
async def stop_torrent(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def stop_torrent(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if update.message is None:
|
if update.message is None:
|
||||||
logger.warning("Received an update without a message object in stop_torrent handler.")
|
logger.warning("Received an update without a message object in stop_torrent handler.")
|
||||||
@ -174,35 +193,14 @@ async def stop_torrent(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
|||||||
|
|
||||||
logger.info(f"Received /stop_torrent command from {update.effective_user.id}")
|
logger.info(f"Received /stop_torrent command from {update.effective_user.id}")
|
||||||
|
|
||||||
if not init_qbittorrent_client():
|
await update.message.reply_text(
|
||||||
await update.message.reply_text(
|
"Используйте команду /status для управления торрентами через кнопки 'Запустить' и 'Остановить'."
|
||||||
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
)
|
||||||
)
|
# Если вы хотите полностью убрать эту команду, можете закомментировать или удалить ее
|
||||||
return
|
# и убрать из add_handler в main()
|
||||||
|
# Остальной код этой функции может быть полезен, если вы все же хотите сохранить старый механизм.
|
||||||
|
# Но для удобства, я предлагаю перенаправить пользователя на /status.
|
||||||
|
|
||||||
try:
|
|
||||||
torrents = qb.torrents_info(status_filter='downloading')
|
|
||||||
if not torrents:
|
|
||||||
await update.message.reply_text("Нет активных загрузок для остановки.")
|
|
||||||
return
|
|
||||||
|
|
||||||
keyboard = []
|
|
||||||
for torrent in torrents:
|
|
||||||
display_name = torrent.name
|
|
||||||
if len(display_name) > 40:
|
|
||||||
display_name = display_name[:37] + "..."
|
|
||||||
|
|
||||||
keyboard.append([InlineKeyboardButton(display_name, callback_data=f"stop_hash_{torrent.hash}")])
|
|
||||||
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
|
||||||
await update.message.reply_text("Выберите торрент для остановки:", reply_markup=reply_markup)
|
|
||||||
|
|
||||||
except APIError as e:
|
|
||||||
logger.error(f"Error fetching torrents for stopping: {e}")
|
|
||||||
await update.message.reply_text(f"Ошибка при получении списка торрентов для остановки: {e}")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"An unexpected error occurred in stop_torrent command: {e}")
|
|
||||||
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
|
||||||
|
|
||||||
# --- Обработка кнопки "Остановить торрент" ---
|
# --- Обработка кнопки "Остановить торрент" ---
|
||||||
async def stop_torrent_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def stop_torrent_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
@ -219,8 +217,8 @@ async def stop_torrent_callback(update: Update, context: ContextTypes.DEFAULT_TY
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
qb.torrents_stop(torrent_hashes=torrent_hash)
|
qb.torrents_pause(torrent_hashes=torrent_hash) # Используем pause вместо stop для возможности возобновления
|
||||||
await query.edit_message_text(f"Торрент ({torrent_hash[:6]}...) успешно остановлен.")
|
await query.edit_message_text(f"Торрент ({torrent_hash[:6]}...) успешно остановлен (поставлен на паузу).")
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
logger.error(f"Error stopping torrent {torrent_hash}: {e}")
|
logger.error(f"Error stopping torrent {torrent_hash}: {e}")
|
||||||
await query.edit_message_text(f"Ошибка при остановке торрента: {e}")
|
await query.edit_message_text(f"Ошибка при остановке торрента: {e}")
|
||||||
@ -228,6 +226,31 @@ async def stop_torrent_callback(update: Update, context: ContextTypes.DEFAULT_TY
|
|||||||
logger.error(f"An unexpected error occurred during torrent stopping: {e}")
|
logger.error(f"An unexpected error occurred during torrent stopping: {e}")
|
||||||
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
|
# --- НОВАЯ функция: Обработка кнопки "Запустить торрент" ---
|
||||||
|
async def start_torrent_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
query = update.callback_query
|
||||||
|
await query.answer()
|
||||||
|
|
||||||
|
torrent_hash = query.data.replace("start_hash_", "")
|
||||||
|
logger.info(f"Attempting to start torrent with hash: {torrent_hash}")
|
||||||
|
|
||||||
|
if not init_qbittorrent_client():
|
||||||
|
await query.edit_message_text(
|
||||||
|
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
qb.torrents_resume(torrent_hashes=torrent_hash)
|
||||||
|
await query.edit_message_text(f"Торрент ({torrent_hash[:6]}...) успешно запущен (возобновлен).")
|
||||||
|
except APIError as e:
|
||||||
|
logger.error(f"Error starting torrent {torrent_hash}: {e}")
|
||||||
|
await query.edit_message_text(f"Ошибка при запуске торрента: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"An unexpected error occurred during torrent starting: {e}")
|
||||||
|
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
|
|
||||||
# --- Обработка magnet-ссылок и URL ---
|
# --- Обработка magnet-ссылок и URL ---
|
||||||
async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
if update.message is None:
|
if update.message is None:
|
||||||
@ -406,8 +429,10 @@ def main() -> None:
|
|||||||
application.add_handler(CallbackQueryHandler(select_category_callback, pattern=r"^select_category_.*"))
|
application.add_handler(CallbackQueryHandler(select_category_callback, pattern=r"^select_category_.*"))
|
||||||
# Добавляем CallbackQueryHandler для кнопок выбора директории
|
# Добавляем CallbackQueryHandler для кнопок выбора директории
|
||||||
application.add_handler(CallbackQueryHandler(select_directory_callback, pattern=r"^select_dir_.*"))
|
application.add_handler(CallbackQueryHandler(select_directory_callback, pattern=r"^select_dir_.*"))
|
||||||
# Новый CallbackQueryHandler для кнопок остановки торрентов
|
# ОБНОВЛЕНИЕ: CallbackQueryHandler для кнопок остановки торрентов
|
||||||
application.add_handler(CallbackQueryHandler(stop_torrent_callback, pattern=r"^stop_hash_.*"))
|
application.add_handler(CallbackQueryHandler(stop_torrent_callback, pattern=r"^stop_hash_.*"))
|
||||||
|
# НОВЫЙ CallbackQueryHandler для кнопок запуска торрентов
|
||||||
|
application.add_handler(CallbackQueryHandler(start_torrent_callback, pattern=r"^start_hash_.*"))
|
||||||
|
|
||||||
|
|
||||||
# --- Обработчик для любого другого текста, не являющегося командой ---
|
# --- Обработчик для любого другого текста, не являющегося командой ---
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user