dotfiles/.local/bin/list_events_v5_json.py
2025-05-28 18:33:04 +02:00

238 lines
7.9 KiB
Python

import caldav
from caldav import DAVClient
from caldav import error # Importar correctamente
import datetime
import locale
import json
import pytz
import os
import logging
# Configurar el registro de logs
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Establecer el locale a español para formatear fechas
try:
locale.setlocale(locale.LC_TIME, "es_ES.utf8")
except locale.Error:
logger.error("El locale 'es_ES.utf8' no está disponible en este sistema.")
exit()
# Ruta al archivo de credenciales
credentials_file = '/home/teraflops/apikeys/icloud'
# Verificar si el archivo existe
if not os.path.isfile(credentials_file):
logger.error(f"El archivo de credenciales '{credentials_file}' no existe.")
exit()
# Leer las credenciales del archivo
credentials = {}
with open(credentials_file, 'r') as f:
for line in f:
line = line.strip()
if line and not line.startswith('#'):
key, value = line.split('=', 1)
# Remover comillas y espacios
key = key.strip()
value = value.strip().strip("'").strip('"')
credentials[key] = value
# Obtener el Apple ID y la contraseña
APPLE_ID = credentials.get('ICLOUD_APPLE_ID')
APP_PASSWORD = credentials.get('ICLOUD_APP_PASSWORD')
if not APPLE_ID or not APP_PASSWORD:
logger.error(
"Las credenciales de Apple no están configuradas correctamente en el archivo.")
exit()
# Configuración de la zona horaria
timezone = pytz.timezone('Europe/Madrid') # Cambia esto a tu zona horaria
# Inicializar el cliente con verificación SSL
client = DAVClient(
url='https://caldav.icloud.com/',
username=APPLE_ID,
password=APP_PASSWORD,
# ssl_verify_cert=True # Asegúrate de que la verificación SSL esté habilitada
)
# Obtener el principal (usuario de iCloud)
principal = client.principal()
# Listar todos los calendarios disponibles
calendars = principal.calendars()
# Mostrar los nombres de los calendarios disponibles
logger.info("Calendarios disponibles:")
for calendar in calendars:
logger.info(f"- '{calendar.name}'")
# Definir los nombres de los calendarios deseados
DESIRED_CALENDARS = ["Trabajo", "Recordatorios ⚠️"]
# Inicializar una lista para los calendarios seleccionados
selected_calendars = []
# Iterar a través de los calendarios para encontrar los deseados
for calendar in calendars:
for desired_name in DESIRED_CALENDARS:
if calendar.name.strip().lower() == desired_name.strip().lower():
selected_calendars.append(calendar)
logger.info(f"Calendario seleccionado: '{calendar.name}'")
break # Salimos del bucle interno una vez que encontramos una coincidencia
# Verificar si se encontraron los calendarios deseados
if not selected_calendars:
logger.error(f"No se encontraron los calendarios especificados: {
', '.join(DESIRED_CALENDARS)}")
exit()
# Definir el rango de tiempo: ahora hasta 7 días después
now = datetime.datetime.now(timezone)
in_7_days = now + datetime.timedelta(days=7)
# Inicializar una lista para todos los eventos
all_events = []
# Obtener eventos de cada calendario seleccionado utilizando calendar.search
for calendar in selected_calendars:
try:
events = calendar.search(
start=now,
end=in_7_days,
event=True,
expand=True
)
logger.info(f"Eventos obtenidos del calendario '{
calendar.name}': {len(events)}")
# Agregar una tupla con el evento y el nombre del calendario
for event in events:
all_events.append((event, calendar.name))
except error.CalDAVError as e:
logger.error(f"Error al obtener eventos del calendario '{
calendar.name}': {e}")
continue
# Función clave para ordenar los eventos
def sort_key(event_tuple):
event = event_tuple[0]
dtstart = event.instance.vevent.dtstart.value
if isinstance(dtstart, datetime.date) and not isinstance(dtstart, datetime.datetime):
# Convertir datetime.date a datetime.datetime a medianoche
dtstart = datetime.datetime.combine(
dtstart, datetime.time(0, 0), tzinfo=timezone)
elif dtstart.tzinfo is None:
# Si dtstart es datetime pero sin información de zona horaria
dtstart = timezone.localize(dtstart)
else:
# Asegurarse de que dtstart esté en la zona horaria correcta
dtstart = dtstart.astimezone(timezone)
return dtstart
# Ordenar los eventos usando la función sort_key
all_events.sort(key=sort_key)
# Preparar eventos para el tooltip con formato de fecha en español
event_list = []
if all_events:
for event, calendar_name in all_events:
try:
event_details = event.vobject_instance
vevent = event_details.vevent
# Título del evento
summary = vevent.summary.value if hasattr(
vevent, 'summary') else 'Sin título'
# Fecha y hora de inicio
dtstart = vevent.dtstart.value if hasattr(
vevent, 'dtstart') else None
# Fecha y hora de fin
dtend = vevent.dtend.value if hasattr(vevent, 'dtend') else None
# Todo el día
all_day = isinstance(dtstart, datetime.date) and not isinstance(
dtstart, datetime.datetime)
# Formatear fecha y hora de inicio
if dtstart:
if all_day:
dtstart_str = dtstart.strftime(
'%A %d de %B') + " (Todo el día)"
else:
if dtstart.tzinfo is None:
dtstart = timezone.localize(dtstart)
else:
dtstart = dtstart.astimezone(timezone)
dtstart_str = dtstart.strftime('%A %d de %B %H:%M')
else:
dtstart_str = ''
# Formatear fecha y hora de fin
if dtend:
if all_day:
dtend_str = dtend.strftime(
'%A %d de %B') + " (Todo el día)"
else:
if dtend.tzinfo is None:
dtend = timezone.localize(dtend)
else:
dtend = dtend.astimezone(timezone)
dtend_str = dtend.strftime('%A %d de %B %H:%M')
else:
dtend_str = ''
# Construir el texto del evento
event_text = f"📅 {summary} ({calendar_name})"
event_text += f"\n 🕒 Inicio: {dtstart_str}" if dtstart_str else ''
event_text += f"\n 🕔 Fin: {dtend_str}" if dtend_str else ''
event_text += "\n 📆 Todo el día: Sí" if all_day else ''
event_list.append(event_text)
except Exception as e:
logger.error(f"Error al procesar el evento: {e}")
continue
else:
event_list.append("No hay eventos en los próximos 7 días")
# **Modificar la creación del JSON de salida para Waybar**
# 1. Mostrar la fecha actual en español en la barra
# 2. Si hay eventos, añadir un icono o número junto a la fecha
# Definir el icono que se mostrará si hay eventos
EVENT_ICON = "󱢞" # Puedes cambiar este icono por otro que prefieras
# Calcular el número de eventos próximos
event_count = len(all_events)
# Construir el texto para la barra
if event_count > 0:
# Puedes optar por mostrar solo el número, el icono, o ambos
# Ejemplos:
# Solo número: text = "Fecha | 3"
# Solo icono: text = "Fecha 📅"
# Icono y número: text = "Fecha 📅3"
# Aquí mostramos el icono y el número
text = f"{now.strftime('%A %d de %B %H:%M')} {EVENT_ICON}{event_count}"
else:
text = now.strftime('%A %d de %B %H:%M')
# Crear el JSON de salida para Waybar con tooltip en español
output = {
"text": text, # Texto modificado
"tooltip": "\n\n".join(event_list),
"class": "calendar"
}
# Salida JSON
print(json.dumps(output))