06-sep: [feat] aviso de voz al terminar (Dalia/edge-tts) en opencode-tg + script aviso_voz
This commit is contained in:
110
vps_opencode_bot/aviso_voz.py
Normal file
110
vps_opencode_bot/aviso_voz.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
aviso_voz.py — Genera y envia una nota de voz corta por Telegram al terminar
|
||||
una respuesta de OpenCode, en vez del pitido/tono de notificacion.
|
||||
|
||||
Usa edge-tts (gratis/ilimitado, Microsoft) con la voz preferida del usuario
|
||||
es-MX-DaliaNeural. Telegram exige las notas de voz en OGG/Opus, asi que el
|
||||
mp3 generado por edge-tts se convierte con ffmpeg la primera vez y se cachea.
|
||||
|
||||
Configuracion (variables del .env del bot):
|
||||
AVISO_VOZ=1 -> habilita el aviso de voz al terminar
|
||||
AVISO_VOZ_FRASE=Listo. -> frase a sintetizar (opcional)
|
||||
AVISO_VOZ_MP3=<ruta> -> usar un mp3 propio (voz ElevenLabs)
|
||||
en vez de sintetizar (opcional)
|
||||
El OGG cacheado vive en <dir>/sounds/aviso_fin.ogg (o junto al mp3 indicado).
|
||||
|
||||
Uso:
|
||||
python3 aviso_voz.py <chat_id> -> envia la nota de voz al chat
|
||||
"""
|
||||
import asyncio
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import requests
|
||||
|
||||
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||
SND = os.path.join(BASE, "sounds")
|
||||
ENV = {}
|
||||
try:
|
||||
for linea in open(os.path.join(BASE, ".env"), encoding="utf-8"):
|
||||
linea = linea.strip()
|
||||
if linea and not linea.startswith("#") and "=" in linea:
|
||||
k, v = linea.split("=", 1)
|
||||
ENV[k.strip()] = v.strip()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
TOKEN = ENV.get("TELEGRAM_BOT_TOKEN", "")
|
||||
API = f"https://api.telegram.org/bot{TOKEN}"
|
||||
FRASE = ENV.get("AVISO_VOZ_FRASE", "Listo.")
|
||||
MP3_SOURCE = ENV.get("AVISO_VOZ_MP3", "")
|
||||
|
||||
|
||||
def _voz_dalia(texto, salida_mp3):
|
||||
"""Sintetiza `texto` con edge-tts voz es-MX-DaliaNeural a un mp3."""
|
||||
import edge_tts
|
||||
asyncio.run(edge_tts.Communicate(
|
||||
texto, "es-MX-DaliaNeural", rate="-8%").save(salida_mp3))
|
||||
|
||||
|
||||
def asegurar_ogg():
|
||||
"""Devuelve la ruta del OGG/Opus de aviso, generandolo/cacheandolo."""
|
||||
try:
|
||||
os.makedirs(SND, exist_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
# Si hay mp3 propio de voz, el cache es su mismo nombre con .ogg
|
||||
if MP3_SOURCE and os.path.isfile(MP3_SOURCE):
|
||||
ogg = os.path.splitext(MP3_SOURCE)[0] + ".ogg"
|
||||
fuente = MP3_SOURCE
|
||||
else:
|
||||
ogg = os.path.join(SND, "aviso_fin.ogg")
|
||||
fuente = os.path.join(SND, "aviso_fin.mp3")
|
||||
if not os.path.exists(fuente):
|
||||
try:
|
||||
_voz_dalia(FRASE, fuente)
|
||||
except Exception as e:
|
||||
print(f"[aviso_voz] edge-tts error: {e}")
|
||||
return None
|
||||
if not os.path.exists(ogg):
|
||||
try:
|
||||
r = subprocess.run(
|
||||
["ffmpeg", "-y", "-loglevel", "error", "-i", fuente,
|
||||
"-c:a", "libopus", "-b:a", "48k", ogg],
|
||||
capture_output=True, timeout=60)
|
||||
if r.returncode != 0:
|
||||
print(f"[aviso_voz] ffmpeg error: {r.stderr.decode(errors='replace')[:300]}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"[aviso_voz] ffmpeg no disponible: {e}")
|
||||
return None
|
||||
return ogg
|
||||
|
||||
|
||||
def enviar_voz(chat_id):
|
||||
ogg = asegurar_ogg()
|
||||
if not ogg:
|
||||
return False
|
||||
try:
|
||||
with open(ogg, "rb") as f:
|
||||
r = requests.post(f"{API}/sendVoice",
|
||||
data={"chat_id": chat_id},
|
||||
files={"voice": ("aviso.ogg", f,
|
||||
"audio/ogg")},
|
||||
timeout=40)
|
||||
return r.json().get("ok", False)
|
||||
except Exception as e:
|
||||
print(f"[aviso_voz] sendVoice error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
chat = sys.argv[1] if len(sys.argv) > 1 else ""
|
||||
if not chat or not TOKEN:
|
||||
print("Uso: python3 aviso_voz.py <chat_id> (con TELEGRAM_BOT_TOKEN en .env)")
|
||||
sys.exit(1)
|
||||
ok = enviar_voz(chat)
|
||||
print(f"[aviso_voz] {'OK' if ok else 'FALLO'} -> chat {chat}")
|
||||
sys.exit(0 if ok else 2)
|
||||
Reference in New Issue
Block a user