diff --git a/Dockerfile b/Dockerfile
index 83df167..4c98250 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,46 +1,50 @@
-# 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"]
+# 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
+
+# Timezone padrão (pode ser sobrescrito via variável de ambiente no Coolify)
+ENV TIMEZONE=America/Sao_Paulo
+ENV TZ=America/Sao_Paulo
+
+# 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"]
diff --git a/app.py b/app.py
index 5e01d70..09e3406 100644
--- a/app.py
+++ b/app.py
@@ -1,190 +1,198 @@
-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'
'
-
- # Cabeçalho
- html += ''
- html += f'| Hora | '
- html += f'Status | '
- html += f'WhatsApp | '
- html += f'Email | '
- html += f'Detalhes | '
- html += '
'
-
- # Corpo
- html += ''
- for log in logs:
- html += ''
- html += f'| {log["Hora"]} | '
- html += f'{log["Status"]} | '
- html += f'{log["WhatsApp"]} | '
- html += f'{log["Email"]} | '
- html += f'{log["Detalhes"]} | '
- html += '
'
- html += '
'
-
- 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")
+import streamlit as st
+import pandas as pd
+import time
+import os
+from datetime import datetime
+import pytz
+from apscheduler.schedulers.background import BackgroundScheduler
+from monitor_logic import run_monitor_check, diagnose_n8n_webhook
+
+# Carregar timezone da variável de ambiente (padrão: America/Sao_Paulo)
+TIMEZONE = os.getenv("TIMEZONE", "America/Sao_Paulo")
+tz = pytz.timezone(TIMEZONE)
+
+# ===== 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''
+
+ # Cabeçalho
+ html += ''
+ html += f'| Hora | '
+ html += f'Status | '
+ html += f'WhatsApp | '
+ html += f'Email | '
+ html += f'Detalhes | '
+ html += '
'
+
+ # Corpo
+ html += ''
+ for log in logs:
+ html += ''
+ html += f'| {log["Hora"]} | '
+ html += f'{log["Status"]} | '
+ html += f'{log["WhatsApp"]} | '
+ html += f'{log["Email"]} | '
+ html += f'{log["Detalhes"]} | '
+ html += '
'
+ html += '
'
+
+ 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)."""
+ now = datetime.now(tz)
+ print(f"[{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=TIMEZONE)
+ # 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()
+ now = datetime.now(tz)
+ st.session_state['last_run'] = now.strftime("%H:%M:%S")
+
+ # Adicionar ao histórico de logs
+ timestamp = 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")
diff --git a/monitor_logic.py b/monitor_logic.py
index ea4d469..8461fa9 100644
--- a/monitor_logic.py
+++ b/monitor_logic.py
@@ -1,274 +1,285 @@
-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(' 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))
+import os
+import requests
+import smtplib
+from email.mime.text import MIMEText
+from datetime import datetime
+import pytz
+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")
+
+# Timezone configurável via variável de ambiente
+TIMEZONE = os.getenv("TIMEZONE", "America/Sao_Paulo")
+tz = pytz.timezone(TIMEZONE)
+
+def get_now():
+ """Retorna datetime atual com timezone configurado."""
+ return datetime.now(tz)
+
+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:
+ now = get_now()
+ subject = "🚨 ERRO - Monitor Evolution API"
+ body = f"Falha no monitoramento.\nHorário: {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 = get_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(' 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
+ now = get_now()
+ payload = {"check": "diagnosis_ping", "timestamp": str(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))
diff --git a/requirements.txt b/requirements.txt
index a1eaff3..3194741 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,6 @@
-streamlit>=1.28.0
-requests>=2.31.0
-python-dotenv>=1.0.0
-apscheduler>=3.10.0
-pandas>=2.0.0
+streamlit>=1.28.0
+requests>=2.31.0
+python-dotenv>=1.0.0
+apscheduler>=3.10.0
+pandas>=2.0.0
+pytz>=2023.3