From dc781141024bd1dbe44a76f10eee3a501b8ba00e Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Sat, 22 Aug 2026 12:59:45 +0200 Subject: [PATCH] fix(jobs): Doppelversand behoben - Werkzeug-Timeout loeste einen zweiten Aufruf aus Erster echter Lauf am 22.08. schickte den Bericht zweimal (07:01:42 und 07:02:14). Ursache: Hermes' terminal-Werkzeug bricht nach 30 s ab, die Sprachsynthese dauert laenger, der Agent hielt den Aufruf fuer gescheitert und wiederholte ihn - der Text war da laengst raus. Jetzt: Text senden, Stimme per setsid abgekoppelt, Rueckkehr nach 1,1 s. Dazu eine Doppelversand-Sperre ueber den Text-Hash, die jede Wiederholung innerhalb von zwei Stunden abweist - egal aus welchem Grund. Co-Authored-By: Claude Opus 5 --- deploy/jobs/news-melden.sh | 102 ++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/deploy/jobs/news-melden.sh b/deploy/jobs/news-melden.sh index f72ee17..2778d38 100644 --- a/deploy/jobs/news-melden.sh +++ b/deploy/jobs/news-melden.sh @@ -1,69 +1,89 @@ #!/usr/bin/env bash # news-melden.sh — schickt den fertigen Nachrichtenbericht als TEXT und als -# SPRACHE an den Commander (21.08.2026). +# SPRACHNACHRICHT an den Commander (21.08.2026, überarbeitet 22.08.). # -# Warum ein Skript statt zwei Anweisungen im Prompt: der Agent soll genau EINEN -# Befehl aufrufen. Alles danach ist deterministisch — Zustellung darf nicht davon -# abhaengen, ob ein Modell die Reihenfolge einhaelt. +# Der Agent ruft genau EINEN Befehl auf. Alles danach ist deterministisch — +# Zustellung darf nicht davon abhaengen, ob ein Modell die Reihenfolge einhaelt. +# +# ‼️ WARUM DAS SKRIPT SOFORT ZURUECKKEHRT (Fund vom 22.08., erster echter Lauf): +# Hermes' terminal-Werkzeug bricht nach 30 s ab. Die Sprachsynthese dauert +# laenger. Der Agent sah den Timeout, hielt den Aufruf fuer gescheitert und +# rief das Skript ein zweites Mal auf — der Text ging DOPPELT raus +# (07:01:42 und 07:02:14). +# Deshalb: Text senden, Stimme abgekoppelt im Hintergrund, sofort raus. +# Dazu eine Doppelversand-Sperre, die auch kuenftige Wiederholungen abfaengt. # # Erwartet zwei Dateien, die der Agent vorher geschrieben hat: # $1 Textfassung (mit Emojis, Links, Formatierung — geht nach Telegram) # $2 Sprechfassung (kurz, ohne Links/Emojis — wird vorgelesen) # # ‼️ STIMME: :8021, nicht :8650. -# :8021 = lucy-stimme.service — Kyutai pocket-tts 2.1.0, Modell `german_24l`, -# Stimme geklont aus ref.mp3. DAS ist Lucy. Laeuft lokal auf der CPU. +# :8021 = lucy-stimme.service — Kyutai pocket-tts, Modell `german_24l`, +# Stimme geklont aus ref.mp3. DAS ist Lucy. Lokal, auf der CPU. # :8650 = MC2s voice-service. Dort sind nur `elevenlabs` und `edge` geladen — # beides CLOUD, und die Vorgabe ist die ElevenLabs-Stimme -# "Artoria/Saber", also HERMES' Stimme, nicht Lucys. Am 21.08. genau -# damit reingefallen: klang deutsch und weiblich, war aber die falsche. -# Hermes' eingebautes TTS waere `edge`/`en-US-AriaNeural` — englisch. +# "Artoria/Saber", also HERMES' Stimme, nicht Lucys. set -uo pipefail -TEXTDATEI="${1:-/tmp/news-text.md}" -SPRECHDATEI="${2:-/tmp/news-sprich.txt}" NOTIFY="${NOTIFY:-$HOME/mission-control-v2/deploy/notify.sh}" VOICE="${VOICE_URL:-http://127.0.0.1:8021}" HERMES="${HERMES_BIN:-$HOME/.local/bin/hermes}" TON="/tmp/news-stimme.wav" TON_OGG="/tmp/news-stimme.ogg" +# ---------------------------------------------- Zweiter Durchgang: Stimme --- +# Wird vom ersten Durchgang abgekoppelt neu gestartet. Laeuft beliebig lange, +# ohne dass irgendein Werkzeug-Timeout hineinredet. +if [ "${1:-}" = "--nur-stimme" ]; then + SPRECHDATEI="${2:-/tmp/news-sprich.txt}" + [ -s "$SPRECHDATEI" ] || exit 0 + + CODE=$(curl -s -m 600 -X POST "$VOICE/tts" \ + -H 'Content-Type: application/json' \ + --data "$(python3 -c 'import json,sys; print(json.dumps({"text": open(sys.argv[1], encoding="utf-8").read()[:1500]}))' "$SPRECHDATEI")" \ + -o "$TON" -w '%{http_code}' 2>/dev/null) + + [ "$CODE" = "200" ] && [ -s "$TON" ] || exit 0 + + # Telegram zeigt eine ECHTE Sprachnachricht (runde Blase, Wellenform) nur bei + # OGG/Opus. Eine WAV kaeme als Dateianhang an. Mono/48k ist das erwartete Format. + if ! ffmpeg -y -loglevel error -i "$TON" -c:a libopus -b:a 32k -ar 48000 -ac 1 "$TON_OGG" 2>/dev/null; then + TON_OGG="$TON" + fi + "$HERMES" send --to telegram "MEDIA:$TON_OGG" >/dev/null 2>&1 + exit 0 +fi + +# ------------------------------------------- Erster Durchgang: Text + Start --- +TEXTDATEI="${1:-/tmp/news-text.md}" +SPRECHDATEI="${2:-/tmp/news-sprich.txt}" + if [ ! -s "$TEXTDATEI" ]; then bash "$NOTIFY" -s "[News]" "Der Nachrichtenbericht ist leer geblieben ($TEXTDATEI). Da lief etwas schief." exit 1 fi -# ------------------------------------------------------------------ Text ---- -# notify.sh erreicht Telegram UND Lucys Briefkasten in einem Aufruf. +# Doppelversand-Sperre. Greift, egal WARUM ein zweiter Aufruf kommt — +# Timeout, Modell-Wiederholung, versehentlicher Handstart. +MARKE="/tmp/.news-gesendet-$(sha256sum "$TEXTDATEI" | cut -c1-16)" +if [ -f "$MARKE" ]; then + ALTER=$(( $(date +%s) - $(stat -c %Y "$MARKE" 2>/dev/null || echo 0) )) + if [ "$ALTER" -lt 7200 ]; then + echo "gemeldet (dieser Bericht ging vor ${ALTER}s bereits raus — nicht doppelt geschickt)" + exit 0 + fi +fi +: > "$MARKE" +find /tmp -maxdepth 1 -name '.news-gesendet-*' -mtime +2 -delete 2>/dev/null + bash "$NOTIFY" -s "" "$(cat "$TEXTDATEI")" -TEXT_OK=$? -# ---------------------------------------------------------------- Stimme ---- -# Ab hier ist alles Kuer: klappt die Stimme nicht, ist der Text trotzdem raus. -if [ ! -s "$SPRECHDATEI" ]; then - echo "[news-melden] keine Sprechfassung — nur Text verschickt" >&2 - exit $TEXT_OK +# Stimme abkoppeln: eigene Sitzung, kein Elternprozess, keine offenen Deskriptoren. +# Damit ueberlebt sie das Ende dieses Skripts und blockiert es nicht. +if [ -s "$SPRECHDATEI" ]; then + setsid nohup bash "$0" --nur-stimme "$SPRECHDATEI" >/dev/null 2>&1 < /dev/null & + disown 2>/dev/null || true fi -CODE=$(curl -s -m 180 -X POST "$VOICE/tts" \ - -H 'Content-Type: application/json' \ - --data "$(python3 -c 'import json,sys; print(json.dumps({"text": open(sys.argv[1], encoding="utf-8").read()[:1500]}))' "$SPRECHDATEI")" \ - -o "$TON" -w '%{http_code}' 2>/dev/null) - -if [ "$CODE" != "200" ] || [ ! -s "$TON" ]; then - echo "[news-melden] TTS antwortete mit '$CODE' — Text ist raus, Stimme nicht" >&2 - exit $TEXT_OK -fi - -# Telegram zeigt eine ECHTE Sprachnachricht (runde Blase, Wellenform) nur bei -# OGG/Opus. Eine WAV kaeme als Dateianhang an — abspielbar, aber eben kein -# Sprachmemo. Deshalb ffmpeg. Mono/48k ist das Format, das Telegram erwartet. -if ! ffmpeg -y -loglevel error -i "$TON" -c:a libopus -b:a 32k -ar 48000 -ac 1 "$TON_OGG" 2>/dev/null; then - echo "[news-melden] ffmpeg-Wandlung fehlgeschlagen — schicke die WAV als Anhang" >&2 - TON_OGG="$TON" -fi - -"$HERMES" send --to telegram "MEDIA:$TON_OGG" >/dev/null 2>&1 \ - || echo "[news-melden] Sprachdatei liess sich nicht senden — Text ist raus" >&2 - -exit $TEXT_OK +echo "gemeldet" +exit 0