Files
tg_loader/bot/modules/message_handler/callbacks.py

350 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Callback button handling
"""
from pyrogram import Client
from pyrogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
from pyrogram.handlers import CallbackQueryHandler
from pyrogram.errors import MessageNotModified
from bot.modules.access_control.auth import is_authorized, is_admin, is_owner
from bot.modules.message_handler.commands import get_start_keyboard
import logging
logger = logging.getLogger(__name__)
async def callback_handler(client: Client, callback_query: CallbackQuery):
"""Handle callback queries"""
user_id = callback_query.from_user.id
data = callback_query.data
# Check authorization
if not await is_authorized(user_id):
await callback_query.answer("У вас нет доступа к этому боту", show_alert=True)
return
# Handle different callback data
if data == "back":
# Return to main menu
welcome_text = (
"👋 **Привет! Я бот для загрузки медиа-файлов.**\n\n"
"📥 **Что я умею:**\n"
"• Загружать видео с YouTube, Instagram и других платформ\n"
"• Загружать файлы по прямым ссылкам\n"
"• Отправлять файлы вам в Telegram\n\n"
"**Как использовать:**\n"
"Просто отправьте мне ссылку на видео или файл, и я загружу его для вас!\n\n"
"Используйте кнопки ниже для управления:"
)
keyboard = await get_start_keyboard(user_id)
await callback_query.edit_message_text(welcome_text, reply_markup=keyboard)
await callback_query.answer()
elif data == "help":
# Show help
help_text = (
"👋 **Привет! Рад помочь!**\n\n"
"🎯 **Как начать работу:**\n"
"Это очень просто! Просто отправьте мне ссылку на видео или файл, и я сразу начну загрузку.\n\n"
"📥 **Что я умею загружать:**\n"
"• 🎬 Видео с YouTube, Instagram, TikTok и других платформ\n"
"• 📁 Файлы по прямым ссылкам\n"
"• 🎵 Аудио и музыку\n"
"• 📸 Изображения и фото\n\n"
"⌨️ **Основные команды:**\n"
"• `/start` - Открыть главное меню с кнопками\n"
"• `/help` - Показать эту справку\n"
"• `/status` - Посмотреть статус ваших загрузок\n\n"
"💡 **Совет:** Используйте кнопки в главном меню для быстрого доступа к функциям!"
)
if await is_admin(user_id):
help_text += (
"\n\n"
"👑 **Команды для администраторов:**\n"
"• `/adduser <user_id или @username>` - Добавить нового пользователя\n"
"• `/blockuser <user_id или @username>` - Заблокировать пользователя\n"
"• `/unblockuser <user_id или @username>` - Разблокировать пользователя\n"
"• `/listusers` - Посмотреть список всех пользователей\n\n"
"💼 **Управление администраторами:**\n"
"• `/addadmin <user_id или @username>` - Назначить администратора\n"
"• `/removeadmin <user_id или @username>` - Снять права администратора\n"
"• `/listadmins` - Список всех администраторов"
)
keyboard = InlineKeyboardMarkup([[
InlineKeyboardButton("🔙 Назад", callback_data="back")
]])
await callback_query.edit_message_text(help_text, reply_markup=keyboard)
await callback_query.answer()
elif data == "status":
# Show task status
from bot.modules.task_scheduler.monitor import get_user_tasks_status
from bot.modules.task_scheduler.queue import TaskStatus
tasks = await get_user_tasks_status(user_id)
active_tasks = [t for t in tasks if t.get('status') in ['pending', 'processing']]
completed = [t for t in tasks if t.get('status') == 'completed']
failed = [t for t in tasks if t.get('status') == 'failed']
status_text = (
"📊 **Статус задач:**\n\n"
f"⏳ Активных задач: {len(active_tasks)}\n"
f"✅ Завершено: {len(completed)}\n"
f"❌ Ошибок: {len(failed)}\n\n"
)
if active_tasks:
status_text += "**Активные задачи:**\n"
for task in active_tasks[:5]: # Show first 5
task_id = task.get('id')
progress = task.get('progress', 0)
status_text += f"• #{task_id} - {progress}%\n"
if len(active_tasks) > 5:
status_text += f"... и еще {len(active_tasks) - 5}\n"
status_text += "\n💡 Используйте `/cancel <task_id>` для отмены"
keyboard = InlineKeyboardMarkup([[
InlineKeyboardButton("🔄 Обновить", callback_data="status"),
InlineKeyboardButton("🔙 Назад", callback_data="back")
]])
try:
await callback_query.edit_message_text(status_text, reply_markup=keyboard)
await callback_query.answer("✅ Статус обновлен")
except MessageNotModified:
# If text didn't change, just answer callback
await callback_query.answer("✅ Статус актуален")
elif data == "download":
# Download information
download_text = (
"📥 **Загрузка файлов:**\n\n"
"**Поддерживаемые источники:**\n"
"• YouTube (видео, плейлисты)\n"
"• Instagram (посты, истории)\n"
"• Прямые ссылки на файлы\n"
"• Другие платформы через yt-dlp\n\n"
"**Как использовать:**\n"
"Просто отправьте мне ссылку на видео или файл, и я начну загрузку!\n\n"
"Примеры:\n"
"• https://www.youtube.com/watch?v=...\n"
"• https://www.instagram.com/p/...\n"
"• https://example.com/file.mp4"
)
keyboard = InlineKeyboardMarkup([[
InlineKeyboardButton("🔙 Назад", callback_data="back")
]])
await callback_query.edit_message_text(download_text, reply_markup=keyboard)
await callback_query.answer()
elif data == "admin_users":
# User management (admin only)
if not await is_admin(user_id):
await callback_query.answer("❌ Только для администраторов", show_alert=True)
return
# Determine user status
is_owner_user = await is_owner(user_id)
# Form text and buttons depending on status
if is_owner_user:
# Main admin - full functionality
users_text = (
"👥 **Управление пользователями:**\n\n"
"**Управление пользователями:**\n"
"• /adduser <user_id или @username> - Добавить пользователя\n"
"• /blockuser <user_id или @username> - Заблокировать пользователя\n"
"• /unblockuser <user_id или @username> - Разблокировать пользователя\n"
"• /listusers - Список всех пользователей\n\n"
"**Управление администраторами:**\n"
"• /addadmin <user_id или @username> - Назначить администратора\n"
"• /removeadmin <user_id или @username> - Снять права администратора\n"
"• /listadmins - Список всех администраторов\n\n"
"⚠️ **Внимание:** Вы не можете снять права администратора у самого себя."
)
else:
# Regular administrator - only user management
users_text = (
"👥 **Управление пользователями:**\n\n"
"**Доступные команды:**\n"
"• /adduser <user_id или @username> - Добавить пользователя\n"
"• /blockuser <user_id или @username> - Заблокировать пользователя\n"
"• /unblockuser <user_id или @username> - Разблокировать пользователя\n"
"• /listusers - Список всех пользователей\n\n"
"_Управление через веб-интерфейс будет доступно позже_"
)
keyboard = InlineKeyboardMarkup([[
InlineKeyboardButton("🔙 Назад", callback_data="back")
]])
await callback_query.edit_message_text(users_text, reply_markup=keyboard)
await callback_query.answer()
elif data == "admin_stats":
# Statistics (admin only)
if not await is_admin(user_id):
await callback_query.answer("❌ Только для администраторов", show_alert=True)
return
stats_text = (
"📈 **Статистика:**\n\n"
"👥 Всего пользователей: 0\n"
"👑 Администраторов: 0\n"
"📥 Всего загрузок: 0\n"
"✅ Успешных: 0\n"
"❌ Ошибок: 0\n\n"
"_Статистика будет реализована в следующем этапе_"
)
keyboard = InlineKeyboardMarkup([[
InlineKeyboardButton("🔙 Назад", callback_data="back")
]])
await callback_query.edit_message_text(stats_text, reply_markup=keyboard)
await callback_query.answer()
elif data.startswith("video_select:"):
# Handle video selection from webpage
selection_id = data.replace("video_select:", "", 1)
# Retrieve video URL from cache using short identifier
from bot.modules.message_handler.video_selection_cache import get_video_selection
video_url = get_video_selection(selection_id, user_id=user_id)
if not video_url:
await callback_query.answer("❌ Ссылка истекла или не найдена. Пожалуйста, попробуйте снова.", show_alert=True)
return
# Create task for selected video
try:
from bot.modules.task_scheduler.queue import task_queue, Task, TaskStatus
from bot.modules.task_scheduler.executor import task_executor, set_app_client
from bot.utils.helpers import generate_unique_task_id
from shared.database.models import Task as DBTask
from shared.database.session import get_async_session_local
from shared.database.user_helpers import ensure_user_exists
from datetime import datetime
from sqlalchemy.exc import IntegrityError
from bot.modules.task_scheduler.executor import set_task_message
user_id = callback_query.from_user.id
# Check concurrent tasks count
from bot.config import settings
active_tasks_count = await task_queue.get_user_active_tasks_count(user_id)
if active_tasks_count >= settings.MAX_CONCURRENT_TASKS:
await callback_query.answer(
f"❌ Превышен лимит одновременных задач ({settings.MAX_CONCURRENT_TASKS})",
show_alert=True
)
return
# Generate task_id
task_id = generate_unique_task_id()
existing_task = await task_queue.get_task_by_id(task_id)
max_retries = 10
retries = 0
while existing_task and retries < max_retries:
task_id = generate_unique_task_id()
existing_task = await task_queue.get_task_by_id(task_id)
retries += 1
if existing_task:
await callback_query.answer("❌ Ошибка при создании задачи", show_alert=True)
return
# Create task
task = Task(
id=task_id,
user_id=user_id,
task_type="download",
url=video_url,
status=TaskStatus.PENDING
)
# Save to database
try:
async with get_async_session_local()() as session:
await ensure_user_exists(user_id, session)
db_task = DBTask(
id=task_id,
user_id=user_id,
task_type=task.task_type,
status=task.status.value,
url=task.url,
progress=0,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
session.add(db_task)
await session.commit()
except Exception as e:
logger.error(f"Error saving task to database: {e}", exc_info=True)
await callback_query.answer("❌ Ошибка при создании задачи", show_alert=True)
return
# Add to queue
success = await task_queue.add_task(task, check_duplicate_url=True)
if not success:
try:
async with get_async_session_local()() as session:
db_task = await session.get(DBTask, task_id)
if db_task:
await session.delete(db_task)
await session.commit()
except Exception as e:
logger.error(f"Error deleting task from database: {e}")
await callback_query.answer("⚠️ Задача с этим URL уже обрабатывается", show_alert=True)
return
# Start executor if needed
set_app_client(client)
if not task_executor._running:
await task_executor.start()
# Send status message
status_message = await callback_query.message.reply(
f"📥 **Загрузка начата**\n\n"
f"🔗 {video_url[:50]}...\n\n"
f"📊 Прогресс: **0%**\n"
f"⏳ Ожидание начала загрузки..."
)
set_task_message(task_id, status_message.id)
await callback_query.answer("✅ Загрузка начата")
# Update original message to show selection
try:
await callback_query.edit_message_reply_markup(
reply_markup=None # Remove buttons
)
original_text = callback_query.message.text or ""
await callback_query.message.edit_text(
original_text + f"\n\n✅ Выбрано: {video_url[:50]}..."
)
except Exception as e:
logger.debug(f"Failed to update selection message: {e}")
except Exception as e:
logger.error(f"Error handling video selection: {e}", exc_info=True)
await callback_query.answer("❌ Ошибка при запуске загрузки", show_alert=True)
else:
await callback_query.answer("❓ Неизвестная команда")
def register_callbacks(app: Client):
"""Register all callback handlers"""
app.add_handler(CallbackQueryHandler(callback_handler))
logger.info("Callback handlers registered")