Files
new_lottery_bot/src/handlers/admin_chat_handlers.py
Andrey K. Choi 0fdad07d82
Some checks failed
continuous-integration/drone/pr Build is failing
refactor
2026-02-17 00:22:42 +09:00

365 lines
15 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.

"""Админские обработчики для управления чатом"""
from aiogram import Router, F
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.filters import Command
from sqlalchemy.ext.asyncio import AsyncSession
from src.filters.case_insensitive import CaseInsensitiveCommand
from src.core.chat_services import (
ChatSettingsService,
BanService,
ChatMessageService
)
from src.core.services import UserService
from src.core.database import async_session_maker
from src.core.config import ADMIN_IDS
from src.core.permissions import admin_only
router = Router(name='admin_chat_router')
def get_chat_mode_keyboard() -> InlineKeyboardMarkup:
"""Клавиатура выбора режима чата"""
return InlineKeyboardMarkup(inline_keyboard=[
[
InlineKeyboardButton(text="📢 Рассылка всем", callback_data="chat_mode:broadcast"),
InlineKeyboardButton(text="➡️ Пересылка в канал", callback_data="chat_mode:forward")
],
[InlineKeyboardButton(text="❌ Закрыть", callback_data="close_menu")]
])
@router.message(CaseInsensitiveCommand("chat_mode"))
@admin_only
async def cmd_chat_mode(message: Message):
"""Команда управления режимом чата (регистронезависимо)"""
async with async_session_maker() as session:
settings = await ChatSettingsService.get_or_create_settings(session)
mode_text = "📢 Рассылка всем пользователям" if settings.mode == 'broadcast' else "➡️ Пересылка в канал"
await message.answer(
f"🎛 <b>Управление режимом чата</b>\n\n"
f"Текущий режим: {mode_text}\n\n"
f"Выберите режим работы:",
reply_markup=get_chat_mode_keyboard(),
parse_mode="HTML"
)
@router.callback_query(F.data.startswith("chat_mode:"))
async def process_chat_mode(callback: CallbackQuery):
"""Обработка выбора режима чата"""
mode = callback.data.split(":")[1]
async with async_session_maker() as session:
settings = await ChatSettingsService.set_mode(session, mode)
mode_text = "📢 Рассылка всем пользователям" if mode == 'broadcast' else "➡️ Пересылка в канал"
await callback.message.edit_text(
f"✅ Режим чата изменен!\n\n"
f"Новый режим: {mode_text}",
reply_markup=None
)
await callback.answer("✅ Режим изменен")
@router.message(CaseInsensitiveCommand("set_forward"))
@admin_only
async def cmd_set_forward(message: Message):
"""Установить ID канала для пересылки (регистронезависимо)"""
args = message.text.split(maxsplit=1)
if len(args) < 2:
await message.answer(
"📝 <b>Использование:</b>\n"
"/set_forward <chat_id>\n\n"
"Пример: /set_forward -1001234567890\n\n"
"💡 Чтобы узнать ID канала/группы:\n"
"1. Добавьте бота в канал/группу\n"
"2. Напишите любое сообщение\n"
"3. Перешлите его боту @userinfobot",
parse_mode="HTML"
)
return
chat_id = args[1].strip()
async with async_session_maker() as session:
settings = await ChatSettingsService.set_forward_chat(session, chat_id)
await message.answer(
f"✅ ID канала для пересылки установлен!\n\n"
f"Chat ID: <code>{chat_id}</code>\n\n"
f"Теперь переключитесь в режим пересылки командой /chat_mode",
parse_mode="HTML"
)
@router.message(CaseInsensitiveCommand("global_ban"))
@admin_only
async def cmd_global_ban(message: Message):
"""Включить/выключить глобальный бан чата (регистронезависимо)"""
async with async_session_maker() as session:
settings = await ChatSettingsService.get_or_create_settings(session)
# Переключаем состояние
new_state = not settings.global_ban
settings = await ChatSettingsService.set_global_ban(session, new_state)
if new_state:
await message.answer(
"🔇 <b>Глобальный бан включен</b>\n\n"
"Теперь только администраторы могут отправлять сообщения в чат",
parse_mode="HTML"
)
else:
await message.answer(
"🔊 <b>Глобальный бан выключен</b>\n\n"
"Все пользователи снова могут отправлять сообщения",
parse_mode="HTML"
)
@router.message(CaseInsensitiveCommand("ban"))
@admin_only
async def cmd_ban(message: Message):
"""Забанить пользователя (регистронезависимо)"""
# Проверяем является ли это ответом на сообщение
if message.reply_to_message:
target_user_id = message.reply_to_message.from_user.id
reason = message.text.split(maxsplit=1)[1] if len(message.text.split(maxsplit=1)) > 1 else None
else:
args = message.text.split(maxsplit=2)
if len(args) < 2:
await message.answer(
"📝 <b>Использование:</b>\n\n"
"1. Ответьте на сообщение пользователя: /ban [причина]\n"
"2. Укажите ID: /ban <user_id> [причина]\n\n"
"Пример: /ban 123456789 Спам",
parse_mode="HTML"
)
return
try:
target_user_id = int(args[1])
reason = args[2] if len(args) > 2 else None
except ValueError:
await message.answer("❌ Неверный ID пользователя")
return
async with async_session_maker() as session:
# Получаем пользователя
user = await UserService.get_user_by_telegram_id(session, target_user_id)
if not user:
await message.answer("❌ Пользователь не найден в базе")
return
# Получаем админа
admin = await UserService.get_or_create_user(
session,
message.from_user.id,
username=message.from_user.username,
first_name=message.from_user.first_name,
last_name=message.from_user.last_name
)
# Баним
ban = await BanService.ban_user(
session,
user_id=user.id,
telegram_id=target_user_id,
banned_by=admin.id,
reason=reason
)
reason_text = f"\n📝 Причина: {reason}" if reason else ""
await message.answer(
f"🚫 <b>Пользователь забанен</b>\n\n"
f"👤 Пользователь: {user.name or 'Неизвестен'}\n"
f"🆔 ID: <code>{target_user_id}</code>"
f"{reason_text}",
parse_mode="HTML"
)
@router.message(CaseInsensitiveCommand("unban"))
@admin_only
async def cmd_unban(message: Message):
"""Разбанить пользователя (регистронезависимо)"""
# Проверяем является ли это ответом на сообщение
if message.reply_to_message:
target_user_id = message.reply_to_message.from_user.id
else:
args = message.text.split()
if len(args) < 2:
await message.answer(
"📝 <b>Использование:</b>\n\n"
"1. Ответьте на сообщение пользователя: /unban\n"
"2. Укажите ID: /unban <user_id>\n\n"
"Пример: /unban 123456789",
parse_mode="HTML"
)
return
try:
target_user_id = int(args[1])
except ValueError:
await message.answer("❌ Неверный ID пользователя")
return
async with async_session_maker() as session:
# Разбаниваем
success = await BanService.unban_user(session, target_user_id)
if success:
await message.answer(
f"✅ <b>Пользователь разбанен</b>\n\n"
f"🆔 ID: <code>{target_user_id}</code>\n\n"
f"Теперь пользователь может отправлять сообщения",
parse_mode="HTML"
)
else:
await message.answer("❌ Пользователь не был забанен")
@router.message(CaseInsensitiveCommand("banlist"))
@admin_only
async def cmd_banlist(message: Message):
"""Показать список заблокированных пользователей (регистронезависимо)"""
async with async_session_maker() as session:
banned_users = await BanService.get_banned_users(session, active_only=True)
if not banned_users:
await message.answer("📋 Список банов пуст")
return
text = "🚫 <b>Забаненные пользователи</b>\n\n"
for ban in banned_users:
user = ban.user
admin = ban.admin
text += f"👤 {user.name or 'Неизвестен'} (<code>{ban.telegram_id}</code>)\n"
text += f"🔨 Забанил: {admin.name if admin else 'Неизвестен'}\n"
if ban.reason:
text += f"📝 Причина: {ban.reason}\n"
text += f"📅 Дата: {ban.banned_at.strftime('%d.%m.%Y %H:%M')}\n"
text += "\n"
await message.answer(text, parse_mode="HTML")
@router.message(CaseInsensitiveCommand("delete_msg"))
@admin_only
async def cmd_delete_message(message: Message):
"""Удалить сообщение из чата (пометить как удаленное) (регистронезависимо)"""
if not message.reply_to_message:
await message.answer(
"📝 <b>Использование:</b>\n\n"
"Ответьте на сообщение которое хотите удалить командой /delete_msg",
parse_mode="HTML"
)
return
async with async_session_maker() as session:
# Получаем админа
admin = await UserService.get_or_create_user(
session,
message.from_user.id,
username=message.from_user.username,
first_name=message.from_user.first_name,
last_name=message.from_user.last_name
)
# Находим сообщение в базе по telegram_message_id
from sqlalchemy import select
from src.core.models import ChatMessage
result = await session.execute(
select(ChatMessage).where(
ChatMessage.telegram_message_id == message.reply_to_message.message_id
)
)
chat_message = result.scalar_one_or_none()
if not chat_message:
await message.answer("❌ Сообщение не найдено в базе данных")
return
# Помечаем как удаленное
success = await ChatMessageService.delete_message(
session,
message_id=chat_message.id,
deleted_by=admin.id
)
if success:
# Пытаемся удалить сообщение у всех пользователей
if chat_message.forwarded_message_ids:
deleted_count = 0
for user_telegram_id, msg_id in chat_message.forwarded_message_ids.items():
try:
await message.bot.delete_message(int(user_telegram_id), msg_id)
deleted_count += 1
except Exception as e:
print(f"Failed to delete message {msg_id} for user {user_telegram_id}: {e}")
await message.answer(
f"✅ <b>Сообщение удалено</b>\n\n"
f"🗑 Удалено у {deleted_count} пользователей",
parse_mode="HTML"
)
else:
await message.answer("✅ Сообщение помечено как удаленное")
else:
await message.answer("Не удалось удалить сообщение")
@router.message(CaseInsensitiveCommand("chat_stats"))
@admin_only
async def cmd_chat_stats(message: Message):
"""Статистика чата (регистронезависимо)"""
async with async_session_maker() as session:
settings = await ChatSettingsService.get_or_create_settings(session)
banned_users = await BanService.get_banned_users(session, active_only=True)
recent_messages = await ChatMessageService.get_recent_messages(session, limit=100)
mode_text = "📢 Рассылка всем" if settings.mode == 'broadcast' else "➡️ Пересылка в канал"
global_ban_text = "🔇 Включен" if settings.global_ban else "🔊 Выключен"
text = (
f"📊 <b>Статистика чата</b>\n\n"
f"🎛 Режим: {mode_text}\n"
f"🚫 Глобальный бан: {global_ban_text}\n"
f"👥 Забанено пользователей: {len(banned_users)}\n"
f"💬 Сообщений за последнее время: {len(recent_messages)}\n"
)
if settings.mode == 'forward' and settings.forward_chat_id:
text += f"\n➡️ ID канала: <code>{settings.forward_chat_id}</code>"
await message.answer(text, parse_mode="HTML")
@router.callback_query(F.data == "close_menu")
async def close_menu(callback: CallbackQuery):
"""Закрыть меню"""
await callback.message.delete()
await callback.answer()