This commit is contained in:
2026-05-06 08:41:06 +02:00
parent 8f0674b6c2
commit 3d98c9ec9c
38 changed files with 87867 additions and 0 deletions
@@ -0,0 +1,26 @@
# Secrets — niemals committen!
.env
*.env
# Python
__pycache__/
*.py[cod]
*.pyo
.venv/
venv/
# Editor
.idea/
.vscode/
*.swp
*~
# macOS
.DS_Store
# Logs
*.log
/logs/
# vdirsyncer Status (lokal)
status/
@@ -0,0 +1,279 @@
# openclaw-mailbox-cal
**OpenClaw Skill-Repository** — mailbox.org Kalender und Aufgaben auf dem Raspberry Pi
Telegram-Bot-Befehle für Kalendertermine (khal), Tasks (todoman) und automatischen CalDAV-Sync (vdirsyncer).
---
## Schnellstart
```bash
# 1. .env befüllen
cp assets/.env.example .env && nano .env
# 2. Installation
chmod +x scripts/install.sh && bash scripts/install.sh
# 3. Selbsttest
bash scripts/test_setup.sh
```
---
## Projektstruktur
```
openclaw-mailbox-cal/
├── SKILL.md ← OpenClaw Skill-Definition
├── README.md ← Diese Datei
├── assets/
│ └── .env.example ← Umgebungsvariablen-Vorlage
├── config/
│ ├── vdirsyncer.conf ← vdirsyncer CalDAV-Konfiguration
│ ├── khal.conf ← khal Kalender-Konfiguration
│ └── todoman.conf ← todoman Aufgaben-Konfiguration
├── scripts/
│ ├── install.sh ← Installations-Script (Raspi)
│ ├── test_setup.sh ← Selbsttest aller Komponenten
│ └── openclaw_skill.py ← OpenClaw/Telegram Bot Handler
└── systemd/
├── vdirsyncer-sync.service ← systemd Service Unit
├── vdirsyncer-sync.timer ← systemd Timer (alle 15 Min.)
└── vdirsyncer-sync.env ← Umgebungsvariablen für systemd
```
---
## Setup-Schritte (detailliert)
### Schritt 1 — Voraussetzungen
- Raspberry Pi mit Raspberry Pi OS Bookworm (64-bit empfohlen) oder Ubuntu 22.04+ ARM
- mailbox.org-Konto
- App-Passwort in mailbox.org anlegen:
`Einstellungen → Sicherheit → App-Passwörter → Neu erstellen`
- OpenClaw installiert: [openclaw.dev](https://openclaw.dev)
- Telegram Bot-Token via [@BotFather](https://t.me/BotFather)
### Schritt 2 — .env konfigurieren
```bash
cp assets/.env.example .env
nano .env
```
Mindestens diese Werte setzen:
```dotenv
MAILBOX_USER=vorname.nachname@mailbox.org
MAILBOX_PASS=app-passwort-xyz
```
### Schritt 3 — install.sh ausführen
```bash
chmod +x scripts/install.sh
bash scripts/install.sh
```
Das Script installiert automatisch:
- `vdirsyncer` — CalDAV-Sync-Tool
- `khal` — CLI-Kalender
- `todoman` — CLI-Aufgabenverwaltung
- `python3-pass` — Passwort-Store-Integration
- `gettext-base` — envsubst für Konfigurationen
Anschließend:
- Alle Konfigurationsdateien werden deployed
- systemd User-Timer wird aktiviert
- Initialer Sync wird durchgeführt
### Schritt 4 — OpenClaw Skill registrieren
Füge in `~/.config/openclaw/config.yaml` hinzu:
```yaml
skills:
- name: mailbox-cal
path: ~/openclaw-mailbox-cal
entry: scripts/openclaw_skill.py
commands:
- termine
- aufgaben
- neu_aufgabe
- sync
- status
```
```bash
systemctl --user restart openclaw
```
### Schritt 5 — Telegram testen
Sende an deinen Bot:
```
/termine
/aufgaben
/sync
/status
```
---
## Test-Befehle
### Vollständiger Selbsttest
```bash
bash scripts/test_setup.sh
```
### vdirsyncer
```bash
# Verbindung testen und Sammlungen entdecken
vdirsyncer discover calendars
vdirsyncer discover tasks
# Synchronisation manuell starten
vdirsyncer sync
# Nur Kalender synchronisieren
vdirsyncer sync calendars
# Verbose-Ausgabe
vdirsyncer -v DEBUG sync
```
### khal
```bash
# Termine heute
khal list
# Termine nächste 7 Tage
khal list today 7d
# Agenda-Ansicht
khal agenda
# Interaktiver Kalender
ikhal
# Ereignis hinzufügen (interaktiv)
khal new
# Ereignis direkt anlegen
khal new 2025-06-15 14:00 15:00 "Arzttermin" --calendar mailbox_kalender
# Cache neu aufbauen
khal rebuild-cache
# Konfiguration anzeigen
khal printformats
```
### todoman
```bash
# Alle offenen Aufgaben
todo list
# Alle Aufgaben inkl. erledigt
todo list --all
# Neue Aufgabe
todo new "Bericht schreiben"
# Aufgabe mit Fälligkeitsdatum
todo new --due 2025-06-30 "Bericht abgeben"
# Aufgabe mit Priorität (1=hoch, 9=niedrig)
todo new --priority 1 "Dringend: Server patchen"
# Aufgabe als erledigt markieren (ID aus 'todo list')
todo done 1
# Aufgabe löschen
todo delete 2
# Aufgaben-Listen anzeigen
todo lists
```
### systemd
```bash
# Timer-Status
systemctl --user status vdirsyncer-sync.timer
# Service manuell starten (einmaliger Sync)
systemctl --user start vdirsyncer-sync.service
# Alle User-Timer anzeigen
systemctl --user list-timers
# Live-Log verfolgen
journalctl --user -u vdirsyncer-sync.service -f
# Log der letzten 50 Zeilen
journalctl --user -u vdirsyncer-sync.service -n 50
# Timer deaktivieren
systemctl --user disable --now vdirsyncer-sync.timer
```
### Python-Skill direkt testen
```bash
python3 scripts/openclaw_skill.py
```
---
## mailbox.org CalDAV-Endpunkte
| Zweck | URL |
|---|---|
| CalDAV-Basis | `https://dav.mailbox.org/caldav/<USER>/` |
| Kalender-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Kalender/` |
| Aufgaben-Sammlung | `https://dav.mailbox.org/caldav/<USER>/Aufgaben/` |
| CardDAV (Kontakte) | `https://dav.mailbox.org/carddav/<USER>/` |
Entdeckung aller Sammlungen:
```bash
curl -u "USER:PASS" -X PROPFIND https://dav.mailbox.org/caldav/ \
-H "Depth: 1" -H "Content-Type: application/xml"
```
---
## Sicherheit
| Maßnahme | Status |
|---|---|
| App-Passwort statt Hauptpasswort | ✅ Empfohlen |
| vdirsyncer-Config: chmod 600 | ✅ Automatisch via install.sh |
| .env nicht in Git | ✅ In .gitignore eingetragen |
| HTTPS für CalDAV | ✅ dav.mailbox.org erzwingt TLS |
| pass(1)-Integration | Optional via python3-pass |
---
## Fehlerbehebung
**vdirsyncer: `[Errno 111] Connection refused`**
→ Netzwerkverbindung prüfen: `ping dav.mailbox.org`
**vdirsyncer: `Conflict!`**
→ Beide Seiten wurden geändert. Konflikt manuell lösen oder `conflict_resolution = "b wins"` in vdirsyncer.conf setzen.
**khal: `No calendars configured`**
`khal rebuild-cache` ausführen; Pfad in `~/.config/khal/config` prüfen.
**todoman: `Error: No lists found`**
`path`-Eintrag in `~/.config/todoman/config.cfg` prüfen; Verzeichnis `~/.local/share/vdirsyncer/tasks/` muss existieren und `.ics`-Dateien enthalten.
**systemd-Timer läuft nach Reboot nicht**
`sudo loginctl enable-linger $USER` ausführen (User-Session ohne Login).
---
## Lizenz
MIT — Freie Nutzung, Veränderung und Weitergabe.
@@ -0,0 +1,197 @@
---
name: mailbox-cal
description: "OpenClaw Telegram-Bot-Skill für mailbox.org Kalender und Aufgaben auf dem Raspberry Pi. Synchronisiert CalDAV-Kalender und VTODO-Aufgaben via vdirsyncer, zeigt Termine mit khal und verwaltet Tasks mit todoman. Befehle: /termine, /aufgaben, /neu_aufgabe, /sync, /status. Lade diesen Skill wenn: der Nutzer Kalender-Einträge oder Aufgaben aus mailbox.org per Telegram abfragen, anlegen oder synchronisieren möchte."
license: MIT
compatibility: "Raspberry Pi OS Bookworm / Ubuntu 22.04+ ARM. Benötigt: vdirsyncer >= 0.19, khal >= 0.11, todoman >= 4.3, python3 >= 3.10. mailbox.org CalDAV-Zugang erforderlich."
metadata:
author: lonely.wolf.64
version: '1.0'
platform: Raspberry Pi
caldav_server: mailbox.org
---
# mailbox.org Kalender + Aufgaben Skill
## Übersicht
Dieser OpenClaw-Skill synchronisiert deinen mailbox.org-Kalender und deine Aufgaben über CalDAV auf einen Raspberry Pi und stellt sie über Telegram-Bot-Befehle bereit. Die Synchronisation läuft automatisch alle 15 Minuten via systemd-Timer.
**Architektur:**
```
mailbox.org (CalDAV)
vdirsyncer (Sync)
┌───┴───────────┐
│ │
khal todoman
(Kalender) (Aufgaben)
│ │
└───────┬───────┘
openclaw_skill.py
Telegram Bot
```
## Unterstützte Befehle
| Telegram-Befehl | Beschreibung | Beispiel |
|---|---|---|
| `/termine` | Heutige Termine | `/termine heute` |
| `/termine morgen` | Morgige Termine | `/termine morgen` |
| `/termine woche` | Termine nächste 7 Tage | `/termine woche` |
| `/aufgaben` | Offene Aufgaben | `/aufgaben` |
| `/aufgaben alle` | Alle Aufgaben inkl. erledigt | `/aufgaben alle` |
| `/neu_aufgabe` | Neue Aufgabe anlegen | `/neu_aufgabe Arzt anrufen` |
| `/sync` | Manuellen Sync auslösen | `/sync` |
| `/status` | Sync-Timer-Status | `/status` |
## Voraussetzungen
- Raspberry Pi mit Raspberry Pi OS Bookworm oder Ubuntu 22.04+ ARM
- mailbox.org-Konto mit CalDAV-Zugang
- OpenClaw installiert und konfiguriert
- Telegram Bot-Token (via @BotFather)
- Internetverbindung vom Raspberry Pi
## Installation
### 1. Repository klonen
```bash
git clone https://github.com/DEIN_USER/openclaw-mailbox-cal.git
cd openclaw-mailbox-cal
```
### 2. .env befüllen
```bash
cp assets/.env.example .env
nano .env
# MAILBOX_USER und MAILBOX_PASS setzen
```
**Empfehlung:** Lege in mailbox.org ein App-Passwort an:
Einstellungen → Sicherheit → App-Passwörter → Neu erstellen
### 3. Installation ausführen
```bash
chmod +x scripts/install.sh
bash scripts/install.sh
```
Das Script führt folgende Schritte aus:
1. Pakete installieren: `vdirsyncer khal todoman python3-pass gettext-base`
2. Konfigurationsdateien deployen (mit envsubst)
3. systemd User-Timer aktivieren
4. Initialen Sync durchführen
5. khal-Datenbank aufbauen
6. OpenClaw Skill verknüpfen
### 4. Skill in OpenClaw registrieren
In OpenClaw-Konfiguration (`~/.config/openclaw/config.yaml`):
```yaml
skills:
- name: mailbox-cal
path: /home/pi/openclaw-mailbox-cal
entry: scripts/openclaw_skill.py
commands:
- termine
- aufgaben
- neu_aufgabe
- sync
- status
```
Danach OpenClaw neu starten:
```bash
systemctl --user restart openclaw
```
## Test-Befehle
Nach der Installation Setup validieren:
```bash
# Vollständiger Selbsttest
bash scripts/test_setup.sh
# Einzeltests
vdirsyncer sync # Sync testen
khal list today tomorrow # Termine heute + morgen
khal list --format "{start} {title}" 2025-01-01 2025-12-31 # Jahresübersicht
todo list # Aufgaben anzeigen
todo list --all # Alle inkl. erledigt
todo new "Test-Aufgabe" # Aufgabe anlegen
# systemd Timer
systemctl --user status vdirsyncer-sync.timer # Timer-Status
systemctl --user list-timers # Alle Timer
journalctl --user -u vdirsyncer-sync.service -f # Live-Log
# Python-Skill direkt testen (ohne OpenClaw/Telegram)
python3 scripts/openclaw_skill.py
```
## Konfigurationsdateien
| Datei | Ziel-Pfad | Beschreibung |
|---|---|---|
| `config/vdirsyncer.conf` | `~/.config/vdirsyncer/config` | CalDAV-Sync-Konfiguration |
| `config/khal.conf` | `~/.config/khal/config` | Kalender-Anzeige-Konfiguration |
| `config/todoman.conf` | `~/.config/todoman/config.cfg` | Aufgaben-Konfiguration |
| `systemd/vdirsyncer-sync.service` | `~/.config/systemd/user/` | Sync-Service Unit |
| `systemd/vdirsyncer-sync.timer` | `~/.config/systemd/user/` | 15-Minuten-Timer |
| `scripts/openclaw_skill.py` | (in-place) | OpenClaw/Telegram-Integration |
## Sicherheitshinweise
- Die vdirsyncer-Config (`~/.config/vdirsyncer/config`) enthält das Passwort — Berechtigungen sind `chmod 600`
- Verwende ein mailbox.org App-Passwort statt des Hauptpassworts
- Für produktive Umgebungen: `pass`-Integration (GNU Password Store) via `python3-pass`
- `.env`-Datei niemals in Git committen (ist in `.gitignore` eingetragen)
## Fehlerbehebung
### vdirsyncer discover schlägt fehl
```bash
# Verbindung testen
curl -u "USER:PASS" https://dav.mailbox.org/caldav/
# SSL-Zertifikate prüfen
openssl s_client -connect dav.mailbox.org:443
```
### khal zeigt keine Termine
```bash
# Cache neu aufbauen
khal rebuild-cache
# Config validieren
khal printformats
```
### systemd-Timer läuft nicht
```bash
# Lingering aktivieren (damit User-Units ohne Login laufen)
sudo loginctl enable-linger pi
# Timer manuell starten
systemctl --user start vdirsyncer-sync.service
```
### Todoman findet keine Aufgaben
```bash
# Pfad in config prüfen
cat ~/.config/todoman/config.cfg
# Verzeichnis prüfen
ls ~/.local/share/vdirsyncer/tasks/
```
## Erweiterungen
- **Mehrere Kalender:** Weitere `[[kalender_name]]`-Sektionen in `khal.conf` hinzufügen
- **Push-Sync:** mailbox.org unterstützt CalDAV-Push — XMPP-Trigger via `vdirsyncer`
- **Erinnerungen:** Integration mit `khal-remind` oder `at`-Daemon
- **Verschlüsselung:** `.env` via `gpg-agent` verschlüsseln
@@ -0,0 +1,39 @@
# ============================================================
# OpenClaw mailbox.org Kalender + Aufgaben — Umgebungsvariablen
# Kopiere diese Datei nach .env und passe die Werte an.
# ============================================================
# --- mailbox.org Zugangsdaten ---
MAILBOX_USER=dein.name@mailbox.org
MAILBOX_PASS=dein_passwort_hier
# Optional: App-Passwort statt Hauptpasswort (empfohlen)
# Unter mailbox.org → Einstellungen → Sicherheit → App-Passwörter erstellen
# MAILBOX_PASS=app-passwort-xyzabc
# --- Kalender-Konfiguration ---
# Basis-URL des CalDAV-Servers von mailbox.org
CALDAV_BASE_URL=https://dav.mailbox.org/caldav/
# Name des primären Kalenders (wie in mailbox.org angelegt)
PRIMARY_CALENDAR=Kalender
# Name des Aufgaben-Kalenders (VTODO)
TASKS_CALENDAR=Aufgaben
# --- Sync-Intervall (für systemd-Timer) ---
# Format: systemd OnCalendar — z. B. *:0/15 = alle 15 Minuten
SYNC_INTERVAL=*:0/15
# --- Lokale Verzeichnisse ---
VDIR_BASE_DIR=/home/pi/.local/share/vdirsyncer
KHAL_DB_PATH=/home/pi/.local/share/khal/db.db
# --- OpenClaw / Telegram ---
OPENCLAW_SKILL_NAME=mailbox-cal
# Telegram Bot Token (aus BotFather)
TELEGRAM_BOT_TOKEN=123456789:AABBCCDDEEFFaabbccddeeff1234567890
# --- Logging ---
LOG_LEVEL=INFO
LOG_FILE=/var/log/openclaw-mailbox-cal/sync.log
@@ -0,0 +1,60 @@
# =============================================================
# khal Konfiguration
# Pfad: ~/.config/khal/config
# Dokumentation: https://lostpackets.de/khal/
# =============================================================
[calendars]
# Primärer Kalender (VEVENT)
[[mailbox_kalender]]
path = ~/.local/share/vdirsyncer/calendars/Kalender/
color = light cyan
readonly = False
# Weitere Kalender-Sammlungen — füge bei Bedarf weitere hinzu
# [[mailbox_privat]]
# path = ~/.local/share/vdirsyncer/calendars/Privat/
# color = light blue
# readonly = False
[sqlite]
# SQLite-Datenbank für khal-Index
path = ~/.local/share/khal/db.db
[locale]
# Zeitzone und Spracheinstellungen (Österreich/Deutschland)
local_timezone = Europe/Vienna
default_timezone = Europe/Vienna
# Datumsformat: DD.MM.YYYY (deutsches Format)
dateformat = %d.%m.%Y
# Zeitformat: HH:MM (24h)
timeformat = %H:%M
# Datum+Zeit-Format
datetimeformat = %d.%m.%Y %H:%M
# Wochenbeginn: Montag
firstweekday = 0
# Sprache (für Ausgabe)
# khal nutzt die System-Locale; stelle sicher, dass de_AT.UTF-8 installiert ist:
# sudo locale-gen de_AT.UTF-8
[view]
# Standard-Ansicht beim Starten von khal
default_calendar = mailbox_kalender
# Agenda-Vorschau: Tage in die Zukunft
days = 7
# Farbige Ausgabe
color = True
# Zeilenumbruch bei langen Titeln
event_format = {title} [{calendar}]
@@ -0,0 +1,32 @@
# =============================================================
# todoman Konfiguration
# Pfad: ~/.config/todoman/config.cfg
# Dokumentation: https://todoman.readthedocs.io/
# =============================================================
[main]
# Verzeichnis mit den synchronisierten VTODO-Dateien (vdirsyncer output)
path = ~/.local/share/vdirsyncer/tasks/*
# Standard-Liste für neue Aufgaben (muss einem Unterverzeichnis entsprechen)
default_list = Aufgaben
# Datum/Zeit-Format (deutsches Format)
date_format = %d.%m.%Y
time_format = %H:%M
datetime_format = %d.%m.%Y %H:%M
# Farbige Ausgabe aktivieren
color = auto
# Abgeschlossene Aufgaben ausblenden (true = nur offene anzeigen)
# Kann via CLI mit --all überschrieben werden
humanize = true
# Priorität anzeigen
show_private = true
# Dateierweiterung für VTODO-Dateien
# (muss mit vdirsyncer fileext übereinstimmen)
# fileext = .ics ← Standard, nicht ändern
@@ -0,0 +1,80 @@
# =============================================================
# vdirsyncer Konfiguration für mailbox.org (CalDAV)
# Pfad: ~/.config/vdirsyncer/config
#
# Ersetze MAILBOX_USER / MAILBOX_PASS durch echte Werte
# oder nutze das install.sh-Script, das .env einliest und
# diese Datei mit envsubst befüllt.
# =============================================================
[general]
# Statusverzeichnis für vdirsyncer-Metadaten
status_path = "~/.local/share/vdirsyncer/status/"
# -----------------------------------------------------------
# STORAGE: Lokale CalDAV-Verzeichnisse (filesystem)
# -----------------------------------------------------------
[storage local_calendars]
type = "filesystem"
path = "~/.local/share/vdirsyncer/calendars/"
fileext = ".ics"
[storage local_tasks]
type = "filesystem"
path = "~/.local/share/vdirsyncer/tasks/"
fileext = ".ics"
# -----------------------------------------------------------
# STORAGE: mailbox.org CalDAV-Server (Remote)
#
# mailbox.org CalDAV-Endpunkte:
# Kalender: https://dav.mailbox.org/caldav/<USERNAME>/
# Aufgaben: https://dav.mailbox.org/caldav/<USERNAME>/Aufgaben/
#
# Für die Entdeckung aller Sammlungen:
# vdirsyncer discover calendars
# -----------------------------------------------------------
[storage remote_calendars]
type = "caldav"
url = "https://dav.mailbox.org/caldav/${MAILBOX_USER}/"
username = "${MAILBOX_USER}"
password = "${MAILBOX_PASS}"
# Nur VEVENT-Einträge (Kalender-Ereignisse)
item_types = ["VEVENT"]
[storage remote_tasks]
type = "caldav"
url = "https://dav.mailbox.org/caldav/${MAILBOX_USER}/"
username = "${MAILBOX_USER}"
password = "${MAILBOX_PASS}"
# Nur VTODO-Einträge (Aufgaben / Tasks)
item_types = ["VTODO"]
# -----------------------------------------------------------
# PAIR: Kalender-Synchronisierung
# -----------------------------------------------------------
[pair calendars]
a = "local_calendars"
b = "remote_calendars"
collections = ["from b"]
# Konflikt-Lösung: Remote gewinnt (mailbox.org ist die Quelle der Wahrheit)
conflict_resolution = "b wins"
# Metadaten-Sync (Kalender-Farbe, Anzeigename)
metadata = ["color", "displayname"]
[pair tasks]
a = "local_tasks"
b = "remote_tasks"
collections = ["from b"]
conflict_resolution = "b wins"
metadata = ["color", "displayname"]
@@ -0,0 +1,157 @@
#!/usr/bin/env bash
# =============================================================
# install.sh — OpenClaw mailbox.org Kalender + Aufgaben
# Zielplattform: Raspberry Pi (Raspberry Pi OS / Ubuntu ARM)
# Ausführen als: pi-Nutzer (sudo-Rechte erforderlich)
# =============================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
ENV_FILE="$REPO_ROOT/.env"
# --- Farben ---
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
# =============================================================
# 1. .env laden
# =============================================================
if [[ ! -f "$ENV_FILE" ]]; then
warn ".env nicht gefunden — kopiere .env.example nach .env"
cp "$REPO_ROOT/assets/.env.example" "$ENV_FILE"
error "Bitte .env mit deinen mailbox.org-Zugangsdaten befüllen und install.sh erneut ausführen."
fi
# shellcheck source=/dev/null
source "$ENV_FILE"
info ".env geladen (Benutzer: ${MAILBOX_USER:-NICHT GESETZT})"
[[ -z "${MAILBOX_USER:-}" ]] && error "MAILBOX_USER in .env nicht gesetzt!"
[[ -z "${MAILBOX_PASS:-}" ]] && error "MAILBOX_PASS in .env nicht gesetzt!"
# =============================================================
# 2. Systempakete installieren
# =============================================================
info "Installiere Systempakete..."
sudo apt-get update -qq
sudo apt-get install -y \
vdirsyncer \
khal \
todoman \
python3-pass \
gettext-base \
curl \
jq \
python3-pip \
python3-venv
# Locale sicherstellen
sudo locale-gen de_AT.UTF-8 2>/dev/null || true
info "Pakete installiert."
# =============================================================
# 3. Verzeichnisse anlegen
# =============================================================
info "Lege Verzeichnisse an..."
VDIR_BASE="${VDIR_BASE_DIR:-$HOME/.local/share/vdirsyncer}"
mkdir -p "$VDIR_BASE/status"
mkdir -p "$VDIR_BASE/calendars"
mkdir -p "$VDIR_BASE/tasks"
mkdir -p "${KHAL_DB_PATH:-$HOME/.local/share/khal}"
mkdir -p "$HOME/.config/vdirsyncer"
mkdir -p "$HOME/.config/khal"
mkdir -p "$HOME/.config/todoman"
sudo mkdir -p /var/log/openclaw-mailbox-cal
sudo chown "$USER:$USER" /var/log/openclaw-mailbox-cal
# =============================================================
# 4. Konfigurationsdateien deployen (envsubst für Variablen)
# =============================================================
info "Deploye Konfigurationsdateien..."
# vdirsyncer config — Variablen ersetzen
export MAILBOX_USER MAILBOX_PASS
envsubst '${MAILBOX_USER} ${MAILBOX_PASS}' \
< "$REPO_ROOT/config/vdirsyncer.conf" \
> "$HOME/.config/vdirsyncer/config"
chmod 600 "$HOME/.config/vdirsyncer/config"
info " → ~/.config/vdirsyncer/config"
# khal config
cp "$REPO_ROOT/config/khal.conf" "$HOME/.config/khal/config"
info " → ~/.config/khal/config"
# todoman config
cp "$REPO_ROOT/config/todoman.conf" "$HOME/.config/todoman/config.cfg"
info " → ~/.config/todoman/config.cfg"
# =============================================================
# 5. systemd Units installieren (User-Session)
# =============================================================
info "Installiere systemd Units (User-Session)..."
SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
mkdir -p "$SYSTEMD_USER_DIR"
# Variablen in systemd-Units ersetzen
for unit_file in "$REPO_ROOT/systemd/"*.{service,timer}; do
[[ -f "$unit_file" ]] || continue
unit_name="$(basename "$unit_file")"
envsubst '${SYNC_INTERVAL}' < "$unit_file" > "$SYSTEMD_USER_DIR/$unit_name"
info "$SYSTEMD_USER_DIR/$unit_name"
done
systemctl --user daemon-reload
systemctl --user enable --now vdirsyncer-sync.timer
info "systemd Timer aktiviert."
# Lingering aktivieren (damit User-Units ohne Login laufen)
loginctl enable-linger "$USER" 2>/dev/null || \
warn "loginctl enable-linger fehlgeschlagen — bitte manuell ausführen: sudo loginctl enable-linger $USER"
# =============================================================
# 6. Erst-Synchronisation
# =============================================================
info "Führe initialen vdirsyncer discover + sync durch..."
vdirsyncer discover calendars || warn "discover calendars hatte Warnungen — prüfe Ausgabe"
vdirsyncer discover tasks || warn "discover tasks hatte Warnungen — prüfe Ausgabe"
vdirsyncer sync || warn "sync hatte Warnungen — prüfe Ausgabe"
info "Erst-Sync abgeschlossen."
# =============================================================
# 7. khal-Index aufbauen
# =============================================================
info "Baue khal-Index auf..."
khal --config "$HOME/.config/khal/config" rebuild-cache || true
# =============================================================
# 8. OpenClaw Skill-Verknüpfung
# =============================================================
OPENCLAW_SKILLS_DIR="${OPENCLAW_SKILLS_DIR:-$HOME/.config/openclaw/skills}"
if [[ -d "$OPENCLAW_SKILLS_DIR" ]]; then
info "Verknüpfe OpenClaw Skill..."
ln -sf "$REPO_ROOT" "$OPENCLAW_SKILLS_DIR/mailbox-cal" 2>/dev/null || true
info "$OPENCLAW_SKILLS_DIR/mailbox-cal"
else
warn "OpenClaw Skills-Verzeichnis nicht gefunden ($OPENCLAW_SKILLS_DIR)."
warn "Bitte Skill manuell in OpenClaw registrieren (siehe README.md)."
fi
# =============================================================
# Fertig
# =============================================================
echo ""
echo -e "${GREEN}╔══════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ Installation abgeschlossen! ║${NC}"
echo -e "${GREEN}╚══════════════════════════════════════════════════════╝${NC}"
echo ""
echo " Nächste Schritte:"
echo " 1. khal agenda # Termine anzeigen"
echo " 2. todo list # Aufgaben anzeigen"
echo " 3. systemctl --user status vdirsyncer-sync.timer"
echo " 4. tail -f /var/log/openclaw-mailbox-cal/sync.log"
echo ""
@@ -0,0 +1,178 @@
#!/usr/bin/env python3
"""
openclaw_skill.py — OpenClaw Telegram-Bot-Skill: mailbox.org Kalender + Aufgaben
Registrierung in OpenClaw: /register skill mailbox-cal
Unterstützte Befehle (via Telegram-Chat an den Bot):
/termine [heute|morgen|woche] — Kalender-Ereignisse anzeigen
/aufgaben [offen|alle] — Aufgabenliste anzeigen
/neu_aufgabe <Text> — Neue Aufgabe anlegen
/sync — manuellen vdirsyncer-Sync auslösen
/status — Sync-Status abfragen
Voraussetzungen:
- khal, todoman, vdirsyncer installiert (install.sh ausgeführt)
- OpenClaw mit Python-Skill-API >= 0.4
"""
from __future__ import annotations
import subprocess
import shlex
import logging
from datetime import datetime, date, timedelta
from typing import Optional
logger = logging.getLogger(__name__)
# ─────────────────────────────────────────────
# Hilfsfunktionen
# ─────────────────────────────────────────────
def _run(cmd: list[str], timeout: int = 30) -> tuple[str, str, int]:
"""Führt einen Subprozess aus und gibt (stdout, stderr, returncode) zurück."""
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=timeout,
)
return result.stdout.strip(), result.stderr.strip(), result.returncode
except subprocess.TimeoutExpired:
return "", "Timeout nach {}s".format(timeout), 1
except FileNotFoundError as e:
return "", str(e), 127
def _khal_agenda(days: int = 1, start: Optional[date] = None) -> str:
"""Gibt khal-Agenda als String zurück."""
start_str = (start or date.today()).strftime("%Y-%m-%d")
end_str = (date.today() + timedelta(days=days)).strftime("%Y-%m-%d")
stdout, stderr, rc = _run(
["khal", "list", "--format", "{start-time} {title}", start_str, end_str]
)
if rc != 0:
logger.warning("khal list Fehler: %s", stderr)
return "⚠️ khal Fehler: " + stderr
return stdout or "Keine Termine gefunden."
def _todo_list(all_tasks: bool = False) -> str:
"""Gibt todoman-Aufgabenliste als String zurück."""
cmd = ["todo", "list"]
if all_tasks:
cmd.append("--all")
stdout, stderr, rc = _run(cmd)
if rc != 0:
return "⚠️ todoman Fehler: " + stderr
return stdout or "Keine Aufgaben."
def _todo_new(title: str) -> str:
"""Legt eine neue Aufgabe an."""
if not title.strip():
return "⚠️ Kein Aufgabentitel angegeben."
stdout, stderr, rc = _run(["todo", "new", "--", title])
if rc != 0:
return "⚠️ Fehler beim Anlegen: " + stderr
return "✅ Aufgabe angelegt: " + title
def _vdirsyncer_sync() -> str:
"""Löst manuellen vdirsyncer-Sync aus."""
stdout, stderr, rc = _run(["vdirsyncer", "sync"], timeout=60)
if rc != 0:
return "⚠️ Sync-Fehler:\n" + stderr
return "✅ Sync erfolgreich.\n" + stdout
def _sync_status() -> str:
"""Prüft systemd-Timer-Status."""
stdout, _, rc = _run(
["systemctl", "--user", "status", "vdirsyncer-sync.timer", "--no-pager", "-l"]
)
if rc not in (0, 3):
return "⚠️ Timer nicht gefunden oder Fehler."
# Nur relevante Zeilen extrahieren
lines = [
line for line in stdout.splitlines()
if any(kw in line for kw in ("Active:", "Trigger:", "Last trigger:"))
]
return "\n".join(lines) or stdout[:500]
# ─────────────────────────────────────────────
# OpenClaw Skill-Handler
# ─────────────────────────────────────────────
def handle(command: str, args: str, context: dict) -> str:
"""
Haupt-Einstiegspunkt für OpenClaw.
:param command: Befehlsname ohne führenden Slash, z. B. "termine"
:param args: Argumente als String, z. B. "woche"
:param context: OpenClaw-Kontext (user_id, chat_id, etc.)
:return: Antwort-String für Telegram
"""
cmd = command.lower().strip()
arg = args.lower().strip()
if cmd == "termine":
if arg in ("morgen",):
start = date.today() + timedelta(days=1)
result = _khal_agenda(days=1, start=start)
return "📅 *Termine morgen:*\n" + result
elif arg in ("woche", "7"):
result = _khal_agenda(days=7)
return "📅 *Termine diese Woche:*\n" + result
else:
result = _khal_agenda(days=1)
return "📅 *Termine heute:*\n" + result
elif cmd == "aufgaben":
all_tasks = arg in ("alle", "all", "fertig")
result = _todo_list(all_tasks=all_tasks)
label = "alle Aufgaben" if all_tasks else "offene Aufgaben"
return "📝 *{}:*\n{}".format(label.capitalize(), result)
elif cmd == "neu_aufgabe":
return _todo_new(args.strip())
elif cmd == "sync":
return "🔄 Starte Sync...\n" + _vdirsyncer_sync()
elif cmd == "status":
return "⏱ *Sync-Status:*\n" + _sync_status()
else:
return (
"❓ Unbekannter Befehl. Verfügbare Befehle:\n"
" /termine [heute|morgen|woche]\n"
" /aufgaben [offen|alle]\n"
" /neu_aufgabe <Text>\n"
" /sync\n"
" /status"
)
# ─────────────────────────────────────────────
# Standalone-Test (ohne OpenClaw)
# ─────────────────────────────────────────────
if __name__ == "__main__":
import sys
logging.basicConfig(level=logging.DEBUG)
tests = [
("termine", "heute"),
("termine", "woche"),
("aufgaben", "offen"),
("sync", ""),
("status", ""),
]
for cmd, arg in tests:
print("\n" + "=" * 50)
print(f"CMD: {cmd!r} ARG: {arg!r}")
print(handle(cmd, arg, {}))
@@ -0,0 +1,146 @@
#!/usr/bin/env bash
# =============================================================
# test_setup.sh — Überprüft die Installation Schritt für Schritt
# Ausführen nach install.sh: bash scripts/test_setup.sh
# =============================================================
set -euo pipefail
# Farben
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
ok() { echo -e "${GREEN} [✓] $*${NC}"; }
fail() { echo -e "${RED} [✗] $*${NC}"; FAILED=$((FAILED+1)); }
info() { echo -e "${CYAN} [i] $*${NC}"; }
FAILED=0
echo ""
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
echo -e "${CYAN} OpenClaw mailbox.org Kalender+Aufgaben — Selbsttest${NC}"
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
echo ""
# --- 1. Binaries vorhanden? ---
echo "▶ 1. Programm-Verfügbarkeit"
for bin in vdirsyncer khal todo python3; do
if command -v "$bin" &>/dev/null; then
ok "$bin$(command -v $bin)"
else
fail "$bin nicht gefunden — install.sh ausführen"
fi
done
# --- 2. Konfigurationsdateien ---
echo ""
echo "▶ 2. Konfigurationsdateien"
declare -A CONFIGS=(
["vdirsyncer"]="$HOME/.config/vdirsyncer/config"
["khal"]="$HOME/.config/khal/config"
["todoman"]="$HOME/.config/todoman/config.cfg"
)
for name in "${!CONFIGS[@]}"; do
path="${CONFIGS[$name]}"
if [[ -f "$path" ]]; then
ok "$name$path"
else
fail "$name-Config fehlt: $path"
fi
done
# --- 3. Berechtigungen vdirsyncer-Config (enthält Passwort) ---
echo ""
echo "▶ 3. Sicherheit"
VDIR_CONF="$HOME/.config/vdirsyncer/config"
if [[ -f "$VDIR_CONF" ]]; then
PERMS=$(stat -c "%a" "$VDIR_CONF")
if [[ "$PERMS" == "600" ]]; then
ok "vdirsyncer config Berechtigungen: $PERMS (korrekt)"
else
fail "vdirsyncer config Berechtigungen: $PERMS (sollte 600 sein) → chmod 600 $VDIR_CONF"
fi
fi
# Prüfe ob Klartext-Passwort in Config steht
if grep -q 'password.*=.*"[^$]' "$VDIR_CONF" 2>/dev/null; then
info "Passwort in Plaintext erkannt — Erwäge pass(1) oder python-keyring als Alternative"
fi
# --- 4. Sync-Verzeichnisse ---
echo ""
echo "▶ 4. Sync-Verzeichnisse"
for dir in \
"$HOME/.local/share/vdirsyncer/status" \
"$HOME/.local/share/vdirsyncer/calendars" \
"$HOME/.local/share/vdirsyncer/tasks"; do
if [[ -d "$dir" ]]; then
COUNT=$(find "$dir" -name "*.ics" 2>/dev/null | wc -l)
ok "$dir (${COUNT} .ics Dateien)"
else
fail "Verzeichnis fehlt: $dir"
fi
done
# --- 5. vdirsyncer Verbindungstest ---
echo ""
echo "▶ 5. vdirsyncer — Verbindungstest"
info "Führe 'vdirsyncer sync' aus (Timeout: 30s)..."
if timeout 30 vdirsyncer sync 2>&1 | tail -5; then
ok "vdirsyncer sync erfolgreich"
else
fail "vdirsyncer sync fehlgeschlagen — Zugangsdaten und Netzwerk prüfen"
fi
# --- 6. khal-Test ---
echo ""
echo "▶ 6. khal — Termine heute"
if khal list --format "{start-time} {title}" today today 2>/dev/null; then
ok "khal list ausgeführt"
else
fail "khal list fehlgeschlagen — khal.conf prüfen"
fi
# --- 7. todoman-Test ---
echo ""
echo "▶ 7. todoman — Aufgabenliste"
if todo list 2>/dev/null; then
ok "todo list ausgeführt"
else
fail "todo list fehlgeschlagen — todoman.conf prüfen"
fi
# --- 8. systemd-Timer ---
echo ""
echo "▶ 8. systemd Timer"
if systemctl --user is-active vdirsyncer-sync.timer &>/dev/null; then
ok "vdirsyncer-sync.timer: aktiv"
NEXT=$(systemctl --user list-timers vdirsyncer-sync.timer --no-legend 2>/dev/null | awk '{print $1, $2}')
info "Nächster Sync: $NEXT"
else
fail "vdirsyncer-sync.timer nicht aktiv → systemctl --user enable --now vdirsyncer-sync.timer"
fi
# --- 9. Log-Datei ---
echo ""
echo "▶ 9. Log-Datei"
LOG="/var/log/openclaw-mailbox-cal/sync.log"
if [[ -f "$LOG" ]]; then
SIZE=$(du -sh "$LOG" | cut -f1)
ok "Log vorhanden: $LOG ($SIZE)"
info "Letzte 5 Zeilen:"
tail -5 "$LOG" | sed 's/^/ /'
else
info "Log noch nicht vorhanden (wird beim ersten Sync erstellt)"
fi
# --- Zusammenfassung ---
echo ""
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
if [[ $FAILED -eq 0 ]]; then
echo -e "${GREEN} Alle Tests bestanden! Setup ist korrekt.${NC}"
else
echo -e "${RED} $FAILED Test(s) fehlgeschlagen — siehe Details oben.${NC}"
fi
echo -e "${CYAN}══════════════════════════════════════════════════${NC}"
echo ""
exit $FAILED
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
# Konfiguration
CAL_URL="https://dav.mailbox.org/caldav/Y2FsOi8vMC8zMg"
USER="minitux@net-so.org"
PASS="dbba-guvm-perd-pdhq"
OUT_FILE="/home/hans/.openclaw/workspace/mailbox-calendar.ics"
# Temporäre Datei, um halb geschriebene Files zu vermeiden
TMP_FILE="$(mktemp)"
# ICS/CalDAV-Daten holen
curl -sS -u "$USER:$PASS" "$CAL_URL" -o "$TMP_FILE"
# Wenn curl erfolgreich war, Datei an Ziel verschieben
mv "$TMP_FILE" "$OUT_FILE"
chmod 644 "$OUT_FILE"
@@ -0,0 +1,29 @@
[Unit]
Description=vdirsyncer — mailbox.org CalDAV Synchronisation
Documentation=https://vdirsyncer.pimutils.org/
# Netzwerk abwarten
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
# Umgebungsvariablen aus .env laden (Pfad anpassen falls nötig)
EnvironmentFile=%h/.config/openclaw-mailbox-cal/.env
# Erst-Sync: discover + sync
# Bei erstem Lauf collect = auto, danach nur sync
ExecStart=/usr/bin/vdirsyncer sync
# Fehler loggen
StandardOutput=append:/var/log/openclaw-mailbox-cal/sync.log
StandardError=append:/var/log/openclaw-mailbox-cal/sync.log
# Timeout: 120 Sekunden (ausreichend für langsame Verbindungen)
TimeoutStartSec=120
# Nach erfolgreichem Sync khal-Cache neu aufbauen
ExecStartPost=/usr/bin/khal rebuild-cache
[Install]
WantedBy=default.target
@@ -0,0 +1,27 @@
[Unit]
Description=vdirsyncer Sync Timer — mailbox.org (alle 15 Minuten)
Documentation=https://vdirsyncer.pimutils.org/
Requires=vdirsyncer-sync.service
[Timer]
# Sync-Intervall: alle 15 Minuten (aus .env SYNC_INTERVAL oder hier anpassen)
# Beispiele:
# *:0/15 → alle 15 Minuten
# *:0/5 → alle 5 Minuten
# hourly → stündlich
# *-*-* 06,12,18,22:00:00 → 4x täglich
OnCalendar=${SYNC_INTERVAL:-*:0/15}
# Beim Booten direkt starten (nicht auf den nächsten Interval warten)
OnBootSec=60s
# Zufällige Verzögerung ±30s (verhindert Last-Spitzen)
RandomizedDelaySec=30s
# Verpasste Runs nachholen (z. B. nach Schlafmodus)
Persistent=true
Unit=vdirsyncer-sync.service
[Install]
WantedBy=timers.target