Commit inicial - upload de todos os arquivos da pasta

This commit is contained in:
2025-12-30 22:08:12 -03:00
commit 00f3a643fe
8 changed files with 644 additions and 0 deletions

40
.dockerignore Normal file
View File

@@ -0,0 +1,40 @@
# Git
.git
.gitignore
# Python
__pycache__
*.py[cod]
*$py.class
*.so
.Python
.env
.venv
env/
venv/
ENV/
# IDE
.idea/
.vscode/
*.swp
*.swo
# Docker
Dockerfile
.dockerignore
# Documentação
*.md
!README_Monitor.md
# Logs e cache
*.log
.cache/
# Arquivos temporários
*.tmp
*.temp
.DS_Store
Thumbs.db

24
.env Normal file
View File

@@ -0,0 +1,24 @@
# Configurações do Evolution API
# URL base da sua instância (sem a barra final)
EVOLUTION_API_URL=http://evo-x40k0g00kw4k4k00k44wssgc.31.97.64.165.sslip.io
# Nome da instância para checagem de saúde
EVOLUTION_INSTANCE_ID=33c2dae2-8c3a-4333-972d-813169e287c8
# Token de Autenticação (Bearer Token)
EVOLUTION_API_TOKEN=OWYyih16w1vZCm4rSza9axTz57bzOJ3Y
# Instância de envio de mensagem (pode ser a mesma ou nome amigável 'Wander')
EVOLUTION_SENDER_INSTANCE=Wander
# Configurações do Webhook N8N
N8N_WEBHOOK_URL=https://n8n.aplicativopro.com/webhook/a352fb9d-8f6c-4fbb-a4ab-46886af81789
# Configurações de Notificação (Whatsapp)
WHATSAPP_NUMBER=5511996947374
# Configurações de Email (SMTP)
SMTP_SERVER=smtp.hostinger.com
SMTP_PORT=465
SMTP_EMAIL=wander@amixxam.com
# Senha do email ou Senha de Aplicativo
SMTP_PASSWORD=Luraba@3
EMAIL_TO=wandermotta@hotmail.com
SMTP_SECURITY=SSL

46
Dockerfile Normal file
View File

@@ -0,0 +1,46 @@
# Dockerfile para Monitor Evolution API + Webhook
# Deploy otimizado para Coolify
FROM python:3.11-slim
# Definir diretório de trabalho
WORKDIR /app
# Variáveis de ambiente para Python
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# Variáveis de ambiente para Streamlit
ENV STREAMLIT_SERVER_PORT=3000
ENV STREAMLIT_SERVER_ADDRESS=0.0.0.0
ENV STREAMLIT_SERVER_HEADLESS=true
ENV STREAMLIT_BROWSER_GATHER_USAGE_STATS=false
# Instalar dependências do sistema
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
# Atualizar pip
RUN pip install --no-cache-dir --upgrade pip
# Copiar arquivos da aplicação
COPY app.py monitor_logic.py ./
# Copiar requirements.txt se existir, caso contrário instalar dependências diretamente
COPY requirements.txt* ./
RUN if [ -f requirements.txt ]; then \
pip install --no-cache-dir -r requirements.txt; \
else \
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
# Expor porta do Streamlit (padrão Coolify)
EXPOSE 3000
# Healthcheck para Coolify
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"]

35
README_Monitor.md Normal file
View File

@@ -0,0 +1,35 @@
# Monitor Evolution API + Webhook
Aplicativo desenvolvido em Python + Streamlit para monitorar a saúde da Evolution API e do Webhook N8N.
## 🛠️ Instalação
1. **Instale as dependências**:
Abra o terminal na pasta do projeto e execute:
```bash
pip install -r requirements.txt
```
2. **Configure o ambiente**:
- Copie o arquivo `.env.example` e renomeie para `.env`.
- Abra o arquivo `.env` e preencha com suas credenciais:
- `EVOLUTION_API_TOKEN`: Seu token da API.
- `SMTP_PASSWORD`: Senha do email `wandermotta@hotmail.com`.
## 🚀 Como Rodar
Execute o comando abaixo no terminal:
```bash
streamlit run app.py
```
O navegador abrirá automaticamente com o painel de controle.
## ⚙️ Funcionalidades
- **Monitoramento Automático**: O sistema verifica os serviços automaticamente às **07:00**, **13:00** e **17:00**.
- **Botão Executar Agor**: Permite testar o fluxo imediatamente.
- **Notificações**:
- Se tudo estiver **OK**: Envia WhatsApp para `5511996947374`.
- Se houver **ERRO**: Envia email para `wandermotta@hotmail.com`.

190
app.py Normal file
View File

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

30
github.bat Normal file
View File

@@ -0,0 +1,30 @@
@echo off
echo === INICIANDO UPLOAD PARA GITHUB ===
REM Inicializar repositório Git
echo Inicializando repositorio Git...
git init
REM Adicionar todos os arquivos
echo Adicionando todos os arquivos...
git add .
REM Fazer commit inicial
echo Realizando commit inicial...
git commit -m "Commit inicial - upload de todos os arquivos da pasta"
REM Adicionar repositório remoto
echo Conectando ao repositorio remoto...
git remote add origin https://gitea.aplicativopro.com/wander/Monitor-N8N.git
REM Definir branch principal
echo Definindo branch principal como 'main'...
git branch -M main
REM Fazer push para o GitHub
echo Fazendo upload para o GitHub...
git push -u origin main
echo === UPLOAD CONCLUIDO COM SUCESSO! ===
pause

274
monitor_logic.py Normal file
View File

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

5
requirements.txt Normal file
View File

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