Rezept: Contao-Redakteur per Chat und WhatsApp
Der Kunde pflegt seine Contao-Website per Chat oder WhatsApp — Entwurf zuerst, live erst nach seinem Ja. Einrichtung unter einer Stunde.
Der Kunde schickt „Neue News: Wir haben ab Montag bis 19 Uhr geöffnet“ oder eine Sprachnachricht mit Foto — TaskMonkey legt den Beitrag als Entwurf in Contao an, zeigt eine kurze Vorschau und fragt „Live schalten?“. Erst nach dem Tipp auf „Ja, live schalten“ geht er online.
Baukasten: die Contao-Tools in config/tenants/_shared/tools/contao/ (Übersicht im _README.md dort) und der fertige Assistant contao_editor in _shared/assistants/contao_editor.php. Beides steht in jedem Workspace bereit; der Assistant ist versteckt, bis du ihn aktivierst.
Was der Redakteur kann (und was nicht)
| Kann | Tool |
|---|---|
| Seitenbaum zeigen, Seite mit Inhalten lesen (auch als Vorschau, mit CSS-ID/Klassen und Link-Zielen) | contaoListPages, contaoGetPage |
| Module und eigene Templates lesen („Wo kommt dieser Button her?“) — nur lesend; Änderungen an Modulen/Templates bleiben im Backend bzw. im Repo der Website | contaoGetModule, contaoReadTemplate |
| Seite anlegen (Entwurf, eindeutiger Alias, Layout vom Elternknoten, Artikel dazu; nicht in der Navigation = automatisch noindex) | contaoCreatePage |
| Seite oder Artikel samt Inhalten kopieren („Seite nachbauen“) — als Entwurf | contaoCopyPage, contaoCopyArticle |
| Artikel anlegen/umbenennen, CSS-Klassen setzen | contaoUpsertArticle |
| Text, Überschrift, Bild, Galerie, HTML, Link anlegen oder ändern | contaoUpsertContentElement |
| Element verschieben (Anfang, Ende, vor/nach einem anderen) | contaoMoveElement |
| News und Termine (inkl. Text und Titelbild) | contaoUpsertNews, contaoUpsertEvent, contaoListNews, contaoListEvents |
| Foto aus dem Chat hochladen (max. 2400 px, WebP, ohne EXIF/GPS) | contaoUploadImage |
| Live schalten / offline nehmen — nur mit Bestätigung, auch als Sammelfreigabe (eine Rückfrage für alle Entwürfe einer Seite) | contaoSetPublished |
| Letzte Änderung zurücknehmen (aus diesem Chat; sonst die letzte aus einem anderen Chat, nach Rückfrage) | contaoUndoLastChange |
| Sicherungen zeigen, eine Sicherung wiederherstellen — nur mit Bestätigung | contaoListBackups, contaoRestoreBackup |
Bewusst nicht: löschen (nur offline nehmen), Benutzer, Layouts, Module, Theme, Navigation konfigurieren, freies SQL. Vorhandene Bilder aus der Dateiverwaltung auswählen geht noch nicht — Bilder kommen aus dem Chat.
Getestet gegen Contao 4.13 und 5.3 (tests/TestCase/Tooling/ContaoToolsTest.php, Szenario Seite → Artikel → Element → Bild → veröffentlichen → Undo). Die Tools schreiben nur Spalten, die es in der Version des Kunden gibt.
Schnellweg: vom Builder einrichten lassen
Im Builder-Chat reicht „Richte den Contao-Redakteur für kunde.de ein“. Der Builder folgt dem Muster contao-redakteur (showPattern) und erledigt die Schritte 1–4 unten selbst:
- schreibt
database_connections.php(profile => 'contao',contao-Block mitauto_publish/allow_html=false) — beiallow_write(Exposure-Flag) nennt die Rückfrage zur Datei gleich die Tragweite, ein Ja genügt, - erzeugt den Connector (
createDatabaseConnector→ Download-Link für den Kunden, oderinstallConnectorViaSftp), prüft ihn mitgetConnectorStatus({test: true})undcontaoListPages, - aktiviert
contao_editorund richtet WhatsApp mit der eigenen Nummer des Kunden ein (channels.whatsapp.reply_assistant+auto_reply_allowlist).
Jede Datei und der Connector laufen durch das Freigabe-Gate: Der Builder zeigt die Änderung, geschrieben wird erst nach deinem „Ja“. Was er nicht kann und dir sagt: den Bildordner in Contao auf „öffentlich“ stellen (Schritt 2) und die Freischaltung von Nummern auf der TaskMonkey-Plattformnummer (macht der Betreiber, Schritt 4).
Sicherung ist eingebaut (ab Gateway-Protokoll 5): automatisches Backup vor der ersten Änderung eines Chats und ein Rollback-Eintrag pro Schreibvorgang beim Kunden (tm-backups/<projekt>/ über public/). Der Redakteur nimmt Änderungen mit contaoUndoLastChange zurück und stellt mit contaoListBackups/contaoRestoreBackup einen Stand wieder her (nur nach Ja im Chat). Der Builder prüft nach der Einrichtung, dass der Connector Protokoll 5 hat und das Backup-Verzeichnis beschreibbar ist.
Zeitplan (unter 1 Stunde)
| Schritt | Dauer |
|---|---|
| 1. Contao anbinden (SSH-Gateway oder Connector) | 15 min |
2. Workspace-Config contao |
5 min |
| 3. Assistant aktivieren | 2 min |
| 4. WhatsApp-Nummern freischalten | 10 min |
| 5. Probelauf mit dem Kunden | 15 min |
1. Contao anbinden
Details: Database Gateway. Kurzfassung in config/tenants/<workspace>/database_connections.php:
<?php
return [
'remote_databases' => [
'kunde_web' => [
// Mit SSH:
'hostname' => 'kunde.de',
'remote_user' => 'p123456',
'ssh_key' => secret('SSH_KUNDE_KEY'),
'web_root' => '~/html/kunde.de/current/public',
// Ohne SSH stattdessen: 'mode' => 'persistent' (Connector, s.u.)
'base_url' => 'https://kunde.de',
'profile' => 'contao',
'allow_write' => true,
],
],
];
Alle erlaubten Schlüssel stehen in der Referenz-Tabelle — nur die verwenden, nichts erfinden (web_url, domain, path gibt es nicht; writeTenantConfig lehnt unbekannte Schlüssel ab).
Vorgehen beim Einrichten (Builder):
-
Nicht den Kunden nach Technik fragen, was sich selbst ermitteln lässt: die Contao-Version erkennt
ensureGateway(version), die DB-Zugangsdaten liest das Gateway aus der Contao-Konfiguration, die Domain steht meist schon in der Workspace-Config (Website,contao/shop-Block) — die alsbase_urlvorschlagen. -
Secrets (SSH-Schlüssel, Passphrase) nie im Chat erfragen: Secrets-Panel aktiv öffnen (
focusUimit Zielsecrets) und sagen, welcher Name einzutragen ist. -
Web-Root selbst finden: wahrscheinlichsten Pfad setzen,
ensureGatewayaufrufen; scheitert SCP mit „No such file or directory“, den nächsten Kandidaten probieren (Contao 4:web/, Contao 5/Deployer:public/,current/public) — höchstens drei Versuche, dann den Kunden fragen. -
Mittwald (
pXXXXXX.mittwaldserver.info): SSH ist eine chroot, Pfade beginnen mit/html/<ordner>/…(nicht/home/pXXXXXX/…), undbase_urlmuss die öffentliche Domain sein — sonst Ping 404. Details: Hoster Mittwald. -
profile => 'contao'festlegen (nichtauto). Das Profil erlaubt die Tabellen, die die Tools brauchen (tl_page,tl_article,tl_content,tl_news*,tl_calendar*,tl_files) und sperrt Benutzer, Sessions, Logs. -
allow_write => trueist ein Exposure-Flag: per Builder nur nach Bestätigung im Chat setzbar (die Rückfrage zur Datei nennt die Tragweite, ein Ja genügt). Ohne es bleibt alles lesend, und jedes Schreib-Tool meldet dem Redakteur „Schreiben ist noch nicht freigeschaltet — bitte im Builder freischalten.“ (Details für den Admin inadmin_detail). -
Wer
tableszur Einschränkung setzt, muss mindestenstl_page,tl_article,tl_content,tl_files(lesen + schreiben) und für News/Terminetl_news,tl_news_archive,tl_calendar,tl_calendar_eventsfreigeben. -
Connector (kein SSH):
'mode' => 'persistent', dann im Manage-Bereich unter Verwaltung → DB-Connectoren herunterladen (oder im Builder-Chat percreateDatabaseConnector), der Kunde lädt die Datei nachpublic/(Contao 4.13 ohnepublic/:web/). Connectoren ab Gateway-Protokoll 4 (Download ab 25.09.2026) legen Versionen intl_versionan und leeren den Contao-Cache; ältere Connectoren funktionieren, nur ohne diese beiden Punkte — dann neu herunterladen. Automatische Sicherung und Undo brauchen Protokoll 5 (Connector-Download ab dem Backup-Release), siehe unten. -
Sicherung beim Kunden: Vor der ersten Änderung eines Chats legt das Gateway automatisch ein Backup der Inhalts-Tabellen an, jede Änderung bekommt einen Rollback-Eintrag — beides in
tm-backups/kunde_web/eine Ebene überpublic/(nicht im Web-Root, nichts davon geht an TaskMonkey). PHP muss dort schreiben dürfen; sonst'backup_dir' => '…'auf ein beschreibbares Verzeichnis außerhalb des Web-Roots setzen. Ohne diese Ablage schreibt das Gateway nicht (fail-closed). Details: Backups und Rollback.
Prüfen im Chat (Builder oder ein Assistant mit Gateway-Tools): beim Connector erst getConnectorStatus({project: "kunde_web", test: true}) (installiert, Protokoll aktuell, keine Fehlschläge), dann „Zeig mir den Seitenbaum von kunde_web“ → contaoListPages muss den Baum liefern. deployGateway bzw. ensureGateway zeigt unter backups.dir, wo die Sicherungen landen.
2. Workspace-Config
Optional, aber empfohlen — z. B. in database_connections.php neben remote_databases:
'contao' => [
'project' => 'kunde_web', // Default-Projekt (sonst das einzige mit profile contao)
'auto_publish' => false, // Default. true oder ['kunde_web'] = ohne Rückfrage live
'upload_dir' => 'files/taskmonkey', // Bild-Ordner unter files/
'allow_html' => false, // Default. true oder ['kunde_web'] = Elementtyp html (freies HTML) erlaubt
'language' => 'de', // Sprache für alt-Texte
'timezone' => 'Europe/Berlin', // für News-Datum und Termine
],
auto_publishbleibtfalsefür Redakteurs-Chats.truenur für autonome Abläufe ohne Menschen im Chat (z. B. der tägliche Blog-Task auf meco-media) — dann gibt es keine Rückfrage.allow_htmlbleibtfalse, solange niemand freies HTML braucht. Ohne Opt-in lehntcontaoUpsertContentElementden Typhtmlab (Anlegen und Ändern); Texte laufen übertext. Auch mit Opt-in wird das HTML bereinigt (keine Scripts, iframes, on*-Handler, Rohtext-Elemente wiexmp).- Bild-Ordner in Contao unter Dateiverwaltung einmal auf „Öffentlich“ stellen (Schloss-Symbol), sonst liefert Contao das Original nicht aus. Den Ordner legt das erste Bild an.
3. Assistant aktivieren
config/tenants/<workspace>/assistants/contao_editor.php:
<?php
return [
'assistants.contao_editor.hidden' => false,
// optional:
// 'assistants.contao_editor.name' => 'Website-Redaktion Muster GmbH',
// 'assistants.contao_editor.llm' => 'sonnet',
];
Der Assistant hat nur die fachlichen Contao-Tools und setSuggestions — kein executeQuery, keine internen Bausteine. Im Web-Chat erscheint er danach in der Assistant-Auswahl, mit eigener Begrüßung („Website-Redaktion … alles als Entwurf …“) statt der allgemeinen public.greeting des Workspaces. Eigener Text: 'assistants.contao_editor.greeting' => '…'.
4. WhatsApp
Nur die Nummern der Redakteure dürfen auf den Assistant zeigen — nie „alle Absender“, denn wer schreibt, kann live schalten.
Über die TaskMonkey-Plattformnummer (Kunde braucht keine eigene WhatsApp-Business-Nummer): in config/whatsapp.php unter routing.senders pro Redakteur (E.164 ohne +):
'4917012345678' => ['tenant' => '<workspace>', 'assistant' => 'contao_editor'],
Diese Datei ist Plattform-Config: Änderung committen und deployen. Im Workspace zusätzlich den Auto-Reply-Fallback einplanen (scheduled.php):
'auto_reply_whatsapp' => [
'enabled' => true,
'tool' => 'autoReplyChannel',
'interval' => '5min',
'args' => ['channel' => 'whatsapp', 'max_threads' => 5, 'phone_number_id' => '<Plattform-Nummer-ID>'],
],
Mit eigener Nummer des Kunden: channels.whatsapp mit enabled => true, reply_assistant => 'contao_editor' und auto_reply_allowlist => ['4917012345678', …] (Setup der Nummer: Kanäle).
Auf WhatsApp kommen die Ja/Nein-Fragen als Antwort-Buttons, Sprachnachrichten als transkribierter Text („🎤 …“, braucht DEEPGRAM_API_KEY), Fotos als Upload, den contaoUploadImage findet.
5. Probelauf mit dem Kunden
Am besten auf einer Testseite oder einem News-Archiv, das nicht im Menü hängt:
- „Leg eine News an: Test von TaskMonkey, ein Satz.“ → Entwurf, Vorschau, Frage „Live schalten?“ mit Buttons.
- „Nein, Entwurf lassen“ → bleibt Entwurf. Im Contao-Backend nachsehen: News ist unveröffentlicht.
- Foto schicken: „Nimm das als Titelbild.“ → Bild in
files/taskmonkey/…webp, News hat Bild. - „Jetzt live.“ → Rückfrage → „Ja, live schalten“ → online, Seite im Browser prüfen (Cache wird geleert).
- „Mach das rückgängig.“ → wieder Entwurf. Im Backend unter Versionen steht der Stand mit Benutzer „TaskMonkey“.
- Sprachnachricht mit einem Termin („Nächsten Samstag 10 Uhr Tag der offenen Tür in der Werkstatt“) → Termin-Entwurf mit richtigem Datum.
- Einen Text auf einer Live-Seite ändern lassen → Entwurfs-Kopie; das Original bleibt online, bis der Kunde freigibt.
- „Bau die Seite Preise als Preise 2027 nach“ → Vorschau (Titel, Adresse, Umfang) mit „Ja, kopieren“ — antwortet der Kunde mit einer anderen Adresse, kommt eine neue Vorschau statt sofort zu kopieren →
contaoCopyPage(Entwurf mit allen Artikeln, Grid, Klassen, Bildern; Ordner-Adressen wieimmobilien/neubau/…bleiben erhalten), Hinweis auf Seitentitel/Beschreibung des Originals, Texte in der Kopie ändern lassen, am Ende eine Rückfrage für alles (Sammelfreigabe). - Sicherung: Auf dem Server liegt seit Schritt 1 ein automatisches Backup in
tm-backups/<projekt>/(nebenpublic/). Im Browserhttps://kunde.de/../tm-backups/…bzw.https://kunde.de/tm-backups/aufrufen → darf nichts liefern. Im Chat „Welche Sicherungen gibt es?“ →contaoListBackups. „Stell die Sicherung von vorhin wieder her“ → Rückfrage mit Buttons → „Nein, abbrechen“ → nichts passiert. (Nur auf einer Testinstallation auch einmal „Ja, wiederherstellen“ zeigen: danach ist der Stand von vor Schritt 1 zurück, der Stand davor liegt als weiteres Backup daneben.)
Sicherheit und Freigabe — wie es funktioniert
- Entwurf zuerst, durchgesetzt von den Tools: neue Seiten, News, Termine unveröffentlicht; neue Elemente in Live-Bereichen unsichtbar; Änderungen an sichtbaren Elementen als unsichtbare Kopie.
- Live nur nach Ja, serverseitig:
contaoSetPublishedmerkt sich die Anfrage und akzeptiertconfirm=truenur, wenn danach eine neue Nachricht des Nutzers im Chat steht. Die KI kann sich nicht selbst bestätigen — auch nicht, wenn ein Website-Text sie dazu auffordert. Offene Anfragen liegen als Liste vor; eine neue überschreibt keine andere. - Sammelfreigabe:
contaoSetPublished(drafts_in="page:<id>")(oderarticle:<id>,type+ids) schaltet die Seite und alle Entwürfe darin mit einer Vorschau und einem Ja live — Elemente zuerst, die Seite zuletzt, alles in einer Undo-Gruppe. Nur Entwürfe, die TaskMonkey selbst angelegt hat; im Backend absichtlich ausgeblendete Inhalte bleiben aus (die Vorschau zählt sie). Eine Seite ohne sichtbaren Inhalt bekommt einen Warnhinweis — neue Seiten werden erst befüllt, dann gesammelt freigegeben. - Kopieren wie im Backend:
contaoCopyPage/contaoCopyArticleübernehmen die kompletten Datensätze (cssID, Grid-Elemente — Verweise in*parent*-Spalten wiebs_grid_parentzeigen auf die Kopie —,rsce_*-Daten, Bild-Referenzen, verschachtelte Elemente in Contao 5, Sichtbarkeit). Kopiert wird erst nach Vorschau und Ja zu genau diesen Angaben (geänderter Titel/Adresse = neue Vorschau). Seitentitel und Meta-Beschreibung stammen danach noch vom Original — der Redakteur weist darauf hin (doppelter Inhalt). Nicht kopiert: Unterseiten. Andere Verweise (Layout, Module, Element-Aliase, Links) zeigen auf dieselben Ziele wie im Original. - Website-Inhalte sind Daten: Tool-Results sind gekürzt, ohne HTML-Kommentare und Scripts und als Daten markiert. Beim Schreiben entfernen die Tools Scripts, iframes,
on*-Handler undjavascript:-Links. - Serialisierte Felder (
headline,cssID,size,multiSRC,meta) baut der Server, nie das LLM. - Nachvollziehbar: vor jeder Änderung eine Version in
tl_version(im Backend wiederherstellbar). Undo über das Rollback-Journal des Gateways (beim Kunden, ein Mechanismus für alle Gateway-Schreibzugriffe, projektweit über alle Chats): jede Änderung mit Vorher-Stand, gruppiert pro Auftrag (eine ganze Kopie oder Sammelfreigabe = ein Schritt), Änderungen aus einem anderen Chat nur nach Rückfrage, Konflikt-Schutz, wenn inzwischen jemand im Backend geändert hat (dann nur auf ausdrücklichen Wunsch mitforce). Aufbewahrung wie die Backups (backup_keep_days, Default 7 Tage). - Gesichert, bevor geändert wird: vor der ersten Änderung eines Chats automatisch ein Backup der Inhalts-Tabellen (Schreib-Allowlist, nie Benutzer & Co.) auf dem Server des Kunden, außerhalb des Web-Roots. Schlägt es fehl, wird nicht geschrieben. Wiederherstellen nur nach einem klaren Ja im Chat; der Stand davor wird vorher selbst gesichert.
- Cache: nach jeder sichtbaren Änderung
clearCmsCache(Tags; bei Seiten der ganze HTTP-Cache, wegen der Navigation).
Fehlerbilder
| Meldung | Ursache |
|---|---|
| „Kein Contao-Projekt angegeben. Verfügbar: …“ | Mehrere Projekte mit profile contao — contao.project setzen |
„Schreiben ist noch nicht freigeschaltet — bitte im Builder freischalten“ (error_code: write_not_enabled, Details in admin_detail) |
allow_write fehlt in remote_databases.<projekt> bzw. Connector vor der Freigabe erzeugt → neu herunterladen |
| Bild online nicht erreichbar | Upload-Ordner in Contao nicht „öffentlich“ |
audit: unsupported, Cache „nicht geleert“ |
Connector vor Protokoll 4 → neu herunterladen |
auto_backup.unsupported, Undo „nichts zum Zurücknehmen“ |
Connector vor Protokoll 5 → neu herunterladen |
| „Automatisches Backup vor dem Schreiben fehlgeschlagen“ / „Rollback journal unavailable“ | PHP darf über public/ nicht schreiben oder backup_dir zeigt in den Web-Root → backup_dir auf ein beschreibbares Verzeichnis außerhalb setzen |
| „Backup aborted: server time limit reached“ | Sehr große Inhalts-Tabellen bei knappem max_execution_time → Hoster fragen oder tables einschränken |
| „Live schalten braucht eine Bestätigung im Chat“ | Aufruf ohne Chat (Scheduled Task) → dort bewusst auto_publish |
| WhatsApp antwortet nicht | Absender nicht in routing.senders bzw. auto_reply_allowlist, oder auto_reply_whatsapp nicht aktiv |
Rollout-Checkliste
-
profile => 'contao',allow_write => true(im Chat bestätigt),base_urlmithttps:// - Gateway/Connector auf Protokoll 5 (Backups + Undo);
backups.dirim Ping liegt außerhalb vonpublic/und ist beschreibbar -
contao.auto_publish=false, Upload-Ordner öffentlich - Assistant aktiviert, WhatsApp nur für die Redakteurs-Nummern
- Probelauf 1–9 mit dem Kunden, Undo und Sicherung einmal gezeigt; Backup-Verzeichnis per Browser nicht erreichbar
- Kunde weiß: löschen geht nur im Backend, alles andere per Chat