diff --git a/audio_handler.py b/audio_handler.py
index cc57179..d39e504 100644
--- a/audio_handler.py
+++ b/audio_handler.py
@@ -26,20 +26,25 @@ def transcribe_audio(file_path: str) -> str:
async def text_to_speech_async(text: str) -> str:
"""Sintetiza texto em áudio MP3 usando Edge TTS (Versão ASYNC)."""
- # Limpeza para narração
+ # Limpeza para narração: remove tudo o que a voz tenta ler literalmente mas não deve
texto_limpo = text.replace("🤖", "").replace("🧑🏫", "").replace("*", "").replace("`", "")
- texto_limpo = re.sub(r'.*?', '', texto_limpo, flags=re.DOTALL).strip()
+ texto_limpo = texto_limpo.replace("#", "").replace("- ", " ").replace("> ", " ")
+ # Remove blocos
+ texto_limpo = re.sub(r'.*?', '', texto_limpo, flags=re.DOTALL)
+ # 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).strip()
if not texto_limpo:
- texto_limpo = "Prompt vazio."
+ texto_limpo = "Prompt processado."
filename = f"audio_reply_{uuid.uuid4().hex[:8]}.mp3"
filepath = os.path.join("/tmp", filename)
- # Voz Masculina PT-BR: Antonio
- # Rate +20% para ser mais rápido
- voice = "pt-BR-AntonioNeural"
- communicate = edge_tts.Communicate(texto_limpo, voice, rate="+20%")
+ # Voz Masculina PT-BR: Donato é uma das mais realistas para comandos rápidos
+ voice = "pt-BR-DonatoNeural"
+ # Rate +35% para ser dinâmico e direto como solicitado
+ communicate = edge_tts.Communicate(texto_limpo, voice, rate="+35%")
await communicate.save(filepath)
return filename
diff --git a/bridge_telegram.py b/bridge_telegram.py
index 3513a2c..a63bae5 100644
--- a/bridge_telegram.py
+++ b/bridge_telegram.py
@@ -12,7 +12,9 @@ load_dotenv()
# Configurações obtidas do .env
TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
ALLOWED_USER_ID = os.getenv("TELEGRAM_CHAT_ID")
-API_BASE_URL = "http://localhost:8001"
+# Sincroniza com a PORTA definida no .env (Dica: .env diz 8000)
+API_PORT = os.getenv("PORT", "8001")
+API_BASE_URL = f"http://localhost:{API_PORT}"
# O ID permitido deve ser comparado como string ou int, padronizando aqui
if ALLOWED_USER_ID:
@@ -103,6 +105,67 @@ async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
logger.error(f"Erro ao enviar Markdown: {e}. Tentando texto puro.")
await update.message.reply_text(reply)
+async def handle_voice(update: Update, context: ContextTypes.DEFAULT_TYPE):
+ """Manipula mensagens de voz do Telegram."""
+ if not update.message or not update.message.voice:
+ return
+
+ chat_id = update.effective_chat.id
+ user_id = update.effective_user.id
+
+ # Filtro de Segurança
+ if ALLOWED_USER_ID and user_id != ALLOWED_USER_ID:
+ return
+
+ await context.bot.send_chat_action(chat_id=chat_id, action="record_voice")
+
+ # 1. Download do áudio do Telegram
+ voice_file = await update.message.voice.get_file()
+ temp_path = f"/tmp/tg_voice_{uuid.uuid4().hex}.ogg"
+ await voice_file.download_to_drive(temp_path)
+
+ logger.info(f"Voz recebida de {user_id}. Enviando para API de Áudio...")
+
+ # 2. Envia para a API interna de áudio
+ # Como o bridge e API estão na mesma máquina, compartilhamos o /tmp se necessário
+ # Mas vamos usar multipart para ser fiel à API
+ async with httpx.AsyncClient(timeout=120.0) as client:
+ try:
+ with open(temp_path, "rb") as f:
+ # O parâmetro history pode ser adicionado futuramente similar ao chat
+ files = {"audio": (os.path.basename(temp_path), f, "audio/ogg")}
+ response = await client.post(f"{API_BASE_URL}/api/chat-audio", files=files)
+ response.raise_for_status()
+ data = response.json()
+
+ user_text = data.get("text", "[Voz não transcrita]")
+ bot_reply = data.get("reply", "Erro no processamento.")
+ audio_url = data.get("audio_url") # Ex: /api/audio/file.mp3
+
+ # Envia transcrição do usuário
+ await update.message.reply_text(f"🎤 *Sua mensagem:* {user_text}", parse_mode='Markdown')
+
+ # Envia resposta em texto
+ await update.message.reply_text(bot_reply, parse_mode='Markdown')
+
+ # 3. Envia resposta em áudio (TTS)
+ if audio_url:
+ filename = audio_url.split("/")[-1]
+ audio_path = os.path.join("/tmp", filename)
+ if os.path.exists(audio_path):
+ with open(audio_path, "rb") as audio_file:
+ await context.bot.send_voice(chat_id=chat_id, voice=audio_file)
+
+ # Atualiza histórico local
+ if chat_id not in chat_histories: chat_histories[chat_id] = []
+ chat_histories[chat_id].append({"user": user_text, "bot": bot_reply})
+
+ except Exception as e:
+ logger.error(f"Erro ao processar áudio: {str(e)}")
+ await update.message.reply_text(f"❌ *Erro no áudio:* {str(e)}")
+ finally:
+ if os.path.exists(temp_path): os.remove(temp_path)
+
if __name__ == '__main__':
if not TOKEN:
logger.error("ERRO: TELEGRAM_BOT_TOKEN não encontrado no .env!")
@@ -115,5 +178,10 @@ if __name__ == '__main__':
text_handler = MessageHandler(filters.TEXT & (~filters.COMMAND), handle_message)
application.add_handler(text_handler)
- logger.info("Bot Ponte Antigravity (Middleware) iniciado e aguardando...")
+ # Adiciona o handler para mensagens de VOZ
+ import uuid
+ voice_handler = MessageHandler(filters.VOICE, handle_voice)
+ application.add_handler(voice_handler)
+
+ logger.info("Bot Ponte Antigravity (Middleware - Texto & Voz) iniciado...")
application.run_polling()