""" Promo Notification Service Sends email notifications to users when their preferred vehicle is added to a promotion """ import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from typing import List, Optional from sqlalchemy.orm import Session import asyncio from ..config import get_settings from ..models.user import User from ..models.car import Car settings = get_settings() def get_users_with_matching_preference( db: Session, maker_name: Optional[str] = None, model_name: Optional[str] = None ) -> List[User]: """ Find users whose promo preference matches the given maker/model Only returns users who have promo_email_enabled = True """ query = db.query(User).filter( User.promo_email_enabled == True, User.email.isnot(None) ) if maker_name: # Match maker only (for users who selected maker but any model) # or match exact maker+model query = query.filter(User.promo_preferred_maker == maker_name) if model_name: # If model is specified, also filter by model (or users with no model preference) query = query.filter( (User.promo_preferred_model == model_name) | (User.promo_preferred_model.is_(None)) | (User.promo_preferred_model == "") ) return query.all() async def send_promo_notification_email( email: str, car: Car, language: str = "en" ) -> bool: """Send promo notification email to a single user""" try: # Email templates by language subjects = { "en": f"AutonetSellCar - Your Preferred Vehicle is Now on Promotion!", "ko": f"AutonetSellCar - 관심 차량이 프로모션에 등록되었습니다!", "mn": f"AutonetSellCar - Таны сонирхсон машин урамшуулалд орлоо!", "ru": f"AutonetSellCar - Ваш желаемый автомобиль теперь в акции!" } car_name = car.car_name or "Unknown Vehicle" car_year = car.year or "" car_mileage = f"{car.mileage:,}km" if car.mileage else "" car_url = f"https://autonetsellcar.com/cars/{car.id}" bodies = { "en": f""" Hello, Great news! A vehicle matching your preference is now available on promotion! Vehicle: {car_name} Year: {car_year} Mileage: {car_mileage} View this vehicle: {car_url} As a promoted vehicle, you can view all photos for free! Best regards, AutonetSellCar Team --- You received this email because you signed up for promotion notifications. To unsubscribe, update your preferences in your account settings. """, "ko": f""" 안녕하세요, 좋은 소식입니다! 관심 차량이 프로모션에 등록되었습니다! 차량: {car_name} 연식: {car_year} 주행거리: {car_mileage} 차량 보기: {car_url} 프로모션 차량은 모든 사진을 무료로 보실 수 있습니다! 감사합니다, AutonetSellCar 팀 --- 프로모션 알림 신청으로 이 이메일을 받으셨습니다. 수신 거부를 원하시면 계정 설정에서 변경해 주세요. """, "mn": f""" Сайн байна уу, Сайхан мэдээ! Таны сонирхсон машин урамшуулалд орлоо! Машин: {car_name} Он: {car_year} Гүйлт: {car_mileage} Машин үзэх: {car_url} Урамшуулалтай машины бүх зургийг үнэгүй үзэх боломжтой! Хүндэтгэсэн, AutonetSellCar баг --- Та урамшуулалын мэдэгдэл хүлээн авахаар бүртгүүлсэн тул энэ имэйлийг хүлээн авлаа. Татгалзахыг хүсвэл данс тохиргооноос өөрчилнө үү. """, "ru": f""" Здравствуйте, Отличные новости! Автомобиль, соответствующий вашим предпочтениям, теперь в акции! Автомобиль: {car_name} Год: {car_year} Пробег: {car_mileage} Посмотреть автомобиль: {car_url} Как акционный автомобиль, вы можете просмотреть все фотографии бесплатно! С уважением, Команда AutonetSellCar --- Вы получили это письмо, потому что подписались на уведомления об акциях. Чтобы отписаться, измените настройки в вашем аккаунте. """ } subject = subjects.get(language, subjects["en"]) body = bodies.get(language, bodies["en"]) # Check if SMTP is configured if not settings.SMTP_USER or not settings.SMTP_PASSWORD: # Development mode - just log print(f"[DEV] Promo notification email for {email}: {car_name}") return True # Send actual email msg = MIMEMultipart() msg['From'] = f"{settings.SMTP_FROM_NAME} <{settings.SMTP_FROM_EMAIL or settings.SMTP_USER}>" msg['To'] = email msg['Subject'] = subject msg.attach(MIMEText(body, 'plain', 'utf-8')) with smtplib.SMTP(settings.SMTP_HOST, settings.SMTP_PORT) as server: server.starttls() server.login(settings.SMTP_USER, settings.SMTP_PASSWORD) server.send_message(msg) print(f"[INFO] Promo notification sent to {email} for car {car.id}") return True except Exception as e: print(f"[ERROR] Failed to send promo notification to {email}: {e}") return False async def notify_users_for_promo_vehicle( db: Session, car: Car ) -> int: """ Notify all users whose preference matches the car being promoted Returns the number of notifications sent """ # Get maker and model names from car maker_name = car.maker.name if car.maker else None model_name = car.model.name if car.model else None if not maker_name: print(f"[INFO] Car {car.id} has no maker, skipping promo notifications") return 0 # Find matching users matching_users = get_users_with_matching_preference( db=db, maker_name=maker_name, model_name=model_name ) if not matching_users: print(f"[INFO] No users with matching preference for {maker_name} {model_name}") return 0 print(f"[INFO] Found {len(matching_users)} users with preference for {maker_name} {model_name}") # Send notifications sent_count = 0 for user in matching_users: if user.email: # Determine language based on user's country language = "en" # Default if user.country: country_lower = user.country.lower() if country_lower in ["korea", "south korea", "kr", "한국"]: language = "ko" elif country_lower in ["mongolia", "mn", "монгол"]: language = "mn" elif country_lower in ["russia", "ru", "россия"]: language = "ru" success = await send_promo_notification_email( email=user.email, car=car, language=language ) if success: sent_count += 1 return sent_count def notify_users_for_promo_vehicle_sync(db: Session, car: Car) -> int: """Synchronous wrapper for notify_users_for_promo_vehicle""" try: loop = asyncio.get_event_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) if loop.is_running(): # If already in async context, create a task import concurrent.futures with concurrent.futures.ThreadPoolExecutor() as executor: future = executor.submit( asyncio.run, notify_users_for_promo_vehicle(db, car) ) return future.result() else: return loop.run_until_complete(notify_users_for_promo_vehicle(db, car))