OneTwoApps App Ratings & ReviewsBenachrichtigung

Optional · Benachrichtigung · Schritt für Schritt

Der Bericht wartet, bis du ihn öffnest. Die Nachricht kommt zu dir.

Am Ende des täglichen Laufs prüft das Skript, ob seit der letzten Nachricht etwas passiert ist: eine neue Rezension mit ein oder zwei Sternen, eine App, deren Ampel umspringt, ein Sprung der Absturzrate. Nur dann geht eine knappe Nachricht raus, per E-Mail, Slack, Teams oder Webhook, mit dem Kernsatz der KI und dem Link zum Bericht. Nichts Neues, keine Nachricht.

3Kanäle: E-Mail, Slack, Teams, Webhook
3Auslöser, einzeln abschaltbar
1Nachricht je Vorfall, nie doppelt
0neue Anfragen an die KI
Beispiel

So kommt sie an

Bewertungen: 2 neue 1–2 ★ · Ampel rot: Scanbox

AMPEL
- Scanbox (Apple): gelb → rot – 3 × 1–2 ★ in 7 Tagen

NEUE 1–2-★-REZENSIONEN
- Scanbox (Apple), 1 ★, 2026-10-24, DEU, Version 2.4.1
  „Login geht nicht mehr“
  Seit dem Update komme ich nicht mehr rein.
  KI: Anmeldung schlägt seit Update fehl (Dringlichkeit hoch)
- Scanbox (Apple), 2 ★, 2026-10-23, AUT
  Dauernd Abstürze beim Öffnen des Postfachs.

Bericht: https://bewertungen.firma.intern/
Sätze mit „KI:“ stammen aus der gespeicherten KI-Analyse
(anthropic (claude-sonnet-5-5)); Zahlen stammen nie von der KI.

Wann sie läuft

Immer zuletzt. Und nur, wenn es etwas zu sagen gibt.

./ratings.sh daily macht drei Dinge nacheinander: Abruf (mit KI-Analyse, falls eingeschaltet), Bericht, Benachrichtigung. Sie steht bewusst am Ende, damit Ampel, KI-Sätze und der verlinkte Bericht auf dem Stand des Tages sind.

1Einmal je Vorfall

Das Skript merkt sich in data/notify-state.json, wann es zuletzt gemeldet hat, welche Ampelstufe jede App hatte und ob ein Absturzgrund vorlag, und führt dort einen Meldungsverlauf (die letzten 500 Vorfälle). Läuft daily am selben Tag noch einmal, findet es nichts Neues und schweigt. Den Verlauf zeigen ./ratings.sh notify --history und der Bericht unter „Datenstand“; neben einer roten oder gelben Ampel steht „seit 3 Tagen“.

2Der erste Lauf ist still

Nach dem Einschalten merkt sich der erste Lauf nur den Stand. Sonst käme eine Nachricht mit jeder jemals abgerufenen 1–2-★-Rezension. Wer das ausdrücklich will, setzt quietOnFirstRun auf false.

3Nicht zugestellt heißt nicht vergessen

Antwortet kein Kanal (Mailserver weg, Webhook mit Fehler), bleibt der gemerkte Stand stehen und der nächste Lauf meldet dieselben Vorfälle erneut. Hat ein Kanal zugestellt, gilt der Vorfall als gemeldet; der Lauf endet trotzdem mit Exit-Code 1, damit der Fehler im Log auffällt.

Die Benachrichtigung braucht keinen Zugang zu Apple, Google oder der KI. Sie liest nur die Dateien unter data/, dieselben wie der Bericht. Im GitHub-Workflow ist es derselbe Schritt, unter Windows der dritte Aufruf in daily.cmd. Mit notify.enabled: false (Standard) passiert gar nichts, daily läuft wie bisher.

Was eine Nachricht auslöst

Drei Auslöser. Dieselben Regeln wie die Ampel im Bericht.

Die Benachrichtigung erfindet keine eigenen Schwellen. Was im Bericht gelb oder rot wird, wird auch gemeldet; die Regeln stehen an einer Stelle (src/metrics.py) und sind im Bericht unter „Ampel“ erklärt.

Neue 1–2-★-Rezensionentriggers.negativeReviews

Jede Rezension mit ein oder zwei Sternen, die seit dem letzten Lauf neu abgerufen wurde: App, Sterne, Datum, Land, Version, Titel und Text (gekürzt). Läuft die KI-Analyse, steht ihr Kernsatz dazu, etwa „KI: Anmeldung schlägt seit Update fehl“. Mehr als zehn werden gezählt statt aufgelistet.

Ampelwechseltriggers.ampelChange

Eine App wird gelb oder rot, oder sie ist wieder grün. Mit den Gründen, wie sie der Bericht formuliert: „3 × 1–2 ★ in 7 Tagen“, „Abstürze 0,8 → 1,9“, „Store unter 4 ★“; „2 ohne Antwort“ nur, wenn diese Regel in der Einrichtung zugeschaltet ist (Vorgabe aus). Eine Besserung von rot auf gelb wird nicht gemeldet, erst die Rückkehr auf grün.

Absturzsprungtriggers.crashJump

Die Absturzrate je 1000 Sitzungen hat sich mindestens um die Hälfte erhöht oder liegt über 2, und das ist neu seit dem letzten Lauf. Auch ohne Ampelwechsel. Nur Apple, erst ab 500 Sitzungen im Fenster, wie im Bericht.

Was nicht auslöstbewusst

Rezensionen mit drei bis fünf Sternen, neue Downloads, Antworten des Supports, ein neues Update. Das steht alles im Bericht; die Nachricht soll selten sein und dann zählen.

Wie die Nachricht aussieht

Betreff, drei Blöcke, ein Link. Lesbar auf dem Handy.

Klartext, keine Bilder, kein HTML. Dieselbe Nachricht geht an E-Mail, Slack und Teams (dort als Karte, eine Zeile je Textblock); der Webhook bekommt sie zusätzlich als strukturiertes JSON.

1

Betreff

Zählt, was drin ist, damit die Posteingangsliste reicht: Bewertungen: 2 neue 1–2 ★ · Ampel rot: Scanbox. In Slack ist das die fette erste Zeile.

2

Blöcke

AMPEL mit Stufe vorher → nachher und Gründen. ABSTÜRZE mit der Rate vorher → nachher. NEUE 1–2-★-REZENSIONEN je eine Zeile Kopf, Titel, Text, KI-Satz. Leere Blöcke fehlen.

3

Fußzeile

Link zum Bericht aus settings.reportUrl (ohne Angabe der Dateipfad). Enthält die Nachricht KI-Sätze, nennt sie Anbieter und Modell. Zahlen kommen nie von der KI. Sprache de oder en.

// Webhook: dasselbe als JSON (gekürzt), Signatur im Header X-Ratings-Signature { "schemaVersion": 1, "reportUrl": "https://…", "subject": "Bewertungen: …", "text": "…", "events": [ { "kind": "negativeReview", "appName": "Scanbox", "platform": "apple", "review": { "rating": 1, "createdAt": "2026-10-24T10:00:00Z", "country": "DEU", "title": "Login geht nicht mehr", "text": "…" }, "ai": { "issue": "Anmeldung schlägt seit Update fehl", "urgency": "hoch" } }, { "kind": "ampelChange", "appName": "Scanbox", "ampel": { "before": 1, "after": 2, "reasons": [ { "code": "neg", "text": "3 × 1–2 ★ in 7 Tagen" } ] } } ] }

Einrichten · Schritt 1 bis 3

Ein Zugang, eine Umgebungsvariable, ein Block in config.json.

Der Block notify steht fertig in config/config.example.json. Du brauchst nur den Zugang zu dem Kanal, über den die Nachricht kommen soll. Alle drei gleichzeitig gehen auch.

1

Zugang beschaffen

E-Mail: SMTP-Server, Port, Benutzername und Passwort eines Postfachs, das senden darf; bei vielen Anbietern ein „App-Passwort“. Versand verschlüsselt, STARTTLS auf 587 oder SSL auf 465.
Slack: In Slack eine App mit „Incoming Webhooks“ anlegen und einen Webhook für den Kanal erzeugen. Die Adresse https://hooks.slack.com/services/… ist das Geheimnis.
Teams: Im Kanal die drei Punkte, „Workflows“, Vorlage „Post to a channel when a webhook request is received“ anlegen, nichts ändern. Die Adresse https://…logic.azure.com/workflows/… ist das Geheimnis. Noch nicht gegen ein echtes Teams geprüft.
Webhook: Eine HTTPS-Adresse, die JSON per POST annimmt (n8n, Zapier, Power Automate mit eigenem Ablauf, eigener Dienst). Optional ein gemeinsamer Schlüssel für die Signatur.

2

Geheimnis als Umgebungsvariable

Passwort, Webhook-Adresse und Signaturschlüssel stehen nie in config.json, nur ihr Variablenname. Für den täglichen Lauf gehört die Variable in die Umgebung des Zeitplaners: systemd-Unit, launchd-Plist, Aufgabenplanung oder die Secrets des CI-Systems (siehe BETRIEB.md).

# ~/.zshrc oder ratings.env des Dienstes
export RATINGS_SMTP_PASSWORD="…"
export RATINGS_SLACK_WEBHOOK="https://hooks.slack.com/services/…"
export RATINGS_TEAMS_WEBHOOK="https://…logic.azure.com/workflows/…"

# prüfen, ohne den Wert zu zeigen
test -n "$RATINGS_SMTP_PASSWORD" && echo gesetzt

Steht ein Passwort oder eine Webhook-Adresse versehentlich in der Datei, lehnt das Skript die Konfiguration ab. In Logs und Fehlermeldungen wird der geheime Teil entfernt.

3

Block eintragen

Am einfachsten in der Einrichtung im Browser, Schritt Benachrichtigung. Von Hand: enabled auf true, beim gewünschten Kanal ebenfalls. Dazu settings.reportUrl mit der Adresse, unter der die Firma den Bericht erreicht.

"settings": { "reportUrl":
  "https://bewertungen.firma.intern/" },
"notify": {
  "enabled": true,
  "language": "de",
  "channels": [
    { "type": "email", "enabled": true,
      "host": "smtp.firma.de", "port": 587,
      "security": "starttls",
      "username": "bericht@firma.de",
      "passwordEnvironmentVariable":
        "RATINGS_SMTP_PASSWORD",
      "from": "bericht@firma.de",
      "to": ["chef@firma.de"] },
    { "type": "slack", "enabled": true,
      "webhookUrlEnvironmentVariable":
        "RATINGS_SLACK_WEBHOOK" },
    { "type": "teams", "enabled": false,
      "urlEnvironmentVariable":
        "RATINGS_TEAMS_WEBHOOK" },
    { "type": "webhook", "enabled": false,
      "urlEnvironmentVariable": "RATINGS_WEBHOOK_URL",
      "secretEnvironmentVariable":
        "RATINGS_WEBHOOK_SECRET" }
  ]
}

Schritt 4 · Testen

Erst sehen, was gemeldet würde. Dann eine Testnachricht. Dann läuft es von selbst.

Beide Befehle gibt es auch im Menü unter „Status und Übersicht“. Keiner ändert den gemerkten Stand.

1

Probelauf

Zeigt die Nachricht, die der Tageslauf jetzt senden würde, mit allen Vorfällen. Es wird nichts gesendet und nichts gemerkt. Vor dem Einschalten ist das meist leer oder „erster Lauf, Stand gemerkt“.

./ratings.sh notify --dry-run
2

Testnachricht

Schickt eine kurze Testnachricht an alle freigeschalteten Kanäle und sagt, welcher zugestellt hat. So prüfst du Passwort, Port und Webhook-Adresse, ohne auf eine echte 1-★-Rezension zu warten.

./ratings.sh notify --test
Testnachricht: 2 von 2 Kanälen zugestellt.
3

Der nächste Tageslauf

Danach ist nichts mehr zu tun. Der erste echte Lauf merkt sich den Stand, jeder weitere meldet, was seitdem dazukam. Läuft der Zeitplaner schon, reicht es, die Umgebungsvariablen dort einzutragen.

./ratings.sh daily
… Bericht erzeugt …
Benachrichtigung: 3 Vorfälle, 2 von 2 Kanälen zugestellt.

Einstellungen

Wenige Schalter. Alle im Block notify, alle optional.

Die Vorgaben passen für den Alltag. Die Tabelle steht auch in BENACHRICHTIGUNG.md und README.md.

FeldBedeutungStandard
enabledSchalter für den ganzen Schrittfalse
languageSprache der Nachricht, de oder ende
triggersnegativeReviews, ampelChange, crashJump einzeln an oder ausalle true
quietOnFirstRunerster Lauf merkt sich nur den Standtrue
maxReviewsPerMessageso viele neue 1–2-★-Rezensionen stehen einzeln in der Nachricht, der Rest wird gezählt10
channels[].typeemail, slack, teams oder webhook; jeder Kanal hat ein eigenes enabledleer
emailhost, port, security (starttls, ssl, none nur zu 127.0.0.1), username, passwordEnvironmentVariable, from, to (Liste)Port 587, STARTTLS
slackwebhookUrlEnvironmentVariable
teamsurlEnvironmentVariable (Adresse des Teams-Workflows)
webhookurlEnvironmentVariable, optional secretEnvironmentVariable (HMAC-SHA256 über den Body, Header X-Ratings-Signature)
settings.reportUrlAdresse des bereitgestellten Berichts für den Link in der Nachrichtnull = Dateipfad

Welche Daten gehen raus

Rezensionsauszüge an deine Adressen. Nichts an uns, nichts an die KI.

Empfänger sind nur die Adressen, die du einträgst: dein Mailserver, dein Slack-Arbeitsbereich, dein Teams-Mandant, dein Webhook-Dienst. Rezensionen sind öffentliche Texte, können aber Angaben enthalten, die der Verfasser selbst hineingeschrieben hat. Die Empfänger gehören deshalb in die Firma, nicht in offene Verteiler.

Das steht in der Nachricht
  • je gemeldeter Rezension Sterne, Datum, Land, Version, Titel und Text (höchstens 300 Zeichen)
  • Name der App und Store
  • Ampelstufe vorher und nachher mit Gründen, Absturzrate vorher und nachher
  • der gespeicherte Kernsatz der KI zur Rezension, falls die Analyse läuft, mit Anbieter und Modell in der Fußzeile
  • der Link zum Bericht
Das nie
  • Nutzernamen, Adressen, Telefonnummern, E-Mail-Adressen der Rezensenten. Sie werden gar nicht erst gespeichert.
  • Rezensions-IDs in E-Mail und Slack; im Webhook-JSON steht die ID, damit ein eigener Dienst die Rezension im Bericht wiederfindet
  • Zugangsdaten, Schlüssel, Kundennamen, Store-Kennzahlen
  • eine neue Anfrage an einen KI-Anbieter: die KI-Sätze sind die gespeicherten Ergebnisse der Analyse
  • Daten an den Anbieter dieses Skripts: es gibt keinen Server

Verschlüsselt. E-Mail nur mit STARTTLS oder SSL und Zertifikatsprüfung, Webhooks nur über HTTPS; unverschlüsselt geht allein ein Relay auf demselben Rechner. Passwort, Webhook-Adresse und Signaturschlüssel kommen nur aus Umgebungsvariablen; in Logs wird auch der geheime Teil einer Slack-Adresse entfernt. Der bereitgestellte Bericht nennt die Benachrichtigung in seiner Datenschutzerklärung (Abschnitt 4b).

Wenn etwas nicht klappt

Die Meldung sagt, was fehlt. Hier steht, was dann zu tun ist.

Das Log liegt in data/logs/notify.log. Ein Kanalfehler hält die anderen Kanäle nicht auf.

„Benachrichtigung ist ausgeschaltet“bei --test

notify.enabled steht auf false.

„Kein Kanal freigeschaltet“bei --test

Beim Kanal fehlt "enabled": true.

„Umgebungsvariable … ist nicht gesetzt oder leer“Zeitplaner

Die Variable steht in deiner Shell, aber nicht in der Umgebung des Dienstes. In systemd-Unit, Plist, Aufgabenplanung oder CI-Secrets eintragen.

„SMTP-Verbindung … fehlgeschlagen“E-Mail

Host, Port, Firewall. Port 587 braucht starttls, Port 465 ssl.

„E-Mail konnte nicht gesendet werden“ mit 535E-Mail

Benutzername oder Passwort falsch; viele Anbieter verlangen ein App-Passwort statt des Kontopassworts.

„Teams-Workflow abgelehnt“Teams

Workflow gelöscht, ausgeschaltet oder Adresse falsch kopiert (sie ist lang und enthält sig=…). In Teams unter „Workflows“ den Ausführungsverlauf ansehen, dort steht der Grund.

„Slack-Webhook abgelehnt“Slack

Webhook in Slack gelöscht oder Adresse falsch kopiert. In Slack einen neuen erzeugen und die Variable ersetzen.

Nachricht kommt, obwohl nichts neu istZustand

data/notify-state.json fehlt oder wurde gelöscht; dann ist es wieder ein erster Lauf, der nur mit quietOnFirstRun: false sendet.

Keine Nachricht trotz neuer 1–2 ★Prüfen

./ratings.sh notify --dry-run zeigt, was anliegt. Kam die Rezension vor dem Einschalten, gilt sie als bekannt.