✅ Реализовано: - Автоверификация по 50+ сообщениям - Кнопка админу '⚡ Автоверификация' - Веб-админка → Пользователи → Статистика + Кнопка - Плавный ввод верификации 🎯 Как работает: 1. Бот считает сообщения пользователей 2. Кто набрал 50+ сообщений — считается 'своим' 3. Админ жмёт кнопку → все активные верифицируются 4. Веб-админка показывает статистику: - Всего пользователей - Верифицировано - Не верифицировано - Активных для верификации 📝 Преимущества: - Не нужно проверять 200 человек вручную - Активные жильцы сразу получают доступ - Админы проверяют только подозрительных - Мотивация: телефоны только после верификации 🧪 Тест: /admin → ⚡ Автоверификация или Веб-админка → Пользователи → Верифицировать всех 💡 Плавный ввод: - Бот предлагает верификацию, но не требует - Телефоны и функции только после верификации - Жильцы сами мотивированы пройти верификацию Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
341 lines
12 KiB
Python
341 lines
12 KiB
Python
"""
|
||
Клавиатуры для админа
|
||
"""
|
||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||
from aiogram.utils.keyboard import InlineKeyboardBuilder, ReplyKeyboardBuilder
|
||
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton
|
||
from typing import Optional
|
||
|
||
|
||
def get_admin_main_menu() -> InlineKeyboardMarkup:
|
||
"""Главное меню админа (inline)"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='📊 Статистика', callback_data='admin_stats'),
|
||
InlineKeyboardButton(text='🕵️ Шпионы', callback_data='admin_spies'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='👥 Пользователи', callback_data='admin_users'),
|
||
InlineKeyboardButton(text='⏳ Непроверенные', callback_data='admin_unverified'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🏆 Рейтинг', callback_data='admin_rating'),
|
||
InlineKeyboardButton(text='☠️ Токсичность', callback_data='admin_toxicity'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🏠 Квартиры', callback_data='admin_apartments'),
|
||
InlineKeyboardButton(text='⚠️ Предупреждения', callback_data='admin_warnings'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔇 Муты', callback_data='admin_muted'),
|
||
InlineKeyboardButton(text='🚫 Баны', callback_data='admin_banned'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔍 Поиск', callback_data='admin_search'),
|
||
InlineKeyboardButton(text='👤 Действия', callback_data='admin_user_actions'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='📥 Экспорт', callback_data='admin_export'),
|
||
InlineKeyboardButton(text='📤 Рассылка', callback_data='admin_broadcast'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🚫 Отозвать вериф.', callback_data='admin_unverify'),
|
||
InlineKeyboardButton(text='📋 Список не вериф.', callback_data='admin_unverified_list'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='⚡ Автоверификация', callback_data='admin_auto_verify'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='❓ Помощь', callback_data='admin_help'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_admin_reply_menu() -> ReplyKeyboardMarkup:
|
||
"""Главное меню админа (reply - кнопки под полем ввода)"""
|
||
builder = ReplyKeyboardBuilder()
|
||
|
||
builder.row(
|
||
KeyboardButton(text='📊 Статистика'),
|
||
KeyboardButton(text='🕵️ Шпионы'),
|
||
)
|
||
builder.row(
|
||
KeyboardButton(text='👥 Пользователи'),
|
||
KeyboardButton(text='⏳ Непроверенные'),
|
||
)
|
||
builder.row(
|
||
KeyboardButton(text='🏠 Квартиры'),
|
||
KeyboardButton(text='⚠️ Предупреждения'),
|
||
)
|
||
builder.row(
|
||
KeyboardButton(text='🏆 Рейтинг'),
|
||
KeyboardButton(text='📥 Экспорт'),
|
||
)
|
||
builder.row(
|
||
KeyboardButton(text='📤 Рассылка'),
|
||
)
|
||
builder.row(
|
||
KeyboardButton(text='🚫 Отозвать вериф.'),
|
||
KeyboardButton(text='📋 Не верифицированные'),
|
||
)
|
||
builder.row(
|
||
KeyboardButton(text='❓ Помощь'),
|
||
)
|
||
|
||
return builder.as_markup(resize_keyboard=True)
|
||
|
||
|
||
def get_admin_help_menu() -> InlineKeyboardMarkup:
|
||
"""Меню помощи админа"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.button(text='🔙 Назад', callback_data='admin_menu')
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_admin_apartments_menu() -> InlineKeyboardMarkup:
|
||
"""Меню квартир"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='📊 Статистика квартир', callback_data='admin_apt_stats'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data='admin_menu'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_admin_warnings_menu() -> InlineKeyboardMarkup:
|
||
"""Меню предупреждений"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='📋 Список предупреждений', callback_data='admin_warn_list'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data='admin_menu'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_admin_rating_menu() -> InlineKeyboardMarkup:
|
||
"""Меню рейтинга"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='🏆 Топ-10', callback_data='admin_top'),
|
||
InlineKeyboardButton(text='📊 Уровни', callback_data='admin_rank'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data='admin_menu'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_admin_toxicity_menu() -> InlineKeyboardMarkup:
|
||
"""Меню токсичности"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='☠️ Топ токсичных', callback_data='admin_toxic_top'),
|
||
InlineKeyboardButton(text='📊 Статистика', callback_data='admin_toxic_stats'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data='admin_menu'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_admin_search_menu() -> InlineKeyboardMarkup:
|
||
"""Меню поиска"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='🏠 Поиск квартиры', callback_data='admin_search_apt'),
|
||
InlineKeyboardButton(text='👤 Поиск пользователя', callback_data='admin_search_user'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data='admin_menu'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_user_pagination_keyboard(
|
||
users: list,
|
||
page: int = 0,
|
||
page_size: int = 20,
|
||
action_prefix: str = 'user_action'
|
||
) -> InlineKeyboardMarkup:
|
||
"""
|
||
Клавиатура с пагинацией для списка пользователей
|
||
|
||
:param users: список пользователей (User модели)
|
||
:param page: текущая страница (0-based)
|
||
:param page_size: размер страницы
|
||
:param action_prefix: префикс для callback_data
|
||
"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
total_pages = (len(users) + page_size - 1) // page_size
|
||
|
||
# Получаем пользователей для текущей страницы
|
||
start_idx = page * page_size
|
||
end_idx = min(start_idx + page_size, len(users))
|
||
page_users = users[start_idx:end_idx]
|
||
|
||
# Кнопки для каждого пользователя
|
||
for user in page_users:
|
||
status = '✅' if user.verified else '⏳'
|
||
name = user.full_name or f'user{user.user_id}'
|
||
apt = f' (кв. {user.apartment})' if user.apartment else ''
|
||
button_text = f"{status} {name}{apt} | {user.user_id}"
|
||
|
||
builder.button(
|
||
text=button_text,
|
||
callback_data=f'{action_prefix}_{user.user_id}'
|
||
)
|
||
|
||
# Кнопки навигации
|
||
nav_buttons = []
|
||
|
||
if page > 0:
|
||
nav_buttons.append(InlineKeyboardButton(text='⬅️ Назад', callback_data=f'{action_prefix}_page_{page - 1}'))
|
||
|
||
if page < total_pages - 1:
|
||
nav_buttons.append(InlineKeyboardButton(text='Вперёд ➡️', callback_data=f'{action_prefix}_page_{page + 1}'))
|
||
|
||
if nav_buttons:
|
||
builder.row(*nav_buttons)
|
||
|
||
# Инфо о странице
|
||
builder.row(
|
||
InlineKeyboardButton(text=f'📄 Стр. {page + 1}/{total_pages}', callback_data='page_info'),
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data='admin_menu'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_user_action_menu(user_id: int, user_name: str, apartment: Optional[str] = None) -> InlineKeyboardMarkup:
|
||
"""
|
||
Меню действий с пользователем
|
||
|
||
:param user_id: ID пользователя
|
||
:param user_name: имя пользователя
|
||
:param apartment: номер квартиры
|
||
"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
apt_info = f' (кв. {apartment})' if apartment else ''
|
||
builder.row(
|
||
InlineKeyboardButton(text=f'👤 {user_name}{apt_info}', callback_data='user_info_label'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text=f'ID: {user_id}', callback_data='user_id_label'),
|
||
)
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='⚠️ Предупредить', callback_data=f'user_warn_{user_id}'),
|
||
InlineKeyboardButton(text='🚫 Забанить', callback_data=f'user_ban_{user_id}'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='✅ Разбанить', callback_data=f'user_unban_{user_id}'),
|
||
InlineKeyboardButton(text='🔓 Снять вериф.', callback_data=f'user_revoke_{user_id}'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='⏳ Отозвать (7д)', callback_data=f'user_unverify_{user_id}'),
|
||
InlineKeyboardButton(text='📊 Рейтинг', callback_data=f'user_rating_{user_id}'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='📝 История', callback_data=f'user_history_{user_id}'),
|
||
InlineKeyboardButton(text='📞 Телефон', callback_data=f'user_phone_{user_id}'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад к списку', callback_data='admin_user_actions'),
|
||
InlineKeyboardButton(text='🔙 В меню', callback_data='admin_menu'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_admin_antispam_menu() -> InlineKeyboardMarkup:
|
||
"""Меню анти-спама/анти-мата"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='📋 Стоп-слова', callback_data='admin_bad_words'),
|
||
InlineKeyboardButton(text='🌐 Домены', callback_data='admin_bad_domains'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔑 Паттерны', callback_data='admin_bad_patterns'),
|
||
InlineKeyboardButton(text='🔑 Ключевые слова', callback_data='admin_bad_keywords'),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data='admin_menu'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_bad_words_list_keyboard(words: list, page: int = 0, page_size: int = 20) -> InlineKeyboardMarkup:
|
||
"""Клавиатура со списком стоп-слов"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
total_pages = (len(words) + page_size - 1) // page_size if words else 1
|
||
start_idx = page * page_size
|
||
end_idx = min(start_idx + page_size, len(words))
|
||
page_words = words[start_idx:end_idx]
|
||
|
||
for word in page_words:
|
||
builder.button(
|
||
text=f'❌ {word}',
|
||
callback_data=f'badword_del_{word}'
|
||
)
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='➕ Добавить слово', callback_data='badword_add'),
|
||
)
|
||
|
||
nav_buttons = []
|
||
if page > 0:
|
||
nav_buttons.append(InlineKeyboardButton(text='⬅️', callback_data=f'badwords_page_{page - 1}'))
|
||
if page < total_pages - 1:
|
||
nav_buttons.append(InlineKeyboardButton(text='➡️', callback_data=f'badwords_page_{page + 1}'))
|
||
|
||
if nav_buttons:
|
||
builder.row(*nav_buttons)
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data='admin_warnings'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_yes_no_keyboard(callback_prefix: str) -> InlineKeyboardMarkup:
|
||
"""Клавиатура Да/Нет с кастомным префиксом"""
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text='✅ Да', callback_data=f'{callback_prefix}_yes'),
|
||
InlineKeyboardButton(text='❌ Нет', callback_data=f'{callback_prefix}_no'),
|
||
)
|
||
|
||
return builder.as_markup()
|
||
|
||
|
||
def get_back_keyboard(back_callback: str = 'admin_menu') -> InlineKeyboardMarkup:
|
||
"""Простая клавиатура с кнопкой Назад"""
|
||
builder = InlineKeyboardBuilder()
|
||
builder.row(
|
||
InlineKeyboardButton(text='🔙 Назад', callback_data=back_callback),
|
||
)
|
||
return builder.as_markup()
|