Zum Hauptinhalt springen
WerkstubeDokumentation
Zur Werkstube
Dokumentation/Automatisierungen
Abläufe, die mitarbeiten

Automatisierungen mit Lua schreiben

Die versionierte Werkstube-Lua-Standardbibliothek mit Kontext, Text, Listen, Datum, Geld und sicheren Aktionen verwenden.

Automatisierungen mit Lua schreiben

Mit einer festen API-Version beginnen

Neue Skripte beginnen in der ersten nichtleeren Zeile mit dieser Direktive:

-- @werkstube api=1

Damit bleibt die Bedeutung deines Skripts stabil, auch wenn später eine neue API-Version hinzukommt. Eine unbekannte, fehlerhafte oder weiter unten platzierte Direktive wird beim Speichern abgelehnt.

API 1 teilt die verfügbaren Funktionen bewusst in drei globale Tabellen:

  • ctx enthält unveränderliche Daten des aktuellen Laufs.
  • std verarbeitet Werte ohne Nebenwirkungen.
  • action protokolliert, ruft KI auf oder führt berechtigungsgeprüfte Werkstube-Aktionen aus.
-- @werkstube api=1
local job_id = ctx.require("entity_id", "Auftrags-ID fehlt")
local title = std.text.normalize_space(ctx.get("payload.title", "Auftrag"))

action.jobs.add_history{
  jobId = job_id,
  text = title .. " wurde automatisch geprüft.",
  actor = "Automatisierung",
}

Laufkontext mit ctx

ctx.trigger enthält das vollständige Ereignis. Bei optionalen oder verschachtelten Feldern sind Punktpfade sicherer:

local status = ctx.get("payload.status", "unbekannt")
local first_item = ctx.get("payload.items.1")
local invoice_id = ctx.require("entity_id", "Rechnungs-ID fehlt")

ctx.require(pfad, meldung) beendet den Lauf mit deiner Meldung, wenn der Wert fehlt. Prüfe Pflichtwerte, bevor eine Aktion ausgeführt wird.

Weitere feste Kontextwerte sind:

  • ctx.dry_run — true während eines Probelaufs
  • ctx.started_at — stabiler Laufstart als RFC-3339-Zeitpunkt
  • ctx.timezone — derzeit Europe/Berlin
  • ctx.limits — die tatsächlich aktiven Laufzeit- und Datengrenzen

Werte mit std verarbeiten

Die Standardbibliothek ist unter std verfügbar. Ihre Funktionen verändern keine Werkstube-Daten und liefern für dieselben Eingaben dasselbe Ergebnis.

BereichFunktionen
Texttrim, is_blank, normalize_space, contains, starts_with, ends_with, split, join, replace, truncate
Listenmap, filter, find, any, all, unique, sort_by, sum, group_by
Objekteget, has, keys, values, copy, merge
Datumtoday, parse, format, add_days, diff_days, is_weekend
Geldparse, percent, add_tax, allocate, format
JSONencode, decode
Validierungrequired, one_of, email

Die rechte Referenz im Skripteditor ist der vollständige, durchsuchbare Funktionskatalog. Ein Klick fügt den exakten Funktionsnamen oder das passende Snippet ein.

Text, Listen und Objekte

local names = std.list.map(ctx.get("payload.items", {}), function(item)
  return std.text.normalize_space(item.name)
end)

local total = std.list.sum(ctx.get("payload.items", {}), "amount_cents")
local grouped = std.list.group_by(ctx.get("payload.items", {}), "status")
local email = std.object.get(ctx.trigger, "payload.customer.email")

action.log(std.text.join(names, ", "), total, grouped, email)

Listen müssen fortlaufende Indizes ab 1 haben. sort_by liefert eine stabil sortierte Kopie, unique behält das erste Vorkommen und object.keys sortiert Schlüssel lexikografisch. copy und merge erzeugen tiefe Kopien; Eingabetabellen werden nicht verändert.

Datum und Zeitzone

local today = std.date.today()
local due = std.date.add_days(today, 14)
local label = std.date.format(due, "%d.%m.%Y")

if std.date.is_weekend(due) then
  action.warn("Das Zieldatum " .. label .. " liegt am Wochenende")
end

Datumsfunktionen arbeiten mit Kalenderdaten im Format YYYY-MM-DD. parse nimmt außerdem RFC-3339-Zeitpunkte an und rechnet sie in ctx.timezone um. Unterstützte Formatteile sind %Y, %m und %d. Ein Tag ist ein Kalendertag, nicht pauschal 24 Stunden.

Geld ohne Fließkommafehler

Geldwerte sind immer ganzzahlige Cent. Prozentwerte werden als Basispunkte angegeben: 1900 bedeutet 19 Prozent.

local net_cents = std.money.parse("1250,00")
local gross_cents = std.money.add_tax(net_cents, 1900)
local shares = std.money.allocate(gross_cents, { 2, 1 })
action.log(std.money.format(gross_cents), shares)

std.money.parse akzeptiert Dezimalbeträge wie 1250, 1250,5 oder 1250.50, aber keine Tausendertrennzeichen. percent und add_tax runden halbe Cent von null weg. allocate verteilt den Restcent deterministisch und erhält die Gesamtsumme.

Striktes JSON und Validierung

local data = std.json.decode('{"priority":"high"}')
local priority = std.validate.one_of(data.priority, { "normal", "high" })
local recipient = std.validate.email(ctx.require("payload.email"))
action.log(std.json.encode({ priority = priority, recipient = recipient }))

std.json akzeptiert nur endliche JSON-Werte, begrenzte Listen oder Objekte und keine gemischten Tabellenschlüssel, Zyklen oder Funktionen. Die Kodierung ist deterministisch. std.validate.required lehnt auch leere Texte ab; one_of vergleicht Typ und Wert.

Werkstube-Aktionen

Aktionen behalten ihre fachlichen Bereiche:

  • action.customers — Kunden anlegen, laden und suchen
  • action.jobs — Aufträge anlegen, laden, weiterschalten und Verlauf ergänzen
  • action.inquiries, action.quotes, action.invoices — kaufmännische Vorgänge
  • action.calendar — Termine anlegen
  • action.inventory — Bestand, Nachbestellung und Bestellungen

Die genaue Liste hängt von den Betriebsmodulen und Rechten der ausführenden Person ab. Die Editor-Referenz kennzeichnet nicht verfügbare Aktionen und verhindert, dass ein Beispiel mit fehlenden Rechten unbemerkt eingesetzt wird. Der Server prüft dieselben Rechte bei jedem Lauf erneut.

action.notify("Titel", "Text", {
  severity = "warning", -- info | success | warning | danger
  link = "/auftraege/42",
})

action.email("chef@example.de", "Betreff", "Nachricht")
local summary = action.ai("Fasse die Eingabe sachlich zusammen: " .. ctx.get("payload.text", ""))

Fehler und Protokoll

local ok, result = pcall(function()
  return action.jobs.get{ id = ctx.require("entity_id") }
end)

if not ok then
  action.warn("Auftrag konnte nicht geladen werden: " .. tostring(result))
  return
end

action.log("Auftrag geladen", result)

action.log, action.warn und action.error schreiben strukturierte Tabellen lesbar ins Laufprotokoll. action.error protokolliert nur; zum Abbrechen verwendest du error("Begründung") oder ctx.require. Behandle Aktionsfehler nur, wenn der Ablauf danach fachlich sicher enden darf.

Sicherheitsgrenzen

Lua läuft ohne Zugriff auf Betriebssystem, Dateien, Prozesse, Netzwerk-Sockets oder native Bibliotheken. Gesperrt sind insbesondere os, io, debug, package, syscall, ffi, socket, http, require und dynamisches Laden von Code.

API 1 begrenzt jeden Lauf auf:

  • 64 KB Skriptquelle
  • 10 Sekunden Laufzeit
  • 64 KB Laufprotokoll
  • 2.000 Tabelleneinträge und 10 Verschachtelungsebenen bei Standardbibliotheks- und JSON-Operationen

Lies die aktuellen Werte programmatisch unter ctx.limits, statt sie im Skript zu wiederholen.

Bestehende Skripte

Skripte ohne Direktive laufen weiterhin mit API 1 und den historischen Aliasen werk.*, action.trigger, action.get, action.require und action.json. Sie sind für Kompatibilität vorhanden, aber als veraltet markiert. Bei Änderungen empfiehlt sich die schrittweise Umstellung auf ctx, std und die fachlichen action-Bereiche.

Vollständige, direkt ausführbare Vorlagen findest du unter Beispiele für Automatisierungen.

Vor dem Aktivieren

  1. Pflichtwerte mit ctx.require prüfen.
  2. Syntaxanzeige im Editor abwarten.
  3. Einen Probelauf mit dem Beispiel-Ereignis ausführen.
  4. Im Protokoll Eingaben, Ergebnisse und gewählten Pfad prüfen.
  5. Irreversible Schritte wie E-Mail oder Bestellung möglichst ans Ende setzen.

Mehr zum sicheren Wiederholen fehlgeschlagener Läufe findest du unter Testen & Laufprotokolle.

War diese Seite hilfreich?Dein Hinweis hilft uns, die Dokumentation besser zu machen.