contacts-to-remind/contacts_to_remind.py
2025-04-18 13:16:23 +02:00

169 lines
6.8 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
contacts_to_remind.py - Exportiert Geburtstage aus Apple Contacts in eine Remind-Datei
Dieses Script exportiert Geburtstage aus Apple Contacts und erstellt eine
Remind-Datei (geburtstage.rem) für Geburtstagserinnerungen.
Creation Date: 2025-04-18
Revision: 1.3
"""
import os
import sys
import datetime
import argparse
from pathlib import Path
try:
# Verwenden von pyobjc-framework-Contacts für den Zugriff auf Apple Contacts
from Contacts import CNContactStore, CNContactFetchRequest, CNContactBirthdayKey, CNContactGivenNameKey, CNContactFamilyNameKey
import objc
except ImportError:
print("Die 'pyobjc-framework-Contacts' Bibliothek wird benötigt. Bitte installieren Sie sie mit:")
print("pip install pyobjc-framework-Contacts")
print("oder")
print("uv pip install pyobjc-framework-Contacts")
sys.exit(1)
def get_birthdays_from_contacts():
"""Ruft alle Kontakte mit Geburtstagsinformationen aus Apple Contacts ab."""
store = CNContactStore.alloc().init()
# Anfrage für Kontakte mit Geburtstagsinformationen erstellen
keys_to_fetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey]
request = CNContactFetchRequest.alloc().initWithKeysToFetch_(keys_to_fetch)
birthdays = []
placeholder_contacts = [] # Kontakte mit dem Platzhalter-Jahr 1604
# Callback-Funktion für jeden gefundenen Kontakt
def handle_contact(contact, stop):
birthday = contact.birthday()
if birthday:
given_name = contact.givenName() or ""
family_name = contact.familyName() or ""
name = f"{given_name} {family_name}".strip()
# Sicherstellen, dass wir tatsächlich Integer-Werte haben
try:
month = birthday.month()
day = birthday.day()
# Prüfen, ob ein Jahr existiert
try:
year = birthday.year()
# In Apple Contacts ist 1604 oft ein Platzhalter für "kein Jahr"
if year == 1604:
placeholder_contacts.append({
'name': name,
'month': month,
'day': day
})
year = None
except:
year = None
# Nur hinzufügen, wenn gültige Monat- und Tagwerte vorhanden sind
if isinstance(month, int) and isinstance(day, int):
birthdays.append({
'name': name,
'month': month,
'day': day,
'year': year if isinstance(year, int) and year != 1604 else None
})
except:
# Fehler beim Extrahieren der Datumsinformationen
pass
# Anfrage ausführen mit der Callback-Funktion
error = objc.nil
store.enumerateContactsWithFetchRequest_error_usingBlock_(request, error, handle_contact)
return birthdays, placeholder_contacts
def create_remind_file(birthdays, output_file='geburtstage.rem'):
"""Erstellt eine Remind-Datei mit den Geburtstagsinformationen."""
current_year = datetime.datetime.now().year
with open(output_file, 'w', encoding='utf-8') as f:
f.write("# Geburtstage aus Apple Contacts\n")
f.write("# Erstellt am: {}\n".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
f.write("# Enthält Erinnerungen am Geburtstag mit 7-Tage-Vorwarnung\n\n")
# Add the since() function definition
f.write("if !defined(\"init\")\n")
f.write(" set init 1\n")
f.write(" fset since(x) ord(year(trigdate())-x)\n")
f.write("endif\n\n")
for person in sorted(birthdays, key=lambda x: (x['month'], x['day'])):
name = person['name']
month = person['month']
day = person['day']
birth_year = person['year']
# Verwende since() Funktion, wenn Geburtsjahr verfügbar ist
if birth_year and isinstance(birth_year, int) and birth_year > 1900 and birth_year < current_year:
# Erinnerung mit 7-Tage-Vorwarnung
remind_entry = f'REM {day} {month_to_abbr(month)} +7 MSG {name}s [since({birth_year})] Geburtstag ist am {day}. {month_to_abbr(month)} %b\n'
f.write(remind_entry)
else:
# Kontakte ohne Geburtsjahr, Erinnerung mit 7-Tage-Vorwarnung
remind_entry = f'REM {day} {month_to_abbr(month)} +7 MSG {name}s Geburtstag ist am {day}. {month_to_abbr(month)} %b\n'
f.write(remind_entry)
def month_to_abbr(month_num):
"""Konvertiert eine Monatsnummer (1-12) in die dreistellige Abkürzung für Remind."""
months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
return months[month_num - 1]
def print_placeholder_report(placeholder_contacts):
"""Gibt einen Bericht über Kontakte mit dem Platzhalter-Jahr 1604 aus."""
if not placeholder_contacts:
print("\nKeine Kontakte mit fehlendem Geburtsjahr gefunden.")
return
print("\nKontakte mit fehlendem Geburtsjahr (1604):")
print("-----------------------------------------")
for person in sorted(placeholder_contacts, key=lambda x: x['name']):
print(f"{person['name']} ({person['day']}.{person['month']}.)")
print("\nHinweis: Bei diesen Kontakten wird kein Alter berechnet.")
def main():
"""Hauptfunktion des Scripts."""
parser = argparse.ArgumentParser(description='Exportiert Geburtstage aus Apple Contacts in eine Remind-Datei.')
parser.add_argument('-o', '--output', default='geburtstage.rem',
help='Ausgabedatei (Standard: geburtstage.rem)')
parser.add_argument('-v', '--verbose', action='store_true',
help='Ausführliche Ausgabe')
args = parser.parse_args()
try:
if args.verbose:
print("Lese Kontakte aus Apple Contacts...")
birthdays, placeholder_contacts = get_birthdays_from_contacts()
if args.verbose:
print(f"{len(birthdays)} Kontakte mit Geburtstagsinformationen gefunden.")
create_remind_file(birthdays, args.output)
print(f"Remind-Datei erfolgreich erstellt: {args.output}")
print(f"Insgesamt {len(birthdays)} Geburtstage exportiert.")
# Bericht über Kontakte mit fehlendem Geburtsjahr ausgeben
print_placeholder_report(placeholder_contacts)
except Exception as e:
print(f"Fehler: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()