✨ Реализованы все улучшения функционала бота
Блок 1: Система никнеймов - ✅ Добавлено поле nickname в модель User - ✅ Создана миграция для nickname - ✅ Обновлена регистрация (3 шага: nickname → карта → телефон) - ✅ Валидация nickname (длина 2-20, проверка служебных слов) - ✅ Подписи в чате используют nickname Блок 2: Админские функции - ✅ Массовая рассылка (кнопка в админке, поддержка текста/фото/видео/документов) - ✅ Экспорт пользователей в JSON (бэкап с метаданными) - ✅ Импорт пользователей из JSON (восстановление с обновлением) Блок 3: Улучшения розыгрышей - ✅ Рассылка результатов розыгрыша всем участникам (кроме победителей) - ✅ Сообщения подтверждения показывают nickname + клубную карту - ✅ Ручное назначение победителя по номеру счета/telegram ID/username
This commit is contained in:
@@ -14,8 +14,49 @@ logger = logging.getLogger(__name__)
|
||||
router = Router()
|
||||
|
||||
|
||||
# Служебные слова, которые нельзя использовать как никнейм
|
||||
FORBIDDEN_NICKNAMES = [
|
||||
'привет', 'здравствуйте', 'добрый', 'день', 'вечер', 'утро',
|
||||
'спасибо', 'пожалуйста', 'извините', 'до свидания', 'пока',
|
||||
'admin', 'administrator', 'moderator', 'bot', 'system',
|
||||
'hello', 'hi', 'thanks', 'please', 'sorry', 'goodbye', 'bye'
|
||||
]
|
||||
|
||||
|
||||
def validate_nickname(nickname: str) -> tuple[bool, str]:
|
||||
"""
|
||||
Валидация никнейма
|
||||
|
||||
Returns:
|
||||
(valid, error_message)
|
||||
"""
|
||||
nickname = nickname.strip()
|
||||
|
||||
# Проверка длины
|
||||
if len(nickname) < 2:
|
||||
return False, "❌ Никнейм слишком короткий (минимум 2 символа)"
|
||||
|
||||
if len(nickname) > 20:
|
||||
return False, "❌ Никнейм слишком длинный (максимум 20 символов)"
|
||||
|
||||
# Проверка на служебные слова
|
||||
nickname_lower = nickname.lower()
|
||||
for forbidden in FORBIDDEN_NICKNAMES:
|
||||
if forbidden in nickname_lower:
|
||||
import random
|
||||
suggestion = f"{nickname[:3]}{random.randint(10, 99)}"
|
||||
return False, f"❌ Это похоже на приветствие или служебное слово.\n\nПридумайте уникальный никнейм (например: {suggestion})"
|
||||
|
||||
# Проверка на команды
|
||||
if nickname.startswith('/'):
|
||||
return False, "❌ Никнейм не может начинаться с '/'"
|
||||
|
||||
return True, ""
|
||||
|
||||
|
||||
class RegistrationStates(StatesGroup):
|
||||
"""Состояния для процесса регистрации"""
|
||||
waiting_for_nickname = State()
|
||||
waiting_for_club_card = State()
|
||||
waiting_for_phone = State()
|
||||
|
||||
@@ -28,7 +69,11 @@ async def start_registration(callback: CallbackQuery, state: FSMContext):
|
||||
text = (
|
||||
"📝 Регистрация в системе\n\n"
|
||||
"Для участия в розыгрышах необходимо зарегистрироваться.\n\n"
|
||||
"Введите номер вашей клубной карты:"
|
||||
"Шаг 1 из 3: Придумайте никнейм\n\n"
|
||||
"🎭 Введите ваш никнейм для чата:\n"
|
||||
"• От 2 до 20 символов\n"
|
||||
"• Может содержать буквы, цифры, пробелы\n"
|
||||
"• Это имя будут видеть другие участники"
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
@@ -37,6 +82,32 @@ async def start_registration(callback: CallbackQuery, state: FSMContext):
|
||||
[InlineKeyboardButton(text="❌ Отмена", callback_data="back_to_main")]
|
||||
])
|
||||
)
|
||||
await state.set_state(RegistrationStates.waiting_for_nickname)
|
||||
|
||||
|
||||
@router.message(StateFilter(RegistrationStates.waiting_for_nickname))
|
||||
async def process_nickname(message: Message, state: FSMContext):
|
||||
"""Обработка никнейма"""
|
||||
nickname = message.text.strip()
|
||||
|
||||
# Валидация никнейма
|
||||
valid, error_msg = validate_nickname(nickname)
|
||||
|
||||
if not valid:
|
||||
await message.answer(
|
||||
f"{error_msg}\n\n"
|
||||
"Попробуйте другой вариант:"
|
||||
)
|
||||
return
|
||||
|
||||
# Сохраняем никнейм
|
||||
await state.update_data(nickname=nickname)
|
||||
|
||||
await message.answer(
|
||||
f"✅ Отлично! Ваш никнейм: {nickname}\n\n"
|
||||
"Шаг 2 из 3: Клубная карта\n\n"
|
||||
"📝 Введите номер вашей клубной карты:"
|
||||
)
|
||||
await state.set_state(RegistrationStates.waiting_for_club_card)
|
||||
|
||||
|
||||
@@ -60,7 +131,8 @@ async def process_club_card(message: Message, state: FSMContext):
|
||||
await state.update_data(club_card_number=club_card_number)
|
||||
|
||||
await message.answer(
|
||||
"📱 Теперь введите ваш номер телефона\n"
|
||||
"Шаг 3 из 3: Телефон\n\n"
|
||||
"📱 Введите ваш номер телефона\n"
|
||||
"(или отправьте '-' чтобы пропустить):"
|
||||
)
|
||||
await state.set_state(RegistrationStates.waiting_for_phone)
|
||||
@@ -73,6 +145,7 @@ async def process_phone(message: Message, state: FSMContext):
|
||||
|
||||
data = await state.get_data()
|
||||
club_card_number = data['club_card_number']
|
||||
nickname = data.get('nickname')
|
||||
|
||||
try:
|
||||
async with async_session_maker() as session:
|
||||
@@ -82,9 +155,16 @@ async def process_phone(message: Message, state: FSMContext):
|
||||
club_card_number=club_card_number,
|
||||
phone=phone
|
||||
)
|
||||
|
||||
# Обновляем никнейм пользователя
|
||||
if nickname:
|
||||
user.nickname = nickname
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
|
||||
text = (
|
||||
"✅ Регистрация завершена!\n\n"
|
||||
f"🎭 Никнейм: {user.nickname}\n"
|
||||
f"🎫 Клубная карта: {user.club_card_number}\n"
|
||||
f"🔑 Ваш код верификации: **{user.verification_code}**\n\n"
|
||||
"⚠️ Сохраните этот код! Он понадобится для подтверждения выигрыша.\n\n"
|
||||
|
||||
Reference in New Issue
Block a user