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
6c72f3a42a
commit
4f5fbf9a39
46
bot.py
46
bot.py
@ -70,6 +70,7 @@ 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-файла, "
|
||||||
@ -79,7 +80,7 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
"Используй /help для списка команд."
|
"Используй /help для списка команд."
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Команда /help (новая функция) ---
|
# --- Команда /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.")
|
||||||
@ -87,13 +88,14 @@ 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 используется, parse_mode="Markdown" необходим
|
||||||
help_text = (
|
help_text = (
|
||||||
"Вот список доступных команд:\n\n"
|
"Вот список доступных команд:\n\n"
|
||||||
"**/start** - Начать работу с ботом и проверить подключение к qBittorrent.\n"
|
"**/start** - Начать работу с ботом и проверить подключение к qBittorrent.\n"
|
||||||
"**/status** - Показать текущий статус всех активных загрузок.\n"
|
"**/status** - Показать текущий статус всех активных загрузок.\n"
|
||||||
"**/stop_torrent** - Выбрать и остановить загрузку торрента.\n"
|
"**/stop_torrent** - Выбрать и остановить загрузку торрента.\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")
|
||||||
@ -116,6 +118,7 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
try:
|
try:
|
||||||
torrents = qb.torrents_info()
|
torrents = qb.torrents_info()
|
||||||
if not torrents:
|
if not torrents:
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text("Загрузок не найдено.")
|
await update.message.reply_text("Загрузок не найдено.")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -143,6 +146,7 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
eta_str = "Завершено"
|
eta_str = "Завершено"
|
||||||
|
|
||||||
|
|
||||||
|
# Здесь Markdown используется, поэтому важно, чтобы он был корректным
|
||||||
status_messages.append(
|
status_messages.append(
|
||||||
f"📊 *{torrent.name}*\n"
|
f"📊 *{torrent.name}*\n"
|
||||||
f" Состояние: {torrent.state}\n"
|
f" Состояние: {torrent.state}\n"
|
||||||
@ -158,9 +162,11 @@ async def status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
|
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
logger.error(f"Error getting torrent status: {e}")
|
logger.error(f"Error getting torrent status: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text(f"Ошибка при получении статуса торрентов: {e}")
|
await update.message.reply_text(f"Ошибка при получении статуса торрентов: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"An unexpected error occurred in status command: {e}")
|
logger.error(f"An unexpected error occurred in status command: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
# --- Команда /stop_torrent ---
|
# --- Команда /stop_torrent ---
|
||||||
@ -178,8 +184,9 @@ async def stop_torrent(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
torrents = qb.torrents_info(status_filter='downloading') # Только активные загрузки
|
torrents = qb.torrents_info(status_filter='downloading')
|
||||||
if not torrents:
|
if not torrents:
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text("Нет активных загрузок для остановки.")
|
await update.message.reply_text("Нет активных загрузок для остановки.")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -192,13 +199,16 @@ async def stop_torrent(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
|
|||||||
keyboard.append([InlineKeyboardButton(display_name, callback_data=f"stop_hash_{torrent.hash}")])
|
keyboard.append([InlineKeyboardButton(display_name, callback_data=f"stop_hash_{torrent.hash}")])
|
||||||
|
|
||||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text("Выберите торрент для остановки:", reply_markup=reply_markup)
|
await update.message.reply_text("Выберите торрент для остановки:", reply_markup=reply_markup)
|
||||||
|
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
logger.error(f"Error fetching torrents for stopping: {e}")
|
logger.error(f"Error fetching torrents for stopping: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text(f"Ошибка при получении списка торрентов для остановки: {e}")
|
await update.message.reply_text(f"Ошибка при получении списка торрентов для остановки: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"An unexpected error occurred in stop_torrent command: {e}")
|
logger.error(f"An unexpected error occurred in stop_torrent command: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
# --- Обработка кнопки "Остановить торрент" ---
|
# --- Обработка кнопки "Остановить торрент" ---
|
||||||
@ -210,6 +220,7 @@ async def stop_torrent_callback(update: Update, context: ContextTypes.DEFAULT_TY
|
|||||||
logger.info(f"Attempting to stop torrent with hash: {torrent_hash}")
|
logger.info(f"Attempting to stop torrent with hash: {torrent_hash}")
|
||||||
|
|
||||||
if not init_qbittorrent_client():
|
if not init_qbittorrent_client():
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
||||||
)
|
)
|
||||||
@ -217,12 +228,15 @@ async def stop_torrent_callback(update: Update, context: ContextTypes.DEFAULT_TY
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
qb.torrents_stop(torrent_hashes=torrent_hash)
|
qb.torrents_stop(torrent_hashes=torrent_hash)
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
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}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(f"Ошибка при остановке торрента: {e}")
|
await query.edit_message_text(f"Ошибка при остановке торрента: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"An unexpected error occurred during torrent stopping: {e}")
|
logger.error(f"An unexpected error occurred during torrent stopping: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
# --- Обработка magnet-ссылок и URL ---
|
# --- Обработка magnet-ссылок и URL ---
|
||||||
@ -235,6 +249,7 @@ async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None
|
|||||||
logger.info(f"Received URL/Magnet: {text} from {update.effective_user.id}")
|
logger.info(f"Received URL/Magnet: {text} from {update.effective_user.id}")
|
||||||
|
|
||||||
if not init_qbittorrent_client():
|
if not init_qbittorrent_client():
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text(
|
await update.message.reply_text(
|
||||||
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
||||||
)
|
)
|
||||||
@ -251,13 +266,16 @@ async def handle_url(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None
|
|||||||
category_keyboard.append([InlineKeyboardButton("Без категории", callback_data="select_category_no_category")])
|
category_keyboard.append([InlineKeyboardButton("Без категории", callback_data="select_category_no_category")])
|
||||||
|
|
||||||
reply_markup = InlineKeyboardMarkup(category_keyboard)
|
reply_markup = InlineKeyboardMarkup(category_keyboard)
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text('Выберите категорию для загрузки:', reply_markup=reply_markup)
|
await update.message.reply_text('Выберите категорию для загрузки:', reply_markup=reply_markup)
|
||||||
|
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
logger.error(f"Error fetching categories: {e}")
|
logger.error(f"Error fetching categories: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text(f"Ошибка при получении категорий: {e}")
|
await update.message.reply_text(f"Ошибка при получении категорий: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"An unexpected error occurred in handle_url: {e}")
|
logger.error(f"An unexpected error occurred in handle_url: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
await update.message.reply_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
|
|
||||||
@ -273,12 +291,14 @@ async def select_category_callback(update: Update, context: ContextTypes.DEFAULT
|
|||||||
|
|
||||||
context.user_data['selected_category'] = selected_category
|
context.user_data['selected_category'] = selected_category
|
||||||
|
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text("Выберите директорию загрузки:")
|
await query.edit_message_text("Выберите директорию загрузки:")
|
||||||
await send_directory_options(query, context)
|
await send_directory_options(query, context)
|
||||||
|
|
||||||
# --- Отправка опций директорий ---
|
# --- Отправка опций директорий ---
|
||||||
async def send_directory_options(query, context):
|
async def send_directory_options(query, context):
|
||||||
if not init_qbittorrent_client():
|
if not init_qbittorrent_client():
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
||||||
)
|
)
|
||||||
@ -287,9 +307,9 @@ async def send_directory_options(query, context):
|
|||||||
try:
|
try:
|
||||||
available_paths = [
|
available_paths = [
|
||||||
qb.app.default_save_path,
|
qb.app.default_save_path,
|
||||||
"/share/Data/Films", # ЗАМЕНИТЕ НА СВОИ АКТУАЛЬНЫЕ ПУТИ
|
"/mnt/user/downloads/movies", # ЗАМЕНИТЕ НА СВОИ АКТУАЛЬНЫЕ ПУТИ
|
||||||
"/share/Data/Serials",
|
"/mnt/user/downloads/tv-shows",
|
||||||
"/share/Data/torrents",
|
"/var/lib/qbittorrent/data/completed",
|
||||||
]
|
]
|
||||||
|
|
||||||
available_paths = sorted(list(set([p.replace(os.sep, '/') for p in available_paths if p])))
|
available_paths = sorted(list(set([p.replace(os.sep, '/') for p in available_paths if p])))
|
||||||
@ -300,13 +320,17 @@ async def send_directory_options(query, context):
|
|||||||
directory_keyboard.append([InlineKeyboardButton(display_path, callback_data=f"select_dir_{path}")])
|
directory_keyboard.append([InlineKeyboardButton(display_path, callback_data=f"select_dir_{path}")])
|
||||||
|
|
||||||
reply_markup = InlineKeyboardMarkup(directory_keyboard)
|
reply_markup = InlineKeyboardMarkup(directory_keyboard)
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
|
# edit_message_reply_markup не принимает parse_mode
|
||||||
await query.edit_message_reply_markup(reply_markup=reply_markup)
|
await query.edit_message_reply_markup(reply_markup=reply_markup)
|
||||||
|
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
logger.error(f"Error fetching default save path: {e}")
|
logger.error(f"Error fetching default save path: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(f"Ошибка при получении директорий сохранения: {e}")
|
await query.edit_message_text(f"Ошибка при получении директорий сохранения: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"An unexpected error occurred in send_directory_options: {e}")
|
logger.error(f"An unexpected error occurred in send_directory_options: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
|
|
||||||
@ -324,10 +348,12 @@ async def select_directory_callback(update: Update, context: ContextTypes.DEFAUL
|
|||||||
category = context.user_data.get('selected_category')
|
category = context.user_data.get('selected_category')
|
||||||
|
|
||||||
if not torrent_url:
|
if not torrent_url:
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text("Ошибка: URL торрента не найден. Пожалуйста, попробуйте снова.")
|
await query.edit_message_text("Ошибка: URL торрента не найден. Пожалуйста, попробуйте снова.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not init_qbittorrent_client():
|
if not init_qbittorrent_client():
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
"Не удалось подключиться к qBittorrent. Проверьте переменные окружения и доступность сервера."
|
||||||
)
|
)
|
||||||
@ -339,6 +365,7 @@ async def select_directory_callback(update: Update, context: ContextTypes.DEFAUL
|
|||||||
category=category,
|
category=category,
|
||||||
save_path=selected_directory
|
save_path=selected_directory
|
||||||
)
|
)
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(
|
await query.edit_message_text(
|
||||||
f"Торрент успешно добавлен в qBittorrent.\n"
|
f"Торрент успешно добавлен в qBittorrent.\n"
|
||||||
f"Категория: {category or 'Без категории'}\n"
|
f"Категория: {category or 'Без категории'}\n"
|
||||||
@ -350,9 +377,11 @@ async def select_directory_callback(update: Update, context: ContextTypes.DEFAUL
|
|||||||
del context.user_data['selected_category']
|
del context.user_data['selected_category']
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
logger.error(f"Error adding torrent with path: {e}")
|
logger.error(f"Error adding torrent with path: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(f"Ошибка при добавлении торрента: {e}")
|
await query.edit_message_text(f"Ошибка при добавлении торрента: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"An unexpected error occurred during torrent addition with path: {e}")
|
logger.error(f"An unexpected error occurred during torrent addition with path: {e}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
await query.edit_message_text(f"Произошла непредвиденная ошибка: {e}")
|
||||||
|
|
||||||
|
|
||||||
@ -360,6 +389,7 @@ async def select_directory_callback(update: Update, context: ContextTypes.DEFAUL
|
|||||||
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
logger.error(f"Exception while handling an update: {context.error}")
|
logger.error(f"Exception while handling an update: {context.error}")
|
||||||
if update and update.effective_message:
|
if update and update.effective_message:
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.effective_message.reply_text(f"Произошла внутренняя ошибка бота: {context.error}")
|
await update.effective_message.reply_text(f"Произошла внутренняя ошибка бота: {context.error}")
|
||||||
|
|
||||||
# --- Обработчик для неизвестных команд ---
|
# --- Обработчик для неизвестных команд ---
|
||||||
@ -368,6 +398,7 @@ async def unknown_command(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
logger.warning("Received an unknown command update without a message object.")
|
logger.warning("Received an unknown command update without a message object.")
|
||||||
return
|
return
|
||||||
logger.info(f"Received unknown command: {update.message.text} from {update.effective_user.id}")
|
logger.info(f"Received unknown command: {update.message.text} from {update.effective_user.id}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text("Извините, я не понял эту команду.")
|
await update.message.reply_text("Извините, я не понял эту команду.")
|
||||||
|
|
||||||
# --- Обработчик для любого другого текста (для отладки) ---
|
# --- Обработчик для любого другого текста (для отладки) ---
|
||||||
@ -376,6 +407,7 @@ async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|||||||
logger.warning(f"Received non-text update in echo handler: {update}")
|
logger.warning(f"Received non-text update in echo handler: {update}")
|
||||||
return
|
return
|
||||||
logger.info(f"Received non-command text: {update.message.text} from {update.effective_user.id}")
|
logger.info(f"Received non-command text: {update.message.text} from {update.effective_user.id}")
|
||||||
|
# В этом сообщении нет Markdown, поэтому parse_mode не нужен
|
||||||
await update.message.reply_text(f"Вы сказали: {update.message.text}")
|
await update.message.reply_text(f"Вы сказали: {update.message.text}")
|
||||||
|
|
||||||
|
|
||||||
@ -391,7 +423,7 @@ def main() -> None:
|
|||||||
application.add_handler(CommandHandler("start", start))
|
application.add_handler(CommandHandler("start", start))
|
||||||
application.add_handler(CommandHandler("status", status))
|
application.add_handler(CommandHandler("status", status))
|
||||||
application.add_handler(CommandHandler("stop_torrent", stop_torrent))
|
application.add_handler(CommandHandler("stop_torrent", stop_torrent))
|
||||||
application.add_handler(CommandHandler("help", help_command)) # Новая команда /help
|
application.add_handler(CommandHandler("help", help_command))
|
||||||
|
|
||||||
# --- Добавление обработчиков сообщений ---
|
# --- Добавление обработчиков сообщений ---
|
||||||
url_regex = r"magnet:\?xt=urn:[a-z0-9]+"
|
url_regex = r"magnet:\?xt=urn:[a-z0-9]+"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user