Dokumente aktualisiert
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
## Kontext
|
||||
|
||||
Dieses Projekt entwickelt `mcp-familywall` – einen MCP-Server für den Lesezugriff
|
||||
auf Family Wall (familywall.com). Der MCP-Server läuft lokal und wird in Claude Desktop
|
||||
eingebunden.
|
||||
Dieses Projekt entwickelt `mcp-familywall` – einen MCP-Server für den
|
||||
Zugriff auf Family Wall (familywall.com). Der MCP-Server läuft lokal
|
||||
und wird in Claude Desktop eingebunden.
|
||||
|
||||
|
||||
## Infrastruktur
|
||||
@@ -20,48 +20,33 @@ eingebunden.
|
||||
## Deploy-Workflow (nach jeder Code-Änderung)
|
||||
|
||||
1. Claude Code committet und pusht (bei Berechtigungsfehler: bis zu 2 Retries, je 1s warten)
|
||||
2. Marcus installiert lokal und startet Claude Desktop neu
|
||||
|
||||
## Aktueller Stand
|
||||
|
||||
### Implementierte Tools (v0.4.16)
|
||||
### Implementierte Tools (v0.4.x)
|
||||
|
||||
| Kategorie | Tools |
|
||||
|---|---|
|
||||
| Kreise | `get_circles`, `get_members` |
|
||||
| Listen | `get_lists` |
|
||||
| Tasks (Lesen) | `get_tasks` (inkl. `category_id`, `due_date`, `assignee_ids`), `get_categories` (inkl. `custom`-Flag) |
|
||||
| Wall | `get_activities`, `like_post` |
|
||||
| Tasks (Schreiben) | `create_task` (inkl. `category_id`, `due_date`, `assignee_ids`), `update_task` (inkl. `category_id`, `due_date`, `clear_due_date`, `assignee_ids`, `list_id`), `toggle_task`, `delete_task` |
|
||||
| Kategorien (Schreiben) | `create_category` (inkl. `icon`), `delete_category` (System-Kategorien geschützt) |
|
||||
|
||||
| Lesen | `get_circles`, `get_members`, `get_lists`, `get_tasks`, `get_categories`, `get_activities` |
|
||||
| Tasks | `create_task`, `update_task`, `toggle_task`, `delete_task` |
|
||||
| Kategorien | `create_category`, `delete_category` |
|
||||
| Aktivitäten | `like_post` |
|
||||
|
||||
## Roadmap
|
||||
|
||||
- v0.x: Erweiterter Lese- + Schreibzugriff ← aktuell
|
||||
- Offen: Unlike (`like_post(like=False)`)
|
||||
- v0.4.x: Kategorie-Management, Task-Felder (due_date, assignee, list_id) ← aktuell
|
||||
- v0.5.x: Erinnerungen + Wiederholungen (Premium-Account erforderlich)
|
||||
- v2.0: Schreibzugriff auf Wall-Posts (Erstellen, Kommentieren)
|
||||
|
||||
|
||||
## Referenzprojekt
|
||||
|
||||
Im Ordner `reference/` liegen Dateien aus einem anderen MCP-Server-Projekt
|
||||
als Orientierung für Struktur und Patterns:
|
||||
|
||||
| Datei | Zweck |
|
||||
|---|---|
|
||||
| `reference/pyproject.toml` | Projektstruktur, Dependencies, Entry-Points, Build-System |
|
||||
| `reference/config.py` | Config-Pattern: YAML laden, validieren, speichern |
|
||||
| `reference/auth.py` | Keyring-Integration, Credential-Resolution-Reihenfolge |
|
||||
| `reference/cli.py` | Setup-Wizard, check, serve – CLI-Struktur |
|
||||
|
||||
**Wichtig:** Diese Dateien sind Referenz, kein Copy-Paste. Alle
|
||||
Synology/DSM-spezifischen Teile werden nicht übernommen.
|
||||
Family Wall nutzt ein anderes Auth-Schema – siehe SPEC.md.
|
||||
|
||||
## Architektur-Entscheidungen
|
||||
|
||||
### Session-Strategie
|
||||
Kein Session-Caching. Jeder Tool-Call führt Login → API-Call → Logout durch.
|
||||
Credentials liegen im OS Keyring (nur `email` + `password`), kein `session_id`.
|
||||
Optimierung: Mehrere API-Calls in einer Session bündeln (login → call1 → call2 → logout)
|
||||
um unnötige HTTP-Roundtrips zu vermeiden. Credentials liegen im OS Keyring
|
||||
(nur `email` + `password`), kein `session_id`.
|
||||
|
||||
### Kreise (Scopes)
|
||||
Family Wall kennt mehrere Kreise (z.B. Familie, erweiterter Familienkreis).
|
||||
@@ -72,68 +57,77 @@ Ohne `scope` werden alle Kreise zurückgegeben.
|
||||
Systembezeichnungen (z.B. `SYS-CAT-SHOPPINGLIST`) werden in deutsche
|
||||
Klarnamen übersetzt. Mapping-Tabelle in `modules/lists.py`.
|
||||
|
||||
### Kategorien
|
||||
- Kategorien sind family-wide, nicht list-spezifisch (`taskcategoryput` gilt für alle Listen)
|
||||
- System-Kategorien: `rights.canDelete=null` → nicht löschbar
|
||||
- Custom-Kategorien: `rights.canDelete="true"` → löschbar
|
||||
- Locale-Filter: default `"de"` – Custom-Kategorien haben kein locale-Feld
|
||||
und werden immer angezeigt unabhängig vom Locale-Parameter
|
||||
|
||||
### Fehlerbehandlung
|
||||
Die API gibt Fehler manchmal als `a00.un.un` zurück (nicht Top-Level).
|
||||
`fw_client.py` prüft beides und wirft `FamilyWallError`. Nie silent-fail
|
||||
als Erfolg werten – immer `a00.un` und Top-Level `ex`/`un` prüfen.
|
||||
|
||||
### Service Worker
|
||||
Die Family Wall Web-App nutzt einen Service Worker der bestimmte
|
||||
HTTP-Requests abfängt und modifiziert. Browser-DevTools und JS-Interceptoren
|
||||
können den echten Request-Body in diesen Fällen nicht sehen.
|
||||
→ Immer `FW_DEBUG=1` für Traffic-Analyse nutzen, nicht Browser-DevTools.
|
||||
|
||||
|
||||
## Claude Code – Implementierungsregeln
|
||||
|
||||
- Keine destruktiven Operationen in v1.0 → kein Confirmation-Pattern erforderlich
|
||||
- **Feature complete before next feature** – jedes Feature vollständig
|
||||
implementieren, testen und verifizieren bevor das nächste beginnt
|
||||
- **Kein destruktives Probing** – keine Probe-Calls auf System-Kategorien,
|
||||
echte Listen oder echte Tasks; immer Test-Objekte anlegen und danach
|
||||
sofort löschen
|
||||
- Fehlerbehandlung: API-Fehler als verständliche Meldung zurückgeben, keine Stacktraces
|
||||
- Keine Secrets in stderr-Ausgaben (Passwort bei Debug-Logging maskieren)
|
||||
- Type Hints und Docstrings konsequent verwenden
|
||||
- Type Hints und Docstrings konsequent verwenden (Englisch)
|
||||
- Formatter: `ruff format`, Linter: `ruff check`, Tests: `pytest`
|
||||
- Alle Texte (Docstrings, Kommentare, README): Englisch
|
||||
- Debug-Logging via `FW_DEBUG=1` Umgebungsvariable
|
||||
- Nach jeder Aufgabe: git commit + push. Bei Berechtigungsfehler: 1s warten, bis zu 2 Retries
|
||||
- .gitignore eigenständig pflegen (Credentials, __pycache__, .venv, .env, *.pyc etc.)
|
||||
- README.md im Projekt-Root pflegen und bei jedem Aufruf aktualisieren
|
||||
- README.md + CLAUDE.md im Projekt-Root bei jedem Aufruf aktualisieren
|
||||
- Confirmation-Pattern in Docstrings: `IMPORTANT: Ask the user for confirmation
|
||||
before calling this tool.` für destruktive oder schreibende Operationen
|
||||
|
||||
|
||||
## Implementierungsreihenfolge
|
||||
## Bekannte API-Eigenheiten und Fallstricke
|
||||
|
||||
### Gruppe 1 – Projektgerüst + Auth + CLI ✦ Prio: hoch
|
||||
### Sentinel-Wert `$empty`
|
||||
Das FiZ-Framework nutzt `$empty` als Sentinel um optionale Felder zu löschen.
|
||||
Normale Werte wie `""`, `null`, `"null"`, `"0"` werden vom Server abgelehnt.
|
||||
Aktuell genutzt für: `dueDate=$empty` (Fälligkeitsdatum entfernen)
|
||||
|
||||
- `pyproject.toml`: Package `mcp-familywall`, Entry-Point `mcp-familywall`
|
||||
- `src/mcp_familywall/config.py`: `~/.config/mcp-familywall/config.yaml`,
|
||||
schema_version 1
|
||||
- `src/mcp_familywall/auth.py`: Keyring-Service `mcp-familywall`,
|
||||
Keys: `email`, `password`. Credential-Resolution:
|
||||
1. Umgebungsvariablen `FW_EMAIL`, `FW_PASSWORD`
|
||||
2. OS Keyring
|
||||
- `src/mcp_familywall/fw_client.py`: HTTP-Client mit `httpx`.
|
||||
Methoden: `login()`, `logout()`, `call(endpoint, params)`.
|
||||
Debug-Logging wenn `FW_DEBUG=1` (Passwort maskieren).
|
||||
- `src/mcp_familywall/cli.py`:
|
||||
- `setup`: fragt E-Mail + Passwort, führt Login/Logout durch,
|
||||
speichert Credentials im Keyring, gibt Claude-Desktop-Snippet aus
|
||||
- `check`: testet Auth und API-Erreichbarkeit
|
||||
- `serve`: startet MCP-Server
|
||||
|
||||
Config-Datei enthält nur:
|
||||
```yaml
|
||||
schema_version: 1
|
||||
### Silent-Fail via `a00.un.un`
|
||||
Fehler bei falschen Parametern kommen nicht immer auf Top-Level:
|
||||
```json
|
||||
{"a00": {"un": {"un": {"message": "missing value in: taskId"}}}}
|
||||
```
|
||||
→ `fw_client` muss beide Ebenen prüfen
|
||||
|
||||
### Gruppe 2 – MCP Tools ✦ Prio: hoch
|
||||
### Bekannte Parameter-Namen (verifiziert)
|
||||
| Endpoint | Parameter | Wert/Format |
|
||||
|---|---|---|
|
||||
| `taskcreate2` | `taskListId`, `text`, `description`, `taskCategoryId`, `dueDate`, `assignee` | – |
|
||||
| `taskupdate2` | `metaId`, `text`, `description`, `taskCategoryId`, `dueDate`, `assignee`, `taskListId` | – |
|
||||
| `taskupdate2` | `dueDate` löschen | `$empty` |
|
||||
| `taskmark` | `taskId`, `complete` | `"true"`/`"false"` |
|
||||
| `metadelete` | `id` | metaId des Tasks |
|
||||
| `wallmood` | `wall_message_id`, `moodType` | `"STAR"` für Like |
|
||||
| `taskcategoryput` | `name`, `emoji` | – |
|
||||
| `taskcategorydelete` | `id` | metaId der Kategorie |
|
||||
|
||||
`src/mcp_familywall/server.py` + `src/mcp_familywall/modules/lists.py`
|
||||
### Self-Like-Restriction
|
||||
Eigene Posts können nicht geliked werden. API antwortet 200, macht aber nichts.
|
||||
|
||||
**`get_circles`**
|
||||
- Ruft `famlistfamily` auf
|
||||
- Gibt alle Kreise zurück: `id`, `name`
|
||||
- Confirmation: nein
|
||||
|
||||
**`get_lists`**
|
||||
- Signatur: `get_lists(scope: str = None) -> str`
|
||||
- Ruft `accgetallfamily` auf (Parameter: `a01call=taskcategorysync`, `a02call=tasksync`)
|
||||
- Gibt zurück: `id` (metaId), `name` (übersetzt), `type`, offene Einträge,
|
||||
Gesamteinträge, Kreis-Name
|
||||
- Ohne `scope`: alle Kreise; mit `scope`: nur dieser Kreis
|
||||
- Confirmation: nein
|
||||
|
||||
**`get_tasks`**
|
||||
- Signatur: `get_tasks(list_id: str, only_open: bool = True) -> str`
|
||||
- `list_id`: metaId aus `get_lists`
|
||||
- Gibt zurück: `id`, `text`, `description`, `completed`
|
||||
- Confirmation: nein
|
||||
### Unlike nicht möglich
|
||||
Service Worker verschlüsselt den Unlike-Request-Body.
|
||||
Endpoint unbekannt. `like_post(like=False)` gibt Fehlermeldung zurück.
|
||||
|
||||
|
||||
## Test-Credentials (nur für Entwicklung)
|
||||
@@ -143,6 +137,9 @@ schema_version: 1
|
||||
| E-Mail | `marcus@gecheckt.de` |
|
||||
| Passwort | `Lasdas1234` |
|
||||
|
||||
Hinweis: Das ist ein kostenloser Test-Account ohne Premium-Features.
|
||||
Der echte Account (Premium) hat andere Credentials die im Keyring gespeichert sind.
|
||||
|
||||
|
||||
## Hintergrund
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Family Wall API – Spezifikation
|
||||
|
||||
Erarbeitet durch Browser-Traffic-Analyse (April 2026).
|
||||
Erarbeitet durch Browser-Traffic-Analyse und React-Fiber-Analyse (April 2026).
|
||||
Es gibt keine offizielle API-Dokumentation.
|
||||
|
||||
## Base URL
|
||||
@@ -18,21 +18,14 @@ Content-Type: application/x-www-form-urlencoded
|
||||
|---|---|
|
||||
| `identifier` | E-Mail-Adresse |
|
||||
| `password` | Passwort |
|
||||
| `type` | nicht senden — wird als `undefined` ignoriert (verifiziert per JS-Analyse) |
|
||||
| `clientId` | weglassen |
|
||||
| `clientSecret` | weglassen |
|
||||
| `generateAutologinToken` | weglassen |
|
||||
| `countryCode` | weglassen |
|
||||
| `type` | `"email"` (verifiziert) |
|
||||
|
||||
**Response (Erfolg):**
|
||||
|
||||
```json
|
||||
{ "a00": { "r": { "r": <SessionObject> }, "cn": "log2in" } }
|
||||
{ "r": { "r": <SessionObject> } }
|
||||
```
|
||||
|
||||
`SessionObject` enthält u.a. `tokenCsrf` und `webApiUrl`.
|
||||
`tokenCsrf` ist die Session-ID – identisch zur `JSESSIONID` im Cookie.
|
||||
|
||||
**Response (Fehler):**
|
||||
|
||||
```json
|
||||
@@ -41,12 +34,10 @@ Content-Type: application/x-www-form-urlencoded
|
||||
```
|
||||
|
||||
Der Server setzt nach erfolgreichem Login ein Session-Cookie:
|
||||
Set-Cookie: JSESSIONID=<session-id> (= tokenCsrf)
|
||||
`Set-Cookie: JSESSIONID=<session-id>`
|
||||
|
||||
### Folgecalls (nach Login)
|
||||
|
||||
Alle API-Calls nach dem Login benötigen:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Cookie** | `JSESSIONID=<session-id>` |
|
||||
@@ -55,7 +46,6 @@ Alle API-Calls nach dem Login benötigen:
|
||||
|
||||
### Logout
|
||||
POST https://api.familywall.com/api/log2out
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
Keine Parameter. Session wird serverseitig invalidiert.
|
||||
|
||||
@@ -63,62 +53,66 @@ Keine Parameter. Session wird serverseitig invalidiert.
|
||||
|
||||
Kein Session-Caching. Jeder MCP-Tool-Call führt folgende Sequenz aus:
|
||||
|
||||
```
|
||||
POST /api/log2in → Session-ID
|
||||
POST /api/<endpoint> → Nutzdaten
|
||||
POST /api/<endpoint> → Nutzdaten (ggf. mehrere Calls in einer Session)
|
||||
POST /api/log2out → Session invalidieren
|
||||
|
||||
```
|
||||
|
||||
Credentials (E-Mail + Passwort) werden einmalig via `mcp-familywall setup`
|
||||
im OS Keyring gespeichert (Keys: `email`, `password`). Kein Keyring-Eintrag
|
||||
für `session_id`.
|
||||
im OS Keyring gespeichert (Keys: `email`, `password`).
|
||||
|
||||
## Fehlerbehandlung
|
||||
|
||||
### Silent-Fail Warnung
|
||||
Die API gibt Fehler manchmal NICHT auf Top-Level zurück, sondern eingebettet:
|
||||
|
||||
```json
|
||||
{"a00": {"un": {"un": {"message": "missing value in: taskId"}}}}
|
||||
```
|
||||
|
||||
`fw_client.py` prüft beide Ebenen (`a00.un.un` und Top-Level `ex`/`un`)
|
||||
und wirft `FamilyWallError`. Nie eine Response als Erfolg werten ohne
|
||||
beide Fehlerebenen zu prüfen.
|
||||
|
||||
### Sentinel-Wert `$empty`
|
||||
Das FiZ-Server-Framework nutzt `$empty` als Sentinel-String um optionale
|
||||
Felder zu löschen. Alle anderen Werte (`""`, `null`, `"null"`, `"0"`,
|
||||
`"-1"`, `"remove"`, `"clear"`, Epoch-Timestamps) werden vom Server mit
|
||||
`"is not a valid Date"` abgelehnt.
|
||||
|
||||
**Aktuell genutzt für:**
|
||||
- `dueDate=$empty` → löscht Fälligkeitsdatum in `taskupdate2`
|
||||
|
||||
**Gefunden durch:** React-Fiber-Analyse des nativen `datetime-local`-Inputs –
|
||||
nach Klick auf "Löschen" im Custom-Datepicker wird der Input-Wert auf
|
||||
`"$empty"` gesetzt, was beim Speichern an die API gesendet wird.
|
||||
|
||||
## Bekannte Endpoints
|
||||
|
||||
### `famlistfamily` – Kreise abrufen
|
||||
### `famlistfamily` – Kreise + Mitglieder abrufen
|
||||
POST https://api.familywall.com/api/famlistfamily
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter:** keine (verifiziert)
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
```
|
||||
a00.r.r[] → Kreise
|
||||
.metaId → eindeutige Kreis-ID (Format family/<id>)
|
||||
.name → Kreisname
|
||||
.family_id → numerische Kreis-ID
|
||||
.members[] → Mitglieder des Kreises
|
||||
.accountId → numerische Account-ID
|
||||
.metaId → Mitglieds-ID (Format familymember/<accountId>_<familyId>)
|
||||
.firstName → Vorname (Display-Name; bevorzugen gegenüber .name)
|
||||
.name → E-Mail-Adresse (Family Wall Default wenn kein Anzeigename)
|
||||
.role → Familienrolle (z.B. "Unknown", "Parent", "Child")
|
||||
.right → Berechtigung (z.B. "SuperAdmin", "Admin", "Member")
|
||||
.color → Profilfarbe als Hex-String (z.B. "#FF8086")
|
||||
.medias[0].pictureUrl → Avatar-URL (generierter Default wenn pictureDefault=true)
|
||||
.identifiers[] → Kontaktdaten
|
||||
.type → Typ (z.B. "Email")
|
||||
.value → Wert (z.B. E-Mail-Adresse)
|
||||
.familyId → Zugehöriger Kreis (= metaId des Kreises)
|
||||
.isloggedaccount → "true" wenn das der angemeldete Account ist
|
||||
.joinDate → Beitrittsdatum (ISO 8601)
|
||||
.lastLoginDate → Letzter Login (ISO 8601)
|
||||
.locale → Spracheinstellung (z.B. "de_DE")
|
||||
.timeZone → Zeitzone (z.B. "Europe/Berlin")
|
||||
.invitations[] → Offene Einladungen (leer wenn keine)
|
||||
.coverUri → Cover-Bild URL
|
||||
```
|
||||
|
||||
### `taskgettasklists` – Listen abrufen
|
||||
POST https://api.familywall.com/api/taskgettasklists
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter:** keine
|
||||
|
||||
**Response-Struktur:** zu verifizieren beim ersten echten Call
|
||||
**Response-Struktur:**
|
||||
```
|
||||
a00.r.r[] → Kreise
|
||||
.metaId → Kreis-ID (z.B. "family/23431854")
|
||||
.name → Kreis-Name
|
||||
.members[] → Mitglieder des Kreises
|
||||
.accountId → numerische User-ID
|
||||
.firstName → Anzeigename (bevorzugen)
|
||||
.name → E-Mail als Fallback
|
||||
.role → Familienrolle (Parent/Child/Unknown)
|
||||
.right → Berechtigung (SuperAdmin etc.)
|
||||
.color → Profilfarbe (#RRGGBB)
|
||||
.medias[0].pictureUrl → Avatar-URL
|
||||
.identifiers[type=Email].value → E-Mail-Adresse
|
||||
```
|
||||
|
||||
### `accgetallfamily` – Listen + Tasks abrufen
|
||||
### `accgetallfamily` – Listen, Tasks, Kategorien abrufen
|
||||
POST https://api.familywall.com/api/accgetallfamily
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter:**
|
||||
|
||||
@@ -127,425 +121,204 @@ Content-Type: application/x-www-form-urlencoded
|
||||
| `a01call` | `"taskcategorysync"` |
|
||||
| `a02call` | `"tasksync"` |
|
||||
|
||||
Hinweis: `a03call=tasklistsync` ist **kein gültiger Endpoint** — API antwortet mit
|
||||
"The call tasklistsync is not registered". Nicht verwenden.
|
||||
`partnerScope`, `a03id`, `withStateBean` werden weggelassen.
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
**Response-Struktur:**
|
||||
```
|
||||
a00 → famlistfamily-Daten (Kreise inkl. members[]) – Nebeneffekt
|
||||
a01.r.r.updatedCreated[] → taskcategorysync (Kategorien/Abteilungen pro Liste)
|
||||
.metaId → Kategorie-ID (Format taskCategory/<familyId>_<sysId>)
|
||||
.name → Kategoriename (sprachabhängig, z.B. "Beverages")
|
||||
.emoji → Emoji-Symbol der Kategorie
|
||||
.systemCategoryId → numerische System-ID (sprach-unabhängig)
|
||||
.taskListType → Listentyp der Kategorie (z.B. "SHOPPING_LIST")
|
||||
**Wichtig:** Alle 171 Kategorien sind SHOPPING_LIST —
|
||||
es gibt keine TODO-Kategorien in der API
|
||||
.sortingIndexByTaskList → dict: Listen-ID → Sortierposition
|
||||
**Achtung:** enthält ALLE Listen-IDs unabhängig vom Typ
|
||||
→ NICHT für Typ-Filterung verwenden!
|
||||
Stattdessen: taskListType der Kategorie mit
|
||||
taskListType der Liste (aus taskgettasklists) vergleichen
|
||||
.locale → Sprache des Namens (z.B. "de", "en", "ru", "fr", "es",
|
||||
"it", "nl", "pt", "sv", "ko", "ja")
|
||||
Jede Sprache = eigener Eintrag mit eigenem metaId/systemCategoryId
|
||||
**Wichtig:** Custom-Kategorien (rights.canDelete='true') haben
|
||||
kein locale-Feld gesetzt — sie werden sprachunabhängig
|
||||
zurückgegeben und dürfen nie über locale gefiltert werden.
|
||||
.hiddenByTaskList → Liste von Listen-IDs, in denen die Kat. versteckt ist
|
||||
a02.r.r.updatedCreated[] → tasksync (Tasks)
|
||||
.metaId → eindeutige Task-ID
|
||||
.taskId → identisch zu metaId (zweiter Alias)
|
||||
.text → Aufgabentext
|
||||
.description → optionale Beschreibung
|
||||
.taskListId → Zugehörigkeit zur Liste
|
||||
.complete → "true" / "false" (String, nicht Boolean!)
|
||||
.categories[] → Listen-Level-Systemkategorie (z.B. SYS-CAT-SHOPPINGLIST);
|
||||
NICHT die spezifische Task-Kategorie — immer identisch
|
||||
für alle Tasks einer Liste
|
||||
.system → "true" (immer System-Kategorie)
|
||||
.name → Listen-Systemkategorien (z.B. "SYS-CAT-SHOPPINGLIST", "SYS-CAT-TODOS")
|
||||
.taskCategoryId → spezifische Task-Kategorie (verifiziert): metaId-Format
|
||||
(z.B. "taskCategory/23431854_200"), null wenn nicht gesetzt
|
||||
.assignee[] → zugewiesene Mitglieder: [{accountId: "..."}]
|
||||
.assigneeIds[] → zugewiesene Member-IDs als String-Array (z.B. ["23431898"])
|
||||
.dueDate → Fälligkeitsdatum (ISO 8601, z.B. "2026-04-30T18:00:00.000Z"; null wenn nicht gesetzt)
|
||||
.reminder → Erinnerungsregel (Objekt mit reminderUnit, reminderType, reminderValue — nicht das Fälligkeitsdatum!)
|
||||
.recurrency → Wiederholungsregel (optional)
|
||||
.sortingIndex → Anzeigereihenfolge
|
||||
a00.r.r[] → Kategorien (taskcategorysync)
|
||||
.metaId → Kategorie-ID
|
||||
.name → Kategoriename (Systembezeichnung)
|
||||
.taskListType → SHOPPING_LIST oder TODOS
|
||||
.sortingIndexByTaskList → Sortierreihenfolge pro Liste
|
||||
.rights.canDelete → "true" = custom, null = System
|
||||
.locale → Sprachcode (de/en/fr/...), fehlt bei custom
|
||||
|
||||
a02.r.r.updatedCreated[] → Tasks (tasksync)
|
||||
.metaId → Task-ID
|
||||
.text → Aufgabentext
|
||||
.description → optionale Beschreibung
|
||||
.taskListId → Listen-ID
|
||||
.complete → "true" / "false" (String!)
|
||||
.taskCategoryId → Kategorie-ID (optional)
|
||||
.dueDate → Fälligkeitsdatum (ISO 8601, optional)
|
||||
.assignee[] → Liste von Member-IDs (optional)
|
||||
.assigneeIds[] → alternativ zu assignee[]
|
||||
```
|
||||
|
||||
**Kategorie-Zuweisung bei taskcreate2 / taskupdate2 (verifiziert):**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) |
|
||||
|
||||
Hinweise:
|
||||
- Wert muss das vollständige metaId-Format `taskCategory/<familyId>_<systemCategoryId>` sein.
|
||||
Nur der numerische `systemCategoryId`-Teil (z.B. `200`) führt zu API-Fehler
|
||||
`"cannot find task category id=200"`.
|
||||
- Das `categories[]`-Feld in der Response zeigt immer `SYS-CAT-SHOPPINGLIST`
|
||||
(Listen-Level-Systemkategorie, unabhängig vom gesetzten `taskCategoryId`).
|
||||
Die tatsächliche Task-Kategorie ist im Feld `taskCategoryId` der Task gespeichert.
|
||||
- Nur für Einkaufslisten (`taskListType=SHOPPING_LIST`) relevant;
|
||||
TODO-Listen haben keine Kategorien.
|
||||
|
||||
## Systembezeichnungen für Listen-Namen
|
||||
|
||||
Bekannte Systembezeichnungen werden deutsch übersetzt:
|
||||
|
||||
| Systembezeichnung | Deutsch |
|
||||
|---|---|
|
||||
| `SYS-CAT-SHOPPINGLIST` | `Einkaufsliste` |
|
||||
|
||||
Unbekannte Bezeichnungen werden unverändert zurückgegeben.
|
||||
Mapping-Tabelle bei Bedarf erweitern.
|
||||
|
||||
### `wallget` – Aktivitäten (Wall) abrufen
|
||||
### `wallget` – Wall-Aktivitäten abrufen
|
||||
POST https://api.familywall.com/api/wallget
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `nb` | Anzahl Einträge (z.B. `"20"`) |
|
||||
| `date` | optional, für Paginierung (Datum des letzten Eintrags) |
|
||||
| `accountId` | optional |
|
||||
| `type` | optional |
|
||||
| `nested` | optional |
|
||||
| `masterNested` | optional |
|
||||
| `sortBy` | optional |
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
**Response-Struktur:**
|
||||
```
|
||||
a00.r.r[]
|
||||
.metaId → ID der Aktivität (= wallMessageId)
|
||||
.refType → Aktivitätstyp (z.B. STATUS, FAMILY_CREATED)
|
||||
.text → Text (optional, fehlt bei System-Events)
|
||||
.creationDate → Datum (ISO 8601)
|
||||
.accountId → Autor-ID
|
||||
.metaId → Post-ID (z.B. "wall/23431854_31119189")
|
||||
.type → STATUS, FAMILY_CREATED, etc.
|
||||
.text → Post-Text
|
||||
.modifDate → Timestamp (ISO 8601)
|
||||
.creator.accountId → Author-ID
|
||||
.moodMap → {"<accountId>": ["STAR"]} für Likes
|
||||
.moodStarShortcut → true wenn geliked
|
||||
.comments[] → Kommentare
|
||||
```
|
||||
|
||||
**Response-Struktur:** zu verifizieren beim ersten echten Call
|
||||
|
||||
### `wallactivityget` – Einzelne Aktivität abrufen
|
||||
POST https://api.familywall.com/api/wallactivityget
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `accountId` | optional |
|
||||
| `masterNested` | optional |
|
||||
|
||||
**Response-Struktur:** zu verifizieren beim ersten echten Call
|
||||
(Endpoint noch nicht implementiert — für spätere Versionen)
|
||||
|
||||
## Fehlerstruktur-Varianten
|
||||
|
||||
Die Family Wall API gibt Fehler in zwei verschiedenen Strukturen zurück:
|
||||
|
||||
| Struktur | Beispiel | Erkennungsregel |
|
||||
|---|---|---|
|
||||
| Top-Level `ex` | `{"ex": {"ex": {...}}}` | `"ex" in body` |
|
||||
| Top-Level `un` | `{"un": {"un": {...}}}` | `"un" in body` |
|
||||
| Verschachtelt `a00.un` | `{"a00": {"un": {"un": {...}}}}` | `"un" in body["a00"]` |
|
||||
|
||||
Ab v0.4.15 erkennt `fw_client.py` alle drei Varianten und wirft `FamilyWallError`.
|
||||
Zuvor wurden `a00.un`-Fehler still ignoriert (silent fail), was zu irreführenden
|
||||
`{"updated": True}`-Antworten bei fehlgeschlagenen Updates führte.
|
||||
|
||||
**Bekannte Endpoints mit `a00.un`-Fehlern:**
|
||||
- `taskupdate2` mit ungültigem `dueDate`-Wert
|
||||
- `taskcategorydelete` mit falschem Parameter-Namen (obsolet nach v0.4.x-Fix)
|
||||
|
||||
## Debug-Logging
|
||||
|
||||
Wenn die Umgebungsvariable `FW_DEBUG=1` gesetzt ist, loggt `fw_client.py`
|
||||
vollständige Request-Bodies und Responses nach stderr. Dient zur Verifikation
|
||||
offener Punkte (z.B. `type`-Parameter beim Login, Kreis-Felder in Response).
|
||||
|
||||
**Wichtig:** Keine Secrets in Debug-Ausgaben (Passwort maskieren).
|
||||
|
||||
### `taskcreate2` – Task erstellen
|
||||
POST https://api.familywall.com/api/taskcreate2
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `taskListId` | ja | Listen-ID aus `get_lists` (z.B. `taskList/123_456`) |
|
||||
| `text` | ja | Aufgabentitel |
|
||||
| `description` | nein | Optionale Beschreibung |
|
||||
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) |
|
||||
| `dueDate` | nein | Fälligkeitsdatum ISO 8601 (z.B. `"2026-04-30T18:00:00"`); gespeichert als `dueDate` im Task-Objekt |
|
||||
| `assignee` | nein | Member-ID aus `get_members` (z.B. `"23431898"`); für mehrere Zuweisungen: mehrfach senden (`assignee=id1&assignee=id2`); gespeichert als `assigneeIds[]` im Task-Objekt |
|
||||
| `taskListId` | ja | Listen-metaId |
|
||||
| `text` | ja | Aufgabentext |
|
||||
| `description` | nein | Beschreibung |
|
||||
| `taskCategoryId` | nein | Kategorie-metaId (vollständig, z.B. `taskCategory/23431854_200`) |
|
||||
| `dueDate` | nein | ISO 8601 (z.B. `2026-04-30T18:00:00`) |
|
||||
| `assignee` | nein | Member-accountId, mehrfach sendbar für mehrere Zuweisungen |
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
**Response:**
|
||||
```
|
||||
a00.r.r → vollständiges Task-Objekt der neu erstellten Task
|
||||
.metaId → eindeutige Task-ID (z.B. "task/23431854_726362809")
|
||||
.taskListId → Listen-ID
|
||||
.text → Titel
|
||||
.complete → "false" (immer, direkt nach Erstellung)
|
||||
a00.r.r → vollständiges Task-Objekt
|
||||
.metaId → neue Task-ID
|
||||
```
|
||||
|
||||
### `taskupdate2` – Task aktualisieren
|
||||
POST https://api.familywall.com/api/taskupdate2
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `metaId` | ja | Task-ID aus `get_tasks` |
|
||||
| `text` | nein | Neuer Titel (mindestens eines der optionalen Felder erforderlich) |
|
||||
| `description` | nein | Neue Beschreibung |
|
||||
| `taskCategoryId` | nein | Kategorie-MetaId aus `get_categories` (z.B. `taskCategory/23431854_200`) |
|
||||
| `dueDate` | nein | Fälligkeitsdatum ISO 8601 (z.B. `"2026-04-30T18:00:00"`); gespeichert als `dueDate` im Task-Objekt |
|
||||
| `assignee` | nein | Member-ID aus `get_members` (z.B. `"23431898"`); für mehrere Zuweisungen: mehrfach senden (`assignee=id1&assignee=id2`); leerer String (`""`) entfernt alle Zuweisungen (nicht verifiziert); gespeichert als `assigneeIds[]` im Task-Objekt |
|
||||
| `taskListId` | nein | Ziel-Listen-ID zum Verschieben des Tasks (verifiziert – ändert `taskListId` im Task-Objekt) |
|
||||
| `metaId` | ja | Task-metaId |
|
||||
| `text` | nein | neuer Titel |
|
||||
| `description` | nein | neue Beschreibung |
|
||||
| `taskCategoryId` | nein | neue Kategorie-metaId |
|
||||
| `dueDate` | nein | ISO 8601 oder `$empty` zum Löschen |
|
||||
| `assignee` | nein | Member-accountId (mehrfach sendbar), `""` zum Entfernen aller |
|
||||
| `taskListId` | nein | neue Listen-metaId (verschiebt Task) |
|
||||
|
||||
Hinweis: `taskListId` ist optional – ohne diesen Parameter bleibt der Task in seiner aktuellen Liste.
|
||||
|
||||
**Response-Struktur:** kein spezifischer Rückgabewert – Erfolg = kein `ex`/`un`-Key auf Top-Level.
|
||||
|
||||
**dueDate Clearing – `"$empty"` Sentinel (verifiziert April 2026, v0.4.16):**
|
||||
|
||||
Der FiZ-Framework verwendet den Sentinel-Wert `"$empty"` um optionale Felder zu leeren.
|
||||
**Hinweis:** `taskListId` ist NICHT Pflicht beim Update.
|
||||
|
||||
**Response:**
|
||||
```
|
||||
POST taskupdate2
|
||||
metaId=task/23431854_726333919&dueDate=$empty
|
||||
→ dueDate wird auf null gesetzt ✓
|
||||
a00.r.r → vollständiges Task-Objekt
|
||||
```
|
||||
|
||||
`update_task(clear_due_date=True)` sendet intern `dueDate=$empty`.
|
||||
|
||||
**Getestete Werte die fehlschlugen (vor der Lösung):**
|
||||
|
||||
| Wert | API-Response |
|
||||
|---|---|
|
||||
| `""` (leerer String) | `a00.un: " is not a valid Date"` |
|
||||
| `"null"` | `a00.un: "Cannot parse date val=null"` |
|
||||
| `"undefined"` | `a00.un: "Cannot parse date val=undefined"` |
|
||||
| `"0"`, `"-1"` | `a00.un: "... is not a valid Date"` |
|
||||
| `"remove"` / `"clear"` | `a00.un: "Cannot parse date val=..."` |
|
||||
| `"1970-01-01T00:00:00"` | Erfolgreich – setzt dueDate auf Unix-Epoch (kein Clearing!) |
|
||||
| Feld weglassen | dueDate bleibt unverändert |
|
||||
| `removeDueDate=1`, `clearDueDate=1` | werden ignoriert |
|
||||
|
||||
**Wichtig:** Die Fehler werden als `a00.un` zurückgegeben (nicht Top-Level `un`).
|
||||
Ab v0.4.15 erkennt `fw_client` diese und wirft `FamilyWallError`.
|
||||
|
||||
### `taskmark` – Task als erledigt/offen markieren
|
||||
### `taskmark` – Task abhaken/wiedereröffnen
|
||||
POST https://api.familywall.com/api/taskmark
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `taskId` | ja | Task-ID aus `get_tasks` (**WICHTIG: `taskId`, nicht `metaId`!**) |
|
||||
| `complete` | ja | `"true"` oder `"false"` (String, nicht Boolean!) |
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `taskId` | Task-metaId ⚠️ nicht `metaId`! |
|
||||
| `complete` | `"true"` oder `"false"` (String!) |
|
||||
|
||||
**Achtung:** Der Endpoint heißt intern `taskId`, nicht `metaId`.
|
||||
Falsche Parameter (`metaId`, `id`, `taskMetaId`) werden serverseitig ignoriert –
|
||||
die API antwortet dann mit einem Fehler in `a00.un.un` (nicht Top-Level!),
|
||||
der vom Standard-Error-Check im fw_client übersehen wird.
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
**Response:**
|
||||
```
|
||||
a00.r.r → vollständiges Task-Objekt mit aktuellem Stand (inkl. lastAction: "MARK_COMPLETED")
|
||||
a00.r.r → Task-Objekt mit lastAction: "MARK_COMPLETED"
|
||||
```
|
||||
|
||||
### `metadelete` – Objekt löschen
|
||||
### `metadelete` – Task löschen
|
||||
POST https://api.familywall.com/api/metadelete
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `id` | ja | Task-ID aus `get_tasks` (**WICHTIG: `id`, nicht `metaId`!**) |
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `id` | Task-metaId ⚠️ nicht `metaId`! |
|
||||
|
||||
Hinweis: `metadelete` ist ein generischer Lösch-Endpoint für beliebige Objekte (Tasks, etc.).
|
||||
Entsprechend vorsichtig verwenden.
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
**Response:**
|
||||
```
|
||||
a00.r.r → "true" (String)
|
||||
a00.r.r → "true" (String)
|
||||
```
|
||||
|
||||
**Fehlerverhalten:** Bei falschem Parameter-Namen (`metaId`, `taskId` etc.) antwortet die API
|
||||
mit `{"a00": {"un": {"un": {"message": "missing value in: id"}}}}` auf Top-Level ohne `un`-Key
|
||||
→ wird vom fw_client fälschlich als Erfolg interpretiert. Daher ist der korrekte Parameter-Name
|
||||
kritisch.
|
||||
### `wallmood` – Post liken
|
||||
POST https://api.familywall.com/api/wallmood
|
||||
|
||||
### `taskcategoryput` – Kategorie erstellen
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `wall_message_id` | Post-metaId ⚠️ nicht `wallId` oder `id`! |
|
||||
| `moodType` | `"STAR"` für Like |
|
||||
|
||||
**Bekannte Einschränkungen:**
|
||||
- Unlike: Endpoint/Parameter unbekannt (Service Worker verschlüsselt Request-Body)
|
||||
- Self-Like: API antwortet 200, macht aber serverseitig nichts
|
||||
- `moodType="NONE"` und andere Werte haben keine Wirkung
|
||||
|
||||
**Response:**
|
||||
```
|
||||
a00.r.r → Wall-Objekt mit moodMap, refAction: "MOOD_STAR"
|
||||
```
|
||||
|
||||
### `taskcategoryput` – Kategorie erstellen/aktualisieren
|
||||
POST https://api.familywall.com/api/taskcategoryput
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert via FW_DEBUG=1):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `name` | ja | Kategorie-Name (beliebiger String) |
|
||||
| `emoji` | nein | Icon: Unicode-Emoji-Zeichen (z.B. `🌿`) oder beliebiger String-Code (z.B. `"FOOD"`) — wird as-is gespeichert |
|
||||
| `name` | ja | Kategoriename |
|
||||
| `emoji` | nein | Unicode-Emoji oder beliebiger String |
|
||||
|
||||
Hinweise:
|
||||
- Die neue Kategorie wird **allen** Listen der Familie zugeordnet — es gibt keine per-Liste-Einschränkung.
|
||||
- Benutzerdefinierte Kategorien haben `systemCategoryId=null` und `rights.canDelete='true'`.
|
||||
- System-Kategorien haben `rights.canDelete=null` — API erlaubt Löschen, aber `delete_category` Tool verweigert es.
|
||||
**Hinweis:** Kategorien sind family-wide – `listId` hat keine Wirkung.
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
**Response:**
|
||||
```
|
||||
a00.r.r → vollständiges Kategorie-Objekt
|
||||
.metaId → neue Kategorie-ID (z.B. "taskCategory/23431854_4956637")
|
||||
.name → Kategorie-Name
|
||||
.taskListType → "SHOPPING_LIST" (automatisch gesetzt)
|
||||
.familyId → Familien-ID
|
||||
.accountId → Account-ID des Erstellers
|
||||
.rights.canDelete → "true" (custom Kategorien)
|
||||
.rights.canUpdate → "true" (custom Kategorien)
|
||||
.emoji → gespeicherter Icon-Wert (falls übergeben)
|
||||
```
|
||||
|
||||
**Fehlerverhalten:** Ohne `name`-Parameter:
|
||||
```json
|
||||
{"a00": {"un": {"un": {"FiZClassId": "502", "message": "cat without a name ..."}}}}
|
||||
a00.r.r → Kategorie-Objekt mit metaId
|
||||
```
|
||||
|
||||
### `taskcategorydelete` – Kategorie löschen
|
||||
POST https://api.familywall.com/api/taskcategorydelete
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert via FW_DEBUG=1):**
|
||||
**Body-Parameter:**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `id` | ja | Kategorie-MetaId aus `get_categories` (**WICHTIG: `id`, nicht `metaId`!**) |
|
||||
|
||||
**Achtung:** Falscher Parameter-Name `metaId` führt zu:
|
||||
```json
|
||||
{"a00": {"un": {"un": {"FiZClassId": "502", "message": "In request, missing value in : id"}}}}
|
||||
```
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
```
|
||||
a00.r.r → "true" (String)
|
||||
```
|
||||
|
||||
**Wichtig – System-Kategorien:** Die API erlaubt technisch das Löschen von System-Kategorien
|
||||
(`taskCategory/<familyId>_200` etc.), entfernt sie aber nur aus der Familie — nicht global.
|
||||
Das `delete_category`-MCP-Tool verweigert dies (Schutz via `rights.canDelete`-Check).
|
||||
Erkennung: custom Kategorien haben `rights.canDelete='true'`; System-Kategorien haben `rights.canDelete=null`.
|
||||
|
||||
### `wallmood` – Wall-Post liken
|
||||
POST https://api.familywall.com/api/wallmood
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
**Body-Parameter (verifiziert via FW_DEBUG=1):**
|
||||
|
||||
| Parameter | Pflicht | Wert |
|
||||
|---|---|---|
|
||||
| `wall_message_id` | ja | Post-ID aus `get_activities` (z.B. `wall/23431854_31119189`) |
|
||||
| `moodType` | ja | `"STAR"` (einziger bekannter Wert — entspricht dem Like-Button in der App) |
|
||||
|
||||
**Verhalten (verifiziert):**
|
||||
- `wallmood` mit `moodType: "STAR"` ist eine **idempotente SET-Operation** — kein Toggle!
|
||||
- Mehrfaches Aufrufen mit denselben Parametern hinterlässt denselben Zustand
|
||||
- `moodType: "LIKE"` wurde serverseitig als `"STAR"` gespeichert → korrekter Wert ist `"STAR"`
|
||||
- Nur für Post-Typ `STATUS` wirksam; `FAMILY_CREATED` und vermutlich System-Posts ignorieren den Call
|
||||
|
||||
**Unlike – nicht implementierbar (Stand: April 2026):**
|
||||
|
||||
Ausgiebig getestete Ansätze, die alle fehlschlugen:
|
||||
|
||||
| Ansatz | Ergebnis |
|
||||
| Parameter | Wert |
|
||||
|---|---|
|
||||
| `moodType: "NONE"` / `"REMOVE"` / `"DELETE"` / `""` | moodMap unverändert |
|
||||
| `moodType` ganz weglassen | moodMap unverändert |
|
||||
| `moodStarShortcut: "false"` als Parameter | moodMap unverändert |
|
||||
| Alternative Endpoints: `wallmooddelete`, `wallmoodremove`, `wallmoodelete`, `wallunmood`, `wallcommentdelete`, `wallmoodstar`, `wallstar`, `wallreact`, `wallreactdelete` | alle: `"The call X is not registered"` (502) |
|
||||
| `metadelete` auf Mood-Comment-ID | löscht den Comment, aber `moodStarShortcut` bleibt gesetzt |
|
||||
| `id` | Kategorie-metaId ⚠️ nicht `metaId`! |
|
||||
|
||||
Die Web-App nutzt einen Service Worker, der Requests abfängt — der echte Unlike-Payload
|
||||
ist dadurch nicht per Browser-DevTools inspizierbar. Unlike bleibt bis zur weiteren
|
||||
Analyse nicht unterstützt.
|
||||
**Hinweis:** System-Kategorien (`rights.canDelete=null`) können technisch
|
||||
gelöscht werden, sind dann aber dauerhaft weg und nicht wiederherstellbar.
|
||||
MCP-Server schützt dagegen durch Check auf `rights.canDelete`.
|
||||
|
||||
**Response-Struktur (verifiziert):**
|
||||
```
|
||||
a00.r.r → vollständiges Wall-Message-Objekt
|
||||
.metaId → Post-ID (= wallMessageId)
|
||||
.wallMessageId → Post-ID (identisch zu metaId)
|
||||
.refType → Aktivitätstyp (z.B. "STATUS")
|
||||
.refAction → Letzte Aktion (z.B. "MOOD_STAR")
|
||||
.text → Post-Text
|
||||
.postAccountId → Account-ID des Post-Autors
|
||||
.accountId → Account-ID (identisch)
|
||||
.moodMap → dict: accountId → ["STAR"] wenn geliked
|
||||
.moodStarShortcut → "true" wenn Like-Shortcut aktiv
|
||||
.comments[] → Liste von Kommentaren/Moods
|
||||
.commentId → Kommentar-ID (Format wallComment/...)
|
||||
.accountId → Account-ID des Kommentators
|
||||
.mood → Mood-Typ (z.B. "STAR")
|
||||
.text → Kommentartext (falls vorhanden)
|
||||
.creationDate → Erstelldatum (ISO 8601)
|
||||
.creationDate → Erstelldatum des Posts (ISO 8601)
|
||||
.modifDate → Letztes Änderungsdatum (ISO 8601)
|
||||
.familyId → Kreis-ID (Format family/...)
|
||||
.rights.canUpdate → "true"/"false"
|
||||
.rights.canDelete → "true"/"false"
|
||||
```
|
||||
### `taskgettasklists` – Listen abrufen (alternativ)
|
||||
POST https://api.familywall.com/api/taskgettasklists
|
||||
|
||||
**Like-Zustand bestimmen (zwei Indikatoren, beide auswerten):**
|
||||
Wird intern zur Verifikation von `taskListType` genutzt.
|
||||
|
||||
| Feld | Typ | Bedeutung |
|
||||
|---|---|---|
|
||||
| `moodStarShortcut` | `"true"` / `"false"` | Primär: direktes User-Like-Flag für den anfragenden Account |
|
||||
| `moodMap[accountId]` | `["STAR"]` | Sekundär: accountId → Mood-Liste; enthält `"STAR"` wenn geliked |
|
||||
## Systembezeichnungen für Listen-Namen
|
||||
|
||||
Beide Indikatoren können den Like-Zustand korrekt abbilden — je nach API-internem
|
||||
Speicherpfad ist nur einer gesetzt. Immer beide prüfen.
|
||||
| Systembezeichnung | Deutsch |
|
||||
|---|---|
|
||||
| `SYS-CAT-SHOPPINGLIST` | `Einkaufsliste` |
|
||||
| `SYS-CAT-TODOLIST` | `Aufgaben` |
|
||||
|
||||
**Silent-Fail-Szenarien (API antwortet 200, aber Like wird nicht gesetzt):**
|
||||
- **Self-Like-Restriction**: Eigener Post kann nicht geliked werden
|
||||
(verifiziert: Account 23431898 kann Post `wall/23431854_31119189` nicht liken,
|
||||
obwohl API regulär antwortet — `modifDate` bleibt eingefroren)
|
||||
- **Unsupported Post-Typ**: `FAMILY_CREATED`-Posts ignorieren wallmood-Calls
|
||||
- **Rate-Limit**: Nach vielen Calls kann die API Still-Fails zurückgeben
|
||||
## Debug-Logging
|
||||
|
||||
Erkennungsmerkmal für Silent-Fail: `modifDate` im Response identisch zum Vorherigen
|
||||
AND `moodStarShortcut: false` AND `moodMap: {}`.
|
||||
Wenn die Umgebungsvariable `FW_DEBUG=1` gesetzt ist, loggt `fw_client.py`
|
||||
vollständige Request-Bodies und Responses nach stderr.
|
||||
|
||||
## Noch zu verifizieren
|
||||
**Wichtig:** Keine Secrets in Debug-Ausgaben (Passwort maskieren).
|
||||
|
||||
- ~~Exakter Wert für `type`-Parameter beim Login~~ → nicht senden (verifiziert per JS-Analyse)
|
||||
- ~~Response-Struktur von `famlistfamily` (Kreise)~~ → a00.r.r[], metaId + name (verifiziert)
|
||||
- ~~Ob `a03call=tasklistsync` benötigt wird~~ → **nein**, kein gültiger Endpoint (verifiziert)
|
||||
- Listen-IDs aus `a01.r.r.updatedCreated[].sortingIndexByTaskList`-Keys (verifiziert)
|
||||
- Listen-Namen und Zähler (remainingTaskNumber, totalTaskNumber) → noch unbekannt
|
||||
- Kreis-Zuordnung in `accgetallfamily`-Response → noch offen
|
||||
- ~~Ob `partnerScope` / `withStateBean` benötigt werden~~ → nein (verifiziert)
|
||||
- Session-Lebensdauer (irrelevant da kein Caching)
|
||||
- ~~`taskcreate2`: Response-Struktur~~ → `a00.r.r` = vollständiges Task-Objekt (verifiziert)
|
||||
- ~~`taskupdate2`: ob `taskListId` Pflichtfeld ist~~ → **nein**, nicht erforderlich (verifiziert)
|
||||
- ~~`taskmark`: korrekter Parameter-Name~~ → **`taskId`** (nicht `metaId`!) (verifiziert)
|
||||
- ~~`metadelete`: korrekter Parameter-Name + Response-Struktur~~ → **`id`**, Response `"true"` (verifiziert)
|
||||
- ~~`wallmood`: Parameter-Name `wallId`~~ → **`wall_message_id`** (verifiziert via API-Fehlermeldung)
|
||||
- ~~`wallmood`: `moodType`-Werte, Toggle vs. explizit, Response-Struktur~~ → verifiziert: idempotentes SET mit `"STAR"`, kein Toggle (siehe oben)
|
||||
- `wallmood` Unlike: Mechanismus unbekannt — Service Worker verhindert Browser-Inspektion; alle getesteten Ansätze fehlgeschlagen (siehe oben)
|
||||
- ~~`taskcreate2` / `taskupdate2`: Kategorie-Paramter-Name~~ → **`taskCategoryId`**, Wert = vollständige metaId (verifiziert)
|
||||
- ~~`taskcategoryput`: Body-Parameter, Response-Struktur~~ → `name` (Pflicht), `emoji` (optional), Response = neues Kategorie-Objekt (verifiziert)
|
||||
- ~~`taskcategorydelete`: Body-Parameter~~ → **`id`** (nicht `metaId`!), Response = `"true"` (verifiziert)
|
||||
- ~~`taskcreate2` / `taskupdate2`: Fälligkeitsdatum-Parameter~~ → **`dueDate`**, ISO 8601 String, gespeichert als `dueDate` im Task-Objekt (verifiziert)
|
||||
- ~~`taskcreate2` / `taskupdate2`: Zuweisung-Parameter~~ → **`assignee`** (Member-ID String; mehrere Werte = mehrfach senden); gespeichert als `assigneeIds[]` (verifiziert)
|
||||
- ~~`taskupdate2`: Task verschieben~~ → **`taskListId`** als optionaler Parameter setzt neue Liste (verifiziert)
|
||||
- `taskupdate2`: Alle Zuweisungen entfernen (leere `assignee`-Liste) → noch nicht verifiziert
|
||||
- ~~`taskupdate2`: `dueDate` entfernen (Clearing)~~ → **`dueDate=$empty`** (FiZ-Sentinel, verifiziert April 2026, v0.4.16)
|
||||
**Wann FW_DEBUG=1 nutzen:**
|
||||
- Neue Endpoints verifizieren
|
||||
- Parameter-Namen unbekannt
|
||||
- Silent-Fail debuggen
|
||||
- Service Worker blockiert Browser-DevTools
|
||||
|
||||
## Service Worker
|
||||
|
||||
Die Family Wall Web-App registriert einen Service Worker der bestimmte
|
||||
HTTP-Requests abfängt und modifiziert bevor sie das Netzwerk erreichen.
|
||||
Betroffen sind u.a. Unlike-Calls und möglicherweise andere schreibende
|
||||
Operationen.
|
||||
|
||||
**Folge:** Browser-DevTools Network-Tab und JS-Interceptoren (XHR/Fetch)
|
||||
zeigen nicht den echten Request-Body für diese Calls.
|
||||
**Lösung:** `FW_DEBUG=1` auf MCP-Server-Seite zeigt was tatsächlich gesendet wird.
|
||||
|
||||
## Offene Punkte
|
||||
|
||||
- Unlike-Endpoint (Service Worker blockiert Analyse)
|
||||
- Erinnerungen (reminder) – nur Premium-Account
|
||||
- Wiederholungen (repeat) – nur Premium-Account
|
||||
- Sortierung von Kategorien via API
|
||||
|
||||
Reference in New Issue
Block a user