756 lines
30 KiB
Python
756 lines
30 KiB
Python
# handlers.py - добавляем обработчики передачи прав
|
||
import io
|
||
from aiogram import Router, F
|
||
from aiogram.filters import Command, StateFilter
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.fsm.state import StatesGroup, State
|
||
from aiogram.types import Message, CallbackQuery, Document
|
||
|
||
from database import (
|
||
add_user, set_user_allowed, is_user_allowed,
|
||
get_target_email, set_target_email, get_all_users,
|
||
delete_user, get_user_by_id, get_admin_id, set_admin_id,
|
||
can_send_notification, update_notification_time, get_last_notification_time,
|
||
reset_notification_timer, get_next_allowed_time # новые импорты
|
||
)
|
||
from email_sender import send_batch_to_email
|
||
from queue_manager import file_queue
|
||
from keyboards import (
|
||
admin_decision_keyboard, admin_panel_keyboard,
|
||
users_list_keyboard, confirm_delete_keyboard,
|
||
confirm_transfer_keyboard, quick_decision_keyboard
|
||
)
|
||
from config import ADMIN_ID as DEFAULT_ADMIN_ID
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
router = Router()
|
||
|
||
|
||
class ChangeEmailState(StatesGroup):
|
||
waiting_for_email = State()
|
||
|
||
|
||
async def is_admin(user_id: int) -> bool:
|
||
"""Проверка, является ли пользователь администратором"""
|
||
current_admin_id = await get_admin_id()
|
||
if current_admin_id:
|
||
return user_id == current_admin_id
|
||
return user_id == DEFAULT_ADMIN_ID
|
||
|
||
|
||
@router.message(Command("start"))
|
||
async def cmd_start(message: Message):
|
||
user_id = message.from_user.id
|
||
username = message.from_user.username or "без юзернейма"
|
||
full_name = message.from_user.full_name
|
||
|
||
await add_user(user_id, username, full_name)
|
||
|
||
# Проверяем, есть ли в БД админ, если нет - устанавливаем
|
||
current_admin = await get_admin_id()
|
||
if not current_admin:
|
||
await set_admin_id(DEFAULT_ADMIN_ID)
|
||
current_admin = DEFAULT_ADMIN_ID
|
||
|
||
if await is_admin(user_id):
|
||
await message.answer(
|
||
"👑 Вы администратор.\n"
|
||
"Вот ваша панель управления:",
|
||
reply_markup=admin_panel_keyboard()
|
||
)
|
||
return
|
||
|
||
if await is_user_allowed(user_id):
|
||
await message.answer(
|
||
"✅ Доступ подтверждён!\n"
|
||
"Отправляйте мне любые файлы (фото, документы, архивы и т.д.).\n"
|
||
"Они будут накапливаться и отправляться пачкой раз в минуту на почту."
|
||
)
|
||
else:
|
||
# Проверяем, можно ли отправлять уведомление админу
|
||
if await can_send_notification(user_id):
|
||
# Обновляем время уведомления
|
||
await update_notification_time(user_id)
|
||
|
||
# Отправляем уведомление админу
|
||
admin_id = await get_admin_id()
|
||
await message.bot.send_message(
|
||
admin_id,
|
||
f"🆕 Запрос доступа от пользователя:\n"
|
||
f"ID: {user_id}\n"
|
||
f"Имя: {message.from_user.full_name}\n"
|
||
f"Username: @{message.from_user.username or 'нет'}",
|
||
reply_markup=admin_decision_keyboard(user_id)
|
||
)
|
||
await message.answer(
|
||
"⏳ Ваш запрос отправлен администратору.\n"
|
||
"Ожидайте подтверждения. Новый запрос можно будет отправить через час."
|
||
)
|
||
else:
|
||
# Получаем время следующего доступного запроса
|
||
next_allowed = await get_next_allowed_time(user_id)
|
||
|
||
if next_allowed:
|
||
await message.answer(
|
||
f"⏳ Вы уже отправляли запрос администратору.\n\n"
|
||
f"🕐 Следующий запрос можно отправить после: **{next_allowed}** (по Екатеринбургу)\n\n"
|
||
f"Если доступ нужен срочно, свяжитесь с администратором напрямую.",
|
||
parse_mode="Markdown"
|
||
)
|
||
else:
|
||
await message.answer(
|
||
"⏳ Ваш запрос уже был отправлен администратору.\n\n"
|
||
"Пожалуйста, ожидайте. Новый запрос можно отправить через час.\n\n"
|
||
"Если доступ нужен срочно, свяжитесь с администратором напрямую."
|
||
)
|
||
|
||
|
||
@router.callback_query(F.data.startswith("approve_"))
|
||
async def approve_user(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
user_id = int(callback.data.split("_")[1])
|
||
await set_user_allowed(user_id, True)
|
||
|
||
# Сбрасываем таймер уведомлений для этого пользователя
|
||
await reset_notification_timer(user_id)
|
||
|
||
await callback.bot.send_message(
|
||
user_id,
|
||
"🎉 Ваш доступ подтверждён! Теперь вы можете отправлять любые файлы.\n"
|
||
"Они будут отправляться пачкой раз в минуту."
|
||
)
|
||
await callback.answer("Пользователь добавлен в список разрешённых")
|
||
await callback.message.edit_reply_markup()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("deny_"))
|
||
async def deny_user(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
user_id = int(callback.data.split("_")[1])
|
||
await set_user_allowed(user_id, False)
|
||
|
||
# Сбрасываем таймер уведомлений для этого пользователя
|
||
await reset_notification_timer(user_id)
|
||
|
||
# Получаем текущее время Екатеринбурга для сообщения
|
||
from timezone_utils import format_time, get_current_time
|
||
current_time = format_time(get_current_time())
|
||
|
||
await callback.bot.send_message(
|
||
user_id,
|
||
f"❌ Ваш запрос доступа отклонён администратором.\n\n"
|
||
f"Вы можете отправить новый запрос командой /start в любое время.\n"
|
||
f"🕐 Текущее время: {current_time} (Екатеринбург)"
|
||
)
|
||
await callback.answer("Доступ отклонён")
|
||
await callback.message.edit_reply_markup()
|
||
|
||
|
||
@router.callback_query(F.data == "change_email")
|
||
async def change_email_request(callback: CallbackQuery, state: FSMContext):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
await callback.message.answer("📧 Введите новый email для получения файлов:")
|
||
await state.set_state(ChangeEmailState.waiting_for_email)
|
||
await callback.answer()
|
||
|
||
|
||
@router.message(ChangeEmailState.waiting_for_email)
|
||
async def set_new_email(message: Message, state: FSMContext):
|
||
if not await is_admin(message.from_user.id):
|
||
await message.answer("Нет прав")
|
||
return
|
||
|
||
new_email = message.text.strip()
|
||
if "@" not in new_email or "." not in new_email:
|
||
await message.answer("❌ Некорректный email. Попробуйте ещё раз:")
|
||
return
|
||
await set_target_email(new_email)
|
||
await message.answer(f"✅ Почта для отправки изменена на: {new_email}")
|
||
await state.clear()
|
||
|
||
|
||
@router.callback_query(F.data == "list_users")
|
||
async def list_users(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
users = await get_all_users()
|
||
if not users:
|
||
await callback.message.answer("📋 Список пользователей пуст")
|
||
await callback.answer()
|
||
return
|
||
|
||
# Разделяем пользователей по статусу для наглядности
|
||
pending = [u for u in users if u[3] == 0]
|
||
allowed = [u for u in users if u[3] == 1]
|
||
|
||
text = "👥 **Управление пользователями**\n\n"
|
||
|
||
if pending:
|
||
text += f"⏳ **Ожидают доступа ({len(pending)}):**\n"
|
||
text += "Нажмите на пользователя для быстрых действий\n\n"
|
||
else:
|
||
text += "⏳ Нет пользователей в ожидании\n\n"
|
||
|
||
if allowed:
|
||
text += f"✅ **Подтверждённые ({len(allowed)}):**\n"
|
||
text += "Нажмите на пользователя для удаления\n\n"
|
||
|
||
await callback.message.answer(
|
||
text,
|
||
reply_markup=users_list_keyboard(users, 0, for_transfer=False),
|
||
parse_mode="Markdown"
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("users_page_"))
|
||
async def users_page(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
parts = callback.data.split("_")
|
||
page = int(parts[2])
|
||
for_transfer = parts[3] == "True" if len(parts) > 3 else False
|
||
|
||
users = await get_all_users()
|
||
|
||
if not users:
|
||
await callback.message.edit_text("📋 Список пользователей пуст")
|
||
await callback.answer()
|
||
return
|
||
|
||
await callback.message.edit_reply_markup(reply_markup=users_list_keyboard(users, page, for_transfer))
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("user_"))
|
||
async def user_selected(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
user_id = int(callback.data.split("_")[1])
|
||
user = await get_user_by_id(user_id)
|
||
|
||
if not user:
|
||
await callback.answer("Пользователь не найден")
|
||
await callback.message.edit_text("❌ Пользователь уже был удалён")
|
||
return
|
||
|
||
user_id, username, full_name, is_allowed = user
|
||
|
||
text = f"🗑️ **Удаление пользователя**\n\n"
|
||
text += f"ID: `{user_id}`\n"
|
||
text += f"Имя: {full_name}\n"
|
||
text += f"Username: @{username if username and username != 'None' else 'нет'}\n"
|
||
text += f"Статус: {'✅ Доступ разрешён' if is_allowed else '⏳ Ожидает'}\n\n"
|
||
text += "Вы уверены, что хотите удалить этого пользователя?"
|
||
|
||
await callback.message.edit_text(
|
||
text,
|
||
reply_markup=confirm_delete_keyboard(user_id, username),
|
||
parse_mode="Markdown"
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("confirm_delete_"))
|
||
async def confirm_delete(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
user_id = int(callback.data.split("_")[2])
|
||
user = await get_user_by_id(user_id)
|
||
|
||
if not user:
|
||
await callback.message.edit_text("❌ Пользователь уже был удалён")
|
||
await callback.answer()
|
||
return
|
||
|
||
user_id, username, full_name, is_allowed = user
|
||
|
||
await delete_user(user_id)
|
||
|
||
try:
|
||
await callback.bot.send_message(
|
||
user_id,
|
||
"❌ Ваш доступ был отозван администратором. Вы больше не можете отправлять файлы.\n"
|
||
"Для повторного доступа нужно снова запросить разрешение через /start"
|
||
)
|
||
except Exception:
|
||
pass
|
||
|
||
await callback.message.edit_text(
|
||
f"✅ Пользователь {full_name} (ID: {user_id}) удалён из базы данных.\n\n"
|
||
f"Если нужно восстановить доступ, пользователь должен снова написать /start"
|
||
)
|
||
await callback.answer("Пользователь удалён")
|
||
|
||
|
||
@router.callback_query(F.data == "cancel_delete")
|
||
async def cancel_delete(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
users = await get_all_users()
|
||
await callback.message.edit_text(
|
||
"👥 Управление пользователями\n\nНажмите на пользователя, чтобы удалить его.",
|
||
reply_markup=users_list_keyboard(users, 0, for_transfer=False)
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data == "close_users")
|
||
async def close_users(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
await callback.message.delete()
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data == "queue_status")
|
||
async def queue_status(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
size = await file_queue.get_queue_size()
|
||
await callback.message.answer(f"📊 В очереди {size} файлов")
|
||
await callback.answer()
|
||
|
||
|
||
# ========== НОВЫЕ ОБРАБОТЧИКИ ДЛЯ ПЕРЕДАЧИ ПРАВ АДМИНА ==========
|
||
|
||
@router.callback_query(F.data == "transfer_admin")
|
||
async def transfer_admin_request(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
users = await get_all_users()
|
||
allowed_users = [u for u in users if u[3] == 1] # Только пользователи с доступом
|
||
|
||
if not allowed_users:
|
||
await callback.message.answer("❌ Нет пользователей с подтверждённым доступом для передачи прав")
|
||
await callback.answer()
|
||
return
|
||
|
||
text = "👑 **Передача прав администратора**\n\n"
|
||
text += "Выберите пользователя, которому хотите передать права:\n"
|
||
text += "(только пользователи с подтверждённым доступом ✅)\n\n"
|
||
text += "⚠️ **Внимание!** После передачи вы потеряете права администратора!"
|
||
|
||
await callback.message.answer(
|
||
text,
|
||
reply_markup=users_list_keyboard(allowed_users, 0, for_transfer=True),
|
||
parse_mode="Markdown"
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("select_new_admin_"))
|
||
async def select_new_admin(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
new_admin_id = int(callback.data.split("_")[3])
|
||
user = await get_user_by_id(new_admin_id)
|
||
|
||
if not user:
|
||
await callback.answer("Пользователь не найден")
|
||
return
|
||
|
||
user_id, username, full_name, is_allowed = user
|
||
|
||
if not is_allowed:
|
||
await callback.answer("У этого пользователя нет доступа", show_alert=True)
|
||
return
|
||
|
||
text = f"⚠️ **Подтверждение передачи прав**\n\n"
|
||
text += f"Вы собираетесь передать права администратора пользователю:\n"
|
||
text += f"👤 {full_name}\n"
|
||
text += f"📱 @{username if username and username != 'None' else 'нет'}\n"
|
||
text += f"🆔 ID: `{user_id}`\n\n"
|
||
text += f"После этого действия **ВЫ ПОТЕРЯЕТЕ ПРАВА АДМИНИСТРАТОРА**.\n\n"
|
||
text += f"Вы уверены?"
|
||
|
||
await callback.message.edit_text(
|
||
text,
|
||
reply_markup=confirm_transfer_keyboard(user_id, username, full_name),
|
||
parse_mode="Markdown"
|
||
)
|
||
await callback.answer()
|
||
|
||
|
||
@router.callback_query(F.data.startswith("confirm_transfer_"))
|
||
async def confirm_transfer(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
new_admin_id = int(callback.data.split("_")[2])
|
||
old_admin_id = callback.from_user.id
|
||
|
||
user = await get_user_by_id(new_admin_id)
|
||
|
||
if not user:
|
||
await callback.message.edit_text("❌ Пользователь не найден")
|
||
await callback.answer()
|
||
return
|
||
|
||
user_id, username, full_name, is_allowed = user
|
||
|
||
if not is_allowed:
|
||
await callback.message.edit_text("❌ У этого пользователя нет доступа")
|
||
await callback.answer()
|
||
return
|
||
|
||
# Передаём права админа
|
||
await set_admin_id(new_admin_id)
|
||
|
||
# Уведомляем нового админа
|
||
try:
|
||
await callback.bot.send_message(
|
||
new_admin_id,
|
||
f"👑 **Поздравляем!**\n\n"
|
||
f"Пользователь {callback.from_user.full_name} передал вам права администратора.\n\n"
|
||
f"Теперь вы можете управлять ботом: изменять почту, одобрять пользователей и т.д.\n\n"
|
||
f"Используйте /start для доступа к панели управления.",
|
||
parse_mode="Markdown"
|
||
)
|
||
except Exception:
|
||
pass
|
||
|
||
# Уведомляем старого админа
|
||
await callback.message.edit_text(
|
||
f"✅ **Права администратора переданы!**\n\n"
|
||
f"Новый администратор: {full_name} (ID: {new_admin_id})\n\n"
|
||
f"Вы больше не являетесь администратором. Для управления ботом обратитесь к новому админу."
|
||
)
|
||
|
||
# Отправляем старому админу сообщение в личку (если не в чате с ботом)
|
||
try:
|
||
await callback.bot.send_message(
|
||
old_admin_id,
|
||
f"👑 Вы передали права администратора пользователю {full_name}.\n"
|
||
f"Вы больше не являетесь администратором бота."
|
||
)
|
||
except Exception:
|
||
pass
|
||
|
||
await callback.answer("Права переданы")
|
||
|
||
|
||
@router.callback_query(F.data == "cancel_transfer")
|
||
async def cancel_transfer(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
await callback.message.edit_text("❌ Передача прав отменена")
|
||
await callback.answer()
|
||
|
||
|
||
# Обработчики файлов
|
||
@router.message(F.document)
|
||
async def handle_document(message: Message):
|
||
user_id = message.from_user.id
|
||
|
||
if not await is_admin(user_id) and not await is_user_allowed(user_id):
|
||
await message.answer("⛔ У вас нет доступа. Запросите его через /start")
|
||
return
|
||
|
||
document = message.document
|
||
file = await message.bot.get_file(document.file_id)
|
||
|
||
from io import BytesIO
|
||
file_content = await message.bot.download_file(file.file_path)
|
||
|
||
if isinstance(file_content, BytesIO):
|
||
file_bytes = file_content.getvalue()
|
||
elif isinstance(file_content, bytes):
|
||
file_bytes = file_content
|
||
else:
|
||
file_bytes = file_content.read()
|
||
|
||
filename = document.file_name
|
||
user_info = f"{message.from_user.full_name}"
|
||
|
||
await file_queue.add_file(file_bytes, filename, user_info, user_id, file_type='document')
|
||
|
||
queue_size = await file_queue.get_queue_size()
|
||
await message.answer(
|
||
f"✅ Файл '{filename}' добавлен в очередь (всего: {queue_size})\n"
|
||
f"📧 Отправка произойдёт в течение минуты"
|
||
)
|
||
|
||
|
||
@router.message(F.photo)
|
||
async def handle_photo(message: Message):
|
||
user_id = message.from_user.id
|
||
|
||
if not await is_admin(user_id) and not await is_user_allowed(user_id):
|
||
await message.answer("⛔ У вас нет доступа. Запросите его через /start")
|
||
return
|
||
|
||
photo = message.photo[-1]
|
||
file = await message.bot.get_file(photo.file_id)
|
||
|
||
from io import BytesIO
|
||
file_content = await message.bot.download_file(file.file_path)
|
||
|
||
if isinstance(file_content, BytesIO):
|
||
file_bytes = file_content.getvalue()
|
||
elif isinstance(file_content, bytes):
|
||
file_bytes = file_content
|
||
else:
|
||
file_bytes = file_content.read()
|
||
|
||
filename = f"photo_{user_id}_{photo.file_unique_id}.jpg"
|
||
user_info = f"{message.from_user.full_name}"
|
||
|
||
await file_queue.add_file(file_bytes, filename, user_info, user_id, file_type='photo')
|
||
|
||
queue_size = await file_queue.get_queue_size()
|
||
await message.answer(
|
||
f"✅ Фото добавлено в очередь (всего: {queue_size})\n"
|
||
f"📧 Отправка произойдёт в течение минуты"
|
||
)
|
||
|
||
@router.message(Command("send_now"))
|
||
async def force_send(message: Message):
|
||
"""Принудительная отправка всех накопленных файлов"""
|
||
if not await is_admin(message.from_user.id):
|
||
await message.answer("⛔ Нет прав. Только администратор может использовать эту команду.")
|
||
return
|
||
|
||
# Получаем все файлы из очереди
|
||
files = await file_queue.get_all_files()
|
||
|
||
if not files:
|
||
await message.answer("📭 Очередь пуста. Нечего отправлять.")
|
||
return
|
||
|
||
target_email = await get_target_email()
|
||
if not target_email:
|
||
await message.answer("⚠️ Целевая почта не настроена. Используйте кнопку 'Сменить почту'.")
|
||
return
|
||
|
||
# Отправляем уведомление о начале отправки
|
||
status_msg = await message.answer(f"📤 Начинаю отправку {len(files)} файлов...")
|
||
|
||
try:
|
||
# Отправляем пачку файлов
|
||
await send_batch_to_email(target_email, files)
|
||
|
||
# Успешно
|
||
await status_msg.edit_text(
|
||
f"✅ Принудительно отправлено {len(files)} файлов на почту {target_email}\n"
|
||
f"📊 Размер: {sum(len(f.file_bytes) for f in files) / (1024 * 1024):.2f} MB"
|
||
)
|
||
|
||
# Логируем действие
|
||
logger.info(f"Админ {message.from_user.id} принудительно отправил {len(files)} файлов")
|
||
|
||
except Exception as e:
|
||
# Ошибка при отправке
|
||
await status_msg.edit_text(
|
||
f"❌ Ошибка при отправке: {str(e)[:200]}\n\n"
|
||
f"Файлы остались в очереди и будут отправлены при следующем автоматическом запуске."
|
||
)
|
||
# Возвращаем файлы обратно в очередь
|
||
for file in files:
|
||
await file_queue.add_file(
|
||
file.file_bytes,
|
||
file.filename,
|
||
file.user_info,
|
||
file.user_id,
|
||
file.file_type
|
||
)
|
||
logger.error(f"Ошибка при принудительной отправке: {e}")
|
||
|
||
|
||
@router.message(Command("queue"))
|
||
async def check_queue(message: Message):
|
||
"""Показать содержимое очереди"""
|
||
if not await is_admin(message.from_user.id):
|
||
await message.answer("⛔ Нет прав")
|
||
return
|
||
|
||
size = await file_queue.get_queue_size()
|
||
|
||
if size == 0:
|
||
await message.answer("📭 Очередь пуста")
|
||
return
|
||
|
||
# Получаем первые 10 файлов для просмотра
|
||
peek_files = await file_queue.peek_queue(10)
|
||
|
||
text = f"📊 **Очередь отправки**\n\n"
|
||
text += f"📎 Всего файлов: {size}\n\n"
|
||
|
||
if peek_files:
|
||
text += "**Первые файлы в очереди:**\n"
|
||
for i, f in enumerate(peek_files, 1):
|
||
text += f"{i}. {f['filename']}\n"
|
||
text += f" └ от {f['user_info']} ({f['size_kb']:.1f} KB) в {f['timestamp']}\n"
|
||
|
||
if size > 10:
|
||
text += f"\n... и ещё {size - 10} файлов\n"
|
||
|
||
text += f"\n⏱️ Автоотправка: каждую минуту\n"
|
||
text += f"🚀 Принудительно: /send_now"
|
||
|
||
await message.answer(text, parse_mode="Markdown")
|
||
|
||
@router.message(Command("stats"))
|
||
async def bot_stats(message: Message):
|
||
"""Показать статистику бота"""
|
||
if not await is_admin(message.from_user.id):
|
||
await message.answer("⛔ Нет прав")
|
||
return
|
||
|
||
users = await get_all_users()
|
||
allowed_users = [u for u in users if u[3] == 1]
|
||
pending_users = [u for u in users if u[3] == 0]
|
||
|
||
target_email = await get_target_email()
|
||
queue_size = await file_queue.get_queue_size()
|
||
|
||
text = f"📊 **Статистика бота**\n\n"
|
||
text += f"👥 Всего пользователей: {len(users)}\n"
|
||
text += f"✅ С доступом: {len(allowed_users)}\n"
|
||
text += f"⏳ Ожидают: {len(pending_users)}\n"
|
||
text += f"📎 В очереди: {queue_size}\n"
|
||
text += f"📧 Почта для отправки: {target_email or 'не настроена'}\n"
|
||
|
||
await message.answer(text, parse_mode="Markdown")
|
||
|
||
|
||
@router.callback_query(F.data.startswith("quick_approve_"))
|
||
async def quick_approve(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
user_id = int(callback.data.split("_")[2])
|
||
user = await get_user_by_id(user_id)
|
||
|
||
if not user:
|
||
await callback.answer("Пользователь не найден")
|
||
await callback.message.edit_text("❌ Пользователь уже был удалён")
|
||
return
|
||
|
||
await set_user_allowed(user_id, True)
|
||
|
||
# Сбрасываем таймер уведомлений для этого пользователя
|
||
await reset_notification_timer(user_id)
|
||
|
||
await callback.bot.send_message(
|
||
user_id,
|
||
"🎉 Ваш доступ подтверждён! Теперь вы можете отправлять любые файлы.\n"
|
||
"Они будут отправляться пачкой раз в минуту."
|
||
)
|
||
|
||
# Обновляем сообщение
|
||
users = await get_all_users()
|
||
await callback.message.edit_text(
|
||
"👥 Управление пользователями\n\n✅ Доступ подтверждён",
|
||
reply_markup=users_list_keyboard(users, 0, for_transfer=False)
|
||
)
|
||
await callback.answer("Доступ подтверждён")
|
||
|
||
|
||
@router.callback_query(F.data.startswith("quick_deny_"))
|
||
async def quick_deny(callback: CallbackQuery):
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
user_id = int(callback.data.split("_")[2])
|
||
user = await get_user_by_id(user_id)
|
||
|
||
if not user:
|
||
await callback.answer("Пользователь не найден")
|
||
await callback.message.edit_text("❌ Пользователь уже был удалён")
|
||
return
|
||
|
||
await set_user_allowed(user_id, False)
|
||
|
||
# Сбрасываем таймер уведомлений для этого пользователя
|
||
await reset_notification_timer(user_id)
|
||
|
||
# Получаем текущее время Екатеринбурга для сообщения
|
||
from timezone_utils import format_time, get_current_time
|
||
current_time = format_time(get_current_time())
|
||
|
||
await callback.bot.send_message(
|
||
user_id,
|
||
f"❌ Ваш запрос доступа отклонён администратором.\n\n"
|
||
f"Вы можете отправить новый запрос командой /start в любое время.\n"
|
||
f"🕐 Текущее время: {current_time} (Екатеринбург)"
|
||
)
|
||
|
||
# Обновляем сообщение админу
|
||
users = await get_all_users()
|
||
await callback.message.edit_text(
|
||
"👥 Управление пользователями\n\n❌ Доступ отклонён",
|
||
reply_markup=users_list_keyboard(users, 0, for_transfer=False)
|
||
)
|
||
await callback.answer("Доступ отклонён")
|
||
|
||
|
||
@router.callback_query(F.data.startswith("pending_user_"))
|
||
async def pending_user_info(callback: CallbackQuery):
|
||
"""Показывает информацию об ожидающем пользователе"""
|
||
if not await is_admin(callback.from_user.id):
|
||
await callback.answer("Нет прав", show_alert=True)
|
||
return
|
||
|
||
user_id = int(callback.data.split("_")[2])
|
||
user = await get_user_by_id(user_id)
|
||
|
||
if not user:
|
||
await callback.answer("Пользователь не найден")
|
||
return
|
||
|
||
user_id, username, full_name, is_allowed = user
|
||
|
||
from database import get_last_notification_time
|
||
last_notification = await get_last_notification_time(user_id)
|
||
|
||
text = f"👤 **Информация о пользователе**\n\n"
|
||
text += f"ID: `{user_id}`\n"
|
||
text += f"Имя: {full_name}\n"
|
||
text += f"Username: @{username if username and username != 'None' else 'нет'}\n"
|
||
text += f"Статус: ⏳ Ожидает подтверждения\n"
|
||
if last_notification:
|
||
text += f"Последний запрос: {last_notification}\n\n"
|
||
|
||
text += "Выберите действие:"
|
||
|
||
await callback.message.edit_text(
|
||
text,
|
||
reply_markup=quick_decision_keyboard(user_id),
|
||
parse_mode="Markdown"
|
||
)
|
||
await callback.answer()
|