From a149988a3d5f3b9818e4192deff5c3178d3e3595 Mon Sep 17 00:00:00 2001 From: midas Date: Wed, 30 Apr 2025 08:41:57 +0000 Subject: [PATCH] =?UTF-8?q?library/my=5Fcalendar=5Fsync.py=20hinzugef?= =?UTF-8?q?=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/my_calendar_sync.py | 112 ++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 library/my_calendar_sync.py diff --git a/library/my_calendar_sync.py b/library/my_calendar_sync.py new file mode 100644 index 0000000..6113a13 --- /dev/null +++ b/library/my_calendar_sync.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +from ansible.module_utils.basic import AnsibleModule +import subprocess +import sys + +# Auto-Install, falls caldav fehlt +try: + from caldav import DAVClient + import vobject +except ImportError: + subprocess.check_call([sys.executable, "-m", "pip", "install", "caldav", "vobject"]) + from caldav import DAVClient + import vobject + + +def connect_calendar(url, user=None, password=None): + client = DAVClient(url, username=user, password=password) if user else DAVClient(url) + principal = client.principal() + calendars = principal.calendars() + if not calendars: + raise Exception(f"Kein Kalender gefunden unter {url}") + return calendars[0] + + +def get_event_uid_map(calendar): + uid_map = {} + for event in calendar.events(): + try: + cal = vobject.readOne(event.data) + uid = cal.vevent.uid.value + uid_map[uid] = event + except Exception: + continue + return uid_map + + +def sync_calendars(source_cal, target_cal): + src_events = get_event_uid_map(source_cal) + tgt_events = get_event_uid_map(target_cal) + + changed = False + added, updated, removed = [], [], [] + + for uid, src_event in src_events.items(): + src_data = src_event.data + if uid not in tgt_events: + target_cal.add_event(src_data) + added.append(uid) + changed = True + elif src_data != tgt_events[uid].data: + tgt_events[uid].delete() + target_cal.add_event(src_data) + updated.append(uid) + changed = True + + for uid in set(tgt_events.keys()) - set(src_events.keys()): + tgt_events[uid].delete() + removed.append(uid) + changed = True + + return changed, added, updated, removed + + +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, default=None, no_log=True), + target_url=dict(type='str', required=True), + target_user=dict(type='str', required=True), + target_password=dict(type='str', required=True, no_log=True), + ) + + result = dict( + changed=False, + added=[], + updated=[], + removed=[], + ) + + module = AnsibleModule(argument_spec=module_args, supports_check_mode=False) + + try: + source_cal = connect_calendar( + module.params['source_url'], + module.params['source_user'], + module.params['source_password'] + ) + target_cal = connect_calendar( + module.params['target_url'], + module.params['target_user'], + module.params['target_password'] + ) + changed, added, updated, removed = sync_calendars(source_cal, target_cal) + + result['changed'] = changed + result['added'] = added + result['updated'] = updated + result['removed'] = removed + + module.exit_json(**result) + except Exception as e: + module.fail_json(msg=str(e), **result) + + +def main(): + run_module() + + +if __name__ == '__main__': + main()