import logging from datetime import datetime from typing import Dict, Any from sqlalchemy.ext.asyncio import AsyncSession from app.database.crud.promocode import ( get_promocode_by_code, use_promocode, check_user_promocode_usage, create_promocode_use, get_promocode_use_by_user_and_code ) from app.database.crud.user import add_user_balance, get_user_by_id from app.database.crud.subscription import extend_subscription, get_subscription_by_user_id from app.database.models import PromoCodeType, SubscriptionStatus, User, PromoCode from app.services.remnawave_service import RemnaWaveService from app.services.subscription_service import SubscriptionService logger = logging.getLogger(__name__) class PromoCodeService: def __init__(self): self.remnawave_service = RemnaWaveService() self.subscription_service = SubscriptionService() async def activate_promocode( self, db: AsyncSession, user_id: int, code: str ) -> Dict[str, Any]: try: user = await get_user_by_id(db, user_id) if not user: return {"success": False, "error": "user_not_found"} promocode = await get_promocode_by_code(db, code) if not promocode: return {"success": False, "error": "not_found"} if not promocode.is_valid: if promocode.current_uses >= promocode.max_uses: return {"success": False, "error": "used"} else: return {"success": False, "error": "expired"} existing_use = await check_user_promocode_usage(db, user_id, promocode.id) if existing_use: return {"success": False, "error": "already_used_by_user"} result_description = await self._apply_promocode_effects(db, user, promocode) if promocode.type == PromoCodeType.SUBSCRIPTION_DAYS.value and promocode.subscription_days > 0: from app.utils.user_utils import mark_user_as_had_paid_subscription await mark_user_as_had_paid_subscription(db, user) logger.info(f"🎯 ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ {user.telegram_id} ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ» ΠΏΠ»Π°Ρ‚Π½ΡƒΡŽ подписку Ρ‡Π΅Ρ€Π΅Π· ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ {code}") await create_promocode_use(db, promocode.id, user_id) promocode.current_uses += 1 await db.commit() logger.info(f"βœ… ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ {user.telegram_id} Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Π» ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ {code}") promocode_data = { "code": promocode.code, "type": promocode.type, "balance_bonus_kopeks": promocode.balance_bonus_kopeks, "subscription_days": promocode.subscription_days, "max_uses": promocode.max_uses, "current_uses": promocode.current_uses, "valid_until": promocode.valid_until, } return { "success": True, "description": result_description, "promocode": promocode_data, } except Exception as e: logger.error(f"Ошибка Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Π° {code} для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ {user_id}: {e}") await db.rollback() return {"success": False, "error": "server_error"} async def _apply_promocode_effects(self, db: AsyncSession, user: User, promocode: PromoCode) -> str: effects = [] if promocode.balance_bonus_kopeks > 0: await add_user_balance( db, user, promocode.balance_bonus_kopeks, f"Бонус ΠΏΠΎ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρƒ {promocode.code}" ) balance_bonus_rubles = promocode.balance_bonus_kopeks / 100 effects.append(f"πŸ’° Баланс ΠΏΠΎΠΏΠΎΠ»Π½Π΅Π½ Π½Π° {balance_bonus_rubles}β‚½") if promocode.subscription_days > 0: from app.config import settings subscription = await get_subscription_by_user_id(db, user.id) if subscription: await extend_subscription(db, subscription, promocode.subscription_days) await self.subscription_service.update_remnawave_user(db, subscription) effects.append(f"⏰ Подписка ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π° Π½Π° {promocode.subscription_days} Π΄Π½Π΅ΠΉ") logger.info(f"βœ… Подписка ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ {user.telegram_id} ΠΏΡ€ΠΎΠ΄Π»Π΅Π½Π° Π½Π° {promocode.subscription_days} Π΄Π½Π΅ΠΉ Π² RemnaWave с Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΌΠΈ сквадами") else: from app.database.crud.subscription import create_paid_subscription trial_squads = [] try: from app.database.crud.server_squad import get_random_trial_squad_uuid trial_uuid = await get_random_trial_squad_uuid( db, settings.TRIAL_SQUAD_UUID, ) if trial_uuid: trial_squads = [trial_uuid] except Exception as error: logger.error( "НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΠΏΠΎΠ΄ΠΎΠ±Ρ€Π°Ρ‚ΡŒ сквад для подписки ΠΏΠΎ ΠΏΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄Ρƒ %s: %s", promocode.code, error, ) if getattr(settings, 'TRIAL_SQUAD_UUID', None): trial_squads = [settings.TRIAL_SQUAD_UUID] new_subscription = await create_paid_subscription( db=db, user_id=user.id, duration_days=promocode.subscription_days, traffic_limit_gb=0, device_limit=1, connected_squads=trial_squads, update_server_counters=True, ) await self.subscription_service.create_remnawave_user(db, new_subscription) effects.append(f"πŸŽ‰ ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½Π° подписка Π½Π° {promocode.subscription_days} Π΄Π½Π΅ΠΉ") logger.info(f"βœ… Π‘ΠΎΠ·Π΄Π°Π½Π° новая подписка для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ {user.telegram_id} Π½Π° {promocode.subscription_days} Π΄Π½Π΅ΠΉ с Ρ‚Ρ€ΠΈΠ°Π» сквадом {trial_squads}") if promocode.type == PromoCodeType.TRIAL_SUBSCRIPTION.value: from app.database.crud.subscription import create_trial_subscription from app.config import settings subscription = await get_subscription_by_user_id(db, user.id) if not subscription: trial_days = promocode.subscription_days if promocode.subscription_days > 0 else settings.TRIAL_DURATION_DAYS trial_subscription = await create_trial_subscription( db, user.id, duration_days=trial_days ) await self.subscription_service.create_remnawave_user(db, trial_subscription) effects.append(f"🎁 Активирована тСстовая подписка Π½Π° {trial_days} Π΄Π½Π΅ΠΉ") logger.info(f"βœ… Π‘ΠΎΠ·Π΄Π°Π½Π° Ρ‚Ρ€ΠΈΠ°Π» подписка для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ {user.telegram_id} Π½Π° {trial_days} Π΄Π½Π΅ΠΉ") else: effects.append("ℹ️ Π£ вас ΡƒΠΆΠ΅ Π΅ΡΡ‚ΡŒ активная подписка") return "\n".join(effects) if effects else "βœ… ΠŸΡ€ΠΎΠΌΠΎΠΊΠΎΠ΄ Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΠΎΠ²Π°Π½"