238 lines
7.9 KiB
Python
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))
|