Enviar arquivos para "/"

This commit is contained in:
2025-12-30 22:36:05 -03:00
parent 00f3a643fe
commit 9c913dcc29
4 changed files with 539 additions and 515 deletions

View File

@@ -1,46 +1,50 @@
# Dockerfile para Monitor Evolution API + Webhook # Dockerfile para Monitor Evolution API + Webhook
# Deploy otimizado para Coolify # Deploy otimizado para Coolify
FROM python:3.11-slim FROM python:3.11-slim
# Definir diretório de trabalho # Definir diretório de trabalho
WORKDIR /app WORKDIR /app
# Variáveis de ambiente para Python # Variáveis de ambiente para Python
ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
# Variáveis de ambiente para Streamlit # Variáveis de ambiente para Streamlit
ENV STREAMLIT_SERVER_PORT=3000 ENV STREAMLIT_SERVER_PORT=3000
ENV STREAMLIT_SERVER_ADDRESS=0.0.0.0 ENV STREAMLIT_SERVER_ADDRESS=0.0.0.0
ENV STREAMLIT_SERVER_HEADLESS=true ENV STREAMLIT_SERVER_HEADLESS=true
ENV STREAMLIT_BROWSER_GATHER_USAGE_STATS=false ENV STREAMLIT_BROWSER_GATHER_USAGE_STATS=false
# Instalar dependências do sistema # Timezone padrão (pode ser sobrescrito via variável de ambiente no Coolify)
RUN apt-get update && apt-get install -y --no-install-recommends \ ENV TIMEZONE=America/Sao_Paulo
curl \ ENV TZ=America/Sao_Paulo
&& rm -rf /var/lib/apt/lists/*
# Instalar dependências do sistema
# Atualizar pip RUN apt-get update && apt-get install -y --no-install-recommends \
RUN pip install --no-cache-dir --upgrade pip curl \
&& rm -rf /var/lib/apt/lists/*
# Copiar arquivos da aplicação
COPY app.py monitor_logic.py ./ # Atualizar pip
RUN pip install --no-cache-dir --upgrade pip
# Copiar requirements.txt se existir, caso contrário instalar dependências diretamente
COPY requirements.txt* ./ # Copiar arquivos da aplicação
RUN if [ -f requirements.txt ]; then \ COPY app.py monitor_logic.py ./
pip install --no-cache-dir -r requirements.txt; \
else \ # Copiar requirements.txt se existir, caso contrário instalar dependências diretamente
pip install --no-cache-dir streamlit>=1.28.0 requests>=2.31.0 python-dotenv>=1.0.0 apscheduler>=3.10.0 pandas>=2.0.0; \ COPY requirements.txt* ./
fi RUN if [ -f requirements.txt ]; then \
pip install --no-cache-dir -r requirements.txt; \
# Expor porta do Streamlit (padrão Coolify) else \
EXPOSE 3000 pip install --no-cache-dir streamlit>=1.28.0 requests>=2.31.0 python-dotenv>=1.0.0 apscheduler>=3.10.0 pandas>=2.0.0; \
fi
# Healthcheck para Coolify
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ # Expor porta do Streamlit (padrão Coolify)
CMD curl --fail http://localhost:3000/_stcore/health || exit 1 EXPOSE 3000
# Comando para iniciar a aplicação na porta 3000 # Healthcheck para Coolify
CMD ["streamlit", "run", "app.py", "--server.port=3000", "--server.address=0.0.0.0"] HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl --fail http://localhost:3000/_stcore/health || exit 1
# Comando para iniciar a aplicação na porta 3000
CMD ["streamlit", "run", "app.py", "--server.port=3000", "--server.address=0.0.0.0"]

388
app.py
View File

@@ -1,190 +1,198 @@
import streamlit as st import streamlit as st
import pandas as pd import pandas as pd
import time import time
from datetime import datetime import os
from apscheduler.schedulers.background import BackgroundScheduler from datetime import datetime
from monitor_logic import run_monitor_check, diagnose_n8n_webhook import pytz
from apscheduler.schedulers.background import BackgroundScheduler
# ===== Configuração da Página (DEVE ser a primeira chamada Streamlit!) ===== from monitor_logic import run_monitor_check, diagnose_n8n_webhook
st.set_page_config(
page_title="Monitor Evolution API", # Carregar timezone da variável de ambiente (padrão: America/Sao_Paulo)
page_icon="🤖", TIMEZONE = os.getenv("TIMEZONE", "America/Sao_Paulo")
layout="wide" tz = pytz.timezone(TIMEZONE)
)
# ===== Configuração da Página (DEVE ser a primeira chamada Streamlit!) =====
# Função para gerar tabela HTML customizada st.set_page_config(
def gerar_tabela_html(logs): page_title="Monitor Evolution API",
"""Gera tabela HTML com estilos inline para garantir funcionamento.""" page_icon="🤖",
if not logs: layout="wide"
return "" )
# Estilos inline # Função para gerar tabela HTML customizada
estilo_tabela = "width: 100%; border-collapse: collapse; margin-top: 10px;" def gerar_tabela_html(logs):
estilo_header = "background-color: #f0f2f6; padding: 12px 15px; text-align: center; font-weight: bold; font-size: 16px; border-bottom: 2px solid #ddd;" """Gera tabela HTML com estilos inline para garantir funcionamento."""
estilo_celula = "padding: 10px 15px; text-align: center; font-size: 14px; border-bottom: 1px solid #eee;" if not logs:
estilo_celula_detalhes = "padding: 10px 15px; text-align: left; font-size: 14px; border-bottom: 1px solid #eee;" return ""
html = f'<table style="{estilo_tabela}">' # Estilos inline
estilo_tabela = "width: 100%; border-collapse: collapse; margin-top: 10px;"
# Cabeçalho estilo_header = "background-color: #f0f2f6; padding: 12px 15px; text-align: center; font-weight: bold; font-size: 16px; border-bottom: 2px solid #ddd;"
html += '<thead><tr>' estilo_celula = "padding: 10px 15px; text-align: center; font-size: 14px; border-bottom: 1px solid #eee;"
html += f'<th style="{estilo_header}">Hora</th>' estilo_celula_detalhes = "padding: 10px 15px; text-align: left; font-size: 14px; border-bottom: 1px solid #eee;"
html += f'<th style="{estilo_header}">Status</th>'
html += f'<th style="{estilo_header}">WhatsApp</th>' html = f'<table style="{estilo_tabela}">'
html += f'<th style="{estilo_header}">Email</th>'
html += f'<th style="{estilo_header}">Detalhes</th>' # Cabeçalho
html += '</tr></thead>' html += '<thead><tr>'
html += f'<th style="{estilo_header}">Hora</th>'
# Corpo html += f'<th style="{estilo_header}">Status</th>'
html += '<tbody>' html += f'<th style="{estilo_header}">WhatsApp</th>'
for log in logs: html += f'<th style="{estilo_header}">Email</th>'
html += '<tr>' html += f'<th style="{estilo_header}">Detalhes</th>'
html += f'<td style="{estilo_celula}">{log["Hora"]}</td>' html += '</tr></thead>'
html += f'<td style="{estilo_celula}">{log["Status"]}</td>'
html += f'<td style="{estilo_celula}">{log["WhatsApp"]}</td>' # Corpo
html += f'<td style="{estilo_celula}">{log["Email"]}</td>' html += '<tbody>'
html += f'<td style="{estilo_celula_detalhes}">{log["Detalhes"]}</td>' for log in logs:
html += '</tr>' html += '<tr>'
html += '</tbody></table>' html += f'<td style="{estilo_celula}">{log["Hora"]}</td>'
html += f'<td style="{estilo_celula}">{log["Status"]}</td>'
return html html += f'<td style="{estilo_celula}">{log["WhatsApp"]}</td>'
html += f'<td style="{estilo_celula}">{log["Email"]}</td>'
# ===== Inicializar estado da sessão ===== html += f'<td style="{estilo_celula_detalhes}">{log["Detalhes"]}</td>'
if 'logs' not in st.session_state: html += '</tr>'
st.session_state['logs'] = [] html += '</tbody></table>'
if 'last_run' not in st.session_state: return html
st.session_state['last_run'] = "Nunca"
# ===== Inicializar estado da sessão =====
if 'intervalo_horas' not in st.session_state: if 'logs' not in st.session_state:
st.session_state['intervalo_horas'] = 3 st.session_state['logs'] = []
# ===== Função wrapper para o Scheduler gravar logs ===== if 'last_run' not in st.session_state:
def scheduled_job(): st.session_state['last_run'] = "Nunca"
"""Executa verificação agendada (roda em background thread)."""
print(f"[{datetime.now()}] Executando verificação agendada...") if 'intervalo_horas' not in st.session_state:
status, messages, whatsapp_status, email_status = run_monitor_check() st.session_state['intervalo_horas'] = 3
print(f"Status: {status} | WhatsApp: {whatsapp_status} | Email: {email_status}")
print("\n".join(messages)) # ===== Função wrapper para o Scheduler gravar logs =====
def scheduled_job():
# ===== Configurar o Agendador (Singleton via cache_resource) ===== """Executa verificação agendada (roda em background thread)."""
@st.cache_resource now = datetime.now(tz)
def init_scheduler(intervalo_horas): print(f"[{now}] Executando verificação agendada...")
scheduler = BackgroundScheduler(timezone="America/Sao_Paulo") status, messages, whatsapp_status, email_status = run_monitor_check()
# Job com intervalo configurável a partir de 00:00 print(f"Status: {status} | WhatsApp: {whatsapp_status} | Email: {email_status}")
scheduler.add_job( print("\n".join(messages))
scheduled_job,
'interval', # ===== Configurar o Agendador (Singleton via cache_resource) =====
hours=intervalo_horas, @st.cache_resource
start_date='2025-01-01 00:00:00' def init_scheduler(intervalo_horas):
) scheduler = BackgroundScheduler(timezone=TIMEZONE)
scheduler.start() # Job com intervalo configurável a partir de 00:00
return scheduler scheduler.add_job(
scheduled_job,
scheduler = init_scheduler(st.session_state['intervalo_horas']) 'interval',
hours=intervalo_horas,
# ===== Interface Principal ===== start_date='2025-01-01 00:00:00'
st.title("🤖 Monitor Evolution API + Webhook") )
st.markdown("---") scheduler.start()
return scheduler
# Métricas superiores
col1, col2 = st.columns(2) scheduler = init_scheduler(st.session_state['intervalo_horas'])
with col1: # ===== Interface Principal =====
st.metric(label="Última Verificação", value=st.session_state['last_run']) st.title("🤖 Monitor Evolution API + Webhook")
st.markdown("---")
with col2:
st.write("### Status Agendador") # Métricas superiores
intervalo_atual = st.session_state['intervalo_horas'] col1, col2 = st.columns(2)
st.success(f"✅ Ativo (a cada {intervalo_atual}h desde 00:00)")
with col1:
st.markdown("### Ações") st.metric(label="Última Verificação", value=st.session_state['last_run'])
# Botão de execução manual with col2:
if st.button("🚀 Executar Verificação Agora", type="primary"): st.write("### Status Agendador")
with st.spinner('Verificando status dos serviços...'): intervalo_atual = st.session_state['intervalo_horas']
status, messages, whatsapp_status, email_status = run_monitor_check() st.success(f"✅ Ativo (a cada {intervalo_atual}h desde 00:00)")
st.session_state['last_run'] = datetime.now().strftime("%H:%M:%S")
st.markdown("### Ações")
# Adicionar ao histórico de logs
timestamp = datetime.now().strftime("%H:%M:%S") # Botão de execução manual
entry = { if st.button("🚀 Executar Verificação Agora", type="primary"):
"Hora": timestamp, with st.spinner('Verificando status dos serviços...'):
"Status": "✅ Sucesso" if status else "❌ Erro", status, messages, whatsapp_status, email_status = run_monitor_check()
"WhatsApp": whatsapp_status, now = datetime.now(tz)
"Email": email_status, st.session_state['last_run'] = now.strftime("%H:%M:%S")
"Detalhes": " | ".join(messages)
} # Adicionar ao histórico de logs
st.session_state['logs'].insert(0, entry) # Adiciona no topo timestamp = now.strftime("%H:%M:%S")
entry = {
if status: "Hora": timestamp,
st.success("✅ Todos os serviços estão operacionais!") "Status": "✅ Sucesso" if status else "❌ Erro",
else: "WhatsApp": whatsapp_status,
st.error("❌ Falha detectada em um ou mais serviços. Verifique os logs.") "Email": email_status,
"Detalhes": " | ".join(messages)
st.markdown("---") }
st.markdown("### 📋 Histórico de Execuções (Sessão Atual)") st.session_state['logs'].insert(0, entry) # Adiciona no topo
if st.session_state['logs']: if status:
# Renderizar tabela HTML customizada st.success("✅ Todos os serviços estão operacionais!")
tabela_html = gerar_tabela_html(st.session_state['logs']) else:
st.markdown(tabela_html, unsafe_allow_html=True) st.error("❌ Falha detectada em um ou mais serviços. Verifique os logs.")
else:
st.info(" Nenhuma verificação executada nesta sessão ainda.") st.markdown("---")
st.markdown("### 📋 Histórico de Execuções (Sessão Atual)")
# ===== Sidebar =====
st.sidebar.title(" Sobre") if st.session_state['logs']:
st.sidebar.info( # Renderizar tabela HTML customizada
""" tabela_html = gerar_tabela_html(st.session_state['logs'])
Este aplicativo monitora: st.markdown(tabela_html, unsafe_allow_html=True)
else:
1. **Evolution API**: Verifica se a instância está online. st.info(" Nenhuma verificação executada nesta sessão ainda.")
2. **N8N Webhook**: Verifica se o endpoint responde.
# ===== Sidebar =====
**Notificações:** st.sidebar.title(" Sobre")
- ✅ **Sucesso**: Envia WhatsApp. st.sidebar.info(
- ❌ **Erro**: Envia Email. """
""" Este aplicativo monitora:
)
1. **Evolution API**: Verifica se a instância está online.
st.sidebar.markdown("---") 2. **N8N Webhook**: Verifica se o endpoint responde.
# Configuração do Intervalo de Disparo **Notificações:**
st.sidebar.markdown("### ⏱️ Intervalo de Disparo") - ✅ **Sucesso**: Envia WhatsApp.
intervalo_slider = st.sidebar.slider( - ❌ **Erro**: Envia Email.
"Executar a cada (horas):", """
min_value=1, )
max_value=12,
value=st.session_state['intervalo_horas'], st.sidebar.markdown("---")
step=1,
help="Define o intervalo entre execuções automáticas a partir de 00:00" # Configuração do Intervalo de Disparo
) st.sidebar.markdown("### ⏱️ Intervalo de Disparo")
intervalo_slider = st.sidebar.slider(
# Mostrar horários de execução baseados no intervalo "Executar a cada (horas):",
horarios = [f"{h:02d}:00" for h in range(0, 24, intervalo_slider)] min_value=1,
st.sidebar.caption(f"Horários: {', '.join(horarios)}") max_value=12,
value=st.session_state['intervalo_horas'],
# Verificar se o intervalo mudou step=1,
if intervalo_slider != st.session_state['intervalo_horas']: help="Define o intervalo entre execuções automáticas a partir de 00:00"
st.sidebar.warning("⚠️ Reinicie a aplicação para aplicar o novo intervalo.") )
st.session_state['intervalo_horas'] = intervalo_slider
# Mostrar horários de execução baseados no intervalo
st.sidebar.markdown("---") horarios = [f"{h:02d}:00" for h in range(0, 24, intervalo_slider)]
st.sidebar.caption(f"Horários: {', '.join(horarios)}")
# Diagnóstico N8N na Sidebar
with st.sidebar.expander("🛠️ Diagnóstico N8N"): # Verificar se o intervalo mudou
st.write("Teste isolado do Webhook N8N") if intervalo_slider != st.session_state['intervalo_horas']:
if st.button("Testar Webhook N8N", key="btn_diag"): st.sidebar.warning("⚠️ Reinicie a aplicação para aplicar o novo intervalo.")
with st.spinner("Enviando POST de teste..."): st.session_state['intervalo_horas'] = intervalo_slider
success, log_data = diagnose_n8n_webhook()
st.sidebar.markdown("---")
if success:
st.success(f"✅ Status: {log_data['status_code']}") # Diagnóstico N8N na Sidebar
else: with st.sidebar.expander("🛠️ Diagnóstico N8N"):
st.error(f"❌ Erro: {log_data.get('status_code') or log_data.get('error')}") st.write("Teste isolado do Webhook N8N")
if st.button("Testar Webhook N8N", key="btn_diag"):
st.json(log_data) with st.spinner("Enviando POST de teste..."):
success, log_data = diagnose_n8n_webhook()
# Informações de configuração
st.sidebar.markdown("---") if success:
st.sidebar.caption(f"Intervalo atual: {st.session_state['intervalo_horas']}h") st.success(f"✅ Status: {log_data['status_code']}")
else:
st.error(f"❌ Erro: {log_data.get('status_code') or log_data.get('error')}")
st.json(log_data)
# Informações de configuração
st.sidebar.markdown("---")
st.sidebar.caption(f"Intervalo atual: {st.session_state['intervalo_horas']}h")

View File

@@ -1,274 +1,285 @@
import os import os
import requests import requests
import smtplib import smtplib
from email.mime.text import MIMEText from email.mime.text import MIMEText
from datetime import datetime from datetime import datetime
from dotenv import load_dotenv import pytz
from dotenv import load_dotenv
# Carregar variáveis de ambiente
load_dotenv() # Carregar variáveis de ambiente
load_dotenv()
# Configurações
EVOLUTION_API_URL = os.getenv("EVOLUTION_API_URL") # Configurações
EVOLUTION_INSTANCE_ID = os.getenv("EVOLUTION_INSTANCE_ID") EVOLUTION_API_URL = os.getenv("EVOLUTION_API_URL")
EVOLUTION_API_TOKEN = os.getenv("EVOLUTION_API_TOKEN") EVOLUTION_INSTANCE_ID = os.getenv("EVOLUTION_INSTANCE_ID")
EVOLUTION_SENDER_INSTANCE = os.getenv("EVOLUTION_SENDER_INSTANCE", "Wander") EVOLUTION_API_TOKEN = os.getenv("EVOLUTION_API_TOKEN")
N8N_WEBHOOK_URL = os.getenv("N8N_WEBHOOK_URL") EVOLUTION_SENDER_INSTANCE = os.getenv("EVOLUTION_SENDER_INSTANCE", "Wander")
WHATSAPP_NUMBER = os.getenv("WHATSAPP_NUMBER") N8N_WEBHOOK_URL = os.getenv("N8N_WEBHOOK_URL")
SMTP_SERVER = os.getenv("SMTP_SERVER", "smtp-mail.outlook.com") WHATSAPP_NUMBER = os.getenv("WHATSAPP_NUMBER")
SMTP_PORT = int(os.getenv("SMTP_PORT", 587)) SMTP_SERVER = os.getenv("SMTP_SERVER", "smtp-mail.outlook.com")
SMTP_EMAIL = os.getenv("SMTP_EMAIL") SMTP_PORT = int(os.getenv("SMTP_PORT", 587))
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD") SMTP_EMAIL = os.getenv("SMTP_EMAIL")
EMAIL_TO = os.getenv("EMAIL_TO") SMTP_PASSWORD = os.getenv("SMTP_PASSWORD")
EMAIL_TO = os.getenv("EMAIL_TO")
def get_headers():
return { # Timezone configurável via variável de ambiente
"apikey": EVOLUTION_API_TOKEN, TIMEZONE = os.getenv("TIMEZONE", "America/Sao_Paulo")
"Authorization": f"Bearer {EVOLUTION_API_TOKEN}", tz = pytz.timezone(TIMEZONE)
"Content-Type": "application/json"
} def get_now():
"""Retorna datetime atual com timezone configurado."""
def send_email_error(error_details): return datetime.now(tz)
"""Envia email de erro caso algo falhe."""
try: def get_headers():
subject = "🚨 ERRO - Monitor Evolution API" return {
body = f"Falha no monitoramento.\nHorário: {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}\n\nDetalhes do Erro:\n{error_details}" "apikey": EVOLUTION_API_TOKEN,
"Authorization": f"Bearer {EVOLUTION_API_TOKEN}",
msg = MIMEText(body) "Content-Type": "application/json"
msg['Subject'] = subject }
msg['From'] = SMTP_EMAIL
msg['To'] = EMAIL_TO def send_email_error(error_details):
"""Envia email de erro caso algo falhe."""
# Ler configuração de segurança (Default: STARTTLS) try:
smtp_security = os.getenv("SMTP_SECURITY", "STARTTLS").upper() now = get_now()
subject = "🚨 ERRO - Monitor Evolution API"
if smtp_security == "SSL": body = f"Falha no monitoramento.\nHorário: {now.strftime('%d/%m/%Y %H:%M:%S')}\n\nDetalhes do Erro:\n{error_details}"
server = smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT)
else: msg = MIMEText(body)
server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) msg['Subject'] = subject
if smtp_security == "STARTTLS": msg['From'] = SMTP_EMAIL
server.starttls() msg['To'] = EMAIL_TO
with server: # Ler configuração de segurança (Default: STARTTLS)
server.login(SMTP_EMAIL, SMTP_PASSWORD) smtp_security = os.getenv("SMTP_SECURITY", "STARTTLS").upper()
server.send_message(msg)
if smtp_security == "SSL":
return True, "Email de erro enviado com sucesso." server = smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT)
except Exception as e: else:
return False, f"Falha ao enviar email: {str(e)}" server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
if smtp_security == "STARTTLS":
def send_whatsapp_success(): server.starttls()
"""Envia mensagem de sucesso via Evolution API."""
try: with server:
url = f"{EVOLUTION_API_URL}/message/sendText/{EVOLUTION_SENDER_INSTANCE}" server.login(SMTP_EMAIL, SMTP_PASSWORD)
server.send_message(msg)
now = datetime.now()
message_text = ( return True, "Email de erro enviado com sucesso."
f"✅ *MONITOR OK*\n\n" except Exception as e:
f"📅 Data: {now.strftime('%d/%m/%Y')}\n" return False, f"Falha ao enviar email: {str(e)}"
f"🕐 Horário: {now.strftime('%H:%M:%S')}\n\n"
f"• Evolution API: Online\n" def send_whatsapp_success():
f"• Webhook Instância: Habilitado\n" """Envia mensagem de sucesso via Evolution API."""
f"• Webhook N8N: Funcionando\n\n" try:
f"Todos os serviços operacionais." url = f"{EVOLUTION_API_URL}/message/sendText/{EVOLUTION_SENDER_INSTANCE}"
)
now = get_now()
payload = { message_text = (
"number": WHATSAPP_NUMBER, f"✅ *MONITOR OK*\n\n"
"text": message_text f"📅 Data: {now.strftime('%d/%m/%Y')}\n"
} f"🕐 Horário: {now.strftime('%H:%M:%S')}\n\n"
f"• Evolution API: Online\n"
# A Evolution API às vezes aceita apikey no header ou como query param, f"• Webhook Instância: Habilitado\n"
# mas o padrão aqui é usar o header configurado. f"• Webhook N8N: Funcionando\n\n"
response = requests.post(url, json=payload, headers=get_headers(), timeout=10) f"Todos os serviços operacionais."
)
if response.status_code == 200 or response.status_code == 201:
return True, "WhatsApp de sucesso enviado." payload = {
else: "number": WHATSAPP_NUMBER,
return False, f"Falha ao enviar WhatsApp: {response.text}" "text": message_text
}
except Exception as e:
return False, f"Erro na requisição WhatsApp: {str(e)}" # A Evolution API às vezes aceita apikey no header ou como query param,
# mas o padrão aqui é usar o header configurado.
def run_monitor_check(): response = requests.post(url, json=payload, headers=get_headers(), timeout=10)
"""Executa o fluxo de verificação completo."""
log_messages = [] if response.status_code == 200 or response.status_code == 201:
whatsapp_status = "-" return True, "WhatsApp de sucesso enviado."
email_status = "-" else:
return False, f"Falha ao enviar WhatsApp: {response.text}"
# Passo 1: Verificar Health Evolution API
try: except Exception as e:
# Endpoint correto: /instance/connectionState/{instanceName} return False, f"Erro na requisição WhatsApp: {str(e)}"
# Usa o nome da instância (EVOLUTION_SENDER_INSTANCE) e não o ID
health_url = f"{EVOLUTION_API_URL}/instance/connectionState/{EVOLUTION_SENDER_INSTANCE}" def run_monitor_check():
resp_health = requests.get(health_url, headers=get_headers(), timeout=10) """Executa o fluxo de verificação completo."""
log_messages = []
if resp_health.status_code == 200: whatsapp_status = "-"
# Validar se a instância está realmente conectada (state: open) email_status = "-"
try:
data = resp_health.json() # Passo 1: Verificar Health Evolution API
instance_data = data.get('instance', {}) try:
state = instance_data.get('state') or data.get('state') # Endpoint correto: /instance/connectionState/{instanceName}
# Usa o nome da instância (EVOLUTION_SENDER_INSTANCE) e não o ID
if state == 'open': health_url = f"{EVOLUTION_API_URL}/instance/connectionState/{EVOLUTION_SENDER_INSTANCE}"
log_messages.append("✅ Evolution API Online") resp_health = requests.get(health_url, headers=get_headers(), timeout=10)
else:
# Estado não é 'open' (ex: close, closed, connecting) = ERRO! if resp_health.status_code == 200:
error_msg = f"Evolution API Desconectada (State: {state})" # Validar se a instância está realmente conectada (state: open)
log_messages.append(f"{error_msg}") try:
data = resp_health.json()
# Enviar email de erro instance_data = data.get('instance', {})
email_ok, _ = send_email_error(error_msg) state = instance_data.get('state') or data.get('state')
email_status = "Sucesso" if email_ok else "Erro"
if state == 'open':
return False, log_messages, whatsapp_status, email_status log_messages.append("✅ Evolution API Online")
else:
except Exception as json_err: # Estado não é 'open' (ex: close, closed, connecting) = ERRO!
# Se falhar ler JSON mas deu 200 OK, verificar se é HTML error_msg = f"Evolution API Desconectada (State: {state})"
raw_text = resp_health.text.strip() log_messages.append(f"{error_msg}")
if raw_text.startswith('<!doctype') or raw_text.startswith('<html'):
error_msg = "Evolution API retornou HTML (possível erro de autenticação)" # Enviar email de erro
elif raw_text: email_ok, _ = send_email_error(error_msg)
error_msg = "Evolution API retornou JSON inválido" email_status = "Sucesso" if email_ok else "Erro"
else:
error_msg = "Evolution API retornou resposta vazia" return False, log_messages, whatsapp_status, email_status
log_messages.append(f"{error_msg}") except Exception as json_err:
email_ok, _ = send_email_error(error_msg) # Se falhar ler JSON mas deu 200 OK, verificar se é HTML
email_status = "Sucesso" if email_ok else "Erro" raw_text = resp_health.text.strip()
if raw_text.startswith('<!doctype') or raw_text.startswith('<html'):
return False, log_messages, whatsapp_status, email_status error_msg = "Evolution API retornou HTML (possível erro de autenticação)"
else: elif raw_text:
error_msg = f"Evolution API Offline. Status: {resp_health.status_code}" error_msg = "Evolution API retornou JSON inválido"
log_messages.append(f"{error_msg}") else:
error_msg = "Evolution API retornou resposta vazia"
# Tenta enviar email de erro
email_ok, _ = send_email_error(error_msg) log_messages.append(f"{error_msg}")
email_status = "Sucesso" if email_ok else "Erro" email_ok, _ = send_email_error(error_msg)
email_status = "Sucesso" if email_ok else "Erro"
return False, log_messages, whatsapp_status, email_status
return False, log_messages, whatsapp_status, email_status
except Exception as e: else:
error_msg = f"Erro ao conectar na Evolution API: {str(e)}" error_msg = f"Evolution API Offline. Status: {resp_health.status_code}"
log_messages.append(f"{error_msg}") log_messages.append(f"{error_msg}")
email_ok, _ = send_email_error(error_msg) # Tenta enviar email de erro
email_status = "Sucesso" if email_ok else "Erro" email_ok, _ = send_email_error(error_msg)
email_status = "Sucesso" if email_ok else "Erro"
return False, log_messages, whatsapp_status, email_status
return False, log_messages, whatsapp_status, email_status
# Passo 1.5: Verificar se o Webhook da Instância está habilitado
try: except Exception as e:
webhook_url = f"{EVOLUTION_API_URL}/webhook/find/{EVOLUTION_SENDER_INSTANCE}" error_msg = f"Erro ao conectar na Evolution API: {str(e)}"
resp_webhook_config = requests.get(webhook_url, headers=get_headers(), timeout=15) log_messages.append(f"{error_msg}")
if resp_webhook_config.status_code == 200: email_ok, _ = send_email_error(error_msg)
webhook_data = resp_webhook_config.json() email_status = "Sucesso" if email_ok else "Erro"
# Verificar se enabled é True
webhook_enabled = webhook_data.get('enabled', False) return False, log_messages, whatsapp_status, email_status
webhook_url_configured = webhook_data.get('url', 'N/A')
# Passo 1.5: Verificar se o Webhook da Instância está habilitado
if webhook_enabled: try:
log_messages.append("✅ Webhook Instância OK") webhook_url = f"{EVOLUTION_API_URL}/webhook/find/{EVOLUTION_SENDER_INSTANCE}"
else: resp_webhook_config = requests.get(webhook_url, headers=get_headers(), timeout=15)
error_msg = f"Webhook da Instância DESABILITADO"
log_messages.append(f"{error_msg}") if resp_webhook_config.status_code == 200:
webhook_data = resp_webhook_config.json()
email_ok, _ = send_email_error(error_msg) # Verificar se enabled é True
email_status = "Sucesso" if email_ok else "Erro" webhook_enabled = webhook_data.get('enabled', False)
webhook_url_configured = webhook_data.get('url', 'N/A')
return False, log_messages, whatsapp_status, email_status
else: if webhook_enabled:
# Se não conseguir verificar, apenas loga aviso mas continua log_messages.append("✅ Webhook Instância OK")
log_messages.append(f"⚠️ Não foi possível verificar webhook da instância (Status: {resp_webhook_config.status_code})") else:
error_msg = f"Webhook da Instância DESABILITADO"
except Exception as e: log_messages.append(f"{error_msg}")
# Se falhar a verificação do webhook, loga aviso mas continua
log_messages.append(f"⚠️ Erro ao verificar webhook da instância: {str(e)[:50]}") email_ok, _ = send_email_error(error_msg)
email_status = "Sucesso" if email_ok else "Erro"
# Passo 2: Testar Webhook N8N
try: return False, log_messages, whatsapp_status, email_status
# Nota: O fluxo original faz um POST simples else:
resp_webhook = requests.post(N8N_WEBHOOK_URL, json={"check": "ping"}, timeout=10) # Se não conseguir verificar, apenas loga aviso mas continua
log_messages.append(f"⚠️ Não foi possível verificar webhook da instância (Status: {resp_webhook_config.status_code})")
if resp_webhook.status_code == 200:
log_messages.append("✅ Webhook N8N OK") except Exception as e:
else: # Se falhar a verificação do webhook, loga aviso mas continua
error_msg = f"Webhook N8N falhou. Status: {resp_webhook.status_code}" log_messages.append(f"⚠️ Erro ao verificar webhook da instância: {str(e)[:50]}")
log_messages.append(f"{error_msg}")
# Passo 2: Testar Webhook N8N
email_ok, _ = send_email_error(error_msg) try:
email_status = "Sucesso" if email_ok else "Erro" # Nota: O fluxo original faz um POST simples
resp_webhook = requests.post(N8N_WEBHOOK_URL, json={"check": "ping"}, timeout=10)
return False, log_messages, whatsapp_status, email_status
if resp_webhook.status_code == 200:
except Exception as e: log_messages.append("✅ Webhook N8N OK")
error_msg = f"Erro ao conectar no Webhook N8N: {str(e)}" else:
log_messages.append(f"{error_msg}") error_msg = f"Webhook N8N falhou. Status: {resp_webhook.status_code}"
log_messages.append(f"{error_msg}")
email_ok, _ = send_email_error(error_msg)
email_status = "Sucesso" if email_ok else "Erro" email_ok, _ = send_email_error(error_msg)
email_status = "Sucesso" if email_ok else "Erro"
return False, log_messages, whatsapp_status, email_status
return False, log_messages, whatsapp_status, email_status
# Passo 3: Se chegou aqui, tudo OK -> Enviar WhatsApp
# Se está tudo OK, email de erro não é enviado, então mantemos email_status como "-" ou podemos por "N/A" except Exception as e:
# O usuário pediu "Sucesso ou Error". Se não enviou nada, talvez "-" seja melhor que "Error". error_msg = f"Erro ao conectar no Webhook N8N: {str(e)}"
log_messages.append(f"{error_msg}")
success, msg = send_whatsapp_success()
if success: email_ok, _ = send_email_error(error_msg)
log_messages.append(f"{msg}") email_status = "Sucesso" if email_ok else "Erro"
whatsapp_status = "Sucesso"
return True, log_messages, whatsapp_status, email_status return False, log_messages, whatsapp_status, email_status
else:
# Se falhar enviar o whats, é um erro também? # Passo 3: Se chegou aqui, tudo OK -> Enviar WhatsApp
whatsapp_status = "Erro" # Se está tudo OK, email de erro não é enviado, então mantemos email_status como "-" ou podemos por "N/A"
# O usuário pediu "Sucesso ou Error". Se não enviou nada, talvez "-" seja melhor que "Error".
# O fluxo original manda email de erro se o check falhar.
# Se o envio do whats falhar, vamos mandar email success, msg = send_whatsapp_success()
error_msg = f"Serviços Online, mas falha ao enviar WhatsApp: {msg}" if success:
log_messages.append(f"⚠️ {error_msg}") log_messages.append(f" {msg}")
whatsapp_status = "Sucesso"
email_ok, _ = send_email_error(error_msg) return True, log_messages, whatsapp_status, email_status
email_status = "Sucesso" if email_ok else "Erro" else:
# Se falhar enviar o whats, é um erro também?
return False, log_messages, whatsapp_status, email_status whatsapp_status = "Erro"
def diagnose_n8n_webhook(): # O fluxo original manda email de erro se o check falhar.
"""Realiza um teste detalhado do Webhook N8N para diagnóstico.""" # Se o envio do whats falhar, vamos mandar email
try: error_msg = f"Serviços Online, mas falha ao enviar WhatsApp: {msg}"
url = N8N_WEBHOOK_URL log_messages.append(f"⚠️ {error_msg}")
payload = {"check": "diagnosis_ping", "timestamp": str(datetime.now())}
email_ok, _ = send_email_error(error_msg)
# Preparar log email_status = "Sucesso" if email_ok else "Erro"
log = {
"url": url, return False, log_messages, whatsapp_status, email_status
"method": "POST",
"payload_sent": payload, def diagnose_n8n_webhook():
"status_code": None, """Realiza um teste detalhado do Webhook N8N para diagnóstico."""
"response_body": None, try:
"headers_received": None, url = N8N_WEBHOOK_URL
"error": None now = get_now()
} payload = {"check": "diagnosis_ping", "timestamp": str(now)}
response = requests.post(url, json=payload, timeout=10) # Preparar log
log = {
log["status_code"] = response.status_code "url": url,
log["response_body"] = response.text "method": "POST",
log["headers_received"] = dict(response.headers) "payload_sent": payload,
"status_code": None,
if response.status_code == 200: "response_body": None,
return True, log "headers_received": None,
else: "error": None
return False, log }
except Exception as e: response = requests.post(url, json=payload, timeout=10)
log["error"] = str(e)
return False, log log["status_code"] = response.status_code
log["response_body"] = response.text
if __name__ == "__main__": log["headers_received"] = dict(response.headers)
# Teste rápido se rodar direto
status, logs, wa_status, em_status = run_monitor_check() if response.status_code == 200:
print(f"Status: {status}") return True, log
print(f"WhatsApp: {wa_status} | Email: {em_status}") else:
print("\n".join(logs)) return False, log
except Exception as e:
log["error"] = str(e)
return False, log
if __name__ == "__main__":
# Teste rápido se rodar direto
status, logs, wa_status, em_status = run_monitor_check()
print(f"Status: {status}")
print(f"WhatsApp: {wa_status} | Email: {em_status}")
print("\n".join(logs))

View File

@@ -1,5 +1,6 @@
streamlit>=1.28.0 streamlit>=1.28.0
requests>=2.31.0 requests>=2.31.0
python-dotenv>=1.0.0 python-dotenv>=1.0.0
apscheduler>=3.10.0 apscheduler>=3.10.0
pandas>=2.0.0 pandas>=2.0.0
pytz>=2023.3