{ lib, stdenv, writeShellScriptBin, fetchurl, curl, python3, mpv, libnotify, busybox, }: let icon = fetchurl { url = "https://s3.m3tam3re.com/public/chiron.jpeg"; hash = "sha256-LIIR3aqJ5ePsZuvVQxJ70c6yd+L85fLE5uY/fLE5Ypo="; }; script = writeShellScriptBin "talk" '' #!/usr/bin/env bash set -euo pipefail # ── Toggle: VOICE_NOTIFY=0 deaktiviert alles ─────────────────── if [ "''${VOICE_NOTIFY:-1}" = "0" ]; then exit 0 fi # ── Config ────────────────────────────────────────────────────── API_KEY="''${ELEVENLABS_API_KEY:?Set ELEVENLABS_API_KEY in your environment}" VOICE_ID="''${ELEVENLABS_VOICE_ID:-KXxZd16DiBqt82nbarJx}" # Argument hat Vorrang, sonst stdin lesen wenn Pipe if [ $# -ge 1 ]; then TEXT="$1" elif [ ! -t 0 ]; then TEXT=$(cat) else echo "Usage: talk \"Text\" oder echo Text | talk" >&2 exit 1 fi AUDIO_FILE="/tmp/voice-notify-$$.mp3" ACTION_FILE="/tmp/voice-action-$$" CURL="${lib.getExe curl}" PYTHON="${lib.getExe python3}" MPV="${lib.getExe mpv}" NOTIFY="${lib.getExe libnotify}" RM="${busybox}/bin/rm" CAT="${busybox}/bin/cat" GREP="${busybox}/bin/grep" SLEEP="${busybox}/bin/sleep" KILL="${busybox}/bin/kill" STAT="${busybox}/bin/stat" trap '$RM -f "$AUDIO_FILE" "$ACTION_FILE"' EXIT # ── TTS generieren ────────────────────────────────────────────── PAYLOAD=$("$PYTHON" -c ' import json, sys print(json.dumps({ "text": sys.argv[1], "model_id": "eleven_multilingual_v2", "voice_settings": {"stability": 0.5, "similarity_boost": 0.75} })) ' "$TEXT") HTTP_CODE=$("$CURL" -s -o "$AUDIO_FILE" -w "%{http_code}" \ -X POST "https://api.elevenlabs.io/v1/text-to-speech/''${VOICE_ID}" \ -H "xi-api-key: ''${API_KEY}" \ -H "Content-Type: application/json" \ -d "$PAYLOAD") || HTTP_CODE="000" if [ "$HTTP_CODE" != "200" ]; then ERR=$($CAT "$AUDIO_FILE" 2>/dev/null | head -c 300) $RM -f "$AUDIO_FILE" "$NOTIFY" -u critical "❌ Voice Fehler" "HTTP ''${HTTP_CODE}\n''${ERR}" exit 1 fi # ── Gleichzeitig: Audio + Notification ────────────────────────── "$MPV" --no-video --no-terminal --really-quiet "$AUDIO_FILE" & MPV_PID=$! ( exec "$NOTIFY" \ --action=cancel="⛔ Abbrechen" \ --urgency=normal \ --icon=${icon} \ "🔊 KI Antwort" \ "$TEXT" > "$ACTION_FILE" 2>/dev/null ) & NOTIFY_PID=$! # ── Poll-Loop ─────────────────────────────────────────────────── while true; do if ! $KILL -0 "$MPV_PID" 2>/dev/null; then $KILL "$NOTIFY_PID" 2>/dev/null || true break fi if [ -s "$ACTION_FILE" ]; then if $GREP -q "cancel" "$ACTION_FILE" 2>/dev/null; then $KILL "$MPV_PID" 2>/dev/null || true fi break fi $SLEEP 0.2 done wait "$MPV_PID" 2>/dev/null || true ''; in stdenv.mkDerivation { pname = "talk"; version = "0.1.0"; dontUnpack = true; installPhase = '' mkdir -p "$out/bin" ln -s ${script}/bin/talk "$out/bin/talk" ''; meta = with lib; { description = "ElevenLabs TTS voice notifications with dismissable popup"; homepage = "https://code.m3ta.dev/m3tam3re/nixpkgs"; license = licenses.mit; platforms = platforms.linux; mainProgram = "talk"; }; }