botapp
botapp é um SDK + dashboard Django para observabilidade de automações RPA. Ele instrumenta seus bots com um único decorator, registra cada execução (sucesso, falha, duração, ambiente), expõe um painel web para acompanhamento e dispara alertas (Slack, Discord, e-mail) quando algo foge do esperado.
Sumário
- Visão geral
- Funcionalidades
- Arquitetura — modos de uso
- Instalação
- Início rápido
- Modelo de dados
- SDK — instrumentando bots
- Dashboard web
- Sistema de alertas
- API REST
- Comandos de gerência
- Variáveis de ambiente
- Deploy em produção
- Troubleshooting
- Desenvolvimento local
- Licença
Visão geral
Quando você mantém uma frota de bots RPA, a dor de cabeça não é escrever o bot — é saber quando ele parou de rodar, quando passou a falhar demais ou quando está mais lento do que deveria. O botapp resolve isso com três camadas acopladas:
- SDK (
from botapp import BotApp) — um decorator@app.taskque envolve suas funções e grava cada execução no banco automaticamente. - Dashboard web — painel administrativo Django com listagens, detalhes de logs, exploração filtrada, exportação CSV/PDF e gestão de alertas.
- Motor de alertas — um scheduler que varre periodicamente o banco em busca de anomalias (
silent_bot,error_spike,heartbeat_lost,duration_regression) e dispara notificações em Slack, Discord e e-mail.
Todos os três podem ser usados em conjunto ou separadamente.
Funcionalidades
Observabilidade
- Decorator único para instrumentar tarefas (
@app.task), com captura automática de:host_name,host_ip,user_login,pid,bot_diros_platform,python_version,env(dev/staging/prod)start_time,end_time,duration,status,exception_type,error_message,trigger_source.
- Denormalização
Bot.last_execution_at/Bot.last_statusviapost_savesignal para listagens rápidas. - Função
reconcile_bot_last_execution()para self-heal quando SDKs antigos escrevem logs sem disparar signals.
Dashboard
- Lista de bots com status, última execução, contadores, filtros por departamento/status.
- Detalhe do bot com histórico paginado de execuções, gráficos de taxa de erro e duração.
- Página Dashboard global com contadores agregados (bots ativos, silenciosos, alertas críticos).
- Explore — filtros combinados em TaskLog com exportação CSV.
- Manutenção — ações de limpeza de órfãos e configuração em massa de thresholds por bot.
- Tema responsivo, CSP opcional, rate limiting em rotas sensíveis.
Alertas
- 4 regras de detecção prontas, todas com severidade graduada (
low,medium,high,critical):- silent_bot — bot ativo sem execução há mais de N horas/minutos.
- error_spike — pico de falhas em janela recente.
- heartbeat_lost — log com
status='started'que nunca fechou. - duration_regression — duração média recente acima do SLA da task.
- Idempotência: não cria alerta duplicado se já existir um ativo do mesmo tipo para o mesmo bot.
- Dispatcher multi-canal com fallback tolerante (Slack, Discord, e-mail).
- Ações de
acknowledgeeresolvevia UI ou REST. - Override de threshold por bot (
silence_threshold_hours,silence_threshold_minutes).
API REST
- DRF com
ViewSetsparaBot,Task,TaskLog. - Autenticação
SessionAuthentication+BasicAuthentication. - Permissão padrão:
IsAuthenticated. - Rate limiting em endpoints sensíveis via
django-ratelimit. - Ingest de alertas externos (
POST /api/alerts/ingest/): recebe alertas de um monitor de fora (ex.: webhook do Grafana/Alertmanager) e cria/resolveAlerts no painel. Máquina-a-máquina (Token/Basic, isento do rate-limit de login); um alerta ou um lote em{"alerts": [...]}; dedup idempotente porfingerprint;statusfiring/resolved. Ver a seção Sistema de alertas.
Operacional
- Comandos de gerência para setup, scheduler, limpeza de órfãos e reconciliação.
- Thread in-process opcional para dev (
BOTAPP_ALERT_SCHEDULER_ENABLED). - Comando dedicado para produção multi-worker:
botapp run_alert_scheduler.
Arquitetura — modos de uso
O botapp pode ser consumido de duas formas, detectadas automaticamente em botapp/__init__.py:
Modo standalone
Quando DJANGO_SETTINGS_MODULE não está definido, o pacote usa seu próprio botapp.settings. É assim que o dashboard é servido como serviço autônomo.
from botapp import BotApp # resolve para botapp.core.BotApp
app = BotApp('meu_banco')
- Grava direto no Postgres usando os modelos Django.
- Ideal para bots que rodam no mesmo host/rede do banco.
Modo plugin (REST client)
Quando o SDK é importado dentro de outro projeto Django (DJANGO_SETTINGS_MODULE definido e ROOT_URLCONF diferente de botapp.urls), BotApp é substituído por BotAppRestful, que fala com um dashboard remoto via HTTP.
# Em outro projeto Django, com BOTAPP_API_URL apontando para o dashboard remoto
from botapp import BotApp # resolve para botapp.core_restful.BotAppRestful
app = BotApp()
- Não exige acesso direto ao banco.
- Perfeito para bots embarcados em aplicações maiores ou em redes segregadas.
- Requer
BOTAPP_API_URL,BOTAPP_API_USUARIO,BOTAPP_API_SENHA.
Instalação
Requisitos: Python 3.8+, Django 3.2+, Postgres 12+ (SQLite funciona para testes).
pip install botapp
Dependências instaladas automaticamente: Django, psycopg2-binary, djangorestframework, django-admin-rangefilter, django-ratelimit, whitenoise, python-dotenv, openpyxl, xhtml2pdf, requests.
Início rápido
1. Configure o ambiente
Crie um .env na raiz do projeto:
BOTAPP_DEBUG=True
BOTAPP_SECRET_KEY=troque-em-producao
BOTAPP_ALLOWED_HOSTS=*
# Postgres
PG_BOTAPP_SCHEMA=botapp_schema
PG_BOTAPP_DBNAME=botapp
PG_BOTAPP_USER=botapp
PG_BOTAPP_PASSWORD=senha
PG_BOTAPP_HOST=localhost
PG_BOTAPP_PORT=5432
# Superusuário inicial (criado pelo comando setup)
BOTAPP_SUPERUSER_USERNAME=admin
BOTAPP_SUPERUSER_EMAIL=admin@example.com
BOTAPP_SUPERUSER_PASSWORD=troque-em-producao
2. Inicialize o banco
botapp setup # cria schema + roda migrations + cria superusuário
3. Suba o dashboard
botapp runserver 0.0.0.0:8000
Acesse http://localhost:8000/.
4. Instrumente um bot
from botapp import BotApp
app = BotApp('botapp') # mesmo PG_BOTAPP_DBNAME
app.set_bot(
bot_name='relatorio_diario',
bot_description='Consolida vendas do dia',
bot_version='1.0.0',
bot_department='Financeiro',
)
@app.task
def gerar_relatorio():
"""Gera e envia o relatório diário."""
# seu código aqui
return 'ok'
gerar_relatorio() # será registrada como TaskLog automaticamente
Modelo de dados
Bot
├── name, description, version, department
├── is_active
├── last_execution_at (denorm) ← atualizado pelo signal em TaskLog
├── last_status (denorm)
├── silence_threshold_hours/minutes ← override por bot do limiar de silent_bot
└── tasks (1:N)
Task
├── bot (FK)
├── name (__name__ da função decorada)
├── description (docstring)
├── expected_duration_seconds ← SLA usado pelo detector de regressão
└── logs (1:N)
TaskLog
├── task (FK)
├── status ← started | completed | failed
├── start_time / end_time / duration
├── result_data (JSON) ← retorno da função serializado
├── error_message / exception_type ← traceback do erro, se houver
├── host_ip, host_name, pid, user_login, os_platform, python_version
├── bot_dir, env, trigger_source, manual_trigger
└── indexes: -start_time, task+-start_time, status+-start_time, env+-start_time
Alert
├── type ← silent_bot | error_spike | heartbeat_lost | duration_regression
├── severity ← low | medium | high | critical
├── bot (FK, nullable) ← null = alerta global
├── message, payload (JSON)
├── created_at / updated_at
├── acked_at, acked_by (FK User) ← reconhecimento manual
├── resolved_at, resolved_by ← resolução manual
└── notified_at ← última vez que o dispatcher enviou
Migrations relevantes:
0008_indexes_performance— índices de performance em TaskLog.0009_bot_last_execution_denorm— campos denormalizados em Bot.0010_alerts— tabelaAlert.0011_task_expected_duration_seconds— SLA por task.0012_bot_silence_threshold_minutes— granularidade fina por bot.
SDK — instrumentando bots
BotApp (standalone)
from botapp import BotApp
app = BotApp(db_name='botapp')
app.set_bot(
bot_name='meu_bot', # normalizado: só alfanuméricos, capitalize
bot_description='O que faz',
bot_version='1.2.3',
bot_department='TI',
)
@app.task
def minha_tarefa(arg1, arg2):
"""Docstring vira a description da Task."""
return {'ok': True}
Comportamento do decorator:
- Se o bot estiver
is_active=False, a execução é bloqueada com exceção. - Cria/recupera a
Taskpelofunc.__name__e atualiza a description se a docstring mudou. - Cria
TaskLog(status='started')com snapshot do ambiente. - Executa a função. Em caso de exceção, grava
exception_type+error_messagee re-lança. - No
finally, setaend_timee o status final.
BotAppRestful (modo plugin)
# Em outro projeto Django
from botapp import BotApp # resolve para BotAppRestful se detectar host Django
app = BotApp() # usa BOTAPP_API_URL do env
app.set_bot('meu_bot', 'desc', '1.0', 'TI')
@app.task
def tarefa():
...
O fluxo é o mesmo, mas todas as operações batem na API REST do dashboard remoto via requests. Credenciais vêm de BOTAPP_API_USUARIO / BOTAPP_API_SENHA (HTTP Basic).
Dashboard web
Rotas principais (prefixadas por / quando em modo standalone):
| Rota | Descrição |
|---|---|
/bots/ |
Lista de bots com filtros. |
/bots/<id>/ |
Detalhe do bot: execuções, métricas, gráficos. |
/bot/<id>/toggle-status/ |
Liga/desliga bot (POST). |
/log/<id>/ |
Detalhe completo de um TaskLog. |
/dashboard/ |
Painel agregado: bots ativos, silenciosos, alertas. |
/alerts/ |
Lista de alertas (filtros: status, severity, type, bot). |
/alerts/<id>/ack/ |
POST — ack um alerta. |
/alerts/<id>/resolve/ |
POST — resolve um alerta. |
/alerts/unread-count/ |
JSON para badge do menu (polling leve). |
/maintenance/cleanup-orphans/ |
UI para acionar cleanup_orphans via HTTP. |
/maintenance/silence-thresholds/ |
Edição em massa de thresholds de silent_bot. |
/bots/bulk-silence-threshold/ |
Ação em massa dos thresholds. |
/explore/ |
Filtros combinados em TaskLog. |
/explore/export.csv |
Exporta o resultado filtrado como CSV. |
/admin/ |
Django admin. |
/api/ |
API REST (ver seção API REST). |
/api/alerts/ingest/ |
POST — ingest de alertas de monitor externo (Token/Basic). |
/accounts/login/ · /accounts/logout/ |
Autenticação. |
/accounts/password_reset/… |
Fluxo completo de reset por e-mail. |
Sistema de alertas
Regras
| Tipo | Dispara quando | Severidade calculada em função de |
|---|---|---|
silent_bot |
Bot is_active sem execução há mais de N horas (override por bot ok). |
horas_silencio / threshold |
error_spike |
≥ X falhas em janela deslizante de M minutos. | falhas / threshold |
heartbeat_lost |
Log com status='started' mais antigo que N horas. |
Severidade fixa por idade |
duration_regression |
Média das últimas N execuções acima de expected_duration_seconds × k. |
duração_média / expected |
Fechamento automático
Cada rodada de check_alerts fecha o alerta cuja condição já passou, antes de
detectar de novo: bot que voltou a executar, execução travada que encerrou, janela
do pico que passou, duração que voltou ao esperado. O alerta fechado assim recebe
payload.fechado_por = "condicao_superada".
Ingest de alertas externos
Além das regras internas, um monitor de fora pode publicar alertas no painel via
POST /api/alerts/ingest/ (autenticado por Token DRF ou Basic — máquina-a-máquina,
isento do rate-limit de login). Aceita um único alerta ou um lote:
{
"alerts": [
{
"status": "firing", // firing (default) | resolved
"type": "host_disk", // slug livre, ≤30 chars
"severity": "critical", // low | medium | high | critical (default medium)
"message": "Disco cheio em docker01 (92%)",
"fingerprint": "grafana-abc123", // chave de dedup (opcional)
"bot_name": "bot_x", // associa a um Bot se existir (opcional)
"payload": { "host": "docker01" } // contexto livre (opcional)
}
]
}
firingcria o alerta, a menos que já exista um ativo com a mesma chave —fingerprintse informado (guardado empayload.fingerprint), senãotype+message. É idempotente: um monitor que reenvia enquanto a condição persiste não duplica.resolvedfecha os alertas ativos que casam com a chave.- Não dispara notificações (Slack/Discord/e-mail) — o alerta aparecer no painel é a
entrega. A resposta traz
{created, resolved, deduped}(eerrors+ HTTP 207 se algum item do lote for inválido).
Também aceita o formato nativo do webhook do Grafana/Alertmanager (itens com
labels/annotations): type ← labels.type|alertname (truncado a 30), severity
normalizada de critical|warning|info|critico|aviso|…, message ←
annotations.summary|description, fingerprint ← fingerprint, status ← status,
resto no payload. Assim o Grafana aponta o webhook direto para cá sem template de
payload — basta a URL e o header Authorization: Token <chave>.
Isso não é só limpeza de painel. A deduplicação de cada regra é contra alerta aberto do mesmo tipo para o mesmo bot — então um alerta que nunca fecha cega aquele tipo para aquele bot, e a próxima ocorrência real não gera alerta nenhum.
O fechamento é conservador: só fecha o que consegue julgar como normal agora.
Sem como julgar (bot ausente, amostra insuficiente de duração), o alerta fica
aberto. --dry-run não fecha nada.
Antes de tudo isso, cada rodada encerra execução morta: TaskLog preso em
started por mais de BOTAPP_ORPHAN_TASKLOG_HOURS (default 24) vira failed
com exception_type='ProcessoMorto'. O @task fecha o log no finally até
quando a task levanta exceção — log eternamente em started significa que o
processo morreu sem passar por ali (container derrubado, OOM, compose down,
job cancelado). Sem esse encerramento o heartbeat_lost daquele bot nunca se
resolve e, pela deduplicação, tampa o próximo alerta do mesmo tipo. O limiar é
deliberadamente maior que o do heartbeat_lost (6h): na janela entre os dois o
alerta deve aparecer, porque ali ainda pode ser job de verdade travado.
end_time fica nulo — a hora da morte é desconhecida, e gravar now inflaria a
duração com o tempo que o registro passou órfão.
Os alertas de CI seguem a mesma ideia, em ci_sync: pipeline_failed e
pipeline_masked_error fecham quando um pipeline posterior do mesmo projeto
passa (resolver_alertas_obsoletos), e schedule_without_run /
project_never_ran fecham quando o projeto volta a executar dentro do intervalo
do cron (resolver_agendamentos_em_dia, payload.fechado_por = "agendamento_em_dia").
Dispatcher (notifiers.py)
Ao criar um alerta, check_alerts chama dispatch_alert(alert) que envia para todos os canais configurados. Cada canal é opt-in: se a env var correspondente não estiver definida, o canal é silenciosamente ignorado. Uma falha em um canal não bloqueia os outros.
- Slack: webhook com attachment colorido por severidade.
- Discord: embed com cor hex equivalente.
- E-mail: usa
settings.EMAIL_*(SMTP do Django) com lista de destinatários emBOTAPP_ALERT_EMAIL_RECIPIENTS.
Se qualquer canal teve sucesso, alert.notified_at é atualizado — assim, executar check_alerts em intervalo curto não re-dispara notificações a cada rodada.
Como rodar o detector
Desenvolvimento (thread in-process, single worker):
BOTAPP_ALERT_SCHEDULER_ENABLED=true
BOTAPP_ALERT_SCHEDULER_INTERVAL_SECONDS=60
Ao subir o Django, o AppConfig.ready() inicia uma thread daemon que loopa check_alerts a cada intervalo. Não use em produção com múltiplos workers — N workers × N schedulers gera concorrência e alertas duplicados.
Produção (processo dedicado):
botapp run_alert_scheduler --interval 60
Ou em container:
services:
botapp_scheduler:
image: meu-registry/botapp
environment:
BOTAPP_ROLE: scheduler
command: botapp run_alert_scheduler
Flags úteis:
--interval N— sobrescreveBOTAPP_ALERT_SCHEDULER_INTERVAL_SECONDS.--once— roda uma vez e sai (útil para cron externo).check_alerts --dry-run— só loga o que faria.check_alerts --no-notify— cria alertas mas não envia webhooks.
API REST
Base: /api/ · Autenticação: Session ou Basic · Permissão: IsAuthenticated.
| Método · Rota | Descrição |
|---|---|
GET /api/bots/ |
Lista bots (parametros: search). |
POST /api/bots/ |
Cria bot. |
GET /api/bots/{id}/ |
Detalhe. |
PATCH /api/bots/{id}/ |
Atualização parcial. |
DELETE /api/bots/{id}/ |
Remove. |
GET /api/tasks/ |
Lista tasks (?bot=<id>&name=<str>). |
POST /api/tasks/ |
Cria task. |
GET /api/tasklog/ |
Lista logs. |
POST /api/tasklog/ |
Cria log (usado pelo BotAppRestful). |
PATCH /api/tasklog/{id}/ |
Atualiza log (status, end_time, duration…). |
Exemplo:
curl -u admin:senha http://localhost:8000/api/bots/?search=relatorio
Comandos de gerência
Disponíveis via entry point botapp (equivalente a python manage.py):
| Comando | Uso |
|---|---|
botapp setup |
Cria schema + roda migrations + cria superusuário. |
botapp migrate [--noinput] |
Aplica migrations. |
botapp collectstatic [--noinput] |
Coleta estáticos (whitenoise em produção). |
botapp runserver [host:port] |
Servidor dev. |
botapp check_alerts [--dry-run] [--no-notify] |
Roda o motor de alertas uma vez. |
botapp run_alert_scheduler [--interval N] [--once] |
Loop blocante do motor (para processo dedicado). |
botapp cleanup_orphans [--threshold-hours N] [--bot-id ID] [--dry-run] [--batch-size N] [--sleep-ms N] |
Fecha logs travados. |
botapp reconcile_bot_denorm |
Reconcilia Bot.last_execution_at com o TaskLog real mais recente. |
Variáveis de ambiente
Essenciais
| Variável | Default | Descrição |
|---|---|---|
DJANGO_SETTINGS_MODULE |
botapp.settings (standalone) |
Aponta para seu settings em modo plugin. |
BOTAPP_DEBUG |
False |
Modo debug do Django. |
BOTAPP_SECRET_KEY |
obrigatório em prod | Secret key. Default de dev não é aceito com DEBUG=False. |
BOTAPP_ALLOWED_HOSTS |
* |
Lista separada por vírgula. |
BOTAPP_CSRF_TRUSTED_ORIGINS |
* (quando DEBUG=False) |
Lista separada por vírgula. |
BOTAPP_PORT_ADMIN |
8000 |
Porta usada por app.open_admin(). |
BOTAPP_LANGUAGE_CODE |
pt-br |
|
BOTAPP_TIME_ZONE |
America/Cuiaba |
Fuso padrão. |
BOTAPP_USE_I18N |
True |
|
BOTAPP_USE_TZ |
True |
Mantenha True para datetimes aware (exigido pelos notifiers). |
BOTAPP_DEPLOY_ENV |
dev |
Gravado em TaskLog.env. |
BOTAPP_LOGGING_LEVEL |
INFO |
Nível do logger botapp. |
BOTAPP_DJANGO_LOG_LEVEL |
WARNING |
Nível do logger django. |
Banco de dados (Postgres)
| Variável | Descrição |
|---|---|
BOTAPP_DATABASES |
(opcional) JSON com o dict DATABASES completo. Sobrescreve as demais. |
PG_BOTAPP_SCHEMA |
Schema das tabelas (default botapp_schema). |
PG_BOTAPP_DBNAME |
Nome do banco. |
PG_BOTAPP_USER |
Usuário. |
PG_BOTAPP_PASSWORD |
Senha. |
PG_BOTAPP_HOST |
Host. |
PG_BOTAPP_PORT |
Porta (default 5432). |
Superusuário inicial (usado por botapp setup)
| Variável | Default |
|---|---|
BOTAPP_SUPERUSER_USERNAME |
admin |
BOTAPP_SUPERUSER_EMAIL |
admin@example.com |
BOTAPP_SUPERUSER_PASSWORD |
admin123 (proibido em prod) |
Cache / Redis
| Variável | Default |
|---|---|
BOTAPP_CACHES |
JSON com o dict CACHES. Se ausente em prod, usa Redis. |
BOTAPP_REDIS_URL |
redis://botapp_redis:6379/1 |
E-mail (SMTP)
| Variável | Default |
|---|---|
BOTAPP_EMAIL_HOST |
— |
BOTAPP_EMAIL_PORT |
587 |
BOTAPP_EMAIL_USER |
— |
BOTAPP_EMAIL_PASSWORD |
— |
BOTAPP_EMAIL_USE_TLS |
True |
BOTAPP_DEFAULT_FROM_EMAIL |
BOTAPP_EMAIL_USER |
Modo plugin (REST client)
| Variável | Default | Descrição |
|---|---|---|
BOTAPP_API_URL |
— | URL do dashboard remoto. Obrigatório no modo plugin. |
BOTAPP_API_USUARIO |
— | HTTP Basic user. |
BOTAPP_API_SENHA |
— | HTTP Basic password. |
BOTAPP_API_TIMEOUT |
10 |
Timeout (s) das chamadas HTTP. |
Scheduler / alertas
| Variável | Default |
|---|---|
BOTAPP_ALERT_SCHEDULER_ENABLED |
false (não inicia thread in-process) |
BOTAPP_ALERT_SCHEDULER_INTERVAL_SECONDS |
60 |
BOTAPP_SILENT_BOT_THRESHOLD_HOURS |
24 |
BOTAPP_ERROR_SPIKE_WINDOW_MINUTES |
60 |
BOTAPP_ERROR_SPIKE_THRESHOLD |
5 |
BOTAPP_HEARTBEAT_LOST_THRESHOLD_HOURS |
6 |
BOTAPP_DURATION_REGRESSION_MULTIPLIER |
1.5 |
BOTAPP_DURATION_REGRESSION_WINDOW |
20 (últimas N execuções) |
Canais de notificação
| Variável | Default | Efeito |
|---|---|---|
BOTAPP_SLACK_WEBHOOK_URL |
— | Se definida, envia para Slack. |
BOTAPP_DISCORD_WEBHOOK_URL |
— | Se definida, envia para Discord. |
BOTAPP_ALERT_EMAIL_RECIPIENTS |
— | Lista separada por vírgula. Se vazia, não envia. |
BOTAPP_DASHBOARD_URL |
— | Base usada para gerar link nas notificações. |
BOTAPP_NOTIFIER_TIMEOUT |
5 |
Timeout (s) das chamadas aos webhooks. |
Limpeza de órfãos
| Variável | Default |
|---|---|
BOTAPP_ORPHAN_THRESHOLD_HOURS |
24 |
BOTAPP_ORPHAN_BATCH_SIZE |
500 |
BOTAPP_ORPHAN_SLEEP_MS |
100 |
BOTAPP_ORPHAN_HTTP_MAX |
5000 |
Export
| Variável | Default | Descrição |
|---|---|---|
BOTAPP_CSV_MAX_ROWS |
0 |
0 = ilimitado. Teto opcional para /explore/export.csv. |
Deploy em produção
Topologia recomendada:
┌──────────────┐ ┌────────────────────┐ ┌─────────────────────┐
│ Bots (SDK) │──▶│ web (gunicorn) │──▶│ Postgres + Redis │
└──────────────┘ │ BOTAPP_ROLE=web │ └─────────────────────┘
│ N workers │
└────────────────────┘
│
┌────────────────────┐
│ scheduler │ (um único processo)
│ BOTAPP_ROLE= │
│ scheduler │
└────────────────────┘
Pontos importantes:
- Nunca deixe
BOTAPP_ALERT_SCHEDULER_ENABLED=trueno containerwebcom múltiplos workers — use um containerschedulerdedicado. - Rode
botapp migrateantes do primeiro start (ou no entrypoint do serviço web). - Configure
BOTAPP_SECRET_KEY,BOTAPP_ALLOWED_HOSTS,BOTAPP_CSRF_TRUSTED_ORIGINSexplicitamente. - HTTPS reverse proxy na frente do gunicorn; o settings já ativa
SECURE_SSL_REDIRECT, HSTS e cookies seguros quandoDEBUG=False. BOTAPP_DASHBOARD_URLdeve apontar para a URL pública do dashboard — usado nos links das notificações.
Exemplo de docker-compose mínimo:
services:
web:
image: botapp:latest
environment:
BOTAPP_ROLE: web
BOTAPP_ALERT_SCHEDULER_ENABLED: "false"
env_file: .env.prod
ports: ["8000:8000"]
scheduler:
image: botapp:latest
environment:
BOTAPP_ROLE: scheduler
BOTAPP_ALERT_SCHEDULER_ENABLED: "true"
env_file: .env.prod
command: botapp run_alert_scheduler
depends_on: [web]
redis:
image: redis:alpine
Troubleshooting
Alertas não estão sendo criados.
Verifique nessa ordem: (1) a migration 0010_alerts rodou (\dt botapp_alert no psql); (2) o scheduler está rodando (botapp run_alert_scheduler em container dedicado ou BOTAPP_ALERT_SCHEDULER_ENABLED=true em dev); (3) existem bots com is_active=True e últimos logs fora do threshold. Rode botapp check_alerts --dry-run para ver o que seria criado sem tocar no banco.
Alertas criados mas sem notificação.
Nenhuma das envs de canal foi definida — o dispatcher pula canais sem configuração. Defina pelo menos uma entre BOTAPP_SLACK_WEBHOOK_URL, BOTAPP_DISCORD_WEBHOOK_URL, BOTAPP_ALERT_EMAIL_RECIPIENTS. Verifique o log do scheduler por notifier.slack: falha / notifier.discord: falha / notifier.email: falha.
Bot.last_execution_at desatualizado.
SDK antigo (sem o signal post_save) está escrevendo TaskLog direto no banco. Rode botapp reconcile_bot_denorm manualmente ou aguarde o check_alerts (ele chama a reconciliação a cada rodada).
Muitos alertas heartbeat_lost.
Crashes deixaram logs em status='started'. Rode botapp cleanup_orphans --threshold-hours 24 para fechá-los retroativamente como failed/orphan_heartbeat.
ProgrammingError: relation "botapp_alert" does not exist.
As migrations não rodaram no ambiente. Execute botapp migrate --noinput.
Unknown command: 'check_alerts'.
Você está em uma versão < 0.3.1, que foi publicada sem o diretório management/. Atualize para 0.3.1+.
Desenvolvimento local
git clone https://github.com/botlorien/botapp
cd botapp
python -m venv .venv && source .venv/bin/activate
pip install -e .
pip install build twine
# Configure .env como na seção "Início rápido"
botapp setup
botapp runserver
Para testar o SDK contra o banco local, use example.py como referência.
Publicando uma nova versão
python setup.py sdist bdist_wheel
twine upload dist/*
Antes de publicar, verifique que find_packages() detecta todos os subpacotes (em particular botapp.management e botapp.management.commands, que precisam de __init__.py):
python -c "from setuptools import find_packages; print(find_packages())"
Integração com CI (GitLab)
A partir da 0.6.0 o painel também lê o servidor de CI, e não só o que o bot conta sobre si mesmo. Isso cobre três pontos cegos da instrumentação:
- Bot que morre antes de instrumentar — falha de credencial, dependência ausente, imagem quebrada: o SDK nunca é chamado e o painel não mostra nada.
- Bot que nunca instrumentou — sem o SDK configurado, ele roda, falha e ninguém vê.
- Verde que mente — pipeline com status de sucesso cujo log tem
Traceback: a exceção foi engolida e o processo saiu com código 0.
A feature nasce desligada. Para ligar, no mínimo:
BOTAPP_CI_ENABLED=true
BOTAPP_CI_BASE_URL=https://seu-gitlab # sem default de propósito
BOTAPP_CI_NAMESPACE=seu-grupo # path ou id numérico
BOTAPP_CI_TOKEN=<token de LEITURA>
python manage.py sync_ci --bootstrap --discovery # cria a conexão e descobre
python manage.py run_ci_scheduler # loop (container separado)
As telas ficam em /ci/ (visão geral), /ci/projects/ e /ci/connections/.
Segurança do token
O token de leitura de API dá acesso a código-fonte e logs de build de todo o namespace. Recomendações que o próprio código impõe:
- use um token de grupo/serviço somente-leitura, nunca um token pessoal de administrador;
- por padrão o token não vai para o banco: o registro guarda só o nome da
variável de ambiente (
token_source="env"), então um dump de backup não contém credencial; - o modo
token_source="db"exigeBOTAPP_CI_TOKEN_KEY(Fernet, extrapip install "botapp[ci-db-token]") e é recusado sem a chave, em vez de gravar em texto puro; - a tela mostra no máximo uma impressão digital do token; a API nunca o serializa; o cliente HTTP mascara o valor em qualquer mensagem de erro;
- o log de job é proxeado pelo servidor — o navegador nunca recebe o token —
e servido como
text/plaincomnosniff, porque log é conteúdo não confiável.
Variáveis
| variável | default | função |
|---|---|---|
BOTAPP_CI_ENABLED |
false |
liga a integração |
BOTAPP_CI_BASE_URL |
— | URL do servidor de CI |
BOTAPP_CI_NAMESPACE |
— | grupo/organização a sincronizar |
BOTAPP_CI_TOKEN |
— | token de leitura |
BOTAPP_CI_TOKEN_KEY |
— | chave Fernet (só p/ token_source="db") |
BOTAPP_CI_DISCOVERY_INTERVAL_MINUTES |
1440 |
refresh de projetos/agendamentos |
BOTAPP_CI_POLL_INTERVAL_MINUTES |
15 |
busca de pipelines |
BOTAPP_CI_ERROR_PATTERNS |
Traceback (most recent call last), CRITICAL, FATAL |
padrões de erro no log (separados por |) |
BOTAPP_CI_IGNORE_PATTERNS |
vazio | ruídos a ignorar antes de casar padrão |
BOTAPP_CI_LOG_TAIL_BYTES |
65536 |
tamanho máx. da cauda persistida |
BOTAPP_CI_ALERT_MAX_AGE_DAYS |
7 |
não alerta sobre execução mais antiga que isso — a carga inicial importa histórico, e falha de meses atrás não é acionável |
BOTAPP_CI_STORE_TRIGGERED_BY |
true |
grava quem disparou o pipeline (dado pessoal — desligue se o seu regime tratar isso como monitoramento de pessoa) |
BOTAPP_CI_ALLOW_INSECURE |
false |
permite http:// na base_url |
BOTAPP_CI_TIMEOUT_SECONDS |
30 |
timeout por chamada |
Projetos inativos
Projeto arquivado no servidor de CI é detectado na redescoberta e marcado como inativo: a consulta traz os arquivados de propósito, porque filtrá-los criaria um ponto cego permanente — um projeto arquivado depois da primeira importação sumiria do resultado, o painel nunca saberia, e seguiria sincronizando e alertando um repositório que ninguém mantém.
Também dá para arquivar no painel um projeto que segue ativo no CI mas está
dormente (/ci/projects/<id>/ › Arquivar no painel). Em ambos os casos o
projeto deixa de ser sincronizado, deixa de gerar alerta, e os alertas abertos
dele são fechados. Ele some da listagem por padrão — o checkbox incluir
inativos o traz de volta.
O painel não arquiva no servidor de CI: o cliente é somente-leitura por desenho. A tela oferece o link direto das configurações do projeto para quem tem permissão de escrita fazer isso lá.
Em lote: python manage.py archive_dormant_ci_projects --days 60 --dry-run.
Detecção de "verde que mente"
É opt-in por projeto (scan_logs), porque custa uma chamada de log por job
bem-sucedido — ligar para tudo multiplica o tráfego. Quando liga, use
BOTAPP_CI_IGNORE_PATTERNS para os ruídos conhecidos da sua stack; sem isso a
detecção vira alarme constante e alguém a desliga.
Detalhes de arquitetura em docs/ci-integration-design.md.
Licença
MIT — ver LICENSE.
Metadata
Release files for botapp 0.6.24
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| botapp-0.6.24.tar.gz | 192.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| botapp-0.6.24-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 400.7 kB
Release files / botapp-0.6.24.tar.gz
| Download URL | botapp-0.6.24.tar.gz |
|---|---|
| Size | 192.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
32571bc5471e1cb05086e82429a082b4c1e3b627bb3ed1ad6f9ae95c0b4abfb0
|
|
BLAKE2b-256 checksum How to use checksums |
d52423457f66f21a84553961a86374eda65f8e0740e6d1badcd7d825c2cb48d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.11
|
Release files / botapp-0.6.24-py3-none-any.whl
| Download URL | botapp-0.6.24-py3-none-any.whl |
|---|---|
| Size | 208.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
83c041066cb655d47f7186d86e678caf325e7183a0aed87bd282337484f3a639
|
|
BLAKE2b-256 checksum How to use checksums |
2c6d00519d69a57d45e24d633e3a1e94f56e920c79854e906f6d50f436fb1113
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.11
|