Files
mission-control-v2/deploy/notify.sh
T
HitonabiandClaude Opus 5.5 12cefb427a Welle 3 · Lucys Freiheiten je Aktionsart, Rückfragen mit „ja 7“, Lucy untersucht rote Hinweise, Wochenbericht
- services/lucy_autonomie.py: Stufe je Aktionsart (nur lesen, erst fragen, selbst handeln), Start „mehr Freiheit“;
  Meldung danach (POST /api/lucy/gehandelt)
- services/rueckfragen.py: nummerierte Rückfragen, nur Knöpfe der Oberfläche (ERLAUBT), „ja“ führt genau die
  gespeicherte Aktion aus, 24 h gültig, hinfällig wenn der Hinweis weg ist, Nachprüfung danach
- notify.sh -k: Antworttasten (Antworttastatur über den Direktweg, ohne Eingriff in Hermes)
- services/ermittlung.py: Wächter meldet rote Hinweise an Lucy; Knopf wird Rückfrage, Lucy sieht über den
  api_server nach und schickt Ursache und Weg als eine Nachricht
- services/wochenbericht.py + mc2-wochenbericht.timer (So 07:30): feste Sätze, Absatz vom Modell, offene
  App-Updates als Rückfragen
- MCP: Werkzeuge fragen die Freiheiten (auf_bitte), neu: freiheiten, rueckfragen, rueckfrage_beantworten,
  sicherheitsupdates_einspielen, snapshot_zurueckspielen, snapshots, geraet_logs, dienst_logs
- Ausführer-Aktion journal (nur lesend), POST /api/homelab/ziele/{id}/sicherheitsupdates
- Läufe des Homelabs mit flock (Oberfläche und Steward schreiben beide)
- Skill homelab-orchestrator, SOUL-Abschnitt als Kopie in deploy/nur-box

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 19:12:50 +02:00

232 lines
9.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Meldeweg der Box (Autonomie E1): eine Nachricht an den User schicken.
# Primär: hermes send → Telegram (nutzt Gateway-Credentials, kein LLM nötig).
# Zweitweg (seit 24.09.2026): direkt an die Telegram-Bot-API mit den Zugangsdaten aus Hermes' .env —
# greift, wenn Hermes steht oder fehlt (die Homelab-Instanz hat gar kein Hermes).
# Letzter Ausweg: Logfile + wall.
#
# Nutzung: notify.sh "Nachricht" (oder via stdin: echo msg | notify.sh)
# notify.sh -s "[Update]" "Text" (Betreffzeile voranstellen)
# notify.sh -d -s "[Update]" "…" (dringend: geht auch nachts sofort raus)
# notify.sh -k "ja 7|nein 7" -s "[Rückfrage]" "…" (Antworttasten, seit 25.09.2026)
#
# Nachtruhe (User-Entscheid 24.09.2026): Zwischen 00:00 und 06:59 wird alles, was nicht dringend
# ist, in der Nacht-Warteschlange gesammelt und um 07:00 von morgenmeldung.sh als EINE Meldung
# verschickt. Dringend ist: -d, ein Betreff mit „Alarm“/„Notfall“ oder ein Text, der mit
# „KRITISCH“ beginnt. (Bis 24.09. lieferte niemand die Warteschlange aus — seit dem 21.08.
# gingen so alle Nachtmeldungen verloren, auch die Sonntags-Updates.)
#
# Exit 0 = zugestellt (Telegram, Fallback-Log oder Nacht-Warteschlange).
# MC_NOTIFY_STRICT=1: Exit 1, wenn Telegram scheitert (für morgenmeldung.sh, damit nichts verloren geht).
# Test-Schalter: MC_NOTIFY_STUNDE (Stunde vortäuschen), MC_NIGHT_QUEUE, MC_NOTIFY_HERMES (hermes-Befehl),
# MC_TELEGRAM_ENV (Datei mit TELEGRAM_BOT_TOKEN/TELEGRAM_HOME_CHANNEL), MC_TELEGRAM_API (Bot-API-Adresse),
# MC_NOTIFY_OHNE_WALL=1 (kein wall im letzten Ausweg), MC_NOTIFY_PYTHON (Python für die Antworttasten).
set -uo pipefail
LOG="${MC_NOTIFY_LOG:-$HOME/mc2-notify.log}"
NIGHT_QUEUE="${MC_NIGHT_QUEUE:-$HOME/.hermes/night-queue.txt}"
HERMES_BIN="${MC_NOTIFY_HERMES:-hermes}"
TG_ENV="${MC_TELEGRAM_ENV:-$HOME/.hermes/.env}"
TG_API="${MC_TELEGRAM_API:-https://api.telegram.org}"
SUBJECT=""
DRINGEND=0
KNOEPFE=""
while getopts ":s:dk:" opt; do
case "$opt" in
s) SUBJECT="$OPTARG" ;;
d) DRINGEND=1 ;;
k) KNOEPFE="$OPTARG" ;;
*) echo "usage: notify.sh [-d] [-k 'ja 7|nein 7'] [-s subject] <nachricht>" >&2; exit 2 ;;
esac
done
shift $((OPTIND - 1))
MSG="${1:-}"
[ -z "$MSG" ] && [ ! -t 0 ] && MSG="$(cat)"
if [ -z "$MSG" ]; then
# Bei leerer Pipe-Eingabe lautlos beenden (wichtig für Cron-Skripte ohne Ausgabe)
exit 0
fi
TS="$(date '+%Y-%m-%d %H:%M:%S')"
# Lucy-Briefkasten (Proaktivität A3): Meldung zusätzlich an MC2 spiegeln — die Desktop-Lucy
# spricht sie dann von sich aus. Best-effort (darf den Telegram-Weg nie aufhalten).
# MC_NOTIFY_NO_ANNOUNCE=1 unterdrückt das (der Health-Wächter legt seine Einträge selbst ab).
if [ "${MC_NOTIFY_NO_ANNOUNCE:-0}" != "1" ]; then
curl -sf -m 3 -X POST "${MC_ANNOUNCE_URL:-http://127.0.0.1:9001/api/voice/announce}" \
-H 'Content-Type: application/json' \
--data "$(python3 - "$SUBJECT" "$MSG" <<'PY'
import json, sys
print(json.dumps({"text": sys.argv[2], "subject": sys.argv[1], "source": "notify"}))
PY
)" >/dev/null 2>&1 || true
fi
# Dringend ist, was nicht bis zum Morgen warten darf.
case "${SUBJECT,,}" in
*alarm*|*notfall*) DRINGEND=1 ;;
esac
case "$MSG" in
KRITISCH*) DRINGEND=1 ;;
esac
# Lucys Stimme (User-Wunsch 25.09.2026: „Hermes' Persona ist Lucy — das muss sich in all ihren Meldungen
# widerspiegeln“). Was über Telegram rausgeht, kommt von ihrem Bot, also spricht sie: Anrede „Commander“, knapp,
# sachlich, ohne Emojis und Ausrufe (~/.hermes/SOUL.md). Die Kopfzeile folgt aus dem Betreff; beginnt die Nachricht
# schon mit ihrer Anrede (Morgenmeldung, von Lucy selbst formulierte Berichte), bleibt sie weg. Melde-Log und
# Auswertungen (update_verlauf.py, protokoll.py) behalten Betreff und Wortlaut wie bisher.
lucy_kopf() {
case "$MSG" in
Commander*|"Guten Morgen, Commander"*) return 0 ;;
esac
case "${SUBJECT,,}" in
*test*) echo "Commander, ein Test des Meldewegs." ; return 0 ;;
*morgenmeldung*) echo "Guten Morgen, Commander." ; return 0 ;;
esac
if [ "$DRINGEND" = "1" ]; then
echo "Commander, das ist dringend."
return 0
fi
case "${SUBJECT,,}" in
*homelab-problem*) echo "Commander, im Homelab stimmt etwas nicht." ;;
*"homelab wieder ok"*) echo "Commander, Entwarnung im Homelab." ;;
*homelab-update*) echo "Commander, Bericht aus dem Homelab." ;;
*box-problem*) echo "Commander, auf der Box stimmt etwas nicht." ;;
*"box wieder ok"*) echo "Commander, Entwarnung auf der Box." ;;
*update*) echo "Commander, Bericht von der Wartung." ;;
*deploy*) echo "Commander, Bericht vom Deploy." ;;
*erinnerung*) echo "Commander, eine Erinnerung." ;;
*radar*) echo "Commander, Bericht vom Modell-Radar." ;;
*rückfrage*) echo "Commander, eine Rückfrage." ;;
*"lucy selbst"*) echo "Commander, das habe ich selbst erledigt." ;;
*"lucy ermittelt"*) echo "Commander, ich habe nachgesehen." ;;
*wochenbericht*) echo "Commander, dein Wochenbericht." ;;
"") echo "Commander, kurz gesagt:" ;;
*) echo "Commander, $(printf '%s' "$SUBJECT" | tr -d '[]')." ;;
esac
}
# Dasselbe als kurze Marke für eine Zeile der Morgenmeldung („Homelab, Störung: …“ statt „[Homelab-Problem]: …“).
lucy_marke() {
case "${SUBJECT,,}" in
"") return 0 ;;
*alarm*|*notfall*) echo "Dringend" ;;
*homelab-problem*) echo "Homelab, Störung" ;;
*"homelab wieder ok"*) echo "Homelab, Entwarnung" ;;
*homelab-update*) echo "Homelab, Update" ;;
*box-problem*) echo "Box, Störung" ;;
*"box wieder ok"*) echo "Box, Entwarnung" ;;
*box-update*) echo "Box, Wartung" ;;
*rückfrage*) echo "Rückfrage" ;;
*"lucy selbst"*) echo "Selbst erledigt" ;;
*"lucy ermittelt"*) echo "Nachgesehen" ;;
*) printf '%s' "$SUBJECT" | tr -d '[]' ;;
esac
}
KOPF="$(lucy_kopf)"
# Nachtruhe: 00:00–06:59 sammeln, außer es ist dringend. 10#… verhindert, dass „08“/„09“ als
# Oktalzahl gelesen werden.
HOUR="${MC_NOTIFY_STUNDE:-$(date +%H)}"
if [ "$DRINGEND" != "1" ] && [ "$((10#$HOUR))" -lt 7 ]; then
mkdir -p "$(dirname "$NIGHT_QUEUE")"
MARKE="$(lucy_marke)"
printf -- '- %s%s\n' "${MARKE:+$MARKE: }" "$MSG" >> "$NIGHT_QUEUE"
# Wortlaut „QUEUED für Morgen-Digest“ nicht ändern: services/update_verlauf.py liest ihn.
echo "$TS QUEUED für Morgen-Digest: $MSG" >> "$LOG"
exit 0
fi
# Zweitweg: direkt an die Bot-API. Das Token geht über --config auf stdin an curl, damit es nicht
# in der Prozessliste steht.
env_wert() {
local wert
wert="$(grep -E "^(export +)?$1=" "$TG_ENV" 2>/dev/null | tail -n 1)"
wert="${wert#*=}"
wert="${wert%$'\r'}"
wert="${wert%\"}"; wert="${wert#\"}"
wert="${wert%\'}"; wert="${wert#\'}"
printf '%s' "$wert"
}
# Warum der Zweitweg scheiterte, steht seit 24.09.2026 mit im FALLBACK-Eintrag (der Telegram-Test der Oberfläche
# zeigt es an). Ohne Klammern im Text: services/update_verlauf.py trennt den Eintrag an „): “.
DIREKT_GRUND=""
telegram_direkt() {
local token chat thread text rc
if [ ! -r "$TG_ENV" ]; then
DIREKT_GRUND="Zugangsdatei $TG_ENV fehlt oder ist nicht lesbar"
return 1
fi
token="$(env_wert TELEGRAM_BOT_TOKEN)"
chat="$(env_wert TELEGRAM_HOME_CHANNEL)"
thread="$(env_wert TELEGRAM_HOME_CHANNEL_THREAD_ID)"
if [ -z "$token" ] || [ -z "$chat" ]; then
DIREKT_GRUND="TELEGRAM_BOT_TOKEN oder TELEGRAM_HOME_CHANNEL fehlt in $TG_ENV"
return 1
fi
text="$MSG"
[ -n "$KOPF" ] && text="$KOPF
$MSG"
printf 'url = "%s/bot%s/sendMessage"\n' "$TG_API" "$token" \
| curl -sf -m 15 -o /dev/null --config - \
--data-urlencode "chat_id=$chat" --data-urlencode "text=$text" \
${thread:+--data-urlencode "message_thread_id=$thread"} \
${MARKUP:+--data-urlencode "reply_markup=$MARKUP"}
rc=$?
case "$rc" in
0) return 0 ;;
22) DIREKT_GRUND="Telegram lehnt ab, HTTP-Fehler – Token oder Chat falsch?" ;;
6|7) DIREKT_GRUND="Telegram nicht erreichbar, keine Verbindung" ;;
28) DIREKT_GRUND="Telegram antwortet nicht rechtzeitig" ;;
*) DIREKT_GRUND="curl endete mit Code $rc" ;;
esac
return 1
}
# Antworttasten (-k "ja 7|nein 7", Rückfragen seit 25.09.2026): eine Tastatur unter dem Eingabefeld. Ein Tipp schickt
# nur diesen Text — Lucy liest ihn wie jede Nachricht, an Hermes ändert das nichts. Zwei je Reihe, nach dem ersten Tipp
# klappt sie weg. hermes send kann keine Tastatur mitschicken, deshalb geht eine Nachricht mit Tasten zuerst direkt.
MARKUP=""
if [ -n "$KNOEPFE" ]; then
MARKUP="$("${MC_NOTIFY_PYTHON:-python3}" - "$KNOEPFE" 2>/dev/null <<'PY'
import json, sys
k = [x.strip() for x in sys.argv[1].split("|") if x.strip()][:8]
print(json.dumps({"keyboard": [[{"text": t} for t in k[i:i + 2]] for i in range(0, len(k), 2)],
"one_time_keyboard": True, "resize_keyboard": True}, ensure_ascii=False))
PY
)"
fi
if [ -n "$MARKUP" ] && telegram_direkt; then
echo "$TS OK telegram direkt: $MSG" >> "$LOG"
exit 0
fi
# Primärweg: Telegram via hermes send (Login-Shell-PATH, falls aus Timer/cron aufgerufen). Die Kopfzeile ist Lucys.
if [ -n "$KOPF" ]; then
SEND_OUT="$(bash -lc '"$1" send --to telegram --subject "$2" -- "$3"' _ "$HERMES_BIN" "$KOPF" "$MSG" 2>&1)"
else
SEND_OUT="$(bash -lc '"$1" send --to telegram -- "$2"' _ "$HERMES_BIN" "$MSG" 2>&1)"
fi
if [ $? -eq 0 ]; then
echo "$TS OK telegram: $MSG" >> "$LOG"
exit 0
fi
if [ -z "$MARKUP" ] && telegram_direkt; then
# „OK telegram direkt“: services/update_verlauf.py erkennt beide Wege.
echo "$TS OK telegram direkt: $MSG" >> "$LOG"
exit 0
fi
# Letzter Ausweg: Logfile + wall — Meldung darf nie verloren gehen.
GRUND="$SEND_OUT"
[ -n "$DIREKT_GRUND" ] && GRUND="${GRUND:+$GRUND; }Zweitweg: $DIREKT_GRUND"
echo "$TS FALLBACK (telegram fehlgeschlagen: $GRUND): $MSG" >> "$LOG"
if [ "${MC_NOTIFY_OHNE_WALL:-0}" != "1" ]; then
printf '%s\n' "MC2-Meldung: ${SUBJECT:+$SUBJECT }$MSG" | wall 2>/dev/null || true
fi
echo "WARN: Telegram fehlgeschlagen, in $LOG protokolliert" >&2
[ "${MC_NOTIFY_STRICT:-0}" = "1" ] && exit 1
exit 0