import os import speech_recognition as sr from pydub import AudioSegment import uuid import re import asyncio import edge_tts def transcribe_audio(file_path: str) -> str: """Converte áudio (qualquer formato compatível com pydub) para WAV e transcreve com Google Speech.""" recognizer = sr.Recognizer() # Se não for wav, converte usando pydub (precisa de ffmpeg na VPS) temp_wav = f"/tmp/{uuid.uuid4()}.wav" try: audio = AudioSegment.from_file(file_path) audio.export(temp_wav, format="wav") with sr.AudioFile(temp_wav) as source: audio_data = recognizer.record(source) text = recognizer.recognize_google(audio_data, language="pt-BR") return text finally: if os.path.exists(temp_wav): os.remove(temp_wav) async def text_to_speech_async(text: str) -> str: """Sintetiza texto em áudio MP3 usando Edge TTS (Versão ASYNC).""" if not text or not text.strip(): text = "Processado com sucesso." # Limpeza para narração: remove tudo o que a voz tenta ler literalmente mas não deve # Preservamos o texto original se ele já vier "limpo" (ex: vindo do main.py via refined) texto_limpo = text.replace("🤖", "").replace("🧑‍🏫", "").replace("*", "").replace("`", "") texto_limpo = texto_limpo.replace("#", "").replace("- ", " ").replace("> ", " ") # Remove tags legadas REFINED se presentes (proteção dupla) texto_limpo = re.sub(r'[<\[]/?REFINED[>\]]', '', texto_limpo, flags=re.IGNORECASE | re.DOTALL).strip() # Remove URLs e links [texto](url) texto_limpo = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', texto_limpo) texto_limpo = re.sub(r'http[s]?://\S+', '', texto_limpo) # Limpeza final: remove espaços extras e garante que sobrou algo audível texto_limpo = texto_limpo.strip() if not texto_limpo: texto_limpo = "Prompt processado." filename = f"audio_reply_{uuid.uuid4().hex[:8]}.mp3" filepath = os.path.join("/tmp", filename) try: # Voz Masculina PT-BR: Antonio (reconhecidamente estável) voice = "pt-BR-AntonioNeural" # Rate +30% (muito rápido mas seguro para palavras curtas) communicate = edge_tts.Communicate(texto_limpo, voice, rate="+30%") await communicate.save(filepath) return filename except Exception as e: # Se falhar o TTS (ex: erro de rede ou "No audio was received"), gera um bip ou áudio simples se possível # Aqui vamos apenas logar e relançar para o main.py tratar print(f"[TTS ERROR] Falha ao gerar áudio para: '{texto_limpo}'. Erro: {e}") raise def text_to_speech(text: str) -> str: """Wrapper síncrono para compatibilidade legada (CUIDADO com loops eventuais).""" try: # Se já houver um loop rodando (ex: Telegram), isso vai falhar return asyncio.run(text_to_speech_async(text)) except RuntimeError: # Fallback: se houver loop, tenta rodar de forma síncrona ou retorna erro # No nosso caso, o bot_logic e main.py devem usar a versão ASYNC diretamente print("[VOICE] Erro: text_to_speech (sync) chamado dentro de um event loop.") raise