126 lines
4.5 KiB
Python
126 lines
4.5 KiB
Python
import requests
|
|
import logging
|
|
import time
|
|
from typing import Dict, Any
|
|
from dataclasses import dataclass, asdict
|
|
from datetime import datetime
|
|
|
|
@dataclass
|
|
class WebhookConfig:
|
|
url: str
|
|
method: str
|
|
headers: Dict[str, str]
|
|
retry: Dict[str, int]
|
|
|
|
class WebhookError(Exception):
|
|
"""Clase para errores relacionados con webhooks"""
|
|
pass
|
|
|
|
class WebhookHandler:
|
|
def __init__(self, webhook_config: dict):
|
|
self.config = webhook_config
|
|
self.logger = logging.getLogger(__name__)
|
|
self.session = requests.Session()
|
|
|
|
def prepare_payload(self, qr_data: dict, scan_count: int) -> dict:
|
|
"""Prepara el payload para el webhook"""
|
|
return {
|
|
"event_type": "qr_detected",
|
|
"timestamp": datetime.now().isoformat(),
|
|
"qr_data": {
|
|
"content": qr_data.get('data'),
|
|
"type": qr_data.get('code_type'),
|
|
"scan_count": scan_count,
|
|
"coordinates": qr_data.get('coordinates'),
|
|
"detection_timestamp": qr_data.get('timestamp')
|
|
},
|
|
"device_info": {
|
|
"id": "qr_detector_01", # Puedes configurar esto
|
|
"location": "main_entrance" # Puedes configurar esto
|
|
}
|
|
}
|
|
|
|
def send_webhook(self, endpoint: WebhookConfig, payload: dict) -> bool:
|
|
"""Envía el webhook con reintentos"""
|
|
max_attempts = endpoint.retry['max_attempts']
|
|
delay = endpoint.retry['delay_seconds']
|
|
|
|
for attempt in range(max_attempts):
|
|
try:
|
|
response = self.session.request(
|
|
method=endpoint.method,
|
|
url=endpoint.url,
|
|
json=payload,
|
|
headers=endpoint.headers,
|
|
timeout=10
|
|
)
|
|
|
|
if response.ok:
|
|
self.logger.info(f"Webhook enviado exitosamente a {endpoint.url}")
|
|
return True
|
|
|
|
self.logger.warning(
|
|
f"Intento {attempt + 1}/{max_attempts} fallido. "
|
|
f"Status: {response.status_code}, "
|
|
f"Response: {response.text[:100]}"
|
|
)
|
|
|
|
except requests.RequestException as e:
|
|
self.logger.error(f"Error en intento {attempt + 1}/{max_attempts}: {e}")
|
|
|
|
if attempt < max_attempts - 1:
|
|
time.sleep(delay)
|
|
|
|
self.logger.error(f"Todos los intentos de webhook fallaron para {endpoint.url}")
|
|
return False
|
|
|
|
def notify(self, qr_data: dict, scan_count: int) -> None:
|
|
"""Maneja el envío de notificaciones basado en las reglas configuradas"""
|
|
try:
|
|
# Verificar reglas de notificación
|
|
is_new = scan_count == 1
|
|
should_notify = (
|
|
(is_new and self.config['notification_rules']['send_on_new']) or
|
|
(not is_new and self.config['notification_rules']['send_on_repeat'])
|
|
)
|
|
|
|
if not should_notify:
|
|
return
|
|
|
|
payload = self.prepare_payload(qr_data, scan_count)
|
|
|
|
for endpoint_config in self.config['endpoints']:
|
|
endpoint = WebhookConfig(
|
|
url=endpoint_config['url'],
|
|
method=endpoint_config['method'],
|
|
headers=endpoint_config['headers'],
|
|
retry=endpoint_config['retry']
|
|
)
|
|
|
|
self.send_webhook(endpoint, payload)
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error procesando notificación: {e}")
|
|
raise WebhookError(f"Error en notificación: {e}")
|
|
|
|
def test_connection(self) -> bool:
|
|
"""Prueba las conexiones a todos los endpoints configurados"""
|
|
test_payload = {
|
|
"event_type": "connection_test",
|
|
"timestamp": datetime.now().isoformat()
|
|
}
|
|
|
|
all_successful = True
|
|
|
|
for endpoint_config in self.config['endpoints']:
|
|
endpoint = WebhookConfig(
|
|
url=endpoint_config['url'],
|
|
method=endpoint_config['method'],
|
|
headers=endpoint_config['headers'],
|
|
retry=endpoint_config['retry']
|
|
)
|
|
|
|
if not self.send_webhook(endpoint, test_payload):
|
|
all_successful = False
|
|
|
|
return all_successful |