110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from caldav import DAVClient
|
|
import vobject
|
|
import requests
|
|
from urllib.parse import urlparse
|
|
|
|
def fetch_ics_events(url, user=None, password=None):
|
|
try:
|
|
response = requests.get(url, auth=(user, password) if user else None)
|
|
response.raise_for_status()
|
|
cal = vobject.readOne(response.text)
|
|
return [comp for comp in cal.components() if comp.name == 'VEVENT']
|
|
except Exception as e:
|
|
raise Exception(f"ICS-Fehler: {str(e)}")
|
|
|
|
def connect_caldav(url, user=None, password=None, verify_ssl=True):
|
|
try:
|
|
client = DAVClient(
|
|
url,
|
|
username=user,
|
|
password=password,
|
|
ssl_verify_cert=verify_ssl
|
|
)
|
|
target_path = urlparse(url).path
|
|
principal = client.principal()
|
|
|
|
for calendar in principal.calendars():
|
|
if target_path in str(calendar.url):
|
|
return calendar
|
|
raise Exception(f"Kalender mit Pfad '{target_path}' nicht gefunden")
|
|
except Exception as e:
|
|
raise Exception(f"CalDAV-Fehler: {str(e)}")
|
|
|
|
def sync_ics_to_caldav(module):
|
|
ics_events = fetch_ics_events(
|
|
module.params['source_url'],
|
|
module.params['source_user'],
|
|
module.params['source_password']
|
|
)
|
|
|
|
target_cal = connect_caldav(
|
|
module.params['target_url'],
|
|
module.params['target_user'],
|
|
module.params['target_password'],
|
|
module.params['verify_ssl']
|
|
)
|
|
|
|
# Bestehende Events aus dem Zielkalender einlesen und UIDs extrahieren
|
|
existing_events = {}
|
|
for event in target_cal.events():
|
|
try:
|
|
vobj = vobject.readOne(event.data)
|
|
uid = str(vobj.vevent.uid)
|
|
existing_events[uid] = event
|
|
except Exception as e:
|
|
raise Exception(f"Fehler beim Parsen eines bestehenden Events: {str(e)}")
|
|
|
|
changed = False
|
|
results = {'added': [], 'updated': [], 'removed': []}
|
|
|
|
# Neue ICS-Events einfügen oder updaten
|
|
for vevent in ics_events:
|
|
uid = str(vevent.uid)
|
|
ical_data = vevent.serialize()
|
|
|
|
if uid not in existing_events:
|
|
target_cal.add_event(ical_data)
|
|
results['added'].append(uid)
|
|
changed = True
|
|
elif ical_data != existing_events[uid].data:
|
|
existing_events[uid].data = ical_data
|
|
existing_events[uid].save()
|
|
results['updated'].append(uid)
|
|
changed = True
|
|
|
|
# Nicht mehr vorhandene Events löschen, wenn gewünscht
|
|
if module.params['purge']:
|
|
current_uids = {str(e.uid) for e in ics_events}
|
|
for uid in set(existing_events.keys()) - current_uids:
|
|
existing_events[uid].delete()
|
|
results['removed'].append(uid)
|
|
changed = True
|
|
|
|
return changed, results
|
|
|
|
def run_module():
|
|
module_args = dict(
|
|
source_url=dict(type='str', required=True),
|
|
source_user=dict(type='str', required=False, default=None),
|
|
source_password=dict(type='str', required=False, no_log=True, default=None),
|
|
target_url=dict(type='str', required=True),
|
|
target_user=dict(type='str', required=False, default=None),
|
|
target_password=dict(type='str', required=False, no_log=True, default=None),
|
|
verify_ssl=dict(type='bool', default=True),
|
|
purge=dict(type='bool', default=False)
|
|
)
|
|
|
|
module = AnsibleModule(argument_spec=module_args, supports_check_mode=False)
|
|
|
|
try:
|
|
changed, results = sync_ics_to_caldav(module)
|
|
module.exit_json(changed=changed, **results)
|
|
except Exception as e:
|
|
module.fail_json(msg=str(e))
|
|
|
|
if __name__ == '__main__':
|
|
run_module()
|