sno-quiz/bot/qr_service.py
2025-09-17 22:22:14 +03:00

177 lines
6.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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, ""