Kostenlos testen
Handbuch

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 mit auto_publish/allow_html = false) — bei allow_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, oder installConnectorViaSftp), prüft ihn mit getConnectorStatus({test: true}) und contaoListPages,
  • aktiviert contao_editor und 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 als base_url vorschlagen.

  • Secrets (SSH-Schlüssel, Passphrase) nie im Chat erfragen: Secrets-Panel aktiv öffnen (focusUi mit Ziel secrets) und sagen, welcher Name einzutragen ist.

  • Web-Root selbst finden: wahrscheinlichsten Pfad setzen, ensureGateway aufrufen; 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/…), und base_url muss die öffentliche Domain sein — sonst Ping 404. Details: Hoster Mittwald.

  • profile => 'contao' festlegen (nicht auto). 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 => true ist 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 in admin_detail).

  • Wer tables zur Einschränkung setzt, muss mindestens tl_page, tl_article, tl_content, tl_files (lesen + schreiben) und für News/Termine tl_news, tl_news_archive, tl_calendar, tl_calendar_events freigeben.

  • Connector (kein SSH): 'mode' => 'persistent', dann im Manage-Bereich unter Verwaltung → DB-Connectoren herunterladen (oder im Builder-Chat per createDatabaseConnector), der Kunde lädt die Datei nach public/ (Contao 4.13 ohne public/: web/). Connectoren ab Gateway-Protokoll 4 (Download ab 25.09.2026) legen Versionen in tl_version an 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 über public/ (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_publish bleibt false für Redakteurs-Chats. true nur 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_html bleibt false, solange niemand freies HTML braucht. Ohne Opt-in lehnt contaoUpsertContentElement den Typ html ab (Anlegen und Ändern); Texte laufen über text. Auch mit Opt-in wird das HTML bereinigt (keine Scripts, iframes, on*-Handler, Rohtext-Elemente wie xmp).
  • 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:

  1. „Leg eine News an: Test von TaskMonkey, ein Satz.“ → Entwurf, Vorschau, Frage „Live schalten?“ mit Buttons.
  2. „Nein, Entwurf lassen“ → bleibt Entwurf. Im Contao-Backend nachsehen: News ist unveröffentlicht.
  3. Foto schicken: „Nimm das als Titelbild.“ → Bild in files/taskmonkey/…webp, News hat Bild.
  4. „Jetzt live.“ → Rückfrage → „Ja, live schalten“ → online, Seite im Browser prüfen (Cache wird geleert).
  5. „Mach das rückgängig.“ → wieder Entwurf. Im Backend unter Versionen steht der Stand mit Benutzer „TaskMonkey“.
  6. Sprachnachricht mit einem Termin („Nächsten Samstag 10 Uhr Tag der offenen Tür in der Werkstatt“) → Termin-Entwurf mit richtigem Datum.
  7. Einen Text auf einer Live-Seite ändern lassen → Entwurfs-Kopie; das Original bleibt online, bis der Kunde freigibt.
  8. „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 wie immobilien/neubau/… bleiben erhalten), Hinweis auf Seitentitel/Beschreibung des Originals, Texte in der Kopie ändern lassen, am Ende eine Rückfrage für alles (Sammelfreigabe).
  9. Sicherung: Auf dem Server liegt seit Schritt 1 ein automatisches Backup in tm-backups/<projekt>/ (neben public/). Im Browser https://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: contaoSetPublished merkt sich die Anfrage und akzeptiert confirm=true nur, 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>") (oder article:<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 wie bs_grid_parent zeigen 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 und javascript:-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 mit force). 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_url mit https://
  • Gateway/Connector auf Protokoll 5 (Backups + Undo); backups.dir im Ping liegt außerhalb von public/ 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
Zuletzt aktualisiert: 2026-10-13