Gateway Telegram · fila durável

Você manda uma linha no chat. O trabalho pesado acontece sozinho.

Bot de Telegram com fila durável em SQLite: roda skills de uma etapa e fluxos de várias fases com estado, portão humano e retomada depois de queda.

inemaccbot — gateway Telegram e fila durável
O que é

Um operador que não perde trabalho no meio

O bot recebe comandos no Telegram, enfileira o trabalho e o executa como agente. A fila é durável: se o processo cair no meio de um render, na próxima subida ele reclama o que ficou em voo antes de aceitar trabalho novo.

🗄️ Fila durável de verdade

SQLite em WAL, claim atômico, lease com heartbeat e drain no desligamento. Um kill -9 no meio de um job não deixa estado indefinido — nada fica running com lease vivo.

⏸️ Fluxos com portão humano

Pipeline com estado por fase e por alvo, definição congelada na criação, pausa para você fazer sua parte (/aprovar) e retomada seletiva com /refazer.

🧩 Domínio novo sem código

Uma skill é um prompt + uma entrada no registry. Um fluxo é um repo com flow.json + prompts/. Nenhum dos dois pede uma linha de TypeScript no bot.

Como funciona

Do comando no chat até o arquivo entregue

O gateway valida quem fala (allowlist de chat), o comando vira job na fila certa, um worker reclama o job com lease, o agente executa e o notificador devolve o resultado no chat.

comando no Telegram allowlist + parse job na fila (SQLite) worker reclama (lease) agente executa artefato + aviso no chat

Cinco filas, concorrências diferentes

io (10) · cpu (1) · texto (2) · render (1) · navegador (1). Um render pesado não trava a fila de texto.

Skill × fluxo

Skill = uma etapa, sem estado — refazer do zero é aceitável. Fluxo = várias fases com estado, quando jogar fora o trabalho parcial seria absurdo.

Boot em ordem

Migrations → raiz de mídia → recuperação de leases vencidos → só então workers e bot. É essa ordem que torna o processo recuperável de uma queda.

Pré-requisitos

O que precisa estar no lugar

Node com TypeScript, um bot criado no Telegram e um .env com as cinco variáveis obrigatórias. Sem uma delas o boot falha alto e cedo, antes de subir qualquer worker.

Node + dependências

Instale e rode os testes antes de subir qualquer coisa.

# na raiz do repo
npm install
npm test

Bot no Telegram

Crie o bot no @BotFather e guarde o token. O valor real nunca vai para o git.

# .env (modo 600, fora do git)
BOT_TOKEN=
ALLOWED_CHAT_IDS=123,456

Estado em disco

Caminho do banco da fila, raiz de estado e arquivo de log — as outras três obrigatórias.

QUEUE_DB=./inemaccbot.db
STATE_DIR=./estado
LOG_FILE=./inemaccbot.log
Guia de uso · passo a passo

Subir o serviço e operar pelo chat

Todos os comandos abaixo são reais — os de shell rodam no repo, os que começam com barra você digita no Telegram.

1

Instale, teste e compile

O registry de skills e fluxos é validado no boot: uma entrada inválida derruba o serviço de propósito.

npm install
npm test          # vitest run
npm run typecheck  # tsc --noEmit
npm run build      # tsc -> dist/index.js
2

Suba o processo

Em produção via systemd, com o .env no mesmo diretório de trabalho (deploy/inemaccbot.service).

node dist/index.js                       # direto
systemctl --user start inemaccbot       # como serviço
3

Confirme que está vivo

No chat autorizado. /ajuda lista tudo; /skills e /fluxos são os dois catálogos.

/ping
/ajuda
/skills   # uma etapa, sem estado
/fluxos   # várias fases, com estado
4

Rode uma skill

O formato é <skill>: <entrada> [| campo]*. Campos genéricos: livesN (destino), modelo=haiku, esforco=high.

transcrever: https://…                  # áudio → texto
explicativo: <assunto> | vertical       # vídeo 9:16
imagem: uma raposa ruiva na neve | ratio=16:9
5

Rode um fluxo — sempre em sombra primeiro

| sombra imprime fase × alvo × fila × tarefa e não enfileira nada. É o jeito barato de descobrir que você ia disparar 12 públicos por engano.

/promoavatar <assunto> | sombra
/promoavatar <assunto> --alvo=jovens
/promoavatar <assunto> | alvos=mulheres | legenda
6

Acompanhe e libere o portão

/status é o painel dos fluxos ABERTOS; /completos lista os que terminaram. A#9, a#9, A9 e a9 são a mesma coisa — só número (13) é sempre job.

/status            # fluxos abertos
/status A#9        # detalhe fase × alvo
/fila              # rodando, pendentes, erro em 24h
/pronto A#9        # = /aprovar — libera o portão
7

Conserte o que falhou, sem refazer tudo

/refazer num fluxo retoma da fase que falhou; num alvo só, refaz aquele alvo. /cancelar tira da lista sem apagar nada do que já foi criado fora.

/refazer A#9 mulheres   # só o público que falhou
/cancelar A#9           # some do /status; o fluxo continua existindo
/furar j13              # põe um job pendente na frente
8

Limpe espaço com dry-run

Sem a palavra confirmar no fim, só mostra o que sairia e quanto libera. O bot só toca no que ELE publicou dentro de ~/projetos/output.

/espaco                  # disco por área (bot × skills)
/limpar A#8              # dry-run
/limpar A#8 confirmar    # executa
9

Antes de reiniciar, confira a fila

claude saiu com código 143 é 128 + 15 = SIGTERM: o job foi morto por um restart do serviço, não por erro do agente. Um render de reel leva 10–15 min — dois restarts seguidos esgotam as duas tentativas do mesmo job.

sqlite3 inemaccbot.db \
  "select id,fila,tarefa,status,flow_ref from jobs
   where status in ('queued','running');"
# vazio → reinicie à vontade. Com render em voo → espere.
Domínio novo

Acrescentar trabalho sem tocar no bot

Este é o teste do desenho: domínio novo não deve exigir linha de código no bot. Escolha entre skill e fluxo pelo critério do trabalho parcial.

Uma SKILL — uma etapa, sem estado

Vale quando "rodar de novo do zero" é aceitável. Escreva o prompt, declare no registry, rode os testes. Campo declarado tem que ser usado no prompt, e variável do prompt tem que ser declarada — há teste para os dois lados.

# 1. prompts/minhaskill.md — usa {{input}} e {{saida}},
#    e a última linha do agente é RESULT: <caminho>
# 2. config/skills.json
{ "command": "minhaskill", "fila": "texto", "kind": "agent",
  "prompt": "prompts/minhaskill.md", "artefato_exts": ["txt"],
  "max_tentativas": 2, "timeout_segundos": 3600 }
# 3. npm test

Um FLUXO — várias fases, com estado

Vale quando há trabalho parcial que seria absurdo jogar fora. Ganha /status, /refazer seletivo, retomada e definição congelada. O domínio diz para QUEM; o bot sabe ONDE — nunca ponha caminho no flow.json.

# 1. repo ~/projetos/<nome> com flow.json + prompts/
# 2. flow.json: nome, prefixo (o P de P#16), versao_def,
#    alvos e fases (id, escopo, fila, kind, tarefa…)
# 3. config/fluxos.json: { command, repo, descricao, exemplo }
# 4. confira sem gastar nada:
/<fluxo> <assunto> | sombra

Todo domínio que entra no catálogo responde ajuda — não por disciplina, por construção: quem entende escreve (HELP.md no fluxo, <prompt>.help.md na skill) e, se não escreveu, a ajuda é derivada do mesmo registro que o bot usa para executar. Um teste varre os dois catálogos e falha se algum domínio não responder ajuda utilizável.

promoavatar (A#)· promoavatar3 (C#)
Estado

O que está pronto, e o que não existe de propósito

Etapas 0 a 5 concluídas, mais os fluxos de domínio. O v1 (inemaccvbot, mkivideos, mkitexto) está desligado.

Pronto
Fila durávelSQLite em WAL, lease com heartbeat, drain, claim atômico, recuperação no boot.
Pronto
Gateway TelegramAllowlist de chat, parse de comandos puro (sem grammy), corte de mensagem, notificação de job terminado.
Pronto
Skills como agentetranscrever · dublar · explicativo · curso · demo · reel · reelinematds · historia · imagem.
Pronto
Motor de fluxosEstado por fase e alvo, definição congelada, portão humano e retomada.
Aberto
Sair do TelegramWhatsApp, e-mail ou chatbot: a costura do gateway já existe; o que trava é chat_id ser INTEGER. Recomendação em análise: não trocar, acrescentar.
Aberto
Imagem e link como materialAceitar imagem e link como entrada de um fluxo — análise escrita, decisão não tomada.
Fora
O que não existe de propósitoBarreira entre fases, preempção de job, teto global de agentes, multiusuário — cada um com o gatilho documentado para reconsiderar.