# -*- 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= -> usar un mp3 propio (voz ElevenLabs) en vez de sintetizar (opcional) El OGG cacheado vive en /sounds/aviso_fin.ogg (o junto al mp3 indicado). Uso: python3 aviso_voz.py -> 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 (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)