Промокоды на место реферального кода

This commit is contained in:
Pavel Stryuk
2025-11-06 09:26:24 +01:00
parent 1bdf1a0f4c
commit 113405ed71
5 changed files with 203 additions and 70 deletions

View File

@@ -22,6 +22,25 @@ async def get_promocode_by_code(db: AsyncSession, code: str) -> Optional[PromoCo
return result.scalar_one_or_none()
async def check_promocode_validity(db: AsyncSession, code: str) -> dict:
"""
Проверяет существование и валидность промокода без активации.
Возвращает словарь с информацией о промокоде.
"""
promocode = await get_promocode_by_code(db, code)
if not promocode:
return {"valid": False, "error": "not_found", "promocode": None}
if not promocode.is_valid:
if promocode.current_uses >= promocode.max_uses:
return {"valid": False, "error": "used", "promocode": None}
else:
return {"valid": False, "error": "expired", "promocode": None}
return {"valid": True, "error": None, "promocode": promocode}
async def create_promocode(
db: AsyncSession,
code: str,

View File

@@ -1,9 +1,8 @@
import logging
from aiogram import Dispatcher, types, F
from aiogram import Dispatcher, types, F, Bot
from aiogram.fsm.context import FSMContext
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import settings
from app.states import PromoCodeStates
from app.database.models import User
from app.keyboards.inline import get_back_keyboard
@@ -32,6 +31,45 @@ async def show_promocode_menu(
await callback.answer()
async def activate_promocode_for_registration(
db: AsyncSession,
user_id: int,
code: str,
bot: Bot = None
) -> dict:
"""
Активирует промокод для пользователя во время регистрации.
Возвращает результат активации без отправки сообщений.
"""
promocode_service = PromoCodeService()
result = await promocode_service.activate_promocode(db, user_id, code)
if result["success"]:
logger.info(f"✅ Пользователь {user_id} активировал промокод {code} при регистрации")
# Отправляем уведомление админу, если бот доступен
if bot:
try:
from app.database.crud.user import get_user_by_id
user = await get_user_by_id(db, user_id)
if user:
notification_service = AdminNotificationService(bot)
await notification_service.send_promocode_activation_notification(
db,
user,
result.get("promocode", {"code": code}),
result["description"],
)
except Exception as notify_error:
logger.error(
"Ошибка отправки админ уведомления об активации промокода %s: %s",
code,
notify_error,
)
return result
@error_handler
async def process_promocode(
message: types.Message,
@@ -40,9 +78,9 @@ async def process_promocode(
db: AsyncSession
):
texts = get_texts(db_user.language)
code = message.text.strip()
if not code:
await message.answer(
texts.t(
@@ -52,31 +90,14 @@ async def process_promocode(
reply_markup=get_back_keyboard(db_user.language)
)
return
promocode_service = PromoCodeService()
result = await promocode_service.activate_promocode(db, db_user.id, code)
result = await activate_promocode_for_registration(db, db_user.id, code, message.bot)
if result["success"]:
await message.answer(
texts.PROMOCODE_SUCCESS.format(description=result["description"]),
reply_markup=get_back_keyboard(db_user.language)
)
logger.info(f"✅ Пользователь {db_user.telegram_id} активировал промокод {code}")
try:
notification_service = AdminNotificationService(message.bot)
await notification_service.send_promocode_activation_notification(
db,
db_user,
result.get("promocode", {"code": code}),
result["description"],
)
except Exception as notify_error:
logger.error(
"Ошибка отправки админ уведомления об активации промокода %s: %s",
code,
notify_error,
)
else:
error_messages = {
"not_found": texts.PROMOCODE_INVALID,
@@ -85,13 +106,13 @@ async def process_promocode(
"already_used_by_user": texts.PROMOCODE_USED,
"server_error": texts.ERROR
}
error_text = error_messages.get(result["error"], texts.PROMOCODE_INVALID)
await message.answer(
error_text,
reply_markup=get_back_keyboard(db_user.language)
)
await state.clear()

View File

@@ -100,15 +100,15 @@ async def handle_potential_referral_code(
db: AsyncSession
):
current_state = await state.get_state()
logger.info(f"🔍 REFERRAL CHECK: Проверка сообщения '{message.text}' в состоянии {current_state}")
logger.info(f"🔍 REFERRAL/PROMO CHECK: Проверка сообщения '{message.text}' в состоянии {current_state}")
if current_state not in [
RegistrationStates.waiting_for_rules_accept.state,
RegistrationStates.waiting_for_referral_code.state,
None
None
]:
return False
user = await get_user_by_telegram_id(db, message.from_user.id)
if user and user.status == UserStatus.ACTIVE.value:
return False
@@ -125,37 +125,73 @@ async def handle_potential_referral_code(
if len(potential_code) < 4 or len(potential_code) > 20:
return False
# Сначала проверяем реферальный код
referrer = await get_user_by_referral_code(db, potential_code)
if not referrer:
await message.answer(texts.t(
"REFERRAL_CODE_INVALID_HELP",
"❌ Неверный реферальный код.\n\n"
"💡 Если у вас есть реферальный код, убедитесь что он введен правильно.\n"
"⏭️ Для продолжения регистрации без реферального кода используйте команду /start",
))
if referrer:
data['referral_code'] = potential_code
data['referrer_id'] = referrer.id
await state.set_data(data)
await message.answer(texts.t("REFERRAL_CODE_ACCEPTED", "Реферальный код принят!"))
logger.info(f"✅ Реферальный код {potential_code} применен для пользователя {message.from_user.id}")
if current_state != RegistrationStates.waiting_for_referral_code.state:
language = data.get('language', DEFAULT_LANGUAGE)
texts = get_texts(language)
rules_text = await get_rules(language)
await message.answer(
rules_text,
reply_markup=get_rules_keyboard(language)
)
await state.set_state(RegistrationStates.waiting_for_rules_accept)
logger.info("📋 Правила отправлены после ввода реферального кода")
else:
await complete_registration(message, state, db)
return True
data['referral_code'] = potential_code
data['referrer_id'] = referrer.id
await state.set_data(data)
# Если реферальный код не найден, проверяем промокод
from app.database.crud.promocode import check_promocode_validity
promocode_check = await check_promocode_validity(db, potential_code)
if promocode_check["valid"]:
# Промокод валиден - сохраняем его в state для активации после создания пользователя
data['promocode'] = potential_code
await state.set_data(data)
await message.answer(texts.t("REFERRAL_CODE_ACCEPTED", "✅ Реферальный код принят!"))
logger.info(f"✅ Реферальный код {potential_code} применен для пользователя {message.from_user.id}")
if current_state != RegistrationStates.waiting_for_referral_code.state:
language = data.get('language', DEFAULT_LANGUAGE)
texts = get_texts(language)
rules_text = await get_rules(language)
await message.answer(
rules_text,
reply_markup=get_rules_keyboard(language)
texts.t(
"PROMOCODE_ACCEPTED_WILL_ACTIVATE",
"✅ Промокод принят! Он будет активирован после завершения регистрации."
)
)
await state.set_state(RegistrationStates.waiting_for_rules_accept)
logger.info("📋 Правила отправлены после ввода реферального кода")
else:
await complete_registration(message, state, db)
logger.info(f"✅ Промокод {potential_code} сохранен для активации для пользователя {message.from_user.id}")
if current_state != RegistrationStates.waiting_for_referral_code.state:
language = data.get('language', DEFAULT_LANGUAGE)
texts = get_texts(language)
rules_text = await get_rules(language)
await message.answer(
rules_text,
reply_markup=get_rules_keyboard(language)
)
await state.set_state(RegistrationStates.waiting_for_rules_accept)
logger.info("📋 Правила отправлены после принятия промокода")
else:
await complete_registration(message, state, db)
return True
# Ни реферальный код, ни промокод не найдены
await message.answer(texts.t(
"REFERRAL_OR_PROMO_CODE_INVALID_HELP",
"❌ Неверный реферальный код или промокод.\n\n"
"💡 Если у вас есть реферальный код или промокод, убедитесь что он введен правильно.\n"
"⏭️ Для продолжения регистрации без кода используйте команду /start",
))
return True
@@ -692,31 +728,57 @@ async def process_rules_accept(
async def process_referral_code_input(
message: types.Message,
state: FSMContext,
message: types.Message,
state: FSMContext,
db: AsyncSession
):
logger.info(f"🎫 REFERRAL: Обработка реферального кода: {message.text}")
logger.info(f"🎫 REFERRAL/PROMO: Обработка кода: {message.text}")
data = await state.get_data() or {}
language = data.get('language', DEFAULT_LANGUAGE)
texts = get_texts(language)
referral_code = message.text.strip()
code = message.text.strip()
referrer = await get_user_by_referral_code(db, referral_code)
# Сначала проверяем, является ли это реферальным кодом
referrer = await get_user_by_referral_code(db, code)
if referrer:
data['referrer_id'] = referrer.id
await state.set_data(data)
await message.answer(texts.t("REFERRAL_CODE_ACCEPTED", "✅ Реферальный код принят!"))
logger.info(f"✅ Реферальный код применен")
else:
await message.answer(texts.t("REFERRAL_CODE_INVALID", "❌ Неверный реферальный код"))
logger.info(f"❌ Неверный реферальный код")
logger.info(f"✅ Реферальный код применен: {code}")
await complete_registration(message, state, db)
return
await complete_registration(message, state, db)
# Если реферальный код не найден, проверяем промокод
from app.database.crud.promocode import check_promocode_validity
promocode_check = await check_promocode_validity(db, code)
if promocode_check["valid"]:
# Промокод валиден - сохраняем его в state для активации после создания пользователя
data['promocode'] = code
await state.set_data(data)
await message.answer(
texts.t(
"PROMOCODE_ACCEPTED_WILL_ACTIVATE",
"✅ Промокод принят! Он будет активирован после завершения регистрации."
)
)
logger.info(f"✅ Промокод сохранен для активации: {code}")
await complete_registration(message, state, db)
return
# Ни реферальный код, ни промокод не найдены
await message.answer(
texts.t(
"REFERRAL_OR_PROMO_CODE_INVALID",
"❌ Неверный реферальный код или промокод"
)
)
logger.info(f"❌ Неверный код (ни реферальный, ни промокод): {code}")
return
async def process_referral_code_skip(
@@ -1157,6 +1219,29 @@ async def complete_registration(
except Exception as e:
logger.error(f"Ошибка при обработке реферальной регистрации: {e}")
# Активируем промокод если был сохранен в state
promocode_to_activate = data.get('promocode')
if promocode_to_activate:
try:
from app.handlers.promocode import activate_promocode_for_registration
promocode_result = await activate_promocode_for_registration(
db, user.id, promocode_to_activate, message.bot
)
if promocode_result["success"]:
await message.answer(
texts.t(
"PROMOCODE_ACTIVATED_AT_REGISTRATION",
"✅ Промокод активирован!\n\n{description}"
).format(description=promocode_result["description"])
)
logger.info(f"✅ Промокод {promocode_to_activate} активирован для пользователя {user.id}")
else:
logger.warning(f"⚠️ Не удалось активировать промокод {promocode_to_activate}: {promocode_result.get('error')}")
except Exception as e:
logger.error(f"❌ Ошибка при активации промокода {promocode_to_activate}: {e}")
campaign_message = await _apply_campaign_bonus_if_needed(db, user, data, texts)
try:

View File

@@ -1133,7 +1133,11 @@
"REFERRAL_CODE_APPLIED": "🎁 Referral code applied! You will receive a bonus after the first purchase.",
"REFERRAL_CODE_INVALID": "❌ Invalid referral code",
"REFERRAL_CODE_INVALID_HELP": "❌ Invalid referral code.\n\n💡 If you have a referral code, please double-check the spelling.\n⏭ To continue without a referral code, use the /start command.",
"REFERRAL_CODE_QUESTION": "\n🤝 <b>Do you have a friend's referral code?</b>\n\nIf you have a promo code or referral link, enter it now to receive a bonus!\n\nSend the code or tap \"Skip\":\n",
"REFERRAL_CODE_QUESTION": "\n🤝 <b>Do you have a referral code or promo code?</b>\n\nIf you have a promo code or referral link from a friend, enter it now to receive a bonus!\n\nSend the code or tap \"Skip\":\n",
"REFERRAL_OR_PROMO_CODE_INVALID": "❌ Invalid referral code or promo code",
"REFERRAL_OR_PROMO_CODE_INVALID_HELP": "❌ Invalid referral code or promo code.\n\n💡 If you have a referral code or promo code, please double-check the spelling.\n⏭ To continue registration without a code, use the /start command.",
"PROMOCODE_ACTIVATED_AT_REGISTRATION": "✅ Promo code activated!\n\n{description}",
"PROMOCODE_ACCEPTED_WILL_ACTIVATE": "✅ Promo code accepted! It will be activated after registration is complete.",
"REFERRAL_CODE_SKIP": "⏭️ Skip",
"REFERRAL_CODE_TITLE": "🆔 <b>Your code:</b> <code>{code}</code>",
"REFERRAL_EARNINGS_BY_TYPE_HEADER": "📈 <b>Earnings by type:</b>",

View File

@@ -1153,7 +1153,11 @@
"REFERRAL_CODE_APPLIED": "🎁 Реферальный код применен! Вы получите бонус после первой покупки.",
"REFERRAL_CODE_INVALID": "❌ Неверный реферальный код",
"REFERRAL_CODE_INVALID_HELP": "❌ Неверный реферальный код.\n\n💡 Если у вас есть реферальный код, убедитесь что он введен правильно.\n⏭ Для продолжения регистрации без реферального кода используйте команду /start",
"REFERRAL_CODE_QUESTION": "\n🤝 <b>У вас есть реферальный код от друга?</b>\n\nЕсли у вас есть промокод или реферальная ссылка от друга, введите её сейчас, чтобы получить бонус!\n\nВведите код или нажмите \"Пропустить\":\n",
"REFERRAL_CODE_QUESTION": "\n🤝 <b>У вас есть реферальный код или промокод?</b>\n\nЕсли у вас есть промокод или реферальная ссылка от друга, введите её сейчас, чтобы получить бонус!\n\nВведите код или нажмите \"Пропустить\":\n",
"REFERRAL_OR_PROMO_CODE_INVALID": "❌ Неверный реферальный код или промокод",
"REFERRAL_OR_PROMO_CODE_INVALID_HELP": "❌ Неверный реферальный код или промокод.\n\n💡 Если у вас есть реферальный код или промокод, убедитесь что он введен правильно.\n⏭ Для продолжения регистрации без кода используйте команду /start",
"PROMOCODE_ACTIVATED_AT_REGISTRATION": "✅ Промокод активирован!\n\n{description}",
"PROMOCODE_ACCEPTED_WILL_ACTIVATE": "✅ Промокод принят! Он будет активирован после завершения регистрации.",
"REFERRAL_CODE_SKIP": "⏭️ Пропустить",
"REFERRAL_CODE_TITLE": "🆔 <b>Ваш код:</b> <code>{code}</code>",
"REFERRAL_EARNINGS_BY_TYPE_HEADER": "📈 <b>Доходы по типам:</b>",