""" QR service for backend API integration """ import asyncio import logging from typing import Dict, List, Optional, Any import aiohttp from config import config logger = logging.getLogger(__name__) class QRService: """Service for QR code operations via backend API""" def __init__(self): self.api_url = config.backend_api_url self.session: Optional[aiohttp.ClientSession] = None async def __aenter__(self): """Async context manager entry""" self.session = aiohttp.ClientSession() return self async def __aexit__(self, exc_type, exc_val, exc_tb): """Async context manager exit""" if self.session: await self.session.close() async def generate_qr_codes(self, qr_type: str, value: str, count: int) -> List[str]: """ Generate QR codes via backend API Args: qr_type: Type of QR code ('reward', 'quiz', 'shop') value: Value for the QR code count: Number of QR codes to generate Returns: List of generated tokens """ if not self.session: raise RuntimeError("QRService must be used as async context manager") url = f"{self.api_url}/api/admin/qrcodes" payload = { "type": qr_type, "value": value, "count": count } try: async with self.session.post(url, json=payload) as response: if response.status == 200: data = await response.json() if data.get("success"): return data.get("data", {}).get("tokens", []) else: logger.error(f"Backend API error: {data.get('message')}") raise Exception(data.get("message", "Unknown error")) else: error_text = await response.text() logger.error(f"Backend API request failed: {response.status} - {error_text}") raise Exception(f"API request failed with status {response.status}") except aiohttp.ClientError as e: logger.error(f"Network error: {e}") raise Exception(f"Network error: {e}") async def validate_qr_token(self, token: str) -> Dict[str, Any]: """ Validate QR token via backend API Args: token: QR token to validate Returns: Validation result from backend """ if not self.session: raise RuntimeError("QRService must be used as async context manager") url = f"{self.api_url}/api/qr/validate" payload = { "payload": token } try: async with self.session.post(url, json=payload) as response: if response.status == 200: data = await response.json() if data.get("success"): return data.get("data", {}) else: logger.error(f"Validation failed: {data.get('message')}") raise Exception(data.get("message", "Validation failed")) else: error_text = await response.text() logger.error(f"Validation request failed: {response.status} - {error_text}") raise Exception(f"Validation failed with status {response.status}") except aiohttp.ClientError as e: logger.error(f"Network error during validation: {e}") raise Exception(f"Network error: {e}") def format_qr_examples(self, tokens: List[str], qr_type: str, description: str) -> str: """ Format QR code examples for display Args: tokens: List of QR tokens qr_type: Type of QR codes description: Description of the QR codes Returns: Formatted message with QR examples """ message = f"🎫 {description}\n\n" message += "📱 QR-коды содержат следующие токены:\n\n" for i, token in enumerate(tokens, 1): message += f"{i}. `{token}`\n" message += f"\n⚡ Всего сгенерировано: {len(tokens)} шт." message += f"\n📅 Срок действия: 30 дней" message += f"\n🔒 Одноразовое использование" return message def validate_qr_request(self, qr_type: str, value: str, count: int) -> tuple[bool, str]: """ Validate QR generation request parameters Args: qr_type: Type of QR code value: Value for the QR code count: Number of QR codes to generate Returns: Tuple of (is_valid, error_message) """ if not qr_type: return False, "Тип QR-кода не может быть пустым" if not value: return False, "Значение QR-кода не может быть пустым" if count <= 0 or count > 100: return False, "Количество должно быть от 1 до 100" valid_types = ["reward", "quiz", "shop"] if qr_type not in valid_types: return False, f"Недопустимый тип. Допустимые: {', '.join(valid_types)}" # Additional validation based on type if qr_type == "reward": try: amount = int(value) if amount <= 0 or amount > 1000: return False, "Сумма награды должна быть от 1 до 1000" except ValueError: return False, "Сумма награды должна быть числом" elif qr_type == "quiz": try: quiz_id = int(value) if quiz_id <= 0: return False, "ID викторины должен быть положительным числом" except ValueError: return False, "ID викторины должен быть числом" return True, ""