Files
autoficher/app/google_calendar.py
Omar Sánchez Pizarro ce179ebff9 Agregar logging estructurado, try-catches y envío de errores por Telegram
- Agregado logging estructurado en todos los módulos
- Implementados try-catches en todos los métodos críticos
- Errores ahora se envían automáticamente por Telegram
- Mejorado manejo de excepciones en requests HTTP
- Agregado try-catch global en main.py para errores no manejados
- Logging detallado en timenet_manager, telegram_bot, google_calendar y config_parser
2026-01-13 01:39:16 +01:00

223 lines
11 KiB
Python

import google.oauth2.credentials
import logging
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from datetime import datetime
from dateutil.relativedelta import relativedelta
import os
logger = logging.getLogger(__name__)
class GoogleCalendar:
def __init__(self):
try:
logger.info("Inicializando GoogleCalendar...")
self.creds = None
self.service = None
self.SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
self.authfile = os.path.join(os.path.dirname(__file__), '../googleoauth.json')
self.grantfile = os.path.join(os.path.dirname(__file__), '../googleoauth_grant.json')
logger.info(f"Auth file: {self.authfile}, Grant file: {self.grantfile}")
if os.path.exists(self.authfile):
try:
logger.info("Cargando credenciales desde archivo...")
self.creds = Credentials.from_authorized_user_file(self.authfile, self.SCOPES)
logger.info("Credenciales cargadas correctamente")
except ValueError as error:
logger.error(f"Error al cargar credenciales: {error}", exc_info=True)
print(f"Error al cargar credenciales: {error}")
except Exception as e:
logger.error(f"Error inesperado al cargar credenciales: {str(e)}", exc_info=True)
print(f"Error inesperado al cargar credenciales: {str(e)}")
else:
logger.info("No se encontró archivo de credenciales, se solicitará autenticación")
logger.info("Verificando autenticación...")
self.checkAuth()
logger.info("Construyendo servicio...")
self.buildService()
logger.info("GoogleCalendar inicializado correctamente")
except Exception as e:
logger.error(f"Error al inicializar GoogleCalendar: {str(e)}", exc_info=True)
print(f"Error al inicializar GoogleCalendar: {str(e)}")
raise
def serverAuth(self):
try:
logger.info("Iniciando autenticación del servidor...")
if not os.path.exists(self.grantfile):
error_msg = f"Archivo de grant no encontrado: {self.grantfile}"
logger.error(error_msg)
raise FileNotFoundError(error_msg)
flow = InstalledAppFlow.from_client_secrets_file(self.grantfile, self.SCOPES)
self.creds = flow.run_local_server(port=0)
logger.info("Autenticación del servidor completada")
except FileNotFoundError as e:
logger.error(f"Archivo de grant no encontrado: {str(e)}", exc_info=True)
raise
except Exception as e:
logger.error(f"Error durante autenticación del servidor: {str(e)}", exc_info=True)
raise
def saveAuth(self):
try:
logger.info("Guardando credenciales...")
with open(self.authfile, 'w') as token:
token.write(self.creds.to_json())
logger.info("Credenciales guardadas correctamente")
except Exception as e:
logger.error(f"Error al guardar credenciales: {str(e)}", exc_info=True)
raise
def refreshAuth(self):
try:
logger.info("Refrescando credenciales...")
self.creds.refresh(Request())
logger.info("Credenciales refrescadas correctamente")
except google.oauth2.credentials.exceptions.RefreshError as e:
logger.warning(f"Error al refrescar credenciales: {str(e)}")
raise
except Exception as e:
logger.error(f"Error inesperado al refrescar credenciales: {str(e)}", exc_info=True)
raise
def checkAuth(self):
try:
logger.info("Verificando estado de autenticación...")
if not self.creds or not self.creds.valid:
logger.info("Credenciales no válidas o no existentes")
if self.creds and self.creds.expired and self.creds.refresh_token:
try:
logger.info("Intentando refrescar credenciales expiradas...")
self.refreshAuth()
except google.oauth2.credentials.exceptions.RefreshError as rerror:
logger.warning(f"No se pudo refrescar credenciales: {str(rerror)}, solicitando nueva autenticación")
self.serverAuth()
except Exception as e:
logger.error(f"Error inesperado al refrescar: {str(e)}", exc_info=True)
self.serverAuth()
else:
logger.info("Solicitando nueva autenticación...")
self.serverAuth()
logger.info("Guardando credenciales después de autenticación...")
self.saveAuth()
else:
logger.info("Credenciales válidas")
except Exception as e:
logger.error(f"Error durante checkAuth: {str(e)}", exc_info=True)
raise
def buildService(self):
try:
logger.info("Construyendo servicio de Google Calendar...")
self.service = build('calendar', 'v3', credentials=self.creds)
logger.info("Servicio de Google Calendar construido correctamente")
except Exception as e:
logger.error(f"Error al construir servicio de Google Calendar: {str(e)}", exc_info=True)
raise
def getNow(self):
return datetime.utcnow().strftime('%Y-%m-%dT00:00:00Z')
def getNowAfterOneDay(self):
return (datetime.utcnow() + relativedelta(days=1)).strftime('%Y-%m-%dT00:00:00Z')
def getEvent(self):
try:
logger.info("Obteniendo eventos de Google Calendar...")
now = self.getNow()
now_after_one_day = self.getNowAfterOneDay()
logger.info(f"Buscando eventos entre {now} y {now_after_one_day}")
try:
logger.info("Consultando calendario autoficher...")
events_result_autoficher = self.service.events().list(
calendarId='e51d3bfb6b352c48afb8bd7a5d494599cc3eaa686800a856796306f50c6d72fd@group.calendar.google.com',
timeMin=now,
timeMax=now_after_one_day, maxResults=10, singleEvents=True,
orderBy='startTime').execute()
logger.info(f"Eventos de autoficher obtenidos: {len(events_result_autoficher.get('items', []))}")
except HttpError as e:
logger.error(f"Error HTTP al consultar calendario autoficher: {str(e)}", exc_info=True)
events_result_autoficher = {'items': []}
except Exception as e:
logger.error(f"Error inesperado al consultar calendario autoficher: {str(e)}", exc_info=True)
events_result_autoficher = {'items': []}
try:
logger.info("Consultando calendario de festivos...")
events_result_holiday = self.service.events().list(
calendarId='es.spain#holiday@group.v.calendar.google.com',
timeMin=now,
timeMax=now_after_one_day,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
logger.info(f"Eventos de festivos obtenidos: {len(events_result_holiday.get('items', []))}")
except HttpError as e:
logger.warning(f"Error HTTP al consultar calendario de festivos (puede ser normal): {str(e)}")
events_result_holiday = {'items': []}
except Exception as e:
logger.warning(f"Error inesperado al consultar calendario de festivos: {str(e)}")
events_result_holiday = {'items': []}
events = events_result_autoficher.get('items', []) + events_result_holiday.get('items', [])
logger.info(f"Total de eventos encontrados: {len(events)}")
if not events:
logger.info("No se encontraron eventos")
return False
# Prints the start and name of the next 10 events
for event in events:
try:
start = event['start'].get('dateTime', event['start'].get('date'))
end = event['end'].get('dateTime', event['end'].get('date'))
logger.debug(f"Evaluando evento: {event.get('summary', 'Sin título')} - {start} a {end}")
if datetime.fromisoformat(start).timestamp() <= datetime.now().timestamp() <= datetime.fromisoformat(
end).timestamp():
logger.info(f"Evento activo encontrado: {event.get('summary', 'Sin título')}")
if event.get('organizer', {}).get('displayName') == 'autoficher':
result = 'Forzado desde autoficher - ' + event.get("summary", "Sin Título")
logger.info(f"Evento de autoficher activo: {result}")
return result
#Desactivado por que no funcionaba muy bien ese calendario... se han puesto en el calendario autoficher los dias festivos correctos
#if 'description' in event and ("Cataluña" in event['description'] or 'Día festivo' in event[
# 'description'] or 'Celebración\n' in event['description']) and not 'Cambio de horario' in event[
# 'summary']:
# return event['summary']
except KeyError as e:
logger.warning(f"Evento sin campo requerido: {str(e)}, evento: {event.get('summary', 'Sin título')}")
continue
except ValueError as e:
logger.warning(f"Error al parsear fecha del evento: {str(e)}, evento: {event.get('summary', 'Sin título')}")
continue
except Exception as e:
logger.warning(f"Error al procesar evento: {str(e)}, evento: {event.get('summary', 'Sin título')}")
continue
logger.info("No se encontraron eventos activos")
return False
except HttpError as error:
error_msg = f'Error HTTP de Google Calendar: {error}'
logger.error(error_msg, exc_info=True)
print(f'An error occurred: {error}')
return 'Error de llamada a Google Calendar'
except Exception as e:
error_msg = f'Error inesperado en getEvent: {str(e)}'
logger.error(error_msg, exc_info=True)
print(f'Error inesperado: {error_msg}')
return 'Error de llamada a Google Calendar'