Implementa configuração de variáveis de ambiente e logging, atualiza documentação. Inclui novos arquivos .env.example e config.py, além de melhorias no tratamento de erros e mensagens de log.
This commit is contained in:
parent
76dd77ecb7
commit
97f50f51bd
@ -13,3 +13,6 @@ GROQ_API_KEY=your_groq_api_key
|
|||||||
# Host e porta do Redis (caso esteja utilizando)
|
# Host e porta do Redis (caso esteja utilizando)
|
||||||
REDIS_HOST=localhost
|
REDIS_HOST=localhost
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
DEBUG_MODE=true
|
||||||
|
LOG_LEVEL=INFO
|
117
config.py
Normal file
117
config.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Configuração de logging com cores e formatação melhorada
|
||||||
|
class ColoredFormatter(logging.Formatter):
|
||||||
|
"""Formatter personalizado que adiciona cores aos logs"""
|
||||||
|
grey = "\x1b[38;21m"
|
||||||
|
blue = "\x1b[38;5;39m"
|
||||||
|
yellow = "\x1b[38;5;226m"
|
||||||
|
red = "\x1b[38;5;196m"
|
||||||
|
bold_red = "\x1b[31;1m"
|
||||||
|
reset = "\x1b[0m"
|
||||||
|
|
||||||
|
def __init__(self, fmt):
|
||||||
|
super().__init__()
|
||||||
|
self.fmt = fmt
|
||||||
|
self.FORMATS = {
|
||||||
|
logging.DEBUG: self.blue + self.fmt + self.reset,
|
||||||
|
logging.INFO: self.grey + self.fmt + self.reset,
|
||||||
|
logging.WARNING: self.yellow + self.fmt + self.reset,
|
||||||
|
logging.ERROR: self.red + self.fmt + self.reset,
|
||||||
|
logging.CRITICAL: self.bold_red + self.fmt + self.reset
|
||||||
|
}
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
log_fmt = self.FORMATS.get(record.levelno)
|
||||||
|
formatter = logging.Formatter(log_fmt)
|
||||||
|
return formatter.format(record)
|
||||||
|
|
||||||
|
# Configuração inicial do logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
handler.setFormatter(ColoredFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
# Carregar variáveis de ambiente
|
||||||
|
env_path = Path('.env')
|
||||||
|
if env_path.exists():
|
||||||
|
logger.debug(f"Arquivo .env encontrado em: {env_path.absolute()}")
|
||||||
|
load_dotenv(override=True)
|
||||||
|
else:
|
||||||
|
logger.warning("Arquivo .env não encontrado! Usando variáveis de ambiente do sistema.")
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
def __init__(self):
|
||||||
|
logger.debug("Iniciando carregamento das configurações...")
|
||||||
|
|
||||||
|
# Carregamento das variáveis com logs detalhados
|
||||||
|
self.DEBUG_MODE = os.getenv('DEBUG_MODE', 'false').lower() == 'true'
|
||||||
|
logger.debug(f"DEBUG_MODE configurado como: {self.DEBUG_MODE}")
|
||||||
|
|
||||||
|
self.WHATSAPP_INSTANCE = os.getenv('WHATSAPP_INSTANCE')
|
||||||
|
logger.debug(f"WHATSAPP_INSTANCE configurada como: {self.WHATSAPP_INSTANCE}")
|
||||||
|
|
||||||
|
self.WHATSAPP_API_KEY = os.getenv('WHATSAPP_API_KEY')
|
||||||
|
logger.debug(f"WHATSAPP_API_KEY configurada: {'Presente' if self.WHATSAPP_API_KEY else 'Ausente'}")
|
||||||
|
|
||||||
|
self.WHATSAPP_API_URL = os.getenv('WHATSAPP_API_URL')
|
||||||
|
logger.debug(f"WHATSAPP_API_URL configurada como: {self.WHATSAPP_API_URL}")
|
||||||
|
|
||||||
|
self.GROQ_API_KEY = os.getenv('GROQ_API_KEY')
|
||||||
|
if self.GROQ_API_KEY:
|
||||||
|
masked_key = f"{self.GROQ_API_KEY[:10]}...{self.GROQ_API_KEY[-4:]}"
|
||||||
|
logger.debug(f"GROQ_API_KEY carregada: {masked_key}")
|
||||||
|
else:
|
||||||
|
logger.error("GROQ_API_KEY não encontrada!")
|
||||||
|
|
||||||
|
self.BUSINESS_MESSAGE = os.getenv('BUSINESS_MESSAGE', '*Impacte AI* Premium Services')
|
||||||
|
logger.debug(f"BUSINESS_MESSAGE configurada como: {self.BUSINESS_MESSAGE}")
|
||||||
|
|
||||||
|
self.PROCESS_GROUP_MESSAGES = os.getenv('PROCESS_GROUP_MESSAGES', 'false').lower() == 'true'
|
||||||
|
logger.debug(f"PROCESS_GROUP_MESSAGES configurado como: {self.PROCESS_GROUP_MESSAGES}")
|
||||||
|
|
||||||
|
self.LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
|
||||||
|
logger.debug(f"LOG_LEVEL configurado como: {self.LOG_LEVEL}")
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""Validação detalhada das configurações críticas"""
|
||||||
|
logger.debug("Iniciando validação das configurações...")
|
||||||
|
|
||||||
|
validation_errors = []
|
||||||
|
|
||||||
|
if not self.GROQ_API_KEY:
|
||||||
|
validation_errors.append("GROQ_API_KEY não está definida")
|
||||||
|
elif not self.GROQ_API_KEY.startswith('gsk_'):
|
||||||
|
validation_errors.append("GROQ_API_KEY inválida: deve começar com 'gsk_'")
|
||||||
|
|
||||||
|
if not self.WHATSAPP_API_KEY:
|
||||||
|
validation_errors.append("WHATSAPP_API_KEY não está definida")
|
||||||
|
|
||||||
|
if not self.WHATSAPP_API_URL:
|
||||||
|
validation_errors.append("WHATSAPP_API_URL não está definida")
|
||||||
|
|
||||||
|
if not self.WHATSAPP_INSTANCE:
|
||||||
|
validation_errors.append("WHATSAPP_INSTANCE não está definida")
|
||||||
|
|
||||||
|
if validation_errors:
|
||||||
|
for error in validation_errors:
|
||||||
|
logger.error(f"Erro de validação: {error}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info("Todas as configurações foram validadas com sucesso!")
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Criar instância das configurações
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
# Validar configurações
|
||||||
|
if not settings.validate():
|
||||||
|
logger.critical("Configurações inválidas detectadas. A aplicação pode não funcionar corretamente!")
|
||||||
|
|
||||||
|
# Ajustar nível de log
|
||||||
|
log_level = logging.DEBUG if settings.DEBUG_MODE else getattr(logging, settings.LOG_LEVEL.upper())
|
||||||
|
logger.setLevel(log_level)
|
||||||
|
logger.info(f"Nível de log definido como: {logging.getLevelName(log_level)}")
|
@ -19,6 +19,8 @@ services:
|
|||||||
GROQ_API_KEY: "substitua_sua_chave_GROQ_aqui" #coloque sua chave GROQ aqui
|
GROQ_API_KEY: "substitua_sua_chave_GROQ_aqui" #coloque sua chave GROQ aqui
|
||||||
BUSINESS_MESSAGE: "substitua_sua_mensagem_de_servico_aqui" #coloque a mensagem que será enviada ao final da transcrição aqui
|
BUSINESS_MESSAGE: "substitua_sua_mensagem_de_servico_aqui" #coloque a mensagem que será enviada ao final da transcrição aqui
|
||||||
PROCESS_GROUP_MESSAGES: "false" # Define se mensagens de grupos devem ser processadas
|
PROCESS_GROUP_MESSAGES: "false" # Define se mensagens de grupos devem ser processadas
|
||||||
|
DEBUG_MODE: "false"
|
||||||
|
LOG_LEVEL: "INFO"
|
||||||
deploy:
|
deploy:
|
||||||
mode: replicated
|
mode: replicated
|
||||||
replicas: 1
|
replicas: 1
|
||||||
|
62
main.py
62
main.py
@ -7,78 +7,72 @@ from services import (
|
|||||||
)
|
)
|
||||||
from models import WebhookRequest
|
from models import WebhookRequest
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from dotenv import load_dotenv
|
from config import settings, logger
|
||||||
import os
|
|
||||||
from services import get_env_var
|
|
||||||
|
|
||||||
# Carregar variáveis do .env
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
# Obter a mensagem do negócio da variável de ambiente
|
|
||||||
BUSINESS_MESSAGE = get_env_var("BUSINESS_MESSAGE", "*Impacte AI* Premium Services")
|
|
||||||
PROCESS_GROUP_MESSAGES = get_env_var("PROCESS_GROUP_MESSAGES", "false").lower() == "true"
|
|
||||||
|
|
||||||
@app.post("/transcreve-audios")
|
@app.post("/transcreve-audios")
|
||||||
async def transcreve_audios(request: Request):
|
async def transcreve_audios(request: Request):
|
||||||
try:
|
try:
|
||||||
# Receber o corpo do webhook
|
logger.info("Iniciando processamento de áudio")
|
||||||
body = await request.json()
|
body = await request.json()
|
||||||
|
|
||||||
|
if settings.DEBUG_MODE:
|
||||||
|
logger.debug(f"Payload recebido: {body}")
|
||||||
|
|
||||||
# Extraindo informações necessárias do JSON
|
# Extraindo informações
|
||||||
server_url = body["server_url"]
|
server_url = body["server_url"]
|
||||||
instance = body["instance"] # os.getenv("WHATSAPP_INSTANCE")
|
instance = body.get("instance", settings.WHATSAPP_INSTANCE)
|
||||||
apikey = body["apikey"] # os.getenv("WHATSAPP_API_KEY")
|
apikey = body.get("apikey", settings.WHATSAPP_API_KEY)
|
||||||
audio_key = body["data"]["key"]["id"]
|
audio_key = body["data"]["key"]["id"]
|
||||||
from_me = body["data"]["key"]["fromMe"]
|
from_me = body["data"]["key"]["fromMe"]
|
||||||
remote_jid = body["data"]["key"]["remoteJid"]
|
remote_jid = body["data"]["key"]["remoteJid"]
|
||||||
|
|
||||||
# Verificar se a mensagem foi enviada por mim
|
|
||||||
if from_me:
|
if from_me:
|
||||||
|
logger.info("Mensagem enviada pelo próprio usuário, ignorando")
|
||||||
return {"message": "Mensagem enviada por mim, sem operação"}
|
return {"message": "Mensagem enviada por mim, sem operação"}
|
||||||
|
|
||||||
# Decidir se processa mensagens de grupos
|
if "@g.us" in remote_jid and not settings.PROCESS_GROUP_MESSAGES:
|
||||||
if "@g.us" in remote_jid and not PROCESS_GROUP_MESSAGES:
|
logger.info("Mensagem de grupo ignorada conforme configuração")
|
||||||
return {"message": "Mensagem enviada por um grupo, sem operação"}
|
return {"message": "Mensagem enviada por um grupo, sem operação"}
|
||||||
|
|
||||||
if "base64" not in body:
|
# Verificar se temos mediaUrl ou precisamos pegar o base64
|
||||||
|
if "mediaUrl" in body["data"]["message"]:
|
||||||
# Pega o áudio em Base64
|
audio_source = body["data"]["message"]["mediaUrl"]
|
||||||
base64_audio = await get_audio_base64(
|
logger.debug(f"Usando mediaUrl: {audio_source}")
|
||||||
server_url, instance, apikey, audio_key
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
base64_audio = body["data"]["message"]["base64"]
|
logger.debug("MediaUrl não encontrada, obtendo áudio via base64")
|
||||||
|
base64_audio = await get_audio_base64(server_url, instance, apikey, audio_key)
|
||||||
|
audio_source = await convert_base64_to_file(base64_audio)
|
||||||
|
logger.debug(f"Áudio convertido e salvo em: {audio_source}")
|
||||||
|
|
||||||
# Converter Base64 para arquivo de áudio
|
# Transcrever o áudio
|
||||||
audio_file = await convert_base64_to_file(base64_audio)
|
transcription_text, is_summary = await transcribe_audio(audio_source)
|
||||||
|
|
||||||
# Transcrever o áudio usando o modelo da API externa
|
|
||||||
transcription_text, is_summary = await transcribe_audio(audio_file)
|
|
||||||
|
|
||||||
header_message = (
|
header_message = (
|
||||||
"*Resumo do áudio:*\n\n" if is_summary else "*Transcrição desse áudio:*\n\n"
|
"*Resumo do áudio:*\n\n" if is_summary else "*Transcrição desse áudio:*\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Formatar o conteúdo da mensagem
|
# Formatar o conteúdo da mensagem
|
||||||
summary_message = f"{header_message}{transcription_text}\n\n{BUSINESS_MESSAGE}"
|
summary_message = f"{header_message}{transcription_text}\n\n{settings.BUSINESS_MESSAGE}"
|
||||||
|
logger.debug(f"Mensagem formatada: {summary_message[:100]}...")
|
||||||
|
|
||||||
# Enviar o resumo transcrito de volta via WhatsApp
|
# Enviar o resumo transcrito de volta via WhatsApp
|
||||||
|
|
||||||
await send_message_to_whatsapp(
|
await send_message_to_whatsapp(
|
||||||
server_url,
|
server_url,
|
||||||
instance,
|
instance,
|
||||||
apikey,
|
apikey,
|
||||||
summary_message,
|
summary_message,
|
||||||
body["data"]["key"]["remoteJid"],
|
remote_jid,
|
||||||
audio_key,
|
audio_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.info("Áudio processado e resposta enviada com sucesso")
|
||||||
return {"message": "Áudio transcrito e resposta enviada com sucesso"}
|
return {"message": "Áudio transcrito e resposta enviada com sucesso"}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
logger.error(f"Erro ao processar áudio: {str(e)}", exc_info=settings.DEBUG_MODE)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=500, detail=f"Erro ao processar a requisição: {str(e)}"
|
status_code=500,
|
||||||
|
detail=f"Erro ao processar a requisição: {str(e)}"
|
||||||
)
|
)
|
||||||
|
199
readme.md
199
readme.md
@ -12,7 +12,7 @@ Antes de começar, certifique-se de ter os seguintes requisitos:
|
|||||||
- Python 3.10+ instalado ([Download](https://www.python.org/downloads/))
|
- Python 3.10+ instalado ([Download](https://www.python.org/downloads/))
|
||||||
- Docker e Docker Compose instalados ([Instruções](https://docs.docker.com/get-docker/))
|
- Docker e Docker Compose instalados ([Instruções](https://docs.docker.com/get-docker/))
|
||||||
- Uma conta Evolution API com chave válida
|
- Uma conta Evolution API com chave válida
|
||||||
- Uma conta GROQ API com chave válida
|
- Uma conta GROQ API com chave válida (começa com 'gsk_')
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -33,55 +33,82 @@ python -m venv .venv
|
|||||||
source .venv/Scripts/activate
|
source .venv/Scripts/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
Para sair do ambiente virtual, use:
|
|
||||||
|
### Configuração do Arquivo .env
|
||||||
|
Copie o arquivo `.env.example` para `.env` e configure suas variáveis:
|
||||||
```bash
|
```bash
|
||||||
deactivate
|
cp .env.example .env
|
||||||
```
|
```
|
||||||
### 🚀 Como Executar Localmente
|
|
||||||
Certifique-se de que todas as dependências foram instaladas.
|
## 📖 **Configuração Detalhada das Variáveis**
|
||||||
Rode o comando abaixo para iniciar o servidor:
|
|
||||||
|
### Variáveis Essenciais
|
||||||
|
|
||||||
|
| Variável | Descrição | Obrigatória | Exemplo |
|
||||||
|
|-----------------------|----------------------------------------------------------|-------------|----------------------------------------------------------|
|
||||||
|
| `WHATSAPP_API_KEY` | Chave da API Evolution para autenticação | Sim | `429683C4C977415CAAFCCE10F7D57E11` |
|
||||||
|
| `WHATSAPP_API_URL` | URL base da sua instância Evolution API | Sim | `https://api.evolution.com` |
|
||||||
|
| `WHATSAPP_INSTANCE` | Nome da instância configurada na Evolution API | Sim | `instance1` |
|
||||||
|
| `GROQ_API_KEY` | Chave da API GROQ (deve começar com 'gsk_') | Sim | `gsk_abc123...` |
|
||||||
|
|
||||||
|
### Variáveis de Personalização
|
||||||
|
|
||||||
|
| Variável | Descrição | Padrão | Exemplo |
|
||||||
|
|-----------------------|----------------------------------------------------------|-------------|----------------------------------------------------------|
|
||||||
|
| `BUSINESS_MESSAGE` | Mensagem de rodapé após transcrição | Vazio | `substitua_sua_mensagem_de_servico_aqui` |
|
||||||
|
| `PROCESS_GROUP_MESSAGES` | Habilita processamento de mensagens em grupos | `false` | `true` ou `false` |
|
||||||
|
|
||||||
|
### Variáveis de Debug e Log
|
||||||
|
|
||||||
|
| Variável | Descrição | Padrão | Valores Possíveis |
|
||||||
|
|-----------------------|----------------------------------------------------------|-------------|----------------------------------------------------------|
|
||||||
|
| `DEBUG_MODE` | Ativa logs detalhados para debugging | `false` | `true` ou `false` |
|
||||||
|
| `LOG_LEVEL` | Define o nível de detalhamento dos logs | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 **Métodos de Execução**
|
||||||
|
|
||||||
|
### Execução Local
|
||||||
```bash
|
```bash
|
||||||
uvicorn main:app --host 0.0.0.0 --port 8005
|
uvicorn main:app --host 0.0.0.0 --port 8005
|
||||||
```
|
```
|
||||||
Acesse o serviço localmente em: `http://127.0.0.1:8005/transcreve-audios`. Insira este endereço na Evolution API para consumir as transcrições.
|
|
||||||
|
|
||||||
### 🌐 Configuração de Webhook na Evolution API
|
### 🐳 Docker Compose Simples
|
||||||
Endpoint para Webhook
|
```yaml
|
||||||
Use o seguinte endpoint para configurar seu webhook na Evolution API:
|
|
||||||
```bash
|
|
||||||
https://transcricaoaudio.seudominio.com.br/transcreve-audios
|
|
||||||
```
|
|
||||||
### Testando Localmente
|
|
||||||
Se estiver rodando localmente, use o comando curl para testar:
|
|
||||||
```bash
|
|
||||||
curl --location 'http://127.0.0.1:8005/transcreve-audios'
|
|
||||||
```
|
|
||||||
### 🐳 Instalação com Docker Swarm e Traefik
|
|
||||||
Se preferir rodar o projeto em um ambiente de produção com Docker Swarm e Traefik, use o arquivo de configuração abaixo como referência.
|
|
||||||
|
|
||||||
docker-compose.yaml
|
|
||||||
```bash
|
|
||||||
version: "3.7"
|
version: "3.7"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
transcricaoaudio:
|
transcricaoaudio:
|
||||||
image: impacteai/transcrevezap:latest
|
image: impacteai/transcrevezap:latest
|
||||||
build: .
|
|
||||||
networks:
|
|
||||||
- suarededocker #troque pela sua rede do docker
|
|
||||||
ports:
|
ports:
|
||||||
- 8005:8005
|
- 8005:8005
|
||||||
environment:
|
environment:
|
||||||
Uvicorn_port: 8005
|
WHATSAPP_API_KEY: "sua_chave_aqui"
|
||||||
Uvicorn_host: 0.0.0.0
|
WHATSAPP_API_URL: "https://sua_url_aqui"
|
||||||
Uvicorn_reload: "true"
|
WHATSAPP_INSTANCE: "sua_instancia"
|
||||||
Uvicorn_workers: 1
|
GROQ_API_KEY: "sua_chave_groq"
|
||||||
WHATSAPP_API_KEY: "substitua_sua_chave_aqui" #coloque sua api key evolution aqui
|
BUSINESS_MESSAGE: "substitua_sua_mensagem_de_servico_aqui"
|
||||||
WHATSAPP_API_URL: "https://suaevolutionapi.sedominio.com.br/" #coloque sua url evolution aqui
|
PROCESS_GROUP_MESSAGES: "false"
|
||||||
WHATSAPP_INSTANCE: "substitua_sua_instancia_aqui" #coloque nome da sua instancia evolution aqui
|
DEBUG_MODE: "false"
|
||||||
GROQ_API_KEY: "substitua_sua_chave_GROQ_aqui" #coloque sua chave GROQ aqui
|
LOG_LEVEL: "INFO"
|
||||||
BUSINESS_MESSAGE: "substitua_sua_mensagem_de_servico_aqui" #coloque a mensagem que será enviada ao final da transcrição aqui
|
```
|
||||||
PROCESS_GROUP_MESSAGES: "false" # Define se mensagens de grupos devem ser processadas
|
|
||||||
|
### 🌟 Docker Swarm com Traefik
|
||||||
|
```yaml
|
||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
transcricaoaudio:
|
||||||
|
image: impacteai/transcrevezap:latest
|
||||||
|
networks:
|
||||||
|
- suarededocker
|
||||||
|
environment:
|
||||||
|
WHATSAPP_API_KEY: "sua_chave_aqui"
|
||||||
|
WHATSAPP_API_URL: "https://sua_url_aqui"
|
||||||
|
WHATSAPP_INSTANCE: "sua_instancia"
|
||||||
|
GROQ_API_KEY: "sua_chave_groq"
|
||||||
|
BUSINESS_MESSAGE: "substitua_sua_mensagem_de_servico_aqui"
|
||||||
|
PROCESS_GROUP_MESSAGES: "false"
|
||||||
|
DEBUG_MODE: "false"
|
||||||
deploy:
|
deploy:
|
||||||
mode: replicated
|
mode: replicated
|
||||||
replicas: 1
|
replicas: 1
|
||||||
@ -90,99 +117,43 @@ services:
|
|||||||
- node.role == manager
|
- node.role == manager
|
||||||
labels:
|
labels:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.http.routers.transcricaoaudio.rule=Host(`transcricaoaudio.seudominio.com.br`) #coloque seu subdominio apontado aqui
|
- traefik.http.routers.transcricaoaudio.rule=Host(`transcricaoaudio.seudominio.com.br`)
|
||||||
- traefik.http.routers.transcricaoaudio.entrypoints=websecure
|
- traefik.http.routers.transcricaoaudio.entrypoints=websecure
|
||||||
- traefik.http.routers.transcricaoaudio.tls.certresolver=letsencryptresolver
|
- traefik.http.routers.transcricaoaudio.tls.certresolver=letsencryptresolver
|
||||||
- traefik.http.services.transcricaoaudio.loadbalancer.server.port=8005
|
- traefik.http.services.transcricaoaudio.loadbalancer.server.port=8005
|
||||||
- traefik.http.services.transcricaoaudio.loadbalancer.passHostHeader=true
|
|
||||||
- traefik.http.routers.transcricaoaudio.service=transcricaoaudio
|
|
||||||
- traefik.http.middlewares.traefik-compress.compress=true
|
|
||||||
- traefik.http.routers.transcricaoaudio.middlewares=traefik-compress
|
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
cpus: "1"
|
cpus: "1"
|
||||||
memory: 1024M
|
memory: 1024M
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
suarededocker: #troque pela sua rede do docker
|
suarededocker:
|
||||||
external: true
|
external: true
|
||||||
name: suarededocker #troque pela sua rede do docker
|
|
||||||
```
|
```
|
||||||
---
|
|
||||||
|
|
||||||
## 🐳 **Rodando com Docker Compose (Sem Traefik)**
|
## 🔧 **Configuração do Traefik**
|
||||||
|
|
||||||
Se você prefere rodar a aplicação em um ambiente simples, sem usar o Traefik para gerenciamento de subdomínios, siga as orientações abaixo.
|
Para usar com Traefik, certifique-se de:
|
||||||
|
1. Ter o Traefik configurado em seu ambiente Docker Swarm
|
||||||
|
2. Configurar o DNS do seu domínio para apontar para o servidor
|
||||||
|
3. Ajustar as labels do Traefik conforme seu ambiente
|
||||||
|
4. Verificar se a rede externa existe no Docker Swarm
|
||||||
|
|
||||||
### **1. Usando `docker run` diretamente**
|
## 📝 **Notas Importantes**
|
||||||
|
- A GROQ_API_KEY deve começar com 'gsk_'
|
||||||
|
- O BUSINESS_MESSAGE suporta formatação do WhatsApp (*negrito*, _itálico_)
|
||||||
|
- Para quebras de linha no BUSINESS_MESSAGE, use \n
|
||||||
|
- Em produção, recomenda-se DEBUG_MODE=false
|
||||||
|
- Configure LOG_LEVEL=DEBUG apenas para troubleshooting
|
||||||
|
|
||||||
Execute o seguinte comando para rodar o contêiner:
|
## 🔍 **Troubleshooting**
|
||||||
|
Se encontrar problemas:
|
||||||
```bash
|
1. Verifique se todas as variáveis obrigatórias estão configuradas
|
||||||
docker run -d \
|
2. Ative DEBUG_MODE=true temporariamente
|
||||||
--name transcricaoaudio \
|
3. Verifique os logs do container
|
||||||
-p 8005:8005 \
|
4. Certifique-se que as APIs estão acessíveis
|
||||||
-e Uvicorn_port=8005 \
|
|
||||||
-e Uvicorn_host=0.0.0.0 \
|
|
||||||
-e Uvicorn_reload="true" \
|
|
||||||
-e Uvicorn_workers=1 \
|
|
||||||
-e WHATSAPP_API_KEY="substitua_sua_chave_aqui" \
|
|
||||||
-e WHATSAPP_API_URL="https://suaevolutionapi.sedominio.com.br/" \
|
|
||||||
-e WHATSAPP_INSTANCE="substitua_sua_instancia_aqui" \
|
|
||||||
-e GROQ_API_KEY="substitua_sua_chave_GROQ_aqui" \
|
|
||||||
-e BUSINESS_MESSAGE="substitua_sua_mensagem_de_servico_aqui" \
|
|
||||||
-e PROCESS_GROUP_MESSAGES="false" \
|
|
||||||
impacteai/transcrevezap:latest
|
|
||||||
```
|
|
||||||
Usando `docker-compose.yaml`
|
|
||||||
Crie um arquivo chamado `docker-compose.yaml` com o seguinte conteúdo:
|
|
||||||
```bash
|
|
||||||
version: "3.7"
|
|
||||||
|
|
||||||
services:
|
|
||||||
transcricaoaudio:
|
|
||||||
image: impacteai/transcrevezap:latest
|
|
||||||
ports:
|
|
||||||
- 8005:8005
|
|
||||||
environment:
|
|
||||||
Uvicorn_port: 8005
|
|
||||||
Uvicorn_host: 0.0.0.0
|
|
||||||
Uvicorn_reload: "true"
|
|
||||||
Uvicorn_workers: 1
|
|
||||||
WHATSAPP_API_KEY: "substitua_sua_chave_aqui" # Coloque sua chave API Evolution aqui
|
|
||||||
WHATSAPP_API_URL: "https://suaevolutionapi.sedominio.com.br/" # URL da sua instância Evolution
|
|
||||||
WHATSAPP_INSTANCE: "substitua_sua_instancia_aqui" # Nome da sua instância Evolution
|
|
||||||
GROQ_API_KEY: "substitua_sua_chave_GROQ_aqui" # Chave da API GROQ
|
|
||||||
BUSINESS_MESSAGE: "substitua_sua_mensagem_de_servico_aqui" # Mensagem adicionada ao final da transcrição
|
|
||||||
PROCESS_GROUP_MESSAGES: "false" # Define se mensagens de grupos devem ser processadas
|
|
||||||
```
|
|
||||||
Para rodar com Docker Compose, execute:
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
- Acessando o serviço
|
|
||||||
- Após rodar a aplicação, acesse:
|
|
||||||
http://127.0.0.1:8005/transcreve-audios para ambiente local, inserindo este endereço na Evolution API.
|
|
||||||
Você pode substituir 127.0.0.1 pelo IP ou domínio público, se configurado.
|
|
||||||
---
|
|
||||||
## 📖 **Configuração das Variáveis de Ambiente**
|
|
||||||
Ao usar o Docker Compose, configure as seguintes variáveis de ambiente no arquivo `docker-compose.yaml`:
|
|
||||||
|
|
||||||
| Variável | Descrição |
|
|
||||||
|------------------------|--------------------------------------------------------------------------------------------|
|
|
||||||
| `WHATSAPP_API_KEY` | Chave da API Evolution para integração com o WhatsApp. |
|
|
||||||
| `WHATSAPP_API_URL` | URL da sua instância da Evolution API. |
|
|
||||||
| `WHATSAPP_INSTANCE` | Nome da instância configurada na Evolution API. |
|
|
||||||
| `GROQ_API_KEY` | Chave da API GROQ para realizar transcrições e resumos de áudios. |
|
|
||||||
| `BUSINESS_MESSAGE` | Mensagem de divulgação que será adicionada ao final das transcrições. |
|
|
||||||
| `PROCESS_GROUP_MESSAGES` | Define se mensagens enviadas em grupos devem ser processadas (`true`) ou ignoradas (`false`). |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📄 **Licença**
|
## 📄 **Licença**
|
||||||
|
Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](LICENSE) para detalhes.
|
||||||
Este projeto está licenciado sob a Licença MIT. Isso significa que você pode usar, modificar e distribuir este software livremente, desde que mantenha o aviso de copyright e a licença original em todas as cópias ou partes substanciais do software.
|
|
||||||
|
|
||||||
Você pode consultar o texto completo da licença no arquivo [LICENSE](LICENSE).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -8,18 +8,19 @@ attrs==24.2.0
|
|||||||
certifi==2024.8.30
|
certifi==2024.8.30
|
||||||
charset-normalizer==2.1.1
|
charset-normalizer==2.1.1
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
fastapi==0.78.0
|
fastapi==0.109.2
|
||||||
frozenlist==1.4.1
|
frozenlist==1.4.1
|
||||||
h11==0.14.0
|
h11==0.14.0
|
||||||
idna==3.10
|
idna==3.10
|
||||||
multidict==6.1.0
|
multidict==6.1.0
|
||||||
pip-system-certs==4.0
|
pip-system-certs==4.0
|
||||||
propcache==0.2.0
|
propcache==0.2.0
|
||||||
pydantic==1.10.18
|
pydantic==2.10.3
|
||||||
python-dotenv==0.20.0
|
pydantic-settings==2.6.1
|
||||||
|
python-dotenv==1.0.1
|
||||||
requests==2.28.1
|
requests==2.28.1
|
||||||
sniffio==1.3.1
|
sniffio==1.3.1
|
||||||
starlette==0.19.1
|
starlette>=0.36.3,<0.37.0
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.12.2
|
||||||
urllib3==1.26.20
|
urllib3==1.26.20
|
||||||
uvicorn==0.17.6
|
uvicorn==0.17.6
|
||||||
|
268
services.py
268
services.py
@ -1,154 +1,200 @@
|
|||||||
import aiohttp
|
import aiohttp
|
||||||
import base64
|
import base64
|
||||||
import aiofiles
|
import aiofiles
|
||||||
from dotenv import load_dotenv
|
|
||||||
import os
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
from config import settings, logger
|
||||||
|
|
||||||
# Carregar variáveis do .env
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
# Função para obter as variáveis
|
|
||||||
def get_env_var(var_name, default=None):
|
|
||||||
return os.getenv(var_name, default)
|
|
||||||
|
|
||||||
# Função assíncrona para converter base64 em arquivo temporário
|
|
||||||
async def convert_base64_to_file(base64_data):
|
async def convert_base64_to_file(base64_data):
|
||||||
audio_data = base64.b64decode(base64_data)
|
"""Converte dados base64 em arquivo temporário"""
|
||||||
audio_file_path = "/tmp/audio_file.mp3"
|
try:
|
||||||
|
logger.debug("Iniciando conversão de base64 para arquivo")
|
||||||
|
audio_data = base64.b64decode(base64_data)
|
||||||
|
audio_file_path = "/tmp/audio_file.mp3"
|
||||||
|
|
||||||
async with aiofiles.open(audio_file_path, "wb") as f:
|
async with aiofiles.open(audio_file_path, "wb") as f:
|
||||||
await f.write(audio_data)
|
await f.write(audio_data)
|
||||||
|
|
||||||
|
logger.debug(f"Arquivo temporário criado em: {audio_file_path}")
|
||||||
|
return audio_file_path
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erro na conversão base64: {str(e)}", exc_info=settings.DEBUG_MODE)
|
||||||
|
raise
|
||||||
|
|
||||||
return audio_file_path
|
|
||||||
|
|
||||||
|
|
||||||
# Função assíncrona para resumir o texto se necessário
|
|
||||||
async def summarize_text_if_needed(text):
|
async def summarize_text_if_needed(text):
|
||||||
|
"""Resumir texto usando a API GROQ"""
|
||||||
|
logger.debug("Iniciando processo de resumo do texto")
|
||||||
|
|
||||||
url_completions = "https://api.groq.com/openai/v1/chat/completions"
|
url_completions = "https://api.groq.com/openai/v1/chat/completions"
|
||||||
headers = {
|
headers = {
|
||||||
"Authorization": f"Bearer {get_env_var('GROQ_API_KEY')}",
|
"Authorization": f"Bearer {settings.GROQ_API_KEY}",
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
json_data = {
|
json_data = {
|
||||||
"messages": [
|
"messages": [{
|
||||||
{
|
"role": "user",
|
||||||
"role": "user",
|
"content": f"""
|
||||||
"content": f"""
|
Entenda o contexto desse áudio e faça um resumo super enxuto sobre o que se trata.
|
||||||
Entenda o contexto desse áudio e faça um resumo super enxuto sobre o que se trata.
|
Esse áudio foi enviado pelo whatsapp, de alguém, para Gabriel.
|
||||||
Esse áudio foi enviado pelo whatsapp, de alguém, para Gabriel.
|
Escreva APENAS o resumo do áudio como se fosse você que estivesse enviando
|
||||||
Escreva APENAS o resumo do áudio como se fosse você que estivesse enviando
|
essa mensagem! Não comprimente, não de oi, não escreva nada antes nem depois
|
||||||
essa mensagem! Não comprimente, não de oi, não escreva nada antes nem depois
|
do resumo, responda apenas um resumo enxuto do que foi falado no áudio.
|
||||||
do resumo, responda apenas um resumo enxuto do que foi falado no áudio.
|
IMPORTANTE: Não faça esse resumo como se fosse um áudio que uma terceira
|
||||||
IMPORTANTE: Não faça esse resumo como se fosse um áudio que uma terceira
|
pessoa enviou, não diga coisas como 'a pessoa está falando...' etc.
|
||||||
pessoa enviou, não diga coisas como 'a pessoa está falando...' etc.
|
Escreva o resumo com base nessa mensagem do áudio,
|
||||||
Escreva o resumo com base nessa mensagem do áudio,
|
como se você estivesse escrevendo esse resumo e enviando em
|
||||||
como se você estivesse escrevendo esse resumo e enviando em
|
texto pelo whatsapp: {text}""",
|
||||||
texto pelo whatsapp: {text}""",
|
}],
|
||||||
}
|
|
||||||
],
|
|
||||||
"model": "llama-3.2-90b-vision-preview",
|
"model": "llama-3.2-90b-vision-preview",
|
||||||
}
|
}
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
try:
|
||||||
async with session.post(
|
async with aiohttp.ClientSession() as session:
|
||||||
url_completions,
|
logger.debug("Enviando requisição para API GROQ")
|
||||||
headers=headers,
|
async with session.post(url_completions, headers=headers, json=json_data) as summary_response:
|
||||||
json=json_data,
|
if summary_response.status == 200:
|
||||||
) as summary_response:
|
summary_result = await summary_response.json()
|
||||||
if summary_response.status == 200:
|
summary_text = summary_result["choices"][0]["message"]["content"]
|
||||||
summary_result = await summary_response.json()
|
logger.info("Resumo gerado com sucesso")
|
||||||
summary_text = summary_result["choices"][0]["message"]["content"]
|
logger.debug(f"Resumo: {summary_text[:100]}...")
|
||||||
return summary_text
|
return summary_text
|
||||||
else:
|
else:
|
||||||
raise Exception("Erro ao resumir o texto")
|
error_text = await summary_response.text()
|
||||||
|
logger.error(f"Erro na API GROQ: {error_text}")
|
||||||
|
raise Exception(f"Erro ao resumir o texto: {error_text}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erro no processo de resumo: {str(e)}", exc_info=settings.DEBUG_MODE)
|
||||||
|
raise
|
||||||
|
|
||||||
|
async def transcribe_audio(audio_source, apikey=None):
|
||||||
# Função assíncrona para transcrever o áudio
|
"""Transcreve áudio usando a API GROQ"""
|
||||||
async def transcribe_audio(audio_file):
|
logger.info("Iniciando processo de transcrição")
|
||||||
url = "https://api.groq.com/openai/v1/audio/transcriptions"
|
url = "https://api.groq.com/openai/v1/audio/transcriptions"
|
||||||
headers = {"Authorization": f"Bearer {get_env_var('GROQ_API_KEY')}"}
|
groq_headers = {"Authorization": f"Bearer {settings.GROQ_API_KEY}"}
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
try:
|
||||||
async with session.post(
|
async with aiohttp.ClientSession() as session:
|
||||||
url,
|
# Se o audio_source for uma URL
|
||||||
headers=headers,
|
if isinstance(audio_source, str) and audio_source.startswith('http'):
|
||||||
data={
|
logger.debug(f"Baixando áudio da URL: {audio_source}")
|
||||||
"file": open(audio_file, "rb"),
|
download_headers = {"apikey": apikey} if apikey else {}
|
||||||
"model": "whisper-large-v3",
|
|
||||||
"language": "pt",
|
async with session.get(audio_source, headers=download_headers) as response:
|
||||||
},
|
if response.status != 200:
|
||||||
) as response:
|
error_text = await response.text()
|
||||||
|
logger.error(f"Erro no download do áudio: Status {response.status}, Resposta: {error_text}")
|
||||||
|
raise Exception(f"Erro ao baixar áudio: {error_text}")
|
||||||
|
|
||||||
|
audio_data = await response.read()
|
||||||
|
temp_file = "/tmp/audio_from_url.mp3"
|
||||||
|
async with aiofiles.open(temp_file, "wb") as f:
|
||||||
|
await f.write(audio_data)
|
||||||
|
audio_source = temp_file
|
||||||
|
logger.debug(f"Áudio salvo temporariamente em: {temp_file}")
|
||||||
|
|
||||||
is_summary = False
|
# Preparar dados para transcrição
|
||||||
if response.status == 200:
|
data = aiohttp.FormData()
|
||||||
result = await response.json()
|
data.add_field('file', open(audio_source, 'rb'), filename='audio.mp3')
|
||||||
message = result.get("text", "")
|
data.add_field('model', 'whisper-large-v3')
|
||||||
|
data.add_field('language', 'pt')
|
||||||
|
|
||||||
if len(message) > 1000:
|
logger.debug("Enviando áudio para transcrição")
|
||||||
is_summary = True
|
async with session.post(url, headers=groq_headers, data=data) as response:
|
||||||
message = await summarize_text_if_needed(message)
|
if response.status == 200:
|
||||||
|
result = await response.json()
|
||||||
|
message = result.get("text", "")
|
||||||
|
logger.info("Transcrição concluída com sucesso")
|
||||||
|
logger.debug(f"Texto transcrito: {message[:100]}...")
|
||||||
|
|
||||||
return message, is_summary
|
is_summary = False
|
||||||
else:
|
if len(message) > 1000:
|
||||||
raise Exception("Erro ao transcrever o áudio")
|
logger.debug("Texto longo detectado, iniciando resumo")
|
||||||
|
is_summary = True
|
||||||
|
message = await summarize_text_if_needed(message)
|
||||||
|
|
||||||
|
return message, is_summary
|
||||||
|
else:
|
||||||
|
error_text = await response.text()
|
||||||
|
logger.error(f"Erro na transcrição: {error_text}")
|
||||||
|
raise Exception(f"Erro na transcrição: {error_text}")
|
||||||
|
|
||||||
def get_body_message_to_whatsapp_v2(message, remote_jid, message_id):
|
except Exception as e:
|
||||||
return {
|
logger.error(f"Erro no processo de transcrição: {str(e)}", exc_info=settings.DEBUG_MODE)
|
||||||
"number": remote_jid,
|
raise
|
||||||
"text": message,
|
|
||||||
"quoted": {"key": {"remoteJid": remote_jid, "fromMe": False, "id": message_id}},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
async def send_message_to_whatsapp(server_url, instance, apikey, message, remote_jid, message_id):
|
||||||
|
"""Envia mensagem via WhatsApp"""
|
||||||
|
logger.debug(f"Preparando envio de mensagem para: {remote_jid}")
|
||||||
|
url = f"{server_url}/message/sendText/{instance}"
|
||||||
|
headers = {"apikey": apikey}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Tentar enviar na V1
|
||||||
|
body = get_body_message_to_whatsapp_v1(message, remote_jid)
|
||||||
|
logger.debug("Tentando envio no formato V1")
|
||||||
|
result = await call_whatsapp(url, body, headers)
|
||||||
|
|
||||||
|
# Se falhar, tenta V2
|
||||||
|
if not result:
|
||||||
|
logger.debug("Formato V1 falhou, tentando formato V2")
|
||||||
|
body = get_body_message_to_whatsapp_v2(message, remote_jid, message_id)
|
||||||
|
await call_whatsapp(url, body, headers)
|
||||||
|
|
||||||
|
logger.info("Mensagem enviada com sucesso")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erro no envio da mensagem: {str(e)}", exc_info=settings.DEBUG_MODE)
|
||||||
|
raise
|
||||||
|
|
||||||
def get_body_message_to_whatsapp_v1(message, remote_jid):
|
def get_body_message_to_whatsapp_v1(message, remote_jid):
|
||||||
|
"""Formata mensagem no formato V1"""
|
||||||
return {
|
return {
|
||||||
"number": remote_jid,
|
"number": remote_jid,
|
||||||
"options": {"delay": 1200, "presence": "composing", "linkPreview": False},
|
"options": {"delay": 1200, "presence": "composing", "linkPreview": False},
|
||||||
"textMessage": {"text": message},
|
"textMessage": {"text": message},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_body_message_to_whatsapp_v2(message, remote_jid, message_id):
|
||||||
|
"""Formata mensagem no formato V2"""
|
||||||
|
return {
|
||||||
|
"number": remote_jid,
|
||||||
|
"text": message,
|
||||||
|
"quoted": {"key": {"remoteJid": remote_jid, "fromMe": False, "id": message_id}},
|
||||||
|
}
|
||||||
|
|
||||||
async def call_whatsapp(url, body, headers):
|
async def call_whatsapp(url, body, headers):
|
||||||
async with aiohttp.ClientSession() as session:
|
"""Realiza chamada à API do WhatsApp"""
|
||||||
async with session.post(url, json=body, headers=headers) as response:
|
try:
|
||||||
if response.status not in [200, 201]:
|
async with aiohttp.ClientSession() as session:
|
||||||
print(f"Erro ao enviar mensagem via WhatsApp: {await response.text()}")
|
logger.debug(f"Enviando requisição para: {url}")
|
||||||
return False
|
async with session.post(url, json=body, headers=headers) as response:
|
||||||
|
if response.status not in [200, 201]:
|
||||||
|
error_text = await response.text()
|
||||||
|
logger.error(f"Erro na API do WhatsApp: Status {response.status}, Resposta: {error_text}")
|
||||||
|
return False
|
||||||
|
logger.debug("Requisição bem-sucedida")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erro na chamada WhatsApp: {str(e)}", exc_info=settings.DEBUG_MODE)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# Função assíncrona para enviar a mensagem de resumo via WhatsApp
|
|
||||||
async def send_message_to_whatsapp(
|
|
||||||
server_url, instance, apikey, message, remote_jid, message_id
|
|
||||||
):
|
|
||||||
url = f"{server_url}/message/sendText/{instance}"
|
|
||||||
headers = {"apikey": apikey}
|
|
||||||
|
|
||||||
# Tentar enviar na V1
|
|
||||||
body = get_body_message_to_whatsapp_v1(message, remote_jid)
|
|
||||||
result = await call_whatsapp(url, body, headers)
|
|
||||||
|
|
||||||
# Se falhar, monta novo body na V2 e reenvia
|
|
||||||
if not result:
|
|
||||||
body = get_body_message_to_whatsapp_v2(message, remote_jid, message_id)
|
|
||||||
await call_whatsapp(url, body, headers)
|
|
||||||
|
|
||||||
|
|
||||||
# Função para obter o áudio em Base64 via API do WhatsApp
|
|
||||||
async def get_audio_base64(server_url, instance, apikey, message_id):
|
async def get_audio_base64(server_url, instance, apikey, message_id):
|
||||||
|
"""Obtém áudio em Base64 via API do WhatsApp"""
|
||||||
|
logger.debug(f"Obtendo áudio base64 para mensagem: {message_id}")
|
||||||
url = f"{server_url}/chat/getBase64FromMediaMessage/{instance}"
|
url = f"{server_url}/chat/getBase64FromMediaMessage/{instance}"
|
||||||
headers = {"apikey": apikey}
|
headers = {"apikey": apikey}
|
||||||
body = {"message": {"key": {"id": message_id}}, "convertToMp4": False}
|
body = {"message": {"key": {"id": message_id}}, "convertToMp4": False}
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
try:
|
||||||
async with session.post(url, json=body, headers=headers) as response:
|
async with aiohttp.ClientSession() as session:
|
||||||
if response.status in [200, 201]:
|
async with session.post(url, json=body, headers=headers) as response:
|
||||||
result = await response.json()
|
if response.status in [200, 201]:
|
||||||
return result.get("base64", "")
|
result = await response.json()
|
||||||
else:
|
logger.info("Áudio base64 obtido com sucesso")
|
||||||
raise HTTPException(
|
return result.get("base64", "")
|
||||||
status_code=500, detail="Falha ao obter áudio em base64"
|
else:
|
||||||
)
|
error_text = await response.text()
|
||||||
|
logger.error(f"Erro ao obter áudio base64: {error_text}")
|
||||||
|
raise HTTPException(status_code=500, detail="Falha ao obter áudio em base64")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Erro na obtenção do áudio base64: {str(e)}", exc_info=settings.DEBUG_MODE)
|
||||||
|
raise
|
||||||
|
Loading…
Reference in New Issue
Block a user