/
Manual oficial

Documentação do Construtor Dromornis

Dromornis é um construtor visual de bots do Telegram. Você monta a lógica do bot encaixando blocos coloridos, como peças de LEGO, e o Dromornis escreve o código Python completo para você — pronto para baixar e executar.

192blocos prontos
14categorias
0linhas de código obrigatórias
Pythoncódigo gerado e exportável

Por onde começar

O que é o Dromornis, em uma frase

É um editor onde você desenha o comportamento do bot em vez de digitá-lo. O resultado não é um bot preso a uma plataforma: é um arquivo bot.py de verdade, com requirements.txt, README.md e .env.example, que roda no seu computador, no seu servidor, onde você quiser.

Você arrasta blocos → Dromornis → bot.py (Python) → roda no seu servidor gera código conversa com o Telegram
Importante entender desde já

O Dromornis não hospeda o seu bot. Ele é a ferramenta que constrói o programa. Publicar significa exportar o projeto e executá-lo — é o que ensinamos em Seu primeiro bot. Isso é uma vantagem: seu bot é seu, o código é legível, e você nunca fica preso.

Mapa da documentação

SeçãoO que você encontraNível
IntroduçãoTodos os conceitos básicos explicados com analogiasIniciante
Primeiros passosDo projeto vazio ao bot respondendo no TelegramIniciante
TelegramBotFather, token, updates, polling × webhook, arquiteturaIntermediário
O construtorCada parte da interface e o que ela fazIniciante
Referência de blocosOs 192 blocos, por categoria, com exemplos e erros comunsIntermediário
Tutoriais17 projetos completos, do "Olá mundo" ao sistema de ticketsIntermediário
DepuraçãoSintomas, causas e correções dos erros mais comunsIntermediário
SegurançaToken, validação de entrada, permissões, segredosAvançado
Conceitos avançadosEstados, idempotência, filas, cache, rate limit, pluginsAvançado

Convenções usadas neste manual

Ao longo do texto você vai encontrar caixas com significados fixos:

Explicação simples

A ideia contada como se você tivesse 10 anos. Se a parte técnica confundir, leia esta primeiro.

Dica

Um atalho, um truque ou uma escolha que economiza tempo.

Boa prática

O jeito recomendado de fazer. Não é obrigatório, mas evita dor de cabeça depois.

Atenção

Uma armadilha comum. Ler agora custa 10 segundos; descobrir sozinho custa uma tarde.

Segurança

Algo que, feito errado, expõe seu token, seus dados ou seus usuários.

Os níveis Iniciante Intermediário Avançado aparecem em tutoriais e blocos para você calibrar por onde ir.

Introdução aos conceitos

Antes de arrastar o primeiro bloco, vale entender oito palavras. Elas se repetem em todo o manual, e depois de entendê-las o resto fica fácil.

O que é um bot

Explicação simples

Imagine que o bot é um pequeno funcionário digital que mora dentro do Telegram. Ele nunca dorme, nunca sai para almoçar e faz exatamente o que você ensinou — nada além disso. Quando alguém manda uma mensagem, ele recebe uma tarefa. Os blocos são as instruções que ensinam esse funcionário o que fazer.

Tecnicamente: um bot do Telegram é um programa que se autentica na Bot API do Telegram usando um token, recebe eventos (mensagens, cliques em botões, entradas em grupo) e responde chamando métodos HTTP da API. Ele tem uma conta própria no Telegram, com @username terminado em bot, mas não é operado por uma pessoa.

O que um bot pode fazer: enviar e apagar mensagens, mostrar botões, banir usuários em grupos onde é administrador, guardar dados, consultar sites e APIs, cobrar pagamentos, agendar tarefas. O que ele não pode: ler mensagens de outras pessoas em conversas privadas onde não foi adicionado, iniciar conversa com alguém que nunca falou com ele, ou ver histórico anterior à sua entrada num grupo.

O que é automação

Explicação simples

Automação é ensinar uma tarefa uma vez para que ela aconteça sempre sozinha. Você não precisa responder "bom dia, o horário é das 9h às 18h" mil vezes: você ensina isso ao bot e ele responde as mil vezes por você.

Tecnicamente: automação é a substituição de uma ação humana repetitiva por uma regra determinística: dado um gatilho, execute uma sequência de operações. A qualidade de uma automação se mede por três coisas — ela cobre os casos normais, trata os casos anormais (erro, dado faltando) e é observável (você consegue ver o que ela fez).

O que é um fluxo

Explicação simples

Um fluxo é uma receita de bolo. Primeiro isso, depois aquilo, e se faltar ovo faça diferente. O bot lê a receita de cima para baixo, sempre na mesma ordem.

Tecnicamente: no Dromornis um fluxo é uma pilha de blocos ligada a um bloco de evento. Ele tem um ponto de entrada único (o evento), executa de cima para baixo, pode se ramificar em condições e pode terminar antes do fim com o bloco parar por aqui. Cada fluxo vira uma função Python async no código gerado — um handler.

┌── quando receber o comando /start ← evento (topo do fluxo) │ ├──── enviar mensagem "Olá!" ← ação ├──── definir variável nome = nome do usuário ← ação └──── se nome estiver vazio → enviar "..." ← condição

O que é um evento

Explicação simples

O evento é a campainha. Enquanto ninguém toca, o funcionário fica parado. Quando alguém toca, ele acorda e começa a receita.

Tecnicamente: um evento é a condição de disparo de um handler. No Dromornis, todo bloco de evento tem formato de "chapéu" — nada se encaixa acima dele, porque ele é o começo. Sem pelo menos um evento no workspace, o Dromornis avisa: "O bot precisa de pelo menos um evento", porque um bot sem evento é um programa que liga e não faz nada.

Exemplos de eventos: receber o comando /start, receber qualquer mensagem de texto, alguém clicar num botão, um usuário entrar no grupo, o bot ser iniciado. Veja todos em Categoria Eventos.

O que é um gatilho

"Gatilho" e "evento" são frequentemente usados como sinônimos, e na prática são. A diferença útil: o evento é o fato que aconteceu no mundo ("chegou uma mensagem"); o gatilho é a regra que você configurou para reagir a ele ("quando chegar uma mensagem que contenha a palavra preço"). O bloco quando a mensagem contiver ( ) é um evento com gatilho filtrado.

O que é um bloco

Explicação simples

Um bloco é uma peça de LEGO com uma frase escrita nela. "enviar mensagem", "somar", "se". Você encaixa as peças e forma a instrução completa. Peças que não combinam simplesmente não encaixam — é assim que o construtor te impede de errar.

Tecnicamente: cada bloco é um nó em uma árvore sintática. Ele declara entradas (o que ele precisa receber), um tipo de saída (o que ele devolve) e um gerador que traduz o nó para uma expressão ou comando Python. Existem três formatos, e reconhecê-los é meio caminho andado:

FormatoComo pareceO que fazExemplo
Evento (chapéu)Topo arredondado, nada encaixa acimaComeça um fluxoquando receber /start
Ação (comando)Encaixe em cima e embaixoFaz alguma coisa acontecerenviar mensagem ( )
Valor (reporter)Bordas arredondadas, encaixa dentro de outroDevolve um dadoid do usuário
Dica

Se um bloco tem bordas arredondadas e você não consegue encaixá-lo sozinho no fluxo, ele é um valor: precisa ir dentro de um buraco de outro bloco. É o mesmo que tentar usar o número 7 como uma frase — 7 não é uma ordem, é um dado.

O que é uma ação

Ação é tudo o que muda alguma coisa: envia mensagem, apaga mensagem, grava no banco, bane um usuário, espera 3 segundos. No código gerado, ações viram linhas de comando dentro da função do handler, executadas na ordem em que estão empilhadas.

A distinção prática entre evento e ação: o evento você não controla — ele acontece quando o usuário quiser. A ação você controla totalmente — ela acontece porque você mandou. Um fluxo é sempre um evento seguido de N ações.

O que é uma condição

Explicação simples

Condição é uma bifurcação na estrada. "Se estiver chovendo, leve o guarda-chuva; senão, não leve." O bot olha a placa, escolhe um caminho e segue.

Tecnicamente: uma condição avalia uma expressão booleana (verdadeiro/falso) e executa um dos ramos. No Dromornis isso é o bloco se … faça, opcionalmente com senão. A expressão dentro do "se" deve ser um bloco de valor booleano: uma comparação, um e/ou, ou um bloco que já devolve sim/não como é administrador?.

[chegou mensagem] ↓ [é /start?] ↙ ↘ SIM NÃO ↓ ↓ [enviar menu] [enviar ajuda]

O que é uma variável

Explicação simples

Uma variável é uma caixinha com etiqueta. Você escreve "nome" na etiqueta e guarda "Maria" dentro. Depois, sempre que precisar, pede a caixinha "nome" e recebe "Maria". Se guardar outra coisa, o valor antigo é substituído — a caixinha só guarda uma coisa por vez.

Tecnicamente: uma variável é um nome ligado a um valor na memória durante a execução do handler. No Dromornis, variáveis criadas na categoria Variáveis viram variáveis Python locais ao handler — elas não sobrevivem ao fim do fluxo. Para guardar algo entre mensagens ou entre reinícios do bot, use Banco de Dados ou os blocos salvar dado / carregar dado. Essa distinção é a fonte número 1 de confusão para iniciantes — ela está detalhada em Variáveis.

Juntando tudo

Um bot completo é: eventos (quando reagir) + ações (o que fazer) + condições (quando fazer diferente) + variáveis (o que lembrar) organizados em fluxos. Todo o resto — banco de dados, APIs, teclados, pagamentos — são variações mais poderosas dessas quatro peças.

Boa prática

Antes de montar qualquer bot, escreva em uma frase: "Quando ACONTECER X, o bot deve FAZER Y". Se você consegue escrever a frase, consegue montar o fluxo. Se não consegue, o problema ainda não está claro — e nenhum bloco resolve isso.

Comece aqui — trilha de aprendizado

Nove etapas, na ordem. Cada uma se apoia na anterior. Se você pular etapas, provavelmente vai travar em algo que a etapa pulada explicava.

  1. Entenda o que é um botConceitos: bot, fluxo, evento, bloco, variável. → Introdução aos conceitos Iniciante
  2. Crie seu bot no TelegramFale com o @BotFather, escolha o nome e receba o token. → Criando o bot no BotFather Iniciante
  3. Conecte o token ao DromornisCole o token no painel Projeto e configure a variável de ambiente. → Configurando o token Iniciante
  4. Crie seu primeiro fluxoUm evento /start e uma mensagem de resposta. Exporte e rode. → Seu primeiro bot Iniciante
  5. Aprenda variáveisGuarde o nome do usuário, monte textos dinâmicos, entenda escopo. → Variáveis Iniciante
  6. Aprenda condiçõesFaça o bot responder diferente conforme a situação. → Controle e Operadores Intermediário
  7. Aprenda teclados e botõesMenus clicáveis, callbacks e navegação. → Teclado Intermediário
  8. Aprenda banco de dadosGuarde usuários, pedidos e histórico entre reinícios. → Banco de Dados Intermediário
  9. Aprenda APIs e automação avançadaConsuma serviços externos, agende tarefas, trate erros. → Avançado Avançado

Quanto tempo leva

EtapaTempo estimadoVocê saberá fazer
1 a 4~40 minutosUm bot que responde comandos, rodando de verdade
5 a 7~2 horasMenus, respostas personalizadas, navegação por botões
8 a 9~4 horasCadastro de usuários, integração com APIs, tarefas agendadas
Dica para iniciantes

Não tente construir seu projeto final na etapa 1. Faça um bot bobo primeiro — que só responde "oi" — e rode ele até o fim. Ter algo funcionando muda tudo: a partir daí você melhora em cima de uma base que já sabe que funciona, em vez de depurar dez coisas novas ao mesmo tempo.

Os três erros que todo iniciante comete

1. Achar que a variável sobrevive

Você guarda o nome numa variável no fluxo do /start e tenta usá-la no fluxo do /perfil. Não funciona: são fluxos diferentes, memórias diferentes. Para persistir, use Banco de Dados.

2. Esquecer que o bot precisa ser administrador

Banir, apagar mensagem, fixar mensagem — tudo isso exige que o bot seja admin do grupo com a permissão específica. Sem isso a API recusa e nada acontece, muitas vezes em silêncio.

3. Colocar o token dentro de um bloco de texto

Nunca. O token vai no painel Projeto e é lido de variável de ambiente. Explicação completa em Segurança.

Seu primeiro bot Iniciante

Tutorial completo, do projeto vazio até o bot respondendo no seu Telegram. Reserve 30 minutos. Não pule etapas — cada uma existe por um motivo.

Antes de começar

Você vai precisar de três coisas:

  • Uma conta no Telegram (no celular ou no desktop).
  • Python 3.10 a 3.13 instalado no computador onde o bot vai rodar. Não use 3.14 ou superior — a biblioteca python-telegram-bot ainda não é compatível com essas versões.
  • Um terminal (PowerShell no Windows, Terminal no macOS/Linux). Você só vai digitar três comandos.
Por que Python?

Porque o Dromornis gera código Python usando a biblioteca python-telegram-bot, o padrão da comunidade. Você não precisa saber Python — precisa apenas tê-lo instalado, do mesmo jeito que você precisa de um leitor de PDF sem saber como PDFs funcionam por dentro.

Passo 1 — Criar o projeto

Abra o Dromornis. Na barra superior, clique em Novo. Um projeto em branco é criado com um workspace vazio.

Se preferir partir de algo pronto, clique em Templates e escolha um exemplo. Atenção: escolher um template substitui os blocos atuais do workspace, então faça isso antes de começar a montar.

Passo 2 — Nome e descrição

Abra o painel lateral Projeto. Preencha:

Nome
Ex.: Bot da Padaria. Vira o nome do arquivo ZIP e o título do README exportado.
Descrição
Uma frase sobre o que o bot faz. Aparece no README e ajuda você a lembrar do projeto meses depois.

Passo 3 — Criar o bot no Telegram (BotFather)

Esta etapa acontece dentro do Telegram, não no Dromornis.

  1. No Telegram, procure por @BotFather (com o selo azul de verificado).
  2. Envie /newbot.
  3. Ele pergunta o nome do bot — o nome de exibição, pode ter espaços e acentos. Ex.: Bot da Padaria.
  4. Ele pergunta o username — precisa ser único no Telegram inteiro e terminar em bot. Ex.: padaria_do_ze_bot.
  5. Ele responde com uma mensagem contendo o token, algo como 123456789:AAH-YOUR_BOT_TOKEN-example.
Segurança

Esse token é a senha do seu bot. Quem o tiver controla o bot inteiro: pode ler as mensagens que chegam a ele, responder no lugar dele e banir pessoas. Nunca poste em print, em repositório público, em grupo de suporte ou em issue do GitHub. Se vazar, mande /revoke ao BotFather imediatamente — o token antigo morre e você recebe um novo.

Detalhes completos sobre o BotFather, incluindo os comandos /setdescription, /setcommands e /setprivacy, estão em Integração com o Telegram.

Passo 4 — Conectar o token ao Dromornis

De volta ao Dromornis, no painel Projeto, campo Token do bot (do @BotFather): cole o token. Use o botão mostrar/ocultar para conferir sem deixá-lo visível na tela.

Onde esse token vai parar

Ele nunca é escrito no bot.py. O Dromornis grava o token apenas no arquivo .env incluído no ZIP exportado, e o código lê o valor em tempo de execução com os.getenv("TELEGRAM_BOT_TOKEN"). Isso significa que você pode compartilhar o bot.py sem risco — mas não pode compartilhar o ZIP.

Na aba inferior Configuração você define o nome da variável de ambiente (padrão: TELEGRAM_BOT_TOKEN), o username do bot (só informativo) e o parse_mode das mensagens. Deixe o padrão por enquanto.

Passo 5 — Criar o primeiro fluxo

No toolbox à esquerda, abra a categoria Eventos (amarela). Arraste para o canvas o bloco:

┌─────────────────────────────────────┐ │ quando receber o comando /start │ ← bloco de evento (chapéu) └─────────────────────────────────────┘

Agora abra Mensagens (roxa) e arraste enviar mensagem ( ) para dentro da boca do bloco de evento. Você vai sentir o encaixe. Se o bloco não encaixar, ele ficou solto — arraste de novo até a linha de conexão aparecer.

No buraco do enviar mensagem, encaixe um bloco de texto (categoria Operadores, o bloco " ") e escreva: Olá! Eu sou o bot da padaria. 🥖

┌── quando receber o comando /start │ └──── enviar mensagem [ "Olá! Eu sou o bot da padaria. 🥖" ]
Atenção

Blocos soltos no canvas, fora de qualquer evento, não geram código nenhum. Se você montou algo e o código não mudou, quase certamente há um bloco órfão. A aba Validação aponta esses casos.

Passo 6 — Conferir o código gerado

Na aba inferior Código Python, o código aparece e se atualiza em tempo real. Você deve ver algo próximo de:

pythonbot.py (trecho)
async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.effective_chat.send_message("Olá! Eu sou o bot da padaria. 🥖")


def main() -> None:
    application = Application.builder().token(TOKEN).build()
    application.add_handler(CommandHandler("start", start_command))
    application.run_polling()

Você não precisa entender cada linha. Precisa apenas confirmar que existe uma função para o seu comando e que ela envia a sua mensagem. Se a aba Validação estiver limpa ("Nenhum problema encontrado"), siga em frente.

Passo 7 — Salvar

Clique em Salvar. O projeto é gravado no localStorage do navegador e aparece na lista "Projetos salvos" do painel Projeto.

Atenção

localStorage é armazenamento do navegador. Limpar dados de navegação, usar aba anônima ou trocar de máquina faz o projeto sumir. Para backup real, use Exportar → Projeto (.json) e guarde o arquivo. Faça isso sempre que terminar uma sessão de trabalho importante.

Passo 8 — Exportar

Clique em Exportar. Você tem três opções:

OpçãoConteúdoQuando usar
Projeto completo (.zip)bot.py, requirements.txt, README.md, .env.example e, se você preencheu o token, um .env já prontoPara rodar o bot. É o que você quer agora.
Somente código (bot.py)Só o arquivo PythonPara revisar o código ou colar num projeto existente
Projeto (.json)Formato interno do DromornisBackup e reimportação no editor

Escolha o .zip e descompacte numa pasta.

Passo 9 — Rodar o bot

Abra o terminal na pasta descompactada. Crie um ambiente virtual (uma "caixa isolada" para as bibliotecas deste projeto):

bashcriar o ambiente virtual
python -m venv .venv

Ative-o. Windows (PowerShell):

powershellativar — Windows
.venv\Scripts\Activate.ps1

Linux / macOS:

bashativar — Linux/macOS
source .venv/bin/activate

Instale as dependências:

bashinstalar dependências
pip install -r requirements.txt

Se o ZIP não trouxe .env (você não preencheu o token no editor), copie .env.example para .env e coloque o token:

env.env
TELEGRAM_BOT_TOKEN=YOUR_BOT_TOKEN

Execute:

bashrodar o bot
python bot.py

O terminal deve mostrar linhas de log e ficar parado, esperando. Isso é o certo — o bot está no ar. Fechar o terminal derruba o bot.

Passo 10 — Testar

No Telegram, procure pelo @username que você escolheu no BotFather, abra a conversa e clique em Iniciar (ou envie /start). Sua mensagem deve chegar em menos de um segundo.

Boa prática

Deixe o terminal visível enquanto testa. Toda mensagem recebida aparece no log, e todo erro aparece com o traceback completo. É a sua ferramenta de diagnóstico número 1.

Se algo deu errado

SintomaCausa provávelCorreção
Erro InvalidToken ao iniciarToken errado, com espaço extra ou variável de ambiente vaziaConfira o .env: sem aspas, sem espaços, nome da variável igual ao configurado
RuntimeError: There is no current event loopPython 3.14+Instale Python 3.12 ou 3.13 e recrie o venv com py -3.12 -m venv .venv
ModuleNotFoundError: telegramVenv não ativado ou dependências não instaladasAtive o venv e rode pip install -r requirements.txt novamente
Bot inicia mas não respondeNenhum evento no workspace, ou você está falando com o bot erradoConfira a aba Validação e o @username exato
Conflict: terminated by other getUpdatesDuas cópias do mesmo bot rodando ao mesmo tempoFeche a outra instância. Um token só pode ter um processo em polling
PowerShell recusa Activate.ps1Política de execução do WindowsRode Set-ExecutionPolicy -Scope Process RemoteSigned e tente de novo

Mais sintomas e diagnósticos em Depuração e erros.

Sobre "publicar"

Publicar um bot é mantê-lo rodando 24 horas por dia. No seu computador ele fica no ar só enquanto o terminal estiver aberto. Para produção, as opções usuais são:

  • VPS (servidor virtual): copie a pasta, instale as dependências e rode sob um supervisor como systemd, pm2 ou supervisor, que reinicia o bot se ele cair.
  • Container: empacote com um Dockerfile simples baseado em python:3.12-slim e rode com política de reinício.
  • Plataforma de apps: qualquer serviço que execute um processo Python de longa duração. Bots em polling precisam de um processo sempre ligado, não de funções serverless que dormem.
Segurança ao publicar

Nunca faça commit do arquivo .env. Coloque-o no .gitignore antes do primeiro commit. Em servidores, prefira definir a variável de ambiente diretamente no serviço em vez de subir o arquivo.

Integração com o Telegram Intermediário

Como o Telegram, o seu bot e o Dromornis conversam entre si. Entender esta seção é o que separa "consegui fazer funcionar" de "sei por que funciona".

O que é a Bot API do Telegram

Explicação simples

A Bot API é o balcão de atendimento do Telegram. Seu bot não fala com o Telegram por telepatia: ele vai até o balcão e pergunta "chegou alguma coisa para mim?". Quando quer responder, entrega o recado no mesmo balcão e o Telegram se encarrega de levar até a pessoa.

Tecnicamente: a Bot API é uma API HTTP sobre https://api.telegram.org/bot<TOKEN>/<método>. Cada operação é um método — sendMessage, deleteMessage, banChatMember — chamado por GET ou POST com parâmetros JSON. A resposta é sempre um JSON com {"ok": true, "result": …} ou {"ok": false, "error_code": …, "description": …}.

O Dromornis não te faz escrever essas chamadas: cada bloco da categoria Mensagens ou Moderação corresponde, no fundo, a um desses métodos. Saber disso ajuda quando você lê uma mensagem de erro da API — ela vem exatamente com o texto que o Telegram devolveu.

O BotFather

O @BotFather é o bot oficial do Telegram que cria e configura outros bots. Ele é a única maneira de obter um token. Comandos que valem conhecer:

ComandoO que fazObservação
/newbotCria um bot e devolve o tokenUsername deve ser único e terminar em bot
/mybotsLista seus bots e abre o painel de configuraçãoCaminho mais prático para tudo abaixo
/tokenMostra o token de um bot existenteUse quando você perdeu o token
/revokeInvalida o token atual e gera outroUse imediatamente se o token vazar
/setdescriptionTexto exibido na conversa vaziaTambém dá para fazer pelo bloco definir descrição do bot
/setabouttextTexto curto no perfil do botLimite de 120 caracteres
/setuserpicFoto de perfil do botSó pelo BotFather
/setcommandsLista de comandos no menu "/"Ou use o bloco definir menu de comandos
/setjoingroupsPermite ou bloqueia adicionar o bot a gruposDesligue se seu bot é só para conversa privada
/setprivacyModo privacidade em gruposCrucial — veja abaixo
/deletebotApaga o bot permanentementeIrreversível
Atenção — modo privacidade

Por padrão o privacy mode está ligado: dentro de grupos, o bot só recebe mensagens que sejam comandos, respostas diretas a ele ou menções ao seu @username. Se você está fazendo moderação (filtro de palavrão, antiflood, boas-vindas por conteúdo), ele não vai ver as mensagens comuns. Desligue com /setprivacy → Disable e depois remova e readicione o bot ao grupo — a mudança só vale a partir da nova entrada.

O token: o que é e como protegê-lo

O token tem o formato <bot_id>:<segredo>, por exemplo 123456789:AAH-YOUR_BOT_TOKEN-example. A primeira parte é o ID numérico do bot (público, aparece em vários lugares); a segunda é o segredo.

Regras práticas:

  • O token equivale a uma senha com poder total sobre o bot. Trate-o como trataria a senha do seu e-mail.
  • Ele não entra no código-fonte. No Dromornis, o bot.py gerado sempre lê os.getenv("TELEGRAM_BOT_TOKEN").
  • Ele fica no arquivo .env, que nunca vai para o controle de versão.
  • Em servidores, prefira definir a variável de ambiente no próprio serviço em vez de subir o arquivo.
  • Se vazar: /revoke no BotFather. Não existe "cancelar parcialmente".
❌ Inseguro
enviar mensagem [ "meu token é 123456:AAH..." ]

# ou, no código:
TOKEN = "123456789:AAH-real-token"
✅ Seguro
# bot.py gerado pelo Dromornis
TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")

# .env (fora do git)
TELEGRAM_BOT_TOKEN=YOUR_BOT_TOKEN

Conectando o token ao Dromornis

  1. Painel Projeto → campo Token do bot → cole o token.
  2. Aba ConfiguraçãoVariável de ambiente do token → confirme o nome (padrão TELEGRAM_BOT_TOKEN). Esse é o nome que vai aparecer no os.getenv() e no .env.
  3. Exporte o ZIP. Se o campo de token estava preenchido, o ZIP traz um .env pronto; se não, traz apenas o .env.example como modelo.

Como validar que o token está correto: rode python bot.py. Se o token estiver errado, a biblioteca falha imediatamente com telegram.error.InvalidToken. Se estiver certo, o bot fica rodando em silêncio, aguardando mensagens. Para uma checagem manual rápida, abra no navegador:

httpvalidar o token (getMe)
https://api.telegram.org/botYOUR_BOT_TOKEN/getMe

Resposta esperada:

jsonresposta do getMe
{
  "ok": true,
  "result": {
    "id": 123456789,
    "is_bot": true,
    "first_name": "Bot da Padaria",
    "username": "padaria_do_ze_bot",
    "can_join_groups": true,
    "can_read_all_group_messages": false
  }
}
Atenção

Abrir essa URL no navegador expõe o token no histórico e, se a máquina for compartilhada, para quem usar depois. Faça isso apenas em uma máquina sua e limpe o histórico se for um token de produção. Repare também em can_read_all_group_messages: false — é o modo privacidade descrito acima.

Como o Telegram entrega as mensagens: updates

Tudo o que acontece com o seu bot chega como um objeto chamado update. Um update é um envelope que contém um tipo de acontecimento: uma mensagem nova, uma mensagem editada, um clique em botão, alguém entrando no grupo, uma resposta de enquete.

Exemplo simplificado de update para uma mensagem de texto:

jsonupdate — mensagem de texto
{
  "update_id": 874512,
  "message": {
    "message_id": 142,
    "date": 1731000000,
    "text": "/start",
    "from": {
      "id": 987654321,
      "is_bot": false,
      "first_name": "Maria",
      "username": "maria_s",
      "language_code": "pt-br"
    },
    "chat": {
      "id": 987654321,
      "type": "private",
      "first_name": "Maria"
    }
  }
}

E para um clique em botão inline:

jsonupdate — callback_query
{
  "update_id": 874513,
  "callback_query": {
    "id": "4382bfdwdsb323b2d9",
    "data": "menu_precos",
    "from": { "id": 987654321, "first_name": "Maria" },
    "message": { "message_id": 143, "chat": { "id": 987654321 } }
  }
}

Os campos que você mais vai usar

CampoO que éBloco correspondente
chat.idIdentificador da conversa. Em privado é igual ao user.id; em grupos é negativo (ex.: -1001234567890)id do chat
from.idIdentificador único e permanente da pessoa. Nunca mudaid do usuário
message_idNúmero da mensagem dentro daquele chat. Necessário para editar, apagar, fixar ou responderid da mensagem
from.username@apelido. Opcional — muita gente não tem — e pode mudar a qualquer momentousername do usuário
from.first_nameNome de exibição. Sempre existenome do usuário
from.language_codeIdioma do app da pessoa (pt-br, en). Útil para respostas multilínguesidioma do usuário
chat.typeprivate, group, supergroup ou channeltipo do chat, é conversa privada?, é grupo?
callback_query.dataTexto escondido que você definiu no botão inline. Máx. 64 bytesdado do callback
Boa prática

Identifique pessoas sempre por user.id, nunca por username. O username é opcional, mutável e pode ser reciclado por outra pessoa depois de liberado. O id é permanente. Isso vale especialmente para listas de administradores e sistemas de banimento.

Polling × Webhook

Existem duas formas de o bot receber os updates:

Polling (run_polling)

O bot pergunta ao Telegram, de segundo em segundo, "chegou algo novo?". É o método usado pelo código que o Dromornis gera. Funciona em qualquer máquina, atrás de qualquer roteador, sem domínio, sem HTTPS, sem configuração.

Webhook

Você registra uma URL pública com HTTPS e o Telegram entrega os updates nela assim que acontecem. Não precisa perguntar. Exige domínio, certificado válido e um servidor web recebendo POSTs.

PollingWebhook
ConfiguraçãoNenhumaDomínio + HTTPS + porta aberta
LatênciaÓtima na prática (long polling, quase instantâneo)Mínima
Custo em escalaUma conexão sempre aberta por botRequisições sob demanda
Roda em rede doméstica✅ Sim❌ Não sem túnel/proxy
Múltiplas instâncias❌ Só uma por token✅ Com balanceador
Usado pelo Dromornisapplication.run_polling()Requer adaptação manual do bot.py
Dica — qual escolher

Use polling até ter um motivo concreto para mudar. Ele funciona muito bem para a esmagadora maioria dos bots, incluindo bots com milhares de usuários. Migre para webhook quando precisar de várias instâncias em paralelo, quando a plataforma de hospedagem não permitir processos de longa duração, ou quando o custo de manter conexão aberta importar.

Erro clássico

Conflict: terminated by other getUpdates request significa que dois processos estão fazendo polling com o mesmo token. Acontece ao rodar o bot localmente enquanto ele já roda no servidor. Um token, um processo. Se você já registrou um webhook e quer voltar ao polling, chame deleteWebhook antes.

A jornada completa de uma mensagem

Este é o caminho inteiro, do dedo da pessoa até a resposta na tela:

1. Maria digita "/start" e envia ↓ 2. Servidores do Telegram recebem e criam um update3. Seu bot.py, em polling, busca o update (getUpdates) ↓ 4. python-telegram-bot examina o update e escolhe o handler │ (CommandHandler("start", start_command)) ↓ 5. Roda a função gerada pelo seu fluxo no Dromornis │ → executa as ações, uma a uma, de cima para baixo ↓ 6. A ação "enviar mensagem" chama sendMessage na Bot API ↓ 7. O Telegram entrega a mensagem no aparelho da Maria ↓ 8. Fim do handler. O bot volta a esperar o próximo update.

Resumido na notação da arquitetura:

Telegram → Bot API → bot.py (Dromornis) → Evento → Fluxo → Ação → Bot API → Telegram

Cada bloco de evento no seu workspace vira o passo 4. Cada bloco de ação vira o passo 5-6. É literalmente isso — não há mágica escondida.

Conversas privadas, grupos e canais

Tipochat.typeCaracterísticasCuidados
PrivadoprivateUm a um. chat.id = user.id. O bot recebe tudoO bot não pode iniciar a conversa; a pessoa precisa mandar algo primeiro
GrupogroupAté 200 membros, formato antigoPode virar supergrupo automaticamente e o chat.id muda
SupergruposupergroupAté 200 mil membros, tópicos, permissões granulares. id começa com -100Moderação exige o bot como admin com a permissão específica
CanalchannelDifusão. Só admins publicamNão há "membros conversando"; o bot precisa ser admin para publicar
Sobre permissões em grupos

Um bot em um grupo é, por padrão, um membro comum: pode ler (conforme o modo privacidade) e escrever, e nada mais. Para banir, silenciar, apagar mensagens, fixar, alterar título ou aprovar entradas, ele precisa ser promovido a administrador com aquela permissão específica marcada. Ser admin "genérico" não basta — se a caixinha "Apagar mensagens" estiver desmarcada, o bloco de apagar vai falhar.

Comandos

Comandos são mensagens que começam com /. O Telegram os trata de forma especial: mostra autocomplete, destaca em azul e permite um menu na interface.

  • Formato: /nome argumento1 argumento2. No Dromornis, os argumentos ficam disponíveis pelo bloco argumentos do comando.
  • Em grupos com vários bots, o Telegram entrega /comando@seu_bot. A biblioteca já cuida disso — você registra apenas comando.
  • Use o bloco definir menu de comandos (categoria Mensagens) para popular o menu "/" automaticamente ao iniciar o bot. É melhor que o /setcommands manual porque a lista fica versionada junto com o fluxo.
Boa prática

Todo bot deve responder a /start e a /help. /start é o que o Telegram dispara quando a pessoa clica em "Iniciar"; se ele não responder, o primeiro contato do usuário com o seu bot é o silêncio.

Telegram avançado Avançado

Referência dos recursos da Bot API que aparecem quando o bot cresce: teclados inline, callbacks, deep linking, formatação, limites e tratamento de erros.

Inline keyboards

São os botões que ficam presos à mensagem, logo abaixo do texto. Clicar não envia nada visível no chat: gera um callback_query que só o seu bot vê.

Cada botão inline tem um rótulo (o que aparece) e uma ação, que pode ser:

Tipo de botãoO que acontece ao clicarQuando usar
callback_dataEnvia um texto oculto ao bot (máx. 64 bytes)Menus, navegação, confirmações
urlAbre um link no navegadorSite, documento, suporte externo
web_appAbre uma Mini App dentro do TelegramFormulários ricos, catálogos
switch_inline_queryAbre um chat com o bot já pré-digitadoCompartilhamento entre conversas
Limite de 64 bytes no callback_data

É bytes, não caracteres — acentos e emojis consomem mais de um. Se estourar, o Telegram rejeita a mensagem inteira. Nunca coloque dados grandes ali: use uma chave curta (p:42) e busque o resto no banco. Estruture como ação:id para conseguir rotear com começa com.

Callback queries

Quando alguém clica num botão inline, o Telegram envia um callback_query e mostra um relógio girando no botão. Esse relógio só some quando o bot chama answerCallbackQuery.

Sempre responda o callback

Se você não usar o bloco responder callback, o usuário vê o botão "carregando" por vários segundos e conclui que o bot travou — mesmo que o resto do fluxo tenha funcionado. Responda primeiro, faça o trabalho depois. O bloco aceita um texto opcional que aparece como aviso rápido no topo ou como alerta modal.

┌── quando clicarem em um botão │ ├──── responder callback [ "" ] ← primeiro: tira o relógio ├──── se [ dado do callback = "precos" ] │ └── editar mensagem [ "Nossa tabela: ..." ] └──── senão └── responder callback [ "Opção desconhecida" ]

Editar a mensagem existente (em vez de enviar uma nova) é o que dá a sensação de "menu que navega". Veja editar mensagem em Mensagens.

Um link no formato https://t.me/seu_bot?start=PARAMETRO abre a conversa e, ao clicar em Iniciar, envia /start PARAMETRO. O parâmetro chega no bloco argumentos do comando.

Usos comuns:

  • Rastrear origem: ?start=instagram, ?start=panfleto — você registra de onde o usuário veio.
  • Programa de indicação: ?start=ref_987654321 — o número é o user.id de quem indicou.
  • Link direto para um item: ?start=produto_42 — o bot já abre a ficha do produto.
  • Vincular conta: ?start=tok_ab12cd — um token de uso único gerado pelo seu site.
Segurança

O parâmetro do start vem do usuário e pode ser qualquer coisa — inclusive um valor que outra pessoa forjou. Nunca conceda privilégio com base nele (?start=admin não pode virar admin). Para vinculação de conta, use tokens aleatórios, de uso único e com validade curta, verificados contra o banco.

Restrições do parâmetro: até 64 caracteres, apenas A-Z a-z 0-9 _ -.

Formatação: MarkdownV2 × HTML

A opção parse_mode, na aba Configuração, vale para todas as mensagens de texto do bot. Três escolhas:

ModoVantagemDesvantagem
Texto puro (None)Nada quebra. Nenhum caractere é especialSem negrito, itálico ou links formatados
HTMLTolerante e fácil de escapar (só & < >)Só um subconjunto de tags é aceito
MarkdownV2Sintaxe curta, cobre tudoRigoroso: 18 caracteres precisam de escape ou a mensagem falha inteira

Tags aceitas em HTML:

htmlformatação HTML aceita pelo Telegram
<b>negrito</b>  <i>itálico</i>  <u>sublinhado</u>  <s>riscado</s>
<code>monoespaçado</code>
<pre><code class="language-python">bloco de código</code></pre>
<a href="https://exemplo.com">link</a>
<a href="tg://user?id=987654321">mencionar alguém sem username</a>
<tg-spoiler>spoiler</tg-spoiler>
<blockquote>citação</blockquote>

Em MarkdownV2, estes caracteres precisam ser precedidos de \ sempre que forem literais:

textcaracteres reservados do MarkdownV2
_ * [ ] ( ) ~ ` > # + - = | { } . !
Armadilha frequente

Com MarkdownV2 ligado, uma mensagem tão inocente quanto Total: R$ 10.50 (à vista) falha, porque ., ( e ) são reservados — e a mensagem inteira não é enviada. Se o conteúdo inclui texto vindo do usuário ou de uma API, prefira HTML, ou texto puro. Formatação bonita não vale uma mensagem que some.

Boa prática

Sempre escape conteúdo dinâmico antes de interpolar. Nome de usuário pode conter <, * ou _ — o Telegram vai tentar interpretar e, no melhor caso, a formatação quebra; no pior, você criou uma injeção de marcação.

Media groups (álbuns)

Um media group agrupa de 2 a 10 fotos/vídeos numa única mensagem em formato de álbum. No Dromornis use item de mídia empilhado dentro de enviar grupo de mídia.

  • Não é possível misturar foto/vídeo com documento ou áudio no mesmo álbum.
  • Apenas a legenda do primeiro item aparece como legenda do álbum.
  • Álbuns não aceitam teclados inline. Se precisar de botões, envie o álbum e depois uma mensagem separada com os botões.

Limites da Bot API

LimiteValorConsequência de estourar
Mensagens por segundo, mesmo chat~1/s (rajadas curtas toleradas)429 Too Many Requests com retry_after
Mensagens por segundo, global~30/sIdem
Mensagens por minuto, mesmo grupo~20/minIdem
Texto de uma mensagem4096 caracteresErro message is too long
Legenda de mídia1024 caracteresErro na chamada
callback_data64 bytesMensagem rejeitada
Download de arquivo pelo bot20 MBErro file is too big
Upload de arquivo pelo bot50 MBErro na chamada
Botões por linha do teclado8 (recomendado: 2 a 3)Layout ilegível no celular
Apagar mensagem de outro usuárioSem limite de tempo para o bot admin
Editar mensagem própria48 horasmessage can't be edited
Dica — envio em massa

Para avisar mil pessoas, não dispare mil mensagens em sequência: você vai levar 429 e perder parte dos envios. Insira um esperar 0.05 segundos entre os envios (≈20/s) e trate falhas individualmente com tentar / se der erro — pessoas que bloquearam o bot devolvem Forbidden, e isso não pode derrubar o resto da fila.

Erros comuns da Bot API

ErroSignificadoO que fazer
401 UnauthorizedToken inválido ou revogadoVerifique o .env; gere outro com /token
403 Forbidden: bot was blocked by the userA pessoa bloqueou o botMarque como inativo no banco e pare de enviar
403 Forbidden: bot is not a memberO bot foi removido do grupoRemova o grupo da sua lista de destinos
400 Bad Request: chat not foundchat_id errado, ou a pessoa nunca falou com o botConfirme o id; lembre que o bot não inicia conversas
400 message is not modifiedVocê editou a mensagem para o mesmo conteúdoCompare antes de editar, ou ignore este erro
400 message to delete not foundMensagem já apagada ou fora do alcanceEnvolva em tentar / se der erro
400 not enough rightsO bot é admin mas falta a permissão específicaMarque a permissão exata nas configurações do grupo
400 can't parse entitiesMarkdown/HTML malformadoEscape o conteúdo dinâmico ou use texto puro
429 Too Many RequestsRate limitAguarde retry_after segundos e reenvie
Boa prática

Trate erro de envio como normal, não como exceção. Em qualquer bot com usuários reais, pessoas bloqueiam, saem de grupos e apagam conversas todos os dias. Um fluxo de broadcast sem tentar / se der erro vai morrer no primeiro usuário que bloqueou o bot — e os outros 999 não recebem nada.

Conhecendo a interface Iniciante

Cada região da tela, o que ela faz e quando você vai precisar dela. Leia uma vez e você nunca mais fica procurando um botão.

Visão geral

┌──────────────────────────────────────────────────────────────────────┐ │ BARRA SUPERIOR Novo · Templates · Salvar · Importar · Exportar · Testar │ ├──────────┬─────────────────────────────────────────┬─────────────────┤ │ │ │ │ │ TOOLBOXCANVASPAINEL │ │ Eventos │ (área onde você monta os blocos) │ Projeto │ │ Mensagens│ │ Extensões │ │ Teclado │ ┌──────────┐ │ │ │ ... │ │ minimapa │ │ │ ├──────────┴─────────────────────────────────────────┴─────────────────┤ │ PAINEL INFERIOR Código Python │ Logs │ Validação │ Configuração │ └──────────────────────────────────────────────────────────────────────┘

Barra superior

Novo
Cria um projeto em branco. Se houver alterações não salvas, pede confirmação antes de descartar.
Templates
Abre a galeria de exemplos prontos. Carregar um template substitui os blocos atuais — a confirmação avisa disso.
Salvar
Grava no localStorage do navegador. Um indicador de "alterações não salvas" aparece na barra quando há mudanças pendentes.
Importar
Carrega um .json exportado antes. Substitui o projeto atual.
Exportar
Abre o diálogo com as três saídas: .zip completo, apenas bot.py, ou .json do projeto. A exportação é bloqueada se a validação apontar problemas.
Testar
Executa a validação do fluxo e informa se o código está pronto. Não executa o bot — o bot roda no seu computador, com python bot.py.
Tema
Alterna entre claro e escuro.
Idioma
Troca o idioma da interface.
Sobre o botão "Testar"

Ele responde à pergunta "o que eu montei gera código válido?", não "o bot funciona como eu quero?". A segunda pergunta só o Telegram responde: exporte, rode e converse com o bot. Um fluxo pode passar na validação e ainda assim ter a lógica errada — validação verifica a estrutura, não a intenção.

Toolbox — a biblioteca de blocos

A coluna esquerda lista as categorias, cada uma com sua cor. Clique numa categoria e a gaveta (flyout) abre com os blocos disponíveis; arraste dali para o canvas.

CategoriaCorPara quêBlocos
EventosAmareloIniciar fluxos24
MensagensRoxoEnviar, editar, apagar, mídia, enquetes42
TecladoMagentaBotões, menus, inline query10
ModeraçãoRosaBanir, silenciar, permissões, avisos, tópicos31
UsuárioCianoDados de quem falou e do chat6
ControleLaranjaSe, repita, esperar, log, tratar erro+ nativos
OperadoresVerdeLógica, matemática, texto, regex+ nativos
VariáveisLaranja escuroCriar e usar variáveisdinâmico
ListasLaranja vivoColeções ordenadas+ nativos
DicionárioÂmbarPares chave → valor4
Banco de DadosAzulSQLite: tabelas, CRUD, SQL12
AvançadoVerde-águaHTTP, JSON, agendamento, código Python17
ArquivosAzul claroLer, escrever, CSV, downloads8
PagamentosVerde escuroFaturas e checkout do Telegram6
FigurinhasLilásCriar e gerenciar sticker sets6
JogosVermelhoTelegram Games e placares6
Dica

No topo do toolbox existe a caixa Buscar. Se você sabe o que quer ("banir", "http", "enquete") mas não sabe em qual categoria mora, digite ali — é mais rápido que abrir gaveta por gaveta.

Canvas

A área central onde os blocos vivem. Comportamentos que valem saber:

  • Arrastar um bloco para perto de outro mostra uma linha de conexão; solte para encaixar.
  • Arrastar o fundo move a visão. A roda do mouse dá zoom.
  • Clique com o botão direito num bloco: duplicar, adicionar comentário, desabilitar, recolher, apagar. No fundo do canvas: desfazer, limpar blocos, organizar.
  • Desabilitar um bloco (botão direito → Desabilitar) o deixa cinza e o exclui do código gerado, sem apagá-lo. Excelente para testar variações.
  • Arrastar para a lixeira (canto inferior) ou pressionar Delete remove.
Blocos órfãos

Um bloco de ação solto no canvas, que não está dentro de nenhum evento, não gera código. Ele fica ali, parecendo parte do bot, sem nunca executar. A aba Validação sinaliza esses casos — leia os avisos antes de exportar.

Controles do workspace

Desfazer / Refazer
Ctrl+Z e Ctrl+Shift+Z. O histórico cobre toda a sessão de edição.
Aproximar / Afastar
Zoom. Também com Ctrl + roda do mouse.
Centralizar
Traz a visão de volta ao ponto de origem — útil quando você "se perdeu" arrastando.
Enquadrar tudo
Ajusta o zoom para todos os blocos caberem na tela. O melhor jeito de encontrar blocos órfãos esquecidos.

Minimapa

O retângulo no canto do canvas mostra o workspace inteiro em miniatura, com um marcador da área visível. Em fluxos grandes, clicar no minimapa é a navegação mais rápida.

Painel inferior

Aba Código Python

Mostra o bot.py gerado, com realce de sintaxe, atualizado a cada mudança. Tem botão Copiar e Baixar bot.py. No topo aparece o status: "Código válido" ou "N avisos — revise antes de executar".

Boa prática

Mesmo sem saber Python, olhe esta aba de vez em quando. Se você adicionou um bloco e o código não mudou, algo está errado — provavelmente o bloco ficou órfão ou desabilitado. É o feedback mais direto que existe.

Aba Logs

Histórico das ações do editor: projeto salvo, template carregado, exportação concluída, exportação bloqueada por validação. Serve para responder "o que eu fiz nos últimos minutos?".

Não confunda

Este é o log do editor, não do bot em execução. Os logs do bot rodando aparecem no terminal onde você executou python bot.py — é lá que os erros de verdade acontecem. O bloco registrar no log escreve naquele terminal, não nesta aba.

Aba Validação

Lista os problemas estruturais do workspace: nenhum evento definido, blocos obrigatórios vazios, blocos soltos, configurações incompletas. Enquanto houver problemas, a exportação fica bloqueada.

Aba Configuração

Variável de ambiente do token
O nome usado em os.getenv(). Padrão: TELEGRAM_BOT_TOKEN. Mude apenas se seu ambiente já usa outro nome.
Username do bot
Apenas informativo — não afeta o código gerado.
Formatação das mensagens (parse_mode)
None, MarkdownV2 ou HTML. Aplica-se a todas as mensagens de texto. Veja MarkdownV2 × HTML.
Descrição para o README
Texto que entra no README.md do ZIP exportado.

Painéis laterais

Projeto

Nome, descrição, token e a lista de Projetos salvos, onde você abre, duplica ("salvar como") e apaga projetos.

Extensões

Gerencia pacotes .dpkg que adicionam blocos novos ao construtor. Cada extensão mostra status (OK, com avisos, com erro), quantidade de blocos e se a assinatura foi verificada.

Segurança

Extensões executam código dentro do editor e injetam blocos que geram Python no seu bot. Instale apenas as de origem confiável e prefira as com assinatura verificada. Remover uma extensão apaga do workspace os blocos que vieram dela, junto com o que estiver encaixado dentro — a ação não pode ser desfeita.

Atalhos de teclado

AtalhoAção
Ctrl+ZDesfazer
Ctrl+Shift+ZRefazer
Ctrl+C / Ctrl+VCopiar / colar bloco selecionado
DeleteApagar bloco selecionado
Ctrl + rodaZoom
EscFechar a gaveta do toolbox

Uso em tablet e celular

O editor é responsivo. Em telas menores, o toolbox e os painéis laterais viram gavetas acionadas por botão, e o painel inferior pode ser expandido ou recolhido. Montar fluxos longos no celular é possível, mas trabalhoso: use telas pequenas para revisar e ajustar, e telas grandes para construir.

Pensando em fluxos Iniciante

A parte difícil de construir um bot não é achar o bloco certo — é organizar o pensamento. Este capítulo é o método.

O método das três perguntas

Antes de arrastar qualquer bloco, responda:

  1. O que dispara? — vira o bloco de evento.
  2. O que precisa ser decidido? — vira as condições.
  3. O que deve acontecer em cada caso? — vira as ações.

Exemplo. "Quero que, quando alguém pedir o horário, o bot responda; e se for fora do expediente, avise que estamos fechados."

1. Dispara: comando /horario 2. Decide: a hora atual está entre 9 e 18? 3. Faz: sim → "Estamos abertos! Até as 18h." não → "Estamos fechados. Abrimos às 9h."

Isso já é o fluxo inteiro, e você ainda não tocou no editor. Traduzido em blocos:

┌── quando receber o comando /horario │ ├──── definir hora = hora atual │ ├──── se [ hora ≥ 9 e hora < 18 ] │ └── enviar mensagem [ "Estamos abertos! Até as 18h." ] │ └──── senão └── enviar mensagem [ "Estamos fechados. Abrimos às 9h." ]

Anatomia de um fluxo

EVENTO ← exatamente um, sempre no topo. Sem ele, nada roda. │ ├── GUARDA ← verificações que barram cedo (é admin? tem permissão?) │ ├── DADOS ← buscar o que precisa (banco, API, variáveis) │ ├── DECISÃO ← condições que escolhem o caminho │ └── RESPOSTA ← o que o usuário vê acontecer

Essa ordem — guarda, dados, decisão, resposta — não é obrigatória, mas fluxos que a seguem são muito mais fáceis de ler e depurar. O contrário (enviar mensagem, buscar dado, verificar permissão, enviar de novo) funciona, mas em três semanas nem você entende mais.

Boa prática — falhe cedo

Coloque as verificações que impedem o fluxo logo no começo, seguidas de parar por aqui. Assim o resto do fluxo pode assumir que tudo está válido, sem aninhar cinco níveis de "se".

┌── quando receber o comando /banir │ ├──── se [ não ( é administrador? ) ] │ ├── enviar mensagem [ "Só administradores podem usar isso." ] │ └── parar por aqui ← barra e encerra │ ├──── ... daqui pra baixo, com certeza é admin ...

Desenhando antes de montar

Para fluxos com mais de duas decisões, desenhe. Papel, quadro branco, qualquer coisa. O padrão usado neste manual:

[Telegram] ↓ [Mensagem recebida] ↓ [É /start?] ↙ ↘ SIM NÃO ↓ ↓ [Menu] [É comando conhecido?] ↙ ↘ SIM NÃO ↓ ↓ [Executa] [Mensagem padrão]

Um desenho revela em 30 segundos problemas que levariam uma hora para descobrir montando: caminhos que não levam a lugar nenhum, decisões redundantes, casos esquecidos (o famoso "e se o usuário mandar outra coisa?").

Quando quebrar em vários fluxos

Um fluxo deve caber, mentalmente, em uma frase. Se você precisa de "e também… e ainda… e quando…" para descrevê-lo, ele já é dois.

Dica

Se o fluxo passou de ~20 blocos ou tem mais de três níveis de "se" aninhado, divida. Um evento por responsabilidade: /start mostra menu, callback menu_* trata cliques, /ajuda explica. Fluxos curtos com nomes claros são mais fáceis de corrigir do que um fluxo gigante que "faz tudo".

Vários eventos no mesmo workspace

Um projeto tem vários blocos de evento, lado a lado no canvas. Eles são independentes: cada um vira uma função separada e só roda quando o seu gatilho acontece. Organize-os espacialmente por assunto — comandos numa coluna, callbacks noutra, eventos de grupo noutra. O canvas é infinito; use isso a seu favor.

Atenção — ordem de captura

Se você tem quando receber qualquer mensagem e também quando a mensagem contiver "preço", os dois podem disparar para a mesma mensagem. Prefira colocar a lógica de "qualquer mensagem" como último recurso, e trate os casos específicos com eventos específicos ou com condições dentro de um único fluxo.

Fluxos que precisam de memória

"Qual o seu nome?" → usuário responde → "Qual seu e-mail?" → usuário responde. Isso é uma conversa com estado: o bot precisa lembrar em que etapa está.

Duas abordagens no Dromornis:

  • Bloco esperar resposta — pausa o fluxo, aguarda a próxima mensagem daquele usuário e continua na linha seguinte. Ideal para sequências curtas de 2 a 4 perguntas.
  • Máquina de estados no banco — você guarda etapa numa tabela e, a cada mensagem, verifica em que etapa a pessoa está. Mais trabalhoso, porém sobrevive a reinícios do bot e permite fluxos longos e ramificados.

Detalhes e exemplos completos em Conceitos avançados → Estados e no tutorial Sistema de cadastro.

Checklist antes de exportar

  • Existe pelo menos um evento? Existe /start?
  • A aba Validação está limpa?
  • Usando "Enquadrar tudo", há algum bloco solto fora de um evento?
  • Toda ação que exige permissão de admin está protegida por uma verificação?
  • Todo callback tem um responder callback?
  • Chamadas de API externa e envios em massa estão dentro de tentar / se der erro?
  • Nenhum token, senha ou chave de API está escrito dentro de um bloco de texto?

Como ler a referência de blocos

O construtor tem 192 blocos em 14 categorias. As páginas seguintes documentam cada categoria com o mesmo formato, para você achar o que precisa sem ler tudo.

O formato de cada bloco

Os blocos mais importantes de cada categoria vêm com uma ficha completa e expansível. Clique no cabeçalho para abrir. A ficha responde sempre às mesmas perguntas:

Objetivo
O que o bloco faz, em uma frase.
Categoria / tipo
Onde ele mora no toolbox e se é evento, ação ou valor.
Quando usar
Os casos em que ele é a escolha certa.
Quando NÃO usar
Os casos em que outro bloco serve melhor — costuma ser a parte mais útil.
Entradas
O que você precisa encaixar ou digitar nele.
Saída
O que ele devolve, e de que tipo.
Antes / depois
Quais blocos normalmente o acompanham.
Erros comuns
O que costuma dar errado e por quê.
Exemplo
Um fluxo real, do gatilho ao resultado.

Os demais blocos aparecem em tabelas de referência dentro da própria categoria, e todos os 192 estão na Referência rápida, pesquisável e filtrável.

Os três tipos de bloco

Evento

Topo de fluxo. Nada encaixa acima dele. Sempre gera um handler no código. Só existe na categoria Eventos (mais alguns em Pagamentos e Jogos).

Ação

Empilha dentro de um fluxo. Faz alguma coisa acontecer e não devolve nada para encaixar.

Valor

Bordas arredondadas. Encaixa dentro de outro bloco e devolve um dado: texto, número, sim/não, lista ou dicionário.

Dica de leitura

Quando esta documentação escreve enviar mensagem ( ), os parênteses vazios representam um encaixe: ali entra um bloco de valor ou um texto digitado. Quando escreve id do usuário sem parênteses, é um bloco de valor sem entradas.

Tipos de dado

TipoO que éExemploCuidado
String (texto)Qualquer sequência de caracteres"Olá", "42""42" é texto, não número
NumberInteiro ou decimal42, 3.14Use ( ) como número para converter texto
BooleanVerdadeiro ou falsoé administrador?Só entra em condições
Array (lista)Vários valores em ordem, com índice["a","b","c"]No Blockly, o primeiro item é o índice 1
Object (dicionário)Pares chave → valor{"nome":"Ana"}Chave inexistente devolve vazio
Null / vazioAusência de valorUsuário sem sobrenomeSempre teste antes de usar
O erro de tipo mais comum

Comparar "10" (texto) com 10 (número) dá falso. Se você pegou um valor de mensagem, de API ou de arquivo, ele chega como texto. Converta com ( ) como número antes de comparar ou somar.

Atalho por objetivo

Eu quero…Vá para
Fazer o bot reagir a algoEventos
Mandar texto, foto, enquete, álbumMensagens
Criar menus e botõesTeclado
Banir, silenciar, dar aviso, gerenciar grupoModeração
Saber quem falou, de onde, quandoUsuário
Decidir, repetir, tratar erroControle e Operadores
Guardar algo durante o fluxoVariáveis
Guardar algo para sempreBanco de Dados
Consultar um site ou APIAvançado
Ler e escrever arquivosArquivos
Cobrar, vender, criar figurinhas, jogosPagamentos, Figurinhas, Jogos

Eventos 24 blocos

Todo fluxo começa aqui. Um evento é a pergunta "quando isso deve acontecer?" — sem ele, nenhum bloco executa.

Evento × ação: a diferença que importa

EventoAção
Quem decide quando rodaO usuário / o TelegramVocê
FormatoChapéu (nada encaixa acima)Bloco empilhável
Quantos por fluxoExatamente 1, no topoQuantos você quiser
No código geradoUma função async + um handler registradoUma linha dentro da função
Pode ficar solto?Sim — vira um fluxo vazio (inútil, mas válido)Não — solto, não gera código

Os eventos que você vai usar sempre

quando receber /start Iniciante tg_event_start

Objetivo

Disparar quando o usuário envia /start — o primeiro contato de qualquer pessoa com o bot.

Tipo
Evento (chapéu)
Entradas
Nenhuma
Saída
Nenhuma (abre o fluxo)

Quando usar

Sempre. Este é o evento obrigatório de qualquer bot: quando alguém abre a conversa e clica em Iniciar, o Telegram envia /start automaticamente. É a sua tela de boas-vindas, o seu menu principal e o seu cartão de visitas.

Quando NÃO usar

Para lógica que precisa rodar em toda mensagem — use quando receber uma mensagem. E não coloque aqui tarefas de inicialização do bot (conectar banco, definir menu de comandos): isso vai em quando o bot iniciar, que roda uma vez só.

Blocos que costumam vir depois

enviar mensagem, enviar mensagem com teclado, inserir na tabela (cadastrar o usuário), argumentos do comando (deep linking).

Exemplo — boas-vindas com registro

┌── quando receber /start │ ├──── definir existe = buscar 1 linha da tabela "usuarios" onde "id" = [ID do usuário] │ ├──── se [ existe = nenhum ] │ ├── inserir na tabela "usuarios" dados { id: ID do usuário, nome: primeiro nome } │ └── enviar mensagem [ juntar("Bem-vindo, ", primeiro nome, "! 👋") ] │ └──── senão └── enviar mensagem [ juntar("Que bom te ver de novo, ", primeiro nome, "!") ]

Erros comuns

  • Não ter esse evento. O usuário abre o bot, clica em Iniciar e nada acontece. Péssima primeira impressão.
  • Esquecer do deep link. Se você divulga links ?start=PARAM, precisa ler argumentos do comando aqui, senão o parâmetro é ignorado.
  • Cadastrar o usuário duas vezes. A pessoa pode enviar /start quantas vezes quiser. Verifique antes de inserir.
Boa prática

Deixe o /start curto e com um menu. Ninguém lê um parágrafo de 12 linhas no primeiro contato — mas todo mundo clica num botão.

quando receber comando /( ) Iniciante tg_event_command

Objetivo

Disparar quando o usuário envia um comando específico que você nomeia no próprio bloco.

Entradas
Nome do comando, sem a barra. Ex.: digite ajuda para responder a /ajuda
Saída
Nenhuma

Quando usar

Para toda funcionalidade que o usuário deve poder invocar por nome: /ajuda, /precos, /perfil, /banir.

Quando NÃO usar

Quando o usuário provavelmente não vai lembrar o nome do comando — nesse caso, botões são melhores. E quando você quer reagir a texto livre: use quando a mensagem contiver.

Regras do nome do comando

  • Apenas letras minúsculas, números e _. Máximo 32 caracteres.
  • Sem a barra no campo. Escrever /ajuda cria o comando //ajuda, que ninguém consegue chamar.
  • Em grupos, /ajuda@meu_bot também funciona — a biblioteca resolve sozinha.

Argumentos

Tudo depois do comando vira uma lista acessível pelo bloco argumentos do comando. /soma 2 3 devolve ["2","3"]. Lembre-se: os itens são texto; converta com ( ) como número.

┌── quando receber comando /soma │ ├──── definir args = argumentos do comando │ ├──── se [ tamanho de args < 2 ] │ ├── enviar mensagem [ "Use assim: /soma 2 3" ] │ └── parar por aqui │ └──── enviar mensagem [ juntar("Resultado: ", (item 1 de args como número) + (item 2 de args como número)) ]

Erros comuns

  • Digitar a barra no campo do bloco.
  • Assumir que os argumentos existem. Se a pessoa manda só /soma, a lista está vazia e pegar o item 1 quebra o fluxo. Sempre valide o tamanho antes.
  • Esquecer de registrar o comando no menu — use definir menu de comandos para que ele apareça no "/" do app.
quando receber uma mensagem Iniciante tg_event_message

Objetivo

Disparar para qualquer mensagem de texto que não seja um comando.

Entradas
Nenhuma
Saída
Nenhuma

Quando usar

Como rede de segurança: responder algo útil quando o usuário digita texto livre em vez de usar comandos. Também para bots de atendimento que interpretam frases, e para moderação por conteúdo.

Quando NÃO usar

Para reagir a uma palavra específica — quando a mensagem contiver é mais direto e não captura tudo. E evite lógica pesada aqui: este evento roda em toda mensagem, então consultas de banco ou chamadas de API a cada mensagem custam caro.

Atenção em grupos

Com o modo privacidade ligado (padrão), este evento não dispara para mensagens comuns de grupo — só para comandos, menções e respostas ao bot. Se seu bot precisa ver tudo, desligue o privacy mode no BotFather e readicione o bot ao grupo. Veja BotFather.

Exemplo — fallback amigável

┌── quando receber uma mensagem │ ├──── se [ texto da mensagem contém "preço" ] │ ├── enviar mensagem [ "Nossa tabela: /precos" ] │ └── parar por aqui │ └──── enviar mensagem [ "Não entendi 🤔 Digite /ajuda para ver o que sei fazer." ]
quando a mensagem contiver ( ) Iniciante tg_event_message_contains

Objetivo

Disparar quando o texto da mensagem contém o trecho informado.

Entradas
Texto a procurar
Saída
Nenhuma

Quando usar

Respostas automáticas por palavra-chave ("horário", "entrega", "cnpj"), filtros de moderação, e atalhos para quem não usa comandos.

Quando NÃO usar

Para muitas palavras-chave: dez eventos desses ficam impossíveis de manter. A partir de umas cinco, prefira um único quando receber uma mensagem com uma cadeia de condições, ou uma tabela de palavras no banco.

Cuidado com falsos positivos

"contém" é literal e casa no meio das palavras. Um filtro para ban pega "banana", "urbano" e "cubano". Para casar palavra inteira, use ( ) corresponde ao padrão ( ) com uma expressão regular, por exemplo \bban\b.

quando o botão ( ) for pressionado Intermediário tg_event_callback

Objetivo

Disparar quando alguém clica num botão inline cujo callback_data é o valor informado.

Entradas
O callback_data exato, igual ao configurado no botão
Saída
Nenhuma
Blocos irmãos
dado do callback, confirmar clique do botão, editar mensagem do botão

Quando usar

Para toda navegação por botões: menus, paginação, confirmações ("Sim/Não"), seleção de itens.

Quando NÃO usar

Para botões do tipo URL ou Web App — eles não geram callback. E para teclados de resposta (reply keyboard), que enviam texto comum e devem ser tratados como mensagem.

Regra de ouro

A primeira ação do fluxo deve ser confirmar clique do botão. Sem isso, o botão fica com o relógio girando por até 30 segundos e o usuário acha que travou.

Exemplo — menu navegável

┌── quando receber /start │ └── enviar mensagem [ "O que você quer ver?" ] com teclado inline: │ • botão "💰 Preços" → callback "precos" │ • botão "📍 Endereço" → callback "endereco" ┌── quando o botão "precos" for pressionado │ ├── confirmar clique do botão [ "" ] │ └── editar mensagem do botão para [ "Pão: R$ 1,00\nBolo: R$ 35,00" ] ┌── quando o botão "endereco" for pressionado │ ├── confirmar clique do botão [ "" ] │ └── editar mensagem do botão para [ "Rua das Flores, 123" ]

Erros comuns

  • callback_data do botão diferente do configurado no evento — sequer um espaço a mais pode existir. Nada acontece e não há erro visível.
  • Passar de 64 bytes no callback_data: a mensagem com o teclado nem é enviada.
  • Esquecer confirmar clique.
Dica — dados dinâmicos no botão

Para "clique no produto 42", use callback_data = p:42 e, em vez de criar um evento por produto, capture com um único fluxo lendo dado do callback e extraindo o número. Guarde no callback_data apenas o identificador, nunca o objeto inteiro.

quando o bot iniciar Intermediário tg_event_bot_start

Objetivo

Executar ações uma única vez, no momento em que o processo do bot sobe.

Entradas
Nenhuma
Saída
Nenhuma

Quando usar

Criar tabelas do banco, definir o menu de comandos, definir nome e descrição do bot, carregar configurações, registrar tarefas agendadas.

Quando NÃO usar

Para qualquer coisa relacionada a um usuário. Não existe update aqui: não há quem falou, nem chat, nem mensagem. Blocos como ID do usuário ou enviar mensagem (que usa o chat atual) não têm contexto e vão falhar.

Exemplo — inicialização típica

┌── quando o bot iniciar │ ├──── criar tabela "usuarios" │ • coluna "id" tipo inteiro (chave) │ • coluna "nome" tipo texto │ ├──── criar tabela "pedidos" │ • coluna "id" tipo inteiro (chave) │ • coluna "usuario" tipo inteiro │ • coluna "item" tipo texto │ └──── definir menu de comandos: • /start — Começar • /ajuda — Como usar • /pedido — Fazer um pedido
Boa prática

Criar tabelas aqui é seguro e recomendado: o bloco criar tabela usa CREATE TABLE IF NOT EXISTS, então rodar em toda inicialização não apaga dados existentes.

quando um usuário entrar / sair do grupo Intermediário tg_event_member_join · tg_event_member_leave

Objetivo

Reagir à entrada ou saída de membros num grupo.

Entradas
Nenhuma
Saída
Nenhuma

Quando usar

Mensagem de boas-vindas com as regras, verificação anti-bot (captcha simples), registro de entrada e saída, e limpeza automática das mensagens de sistema "Fulano entrou no grupo".

Quando NÃO usar

Para detectar que o próprio bot foi adicionado ou removido — para isso existe quando o status do bot no chat mudar.

Exemplo — boas-vindas com regras

┌── quando um usuário entrar no grupo │ ├──── se [ autor é um bot? ] │ └── parar por aqui ← não dá boas-vindas a bots │ ├──── enviar mensagem [ juntar("Bem-vindo(a), ", primeiro nome, │ "! Leia as regras fixadas antes de postar. 📌") ] └──── esperar 30 segundos └── apagar mensagem recebida ← evita poluir o grupo
Atenção

Em grupos grandes com muitas entradas, uma mensagem de boas-vindas por pessoa vira spam e pode esbarrar no limite de ~20 mensagens/minuto por grupo. Considere agrupar as boas-vindas ou apagá-las automaticamente após um tempo.

Todos os eventos

BlocoDispara quando…Nível
quando receber /startO usuário envia /start ou clica em IniciarInic.
quando receber comando /( )Chega o comando que você nomeouInic.
quando receber uma mensagemChega qualquer texto que não seja comandoInic.
quando a mensagem contiver ( )O texto contém o trecho informadoInic.
quando o botão ( ) for pressionadoAlguém clica num botão inline com esse callbackInterm.
quando o bot iniciarO processo do bot sobe (uma vez por execução)Interm.
quando um usuário entrar no grupoUm novo membro entraInterm.
quando um usuário sair do grupoUm membro sai ou é removidoInterm.
quando alguém pedir para entrar no grupoGrupo com aprovação recebe um pedidoInterm.
quando alguém digitar @seubot …Consulta inline em qualquer conversaAvanç.
quando uma mensagem for editadaAlguém edita uma mensagem já enviadaInterm.
quando alguém responder uma enqueteVoto em enquete não anônimaInterm.
quando o status do bot no chat mudarO bot é adicionado, removido ou promovidoAvanç.
quando chegar dado de um Web AppUma Mini App envia dados ao botAvanç.
quando uma chamada de vídeo/voz começarVideochamada iniciada no grupoInterm.
quando uma chamada de vídeo/voz terminarVideochamada encerradaInterm.
quando o usuário compartilhar usuário(s)Resposta a um botão "pedir usuários"Avanç.
quando o usuário compartilhar um chatResposta a um botão "pedir chat"Avanç.
quando um pagamento estiver para ser confirmadoPré-checkout de pagamentoAvanç.
quando um pagamento for concluídoCobrança paga com sucessoAvanç.
quando alguém abrir um jogoClique em "Jogar" num Telegram GameAvanç.

Blocos de leitura que acompanham eventos

Alguns eventos trazem dados que só existem dentro deles. Usá-los fora do evento correspondente devolve valores vazios:

Bloco de valorSó faz sentido dentro deDevolve
texto da mensagem editadaquando uma mensagem for editadaTexto novo
novo status do bot no chatquando o status do bot mudarmember, administrator, left, kicked
dados recebidos do Web Appquando chegar dado de um Web AppString enviada pela Mini App
resposta da enquete ( )quando alguém responder uma enqueteOpções escolhidas / id da enquete
IDs dos usuários compartilhadosquando o usuário compartilhar usuário(s)Lista de ids
ID do chat compartilhadoquando o usuário compartilhar um chatNúmero
informação do pagamento ( )quando um pagamento for concluídoValor, moeda, payload…
nome do jogo abertoquando alguém abrir um jogoShort name do jogo
Erro silencioso

Usar um desses blocos no evento errado normalmente não gera erro — devolve vazio. O sintoma é uma mensagem com um buraco no meio: "Você votou em ." Se um valor aparece vazio sem explicação, confira se ele pertence ao evento em que está.

Mensagens 42 blocos

Tudo que o bot fala, mostra ou apaga. É a categoria mais usada e a maior do construtor.

Enviar texto

enviar mensagem ( ) Iniciante tg_send_message

Objetivo

Enviar uma mensagem de texto no chat onde o evento aconteceu.

Categoria
Mensagens
Tipo
Ação
Entradas
Texto — string. Aceita texto digitado ou qualquer bloco de valor
Saída
Nenhuma
Configurações
A formatação vem do parse_mode global, na aba Configuração
Limite
4096 caracteres

Quando usar

Praticamente sempre que o bot precisa falar. É o bloco mais usado do construtor.

Quando NÃO usar

  • Quando a mensagem deve citar a mensagem do usuário → responder mensagem.
  • Quando precisa de botões → enviar mensagem com teclado.
  • Quando o destino é outro chat, não o atual → use copiar mensagem para o chat ou envie a partir de um fluxo com o chat correto.

Variáveis e texto dinâmico

Para montar texto com valores, use o bloco juntar (categoria Operadores) encaixando pedaços:

enviar mensagem [ juntar( "Olá, ", primeiro nome, "! Você tem ", [saldo como texto], " pontos." ) ]

Erros comuns

  • Passar de 4096 caracteres — a API recusa a mensagem inteira. Se o conteúdo é variável (lista de itens, resposta de API), corte ou divida.
  • MarkdownV2 quebrando por um ponto final. Veja formatação.
  • Concatenar número sem converter. Use ( ) como texto antes de juntar.
  • Enviar em rajada. Cinco enviar mensagem seguidos chegam fora de ordem ou levam 429. Prefira uma mensagem com quebras de linha.
Boa prática

Uma mensagem bem formatada vale mais que cinco mensagens curtas. Use \n para quebras de linha e emojis como marcadores visuais — em telas de celular, isso é a diferença entre legível e ilegível.

responder mensagem ( ) Iniciante tg_reply_message

Objetivo

Enviar uma mensagem citando a mensagem que disparou o evento.

Entradas
Texto
Saída
Nenhuma

Quando usar

Em grupos, sempre que a resposta é para uma pessoa específica. A citação deixa claro a quem o bot está falando e gera notificação para o autor original.

Quando NÃO usar

Em conversas privadas — a citação é ruído visual, já que só existem duas pessoas ali. Também não use quando a mensagem original pode ter sido apagada: a API devolve erro.

Erros comuns

  • message to be replied not found quando a mensagem original já foi apagada. Envolva em tentar / se der erro em fluxos de moderação.
enviar mensagem com teclado Intermediário tg_send_message_keyboard

Objetivo

Enviar texto acompanhado de um teclado (inline ou de resposta).

Entradas
Texto (string) e Teclado — encaixe um bloco teclado inline, teclado de respostas, remover teclado ou forçar resposta
Saída
Nenhuma

Quando usar

Menus, confirmações, seleção de opções — sempre que clicar for melhor que digitar.

Quando NÃO usar

Para mensagens informativas sem interação: botões que não fazem nada confundem. E não anexe teclado a álbuns de mídia (a API não aceita).

Blocos que costumam vir antes/depois

Antes: um evento, e a montagem do teclado. Depois: os fluxos quando o botão ( ) for pressionado correspondentes a cada botão.

Atenção

Todo botão de callback que você cria precisa ter um evento correspondente. Botão sem evento = usuário clica, relógio gira, nada acontece. Ao adicionar um botão, crie o fluxo dele na mesma hora.

Editar e apagar

editar mensagem do botão para ( ) Intermediário tg_edit_message

Objetivo

Substituir o texto da mensagem que contém o botão clicado.

Entradas
Novo texto
Contexto obrigatório
Dentro de um evento de callback

Quando usar

Para navegação em menus: em vez de encher o chat com uma mensagem por clique, a mesma mensagem se transforma. É o que dá a sensação de um app dentro do Telegram.

Quando NÃO usar

Fora de eventos de callback (não há mensagem alvo), em mensagens com mais de 48 horas, e quando o histórico importa — editar apaga o conteúdo anterior para sempre.

Erros comuns

  • message is not modified: você editou para exatamente o mesmo texto. Inofensivo, mas polui o log — verifique antes de editar quando o conteúdo pode repetir.
  • Editar mensagem de mídia com este bloco: para legendas use editar legenda da mensagem do botão.
apagar mensagem recebida Intermediário tg_delete_message

Objetivo

Apagar a mensagem que disparou o evento.

Entradas
Nenhuma
Permissão
Em grupos, exige o bot administrador com "Apagar mensagens"

Quando usar

Moderação (spam, links proibidos, palavrão), limpeza de comandos em grupos, remoção de mensagens de sistema.

Quando NÃO usar

Sem antes verificar permissão. E jamais em canais de registro/auditoria — apagar é irreversível e você perde a evidência do que aconteceu.

Exemplo — antispam de link

┌── quando receber uma mensagem │ ├──── se [ conversa é em grupo? e texto da mensagem contém "http" ] │ ├── se [ não (usuário é administrador?) ] │ │ ├── tentar │ │ │ └── apagar mensagem recebida │ │ │ se der erro │ │ │ └── registrar no log [ "Sem permissão para apagar" ] │ │ ├── definir avisos = adicionar aviso ao usuário [ID do usuário] │ │ └── enviar mensagem [ juntar("Links não são permitidos. Aviso ", │ │ avisos como texto, "/3") ]
Segurança

Nunca apague mensagens de administradores automaticamente sem uma exceção explícita — um filtro mal calibrado que apaga o post do dono do grupo custa a confiança de todos.

Mídia

Sete blocos, todos com a mesma mecânica: você fornece uma URL pública, um caminho de arquivo local ou um file_id do Telegram.

BlocoFormatoAparece comoObservação
enviar foto ( )JPG, PNG, WebPImagem no chatMáx. 10 MB por URL
enviar vídeo ( )MP4Player de vídeoOutros formatos podem virar documento
enviar documento ( )QualquerArquivo para downloadUse para PDF, planilha, ZIP
enviar áudio ( )MP3, M4AFaixa de músicaMostra título e artista
enviar áudio de voz ( )OGG/OPUSMensagem de vozOnda sonora, como gravação
enviar nota de vídeo ( )MP4 quadradoVídeo redondoMáx. 1 minuto
enviar animação/GIF ( )GIF, MP4 mudoLoop automático
Dica — reaproveite o file_id

Ao enviar um arquivo, o Telegram guarda uma cópia e devolve um file_id. Enviar o mesmo arquivo mil vezes por URL significa mil downloads; enviar por file_id é instantâneo e não consome banda. Para mídia fixa (logo, catálogo, cardápio), envie uma vez a si mesmo, copie o file_id e use-o dali em diante.

Limites

Upload pelo bot: 50 MB. Download pelo bot: 20 MB. URLs precisam ser públicas e diretas — link do Google Drive ou de página HTML não funciona, porque o Telegram baixa o conteúdo bruto da URL.

Álbuns de mídia

Use item tipo ( ) URL ( ) empilhado dentro de enviar álbum de mídia. De 2 a 10 itens. Apenas a legenda do primeiro aparece. Não aceita botões.

Enquetes, localização e contatos

BlocoPara quêCuidado
enviar enquete ( )Votação simples. Ligue uma lista de textos em "opções"Enquete anônima não dispara o evento de resposta
enviar quiz ( )Enquete com resposta certa e feedbackO índice da resposta correta começa em 0
encerrar enquete ( )Fecha para novos votosPrecisa do id da mensagem da enquete — guarde numa variável ao enviar
enviar localização ( ) ( )Ponto fixo no mapaLatitude e longitude são números decimais
enviar localização ao vivoPonto que se move por até 24 hRetorna um id necessário para atualizar/parar
enviar local ( ) (venue)Estabelecimento com nome e endereçoMelhor que localização pura para lojas
enviar contato ( ) ( )Cartão de contatoTelefone em formato internacional
enviar ( ) (dado/dardo)Emoji animado com resultado aleatórioO resultado é do Telegram, não seu

Utilitários de conversa

mostrar status ( ) — "digitando…" Iniciante tg_send_chat_action

Objetivo

Exibir o indicador temporário "digitando…", "enviando foto…" etc.

Quando usar

Antes de qualquer operação demorada: consulta a API externa, processamento, geração de arquivo. O usuário vê que o bot está trabalhando em vez de achar que ele morreu.

Quando NÃO usar

Antes de respostas instantâneas — o status pisca e some, o que parece defeito.

Dica

O status dura ~5 segundos ou até a próxima mensagem. Para operações mais longas, repita o bloco periodicamente.

esperar ( ) segundos Iniciante tg_wait

Objetivo

Pausar o fluxo pelo tempo indicado.

Entradas
Número de segundos (aceita decimais)

Quando usar

Ritmar mensagens em sequência (efeito de conversa natural), espaçar envios em massa para respeitar rate limits, dar tempo para o usuário ler antes da próxima mensagem.

Quando NÃO usar

Para agendar algo no futuro. esperar 3600 segura o fluxo por uma hora — se o bot reiniciar nesse meio-tempo, tudo se perde. Para tarefas futuras use a cada ( ) ou todo dia às ( ).

Diferença crucial: delay × agendamento

Delay pausa este fluxo, agora, e morre se o bot cair. Agendamento registra uma tarefa que o bot executa depois, de forma independente do fluxo que a criou. Se a pergunta é "quero que isso aconteça amanhã", a resposta nunca é delay.

aguardar próxima mensagem do usuário Avançado tg_wait_for_reply

Objetivo

Pausar o fluxo até a pessoa enviar a próxima mensagem de texto, e continuar dali.

Entradas
Variável que receberá a resposta
Saída
A resposta fica disponível nos blocos seguintes

Quando usar

Sequências curtas de perguntas: cadastro de 2 a 4 campos, formulário simples, confirmação por texto.

Quando NÃO usar

  • Fluxos longos ou ramificados — vira uma escada ilegível. Use máquina de estados no banco.
  • Quando a conversa precisa sobreviver a um reinício do bot: a espera está na memória do processo e se perde.
  • Em grupos movimentados: qualquer mensagem daquele usuário pode ser capturada como resposta.

Exemplo — cadastro de duas perguntas

┌── quando receber comando /cadastro │ ├──── enviar mensagem [ "Qual é o seu nome?" ] ├──── aguardar próxima mensagem, então → nome │ ├──── enviar mensagem [ "E o seu e-mail?" ] ├──── aguardar próxima mensagem, então → email │ ├──── se [ não (email contém "@") ] │ ├── enviar mensagem [ "E-mail inválido. Recomece com /cadastro." ] │ └── parar por aqui │ ├──── inserir na tabela "usuarios" { id: ID do usuário, nome: nome, email: email } └──── enviar mensagem [ juntar("Pronto, ", nome, "! Cadastro concluído. ✅") ]
Boa prática

Sempre valide a resposta e sempre ofereça uma saída ("digite /cancelar"). Um usuário preso numa pergunta que não aceita a resposta dele abandona o bot.

Configuração do bot pelo fluxo

Estes blocos alteram o próprio bot e devem ficar em quando o bot iniciar:

BlocoEfeito
definir menu de comandos + /( ) — ( )Popula a lista do botão "/" no app. Empilhe um item por comando
definir nome do bot ( )Muda o nome exibido
definir descrição do bot ( )Texto da tela vazia, antes do primeiro /start
definir descrição curta do bot ( )Texto do perfil e da prévia de link
mostrar botão de comandos no menu do chatBotão ao lado do campo de texto abre os comandos
definir botão de menu do chat como Web AppBotão abre uma Mini App
Boa prática

Definir o menu de comandos pelo fluxo, e não pelo /setcommands do BotFather, mantém a configuração versionada junto com o projeto. Quem clonar o projeto recebe o menu certo automaticamente.

Demais blocos da categoria

BlocoPara quê
encaminhar mensagem recebida para o chat ( )Repassa mantendo "encaminhado de"
copiar mensagem recebida para o chat ( )Repassa sem a marca de origem — ideal para canais de denúncia anônima
fixar mensagem recebidaFixa no topo. Exige admin com permissão de fixar
desafixar mensagens do chatRemove a fixação
reagir à mensagem recebida com ( )Adiciona um emoji de reação
enviar figurinha ( )Envia sticker por URL ou file_id
confirmar clique do botão ( )Encerra o "carregando" do botão inline
editar legenda da mensagem do botãoEdita a legenda de mídia

Teclado 10 blocos

Botões transformam um bot que exige comandos decorados em um bot que qualquer pessoa usa. Esta categoria monta os dois tipos de teclado do Telegram.

Inline × Resposta: escolha certa desde o começo

Explicação simples

O teclado inline são botões grudados na mensagem, como os botões de um caixa eletrônico na própria tela. O teclado de respostas substitui o teclado do celular por botões grandes; ao tocar, ele digita aquela palavra e envia como se você tivesse escrito.

Teclado inlineTeclado de respostas
Onde aparecePreso abaixo da mensagemNo lugar do teclado do telefone
Ao clicarGera callback_query (invisível no chat)Envia o texto do botão como mensagem normal
Evento que trataquando o botão ( ) for pressionadoquando a mensagem contiver / quando receber uma mensagem
Fica no históricoSim, junto com a mensagemPersiste até você removê-lo
Polui o chatNãoSim — cada toque vira uma mensagem
Permite editar a mensagemSim (menus navegáveis)Não
Melhor paraMenus, paginação, confirmaçõesMenu principal fixo, "Cancelar" sempre à mão, pedir contato/localização
Dica

Na dúvida, use inline. Ele cobre 90% dos casos, não suja o histórico e permite transformar a mensagem em vez de empilhar novas.

Montando um teclado inline

São dois blocos que trabalham juntos: teclado inline (o container, um bloco de valor) e botão ( ) tipo ( ) valor ( ) (empilhado dentro dele).

enviar mensagem [ "Escolha uma opção:" ] com teclado inline ├── botão "💰 Preços" tipo callback valor "precos" ├── botão "📍 Endereço" tipo callback valor "endereco" ☐ mesma linha └── botão "🌐 Site" tipo URL valor "https://exemplo.com"
Rótulo
O que o usuário vê. Curto: 2 a 4 palavras. Emojis ajudam a escanear
Tipo
callback (dispara evento), URL (abre link) ou Web App (abre Mini App)
Valor
O callback_data, a URL ou o endereço da Mini App
Mesma linha
Marcado, o botão fica ao lado do anterior; desmarcado, começa uma nova linha
Limites e armadilhas
  • callback_data: 64 bytes. Acento e emoji contam mais de um byte. Estourar impede o envio da mensagem inteira.
  • Máximo de 8 botões por linha; na prática, 2 ou 3, ou o texto fica ilegível no celular.
  • Botões de URL e Web App não geram callback — não crie eventos para eles.
  • URLs precisam ser https://.
Boa prática — nomeie os callbacks com padrão

Use área:ação:id, como menu:precos, pedido:confirmar:42. Fica óbvio de onde vem cada clique e você consegue rotear com começa com em vez de criar um evento por item.

Teclado de respostas

teclado de respostas + botão ( ) empilhados. Três botões especiais merecem destaque:

BlocoO que fazEvento que trata a resposta
botão ( )Envia o próprio texto como mensagemquando a mensagem contiver
botão ( ) pedir usuário(s)Abre a lista de contatos para escolher pessoasquando o usuário compartilhar usuário(s)
botão ( ) pedir chatAbre a lista de grupos/canaisquando o usuário compartilhar um chat

Dois blocos controlam o estado do teclado de respostas:

remover teclado de respostas
Volta ao teclado normal do telefone. Encaixe-o no lugar do teclado em enviar mensagem com teclado. Sempre remova ao final de um fluxo que usou reply keyboard, senão os botões ficam para sempre.
forçar resposta
Abre o teclado do usuário já no modo "responder", com uma dica no campo. Útil para perguntas em grupo, onde a resposta precisa citar o bot.

Consultas inline (@seubot …)

Permite usar o bot em qualquer conversa digitando @seubot algo. Três peças:

  1. Evento quando alguém digitar @seubot ….
  2. Leia o texto com texto da consulta inline.
  3. Responda com responder consulta inline com ( ), contendo blocos resultado id ( ) título ( ) empilhados.
Requisito

É preciso ativar o modo inline no BotFather (/setinline) e definir um placeholder. Sem isso, digitar @seubot não abre nada e o evento nunca dispara. Cada resultado precisa de um id único; ids repetidos fazem a lista inteira ser rejeitada.

Regras de UX para menus

  • Máximo de 6 a 8 opções por tela. Mais que isso, agrupe em submenus.
  • Sempre inclua "⬅️ Voltar" em submenus. Um usuário sem saída fecha o bot.
  • Emoji no começo do rótulo — o olho encontra o botão certo muito mais rápido.
  • Confirme ações destrutivas com um menu de dois botões: "Sim, apagar" e "Cancelar".
  • Edite a mensagem em vez de enviar outra. Um menu que se transforma parece um app; cinco mensagens empilhadas parecem um bug.
Menu de confirmação — padrão recomendado ┌── quando o botão "pedido:cancelar:42" for pressionado │ ├── confirmar clique do botão [ "" ] │ └── editar mensagem do botão para [ "Cancelar o pedido #42?" ] com teclado: │ • "✅ Sim, cancelar" → callback "pedido:cancelar_ok:42" │ • "↩️ Voltar" → callback "pedido:ver:42" ☑ mesma linha

Moderação 31 blocos

Banir, silenciar, avisar, gerenciar permissões e tópicos. A categoria mais poderosa — e a que mais exige cuidado, porque quase toda ação aqui é visível para o grupo inteiro.

Antes de tudo: permissões

Praticamente todo bloco desta categoria exige que o bot seja administrador do grupo com a permissão específica marcada. Ser admin genérico não basta: se "Banir usuários" estiver desmarcado, o bloco de banir falha com 400 not enough rights. Configure em Grupo → Administradores → seu bot e marque exatamente o que o seu fluxo usa — nada além disso (princípio do menor privilégio).

Blocos de verificação (use-os antes de agir)

BlocoDevolveUso típico
usuário é administrador?Sim/NãoGuarda no topo de comandos administrativos
autor é um bot?Sim/NãoEvitar dar boas-vindas ou punir outros bots
quantidade de membros do grupoNúmeroEstatísticas, regras por tamanho de grupo
ID do usuário respondidoNúmeroAplicar ação em quem foi citado com "responder"
link de convite do grupoTextoCompartilhar convite. Exige admin
quantidade de boosts do usuário ( )NúmeroBenefícios para quem impulsiona o canal
número de avisos do usuário ( )NúmeroConsultar o histórico antes de punir
usuário é administrador? Intermediário tg_is_admin

Objetivo

Devolve verdadeiro se quem enviou a mensagem é admin ou dono do grupo.

Tipo
Valor (booleano)
Entradas
Nenhuma — usa o autor da mensagem atual

Quando usar

Sempre antes de qualquer comando administrativo. É a sua trava de segurança.

Quando NÃO usar

Em conversas privadas — não existe "admin" numa DM, e o resultado não é significativo. Também não use como única proteção para operações críticas de negócio (como aprovar pagamento): admin do grupo é qualquer pessoa que o dono promoveu, não necessariamente alguém de confiança do seu sistema.

Padrão de uso — guarda no topo

┌── quando receber comando /banir │ ├──── se [ não (usuário é administrador?) ] │ ├── responder mensagem [ "❌ Apenas administradores." ] │ └── parar por aqui │ ├──── definir alvo = ID do usuário respondido ├──── se [ alvo = nenhum ] │ ├── responder mensagem [ "Responda à mensagem de quem você quer banir." ] │ └── parar por aqui │ ├──── tentar │ └── banir usuário [ alvo ] │ se der erro │ └── responder mensagem [ "Não consegui banir. Sou admin com permissão?" ] └──── responder mensagem [ "Usuário banido. 🔨" ]
Boa prática

Para uma lista de "super administradores" do seu bot (independente do grupo), guarde os user.id numa tabela do banco e verifique contra ela. Assim o poder não depende de quem o dono do grupo promoveu.

Punições: qual usar

BlocoEfeitoPode voltar?Quando usar
expulsar usuário ( )Remove do grupo✅ Sim, na horaChamar atenção sem punir de verdade
banir usuário ( )Remove e bloqueia a entrada❌ Só com desbanimentoSpam, abuso, reincidência
silenciar usuário ( ) por ( ) minImpede de escrever, continua no grupo✅ Automático ao fim do prazoPunição proporcional — a mais usada
remover silêncio do usuário ( )Devolve a permissão de escreverPerdão antecipado
desbanir usuário ( )Remove o banimentoRevisão de decisão
Dica — escada de punição

Bots de moderação bons não banem de primeira. Use adicionar aviso, que devolve o total acumulado, e escale: 1º aviso avisa, 2º silencia 60 minutos, 3º bane. Isso é justo, transparente e reduz drasticamente reclamações.

┌── quando receber uma mensagem │ ├──── se [ mensagem contém palavra proibida e não (usuário é administrador?) ] │ ├── apagar mensagem recebida │ ├── definir n = adicionar aviso ao usuário [ ID do usuário ] │ │ │ ├── se [ n = 1 ] → responder [ "⚠️ Aviso 1 de 3." ] │ ├── se [ n = 2 ] → silenciar usuário [ID do usuário] por 60 minutos │ │ → responder [ "🔇 Aviso 2. Silenciado por 1h." ] │ └── se [ n ≥ 3 ] → banir usuário [ ID do usuário ] │ → responder [ "🔨 Banido após 3 avisos." ]
Atenção — silenciar com 0 minutos

No bloco silenciar usuário, 0 minutos significa para sempre, não "sem silêncio". É um engano fácil de cometer e difícil de perceber.

Sistema de avisos

adicionar aviso ao usuário ( )
Registra mais um aviso e devolve o total. Como é um bloco de valor, guarde o retorno numa variável para decidir a punição.
número de avisos do usuário ( )
Consulta sem incrementar.
zerar avisos do usuário ( )
Limpa o histórico. Ofereça um comando /perdoar aos admins.
Onde ficam os avisos

Salvos em disco pelo bot, o que significa que sobrevivem a reinícios — mas ficam no servidor onde o bot roda. Trocar de máquina sem copiar os arquivos de dados zera o histórico. Se os avisos são importantes, inclua-os na sua rotina de backup.

Permissões do grupo

definir permissões do grupo recebe blocos permissão ( ) ( ) empilhados dentro, cada um ligando ou desligando uma capacidade dos membros comuns (admins não são afetados).

Modo silêncio geral — só admins falam ┌── quando receber comando /trancar │ ├── se [ não (usuário é administrador?) ] → parar por aqui │ ├── definir permissões do grupo: │ │ • enviar mensagens .... ❌ │ │ • enviar mídia ........ ❌ │ │ • fixar mensagens ..... ❌ │ └── enviar mensagem [ "🔒 Grupo trancado. Só administradores podem falar." ]
Atenção

definir permissões substitui o conjunto inteiro: as permissões que você não listar voltam ao padrão. Ao "destrancar", liste explicitamente tudo que deve ficar ligado — não confie no que estava antes.

Gerenciamento do grupo

BlocoEfeitoPermissão exigida
definir título do grupo ( )Muda o nomeAlterar informações
definir descrição do grupo ( )Muda a descriçãoAlterar informações
definir foto do grupo (URL) ( )Baixa a imagem e aplicaAlterar informações
promover usuário ( ) a administradorPromove com permissões padrãoAdicionar admins
rebaixar administrador ( )Remove todas as permissõesAdicionar admins
aprovar pedido de entradaAceita quem pediu para entrarConvidar usuários
recusar pedido de entradaNega o pedidoConvidar usuários
sair do grupoO bot sai
Segurança — promoção automática

Nunca promova alguém a administrador com base em algo que o próprio usuário controla (o texto que ele mandou, um parâmetro de deep link, o username dele). Promoção deve exigir a ação explícita de um admin já existente, verificada com usuário é administrador?.

Tópicos (fóruns)

Supergrupos com "Tópicos" ativado dividem a conversa em canais internos. Seis blocos cobrem o ciclo completo:

BlocoEfeito
criar tópico chamado ( ) cor ( )Cria e devolve o id — guarde numa variável
ID do tópico atualEm qual tópico a mensagem chegou
renomear tópico ( ) para ( )Muda o nome
fechar tópico ( )Ninguém mais escreve nele
reabrir tópico ( )Volta a aceitar mensagens
apagar tópico ( )Apaga o tópico e todas as mensagens. Irreversível
Dica — tickets com tópicos

Um tópico por chamado é um sistema de suporte pronto: criar tópico ao abrir o ticket, guarde o id junto do registro no banco, fechar tópico ao resolver. O histórico fica organizado e visível para toda a equipe.

Checklist de um bot de moderação

  • Todo comando administrativo começa com a guarda se não (usuário é administrador?) → parar por aqui.
  • Admins e o próprio bot estão isentos dos filtros automáticos.
  • Toda ação de moderação está dentro de tentar / se der erro.
  • Punições escalam por avisos, não vão direto ao banimento.
  • Existe um comando para desfazer cada punição (/desbanir, /perdoar).
  • Ações relevantes são registradas com registrar no log ou enviadas a um canal de auditoria.
  • O bot tem apenas as permissões que realmente usa.

Usuário 16 blocos

Blocos de leitura pura: quem falou, de onde, o quê e quando. Todos são blocos de valor — encaixam dentro de outros blocos e nunca sozinhos.

Referência completa

BlocoDevolveSempre existe?Observação
ID do usuárioNúmero✅ SimA forma correta de identificar alguém. Permanente
@ username do usuárioTexto❌ NãoOpcional e mutável. Nunca use como chave
primeiro nomeTexto✅ SimIdeal para saudações
sobrenomeTexto❌ NãoMuita gente não preenche
nome completoTexto✅ SimNome + sobrenome quando houver
ID do chatNúmero✅ SimEm DM é igual ao ID do usuário; em grupo é negativo
texto da mensagemTexto❌ NãoVazio em foto sem legenda, sticker, áudio
ID da mensagemNúmero✅ SimNecessário para editar, apagar e fixar
argumentos do comandoLista de textos✅ (pode ser vazia)/soma 2 3["2","3"]
idioma do usuárioTexto❌ Nãopt-br, en… Para bots multilíngues
tipo do chatTexto✅ Simprivate, group, supergroup, channel
conversa é privada (DM)?Sim/Não✅ SimMais legível que comparar o tipo
conversa é em grupo?Sim/Não✅ SimCobre grupo e supergrupo
texto da mensagem respondidaTexto❌ NãoSó quando o usuário usou "responder"
texto da consulta inlineTexto❌ NãoSó dentro do evento de consulta inline
foto de perfil do usuário ( )file_id❌ NãoVazio se a pessoa não tem foto ou restringiu
agora (( ))Data/hora ou número✅ SimHorário do servidor, não do usuário

Identificando pessoas do jeito certo

❌ Frágil
se [ @username do usuário = "joao_admin" ]
   → dar acesso de administrador

# username é opcional, pode mudar,
# e pode ser reciclado por outra pessoa
✅ Correto
definir admins = buscar todas as linhas
                 da tabela "admins"

se [ admins contém (ID do usuário) ]
   → dar acesso de administrador
Segurança

Basear permissão em username é uma falha de autenticação real: se o "joao_admin" liberar o apelido, qualquer pessoa pode registrá-lo e virar admin do seu bot. Sempre user.id.

Lidando com valores ausentes

Metade dos blocos desta categoria pode devolver vazio. Uma mensagem como "Olá, !" ou "Seu contato: @" é sempre o mesmo bug: um campo opcional que não existia.

┌── quando receber /perfil │ ├──── definir apelido = @ username do usuário │ ├──── se [ apelido está vazio ] │ └── definir apelido = "(sem @username)" │ └──── enviar mensagem [ juntar("👤 ", nome completo, "\n", "🆔 ", ID do usuário como texto, "\n", "🔗 ", apelido) ]
Boa prática

Todo campo opcional recebe um valor padrão antes de ser usado. É uma linha a mais no fluxo e elimina uma classe inteira de bugs.

O bloco "agora"

Devolve a data/hora do servidor onde o bot roda, em vários formatos selecionáveis (data completa, hora como número, dia da semana…).

Fuso horário

Se o bot roda num servidor em UTC e seus usuários estão no Brasil, "agora" está 3 horas adiantado em relação a eles. Isso quebra silenciosamente qualquer regra de horário comercial ou envio agendado. Ajuste o fuso do servidor, ou some/subtraia a diferença explicitamente no fluxo.

┌── quando receber comando /aberto │ ├──── definir h = agora (hora — número) │ ├──── se [ h ≥ 9 e h < 18 ] │ └── enviar mensagem [ "🟢 Estamos abertos!" ] └──── senão └── enviar mensagem [ "🔴 Fechados. Abrimos às 9h." ]

Controle e Operadores

Decidir, repetir, comparar, calcular e manipular texto. Se Eventos é quando e Mensagens é o quê, esta é a categoria do como.

Lógica booleana em cinco minutos

Explicação simples

Booleano é uma resposta de sim ou não. "Está chovendo?" só aceita sim ou não — não aceita "mais ou menos". O computador chama sim de verdadeiro e não de falso, e toda decisão que ele toma se resume a isso.

Três operadores combinam respostas de sim/não:

OperadorVerdadeiro quando…AnalogiaExemplo
e (AND)As duas partes são verdadeiras"Só saio se parar de chover e eu terminar o trabalho"é grupo? e é admin?
ou (OR)Pelo menos uma é verdadeira"Aceito café ou chá"contém "oi" ou contém "olá"
não (NOT)Inverte o valor"Não está chovendo"não (é admin?)

Tabela-verdade completa:

ABA e BA ou Bnão A
VV✅ V✅ V❌ F
VF❌ F✅ V❌ F
FV❌ F✅ V✅ V
FF❌ F❌ F✅ V
Erro clássico

"Quero atender de segunda a sexta" vira dia ≥ 1 e dia ≤ 5, não ou. Com ou, todo dia satisfaz pelo menos uma das partes e a condição é sempre verdadeira. Sintoma: "minha condição nunca filtra nada".

O bloco "se"

se … faça / senão Iniciante controls_if

Objetivo

Executar um conjunto de ações apenas quando a condição é verdadeira.

Entradas
Uma condição booleana + as ações do corpo
Expansível
Pela engrenagem ⚙️ do bloco você adiciona senão se e senão

Quando usar

Sempre que o bot precisa se comportar de formas diferentes conforme a situação.

Quando NÃO usar

Quando o encadeamento passa de 3 ou 4 níveis. Nesse ponto, prefira guardas com parar por aqui no topo, ou divida em fluxos separados.

As três formas

1. Simples 2. Com senão 3. Cadeia se [ cond ] se [ cond ] se [ a ] → faz X → faz X → X senão senão se [ b ] → faz Y → Y senão → Z
Dica — não aninhe, retorne cedo

Em vez de "se A então se B então se C então age", escreva três guardas seguidas que barram e param. O fluxo fica plano, legível e cada regra fica isolada.

Comparações

BlocoVerdadeiro quandoCuidado
( ) = ( )Os valores são iguais"10"10 — converta antes
( ) ≠ ( )São diferentesIdem
( ) > ( ) / < / / Comparação numéricaCom texto, compara alfabeticamente
( ) contém ( )O texto inclui o trechoCasa no meio das palavras
( ) está vazioTexto ou lista sem conteúdoA forma segura de testar campos opcionais
( ) corresponde ao padrão ( )Regex encontra algoPoderoso e fácil de errar
Comparação de texto é sensível a maiúsculas

"Sim" = "sim" é falso. Converta os dois lados para minúsculas antes de comparar entrada de usuário.

Repetição

BlocoRepeteUse para
repetir ( ) vezesUm número fixoQuando você sabe a quantidade
repetir enquanto / atéEnquanto a condição valerQuando o fim depende de algo variável
para cada item ( ) na lista ( )Uma vez por itemO mais usado: percorrer resultados do banco ou de uma API
sair / continuarInterrompe ou pula uma voltaSair ao achar o que procurava
Perigo — laço infinito

Um repetir enquanto cuja condição nunca fica falsa trava o bot inteiro: ele para de responder a todo mundo, não só a você. Garanta que algo dentro do laço muda a condição. Na dúvida, prefira repetir N vezes com um teto de segurança.

Laço + envio de mensagem

Enviar dentro de um laço é a receita para o erro 429. Para listas grandes, monte um texto acumulado dentro do laço e envie uma única mensagem no fim; ou insira esperar 0.05 segundos a cada volta.

Padrão recomendado — acumular e enviar uma vez ├── definir lista = buscar todas as linhas da tabela "produtos" ├── definir texto = "📋 Produtos:\n" │ ├── para cada item em lista │ └── definir texto = juntar(texto, "• ", obter "nome" de item, "\n") │ └── enviar mensagem [ texto ] ← uma única chamada à API

Tratamento de erros

tentar … / se der erro … Intermediário tg_try_except

Objetivo

Executar ações que podem falhar sem derrubar o fluxo inteiro.

Entradas
Dois corpos: as ações a tentar e as ações de recuperação

Quando usar

  • Toda chamada HTTP a serviço externo.
  • Toda ação de moderação (permissão pode faltar).
  • Envio em massa (usuários que bloquearam o bot).
  • Leitura de arquivo que pode não existir.
  • Conversão de texto para número quando a origem é o usuário.

Quando NÃO usar

Para esconder bugs. Se o erro é do seu fluxo (variável errada, lógica furada), capturar e ignorar apenas transforma um erro visível em um comportamento estranho e inexplicável.

Boa prática

No ramo "se der erro", faça duas coisas: registre com registrar no log (para você) e avise o usuário com uma mensagem clara (para ele). Um bloco de erro vazio é pior que nenhum tratamento — o problema some sem deixar rastro.

├── tentar │ ├── definir dados = baixar JSON da URL "https://api.exemplo.com/clima" │ └── enviar mensagem [ juntar("Agora: ", obter "temp" de dados, "°C") ] │ └── se der erro ├── registrar no log [ "Falha na API de clima" ] └── enviar mensagem [ "Não consegui consultar o clima agora. Tente em instantes." ]

Demais blocos de Controle

parar por aqui
Encerra o fluxo imediatamente. A ferramenta das guardas.
registrar no log ( )
Escreve no terminal onde o bot roda. Invisível ao usuário e essencial para depurar.
esperar ( ) segundos
Pausa o fluxo — veja Mensagens.

Operadores de texto

BlocoFazExemplo
" " (texto)Um texto literal"Olá"
juntarConcatena vários pedaçosjuntar("Oi, ", nome)
tamanho de ( )Número de caracteres5
( ) para MAIÚSCULAS / minúsculasMuda a caixaNormalizar antes de comparar
( ) está vazioTesta se não há conteúdoCampos opcionais
encontrar ( ) em ( )Posição da ocorrência0 se não achar
pedaço de ( ) de ( ) a ( )Recorta um trechoTruncar textos longos
( ) contém ( )Sim/NãoFiltros simples
substituir ( ) por ( ) em ( )Troca todas as ocorrênciasLimpeza de texto
( ) como númeroTexto → númeroEssencial antes de contas
( ) como textoQualquer coisa → textoAntes de juntar
formatar ( ) com ( ) casasArredonda para exibição3.14159"3,14"
( ) corresponde ao padrão ( )Regex — testaValidar e-mail, CPF, placa
extrair padrão ( ) de ( )Regex — capturaPegar o número de um texto
Dica — regex sem sofrer

Expressões regulares são poderosas e fáceis de errar. Padrões úteis prontos:

  • E-mail (aproximado): [^@\s]+@[^@\s]+\.[a-z]{2,}
  • Só dígitos: ^[0-9]+$
  • Palavra inteira: \bpalavra\b
  • Link: https?://\S+

Teste sempre com casos reais antes de colocar em produção — inclusive com o caso que não deveria casar.

Operadores matemáticos

BlocoFaz
( ) + − × ÷ ^ ( )Aritmética básica
resto de ( ) ÷ ( )Módulo. Útil para "a cada N", par/ímpar
número aleatório de ( ) a ( )Sorteio inteiro
raiz / abs / ln / sin…Funções matemáticas
arredondar ( )Para cima, para baixo ou o mais próximo
limitar ( ) entre ( ) e ( )Prende o valor num intervalo
item aleatório de ( )Sorteia um item de uma lista
Divisão por zero

Dividir por zero derruba o fluxo. Se o divisor vem de uma variável (contagem, resultado de banco), teste se é maior que zero antes.

Variáveis

Variáveis são a memória de curto prazo do bot. Entender o que elas guardam — e por quanto tempo — evita a confusão número 1 de quem está começando.

O que é, de verdade

Explicação simples

Uma variável é uma caixinha com etiqueta. Você escreve o nome na etiqueta ("saldo") e guarda um valor dentro (150). Depois pede a caixinha "saldo" e recebe 150. Guardar outra coisa substitui o que estava lá: a caixinha só cabe uma coisa por vez.

Na categoria Variáveis do toolbox você encontra:

Criar variável…
Cria um nome novo. Depois de criado, ele aparece como bloco de valor.
definir ( ) para ( )
Ação. Guarda um valor na variável.
alterar ( ) em ( )
Ação. Soma um número ao valor atual (atalho para contadores).
( ) (o nome da variável)
Valor. Lê o conteúdo atual.
├── definir contador para [ 0 ] ├── alterar contador em [ 1 ] ← agora vale 1 ├── alterar contador em [ 1 ] ← agora vale 2 └── enviar mensagem [ juntar("Total: ", contador como texto) ] → "Total: 2"

Escopo: a parte que confunde todo mundo

Leia isto antes de continuar

Uma variável criada no Dromornis vive apenas dentro do fluxo que a definiu, durante aquela execução. Quando o fluxo termina, ela desaparece. Ela não é compartilhada entre fluxos, não sobrevive à próxima mensagem e não sobrevive a um reinício do bot.

❌ Não funciona
quando receber /nome
  ├─ aguardar resposta → nome
  └─ (fim do fluxo)

quando receber /oi
  └─ enviar "Olá, " + nome
     ← nome está VAZIO aqui
✅ Funciona
quando receber /nome
  ├─ aguardar resposta → nome
  └─ inserir na tabela "usuarios"
       { id: ID do usuário, nome: nome }

quando receber /oi
  ├─ u = buscar 1 linha "usuarios"
         onde "id" = ID do usuário
  └─ enviar "Olá, " + obter "nome" de u

Os quatro níveis de memória

NívelComo fazerDuraUse para
Temporária / de fluxoBlocos de VariáveisUma execução do fluxoCálculos intermediários, resultados de API, contadores locais
Global do botsalvar dado / carregar dadoPara sempre (arquivo em disco)Configurações, contadores globais, estado geral
Por usuárioTabela no banco com id do usuárioPara semprePerfil, saldo, etapa da conversa, preferências
Do ambientevariável de ambiente ( )Definida fora do botSegredos: chaves de API, tokens
Como escolher

Pergunte: "o bot precisa lembrar disso depois que essa mensagem terminar?" Não → variável comum. Sim, e é do usuário → banco. Sim, e é do bot todo → salvar dado. É um segredo → variável de ambiente.

Tipos de dado nas variáveis

Uma variável não tem tipo fixo — ela guarda o que você colocar. O tipo vem do valor:

TipoExemplo de origemComo testar
Stringtexto da mensagem, juntar( ) está vazio
NumberID do usuário, ( ) como número, contasComparações >, <
Booleané administrador?, comparaçõesDireto no se
Arrayargumentos do comando, buscar todas as linhastamanho de ( ), está vazia
Objectbuscar 1 linha, baixar JSONobter chave ( ) do dicionário ( )
Null / vazioCampo opcional, busca sem resultado( ) está vazio antes de usar
A armadilha do texto que parece número

argumentos do comando, texto da mensagem e valores de API vêm como texto. "5" + "3" pode virar "53" em vez de 8, e "10" > "9" é falso (comparação alfabética: "1" vem antes de "9"). Converta com ( ) como número antes de qualquer conta ou comparação numérica.

Nomes de variáveis

❌ Ruim
x
temp
dados
aux2
teste
a
✅ Bom
saldo_usuario
resposta_api_clima
total_avisos
linha_pedido
etapa_cadastro
lista_produtos
Boa prática

Use minusculas_com_underline, em português, descrevendo o conteúdo, não o tipo. Prefixos ajudam a agrupar: tmp_ para intermediários, cfg_ para configuração. Nomes bons são documentação gratuita — em três meses, x não significa nada para você.

Valores do Telegram

Você não "cria" variáveis para os dados do Telegram — eles já existem como blocos prontos na categoria Usuário. Equivalência com a notação de template que você talvez conheça de outras ferramentas:

Notação comumBloco no Dromornis
{{user.id}}ID do usuário
{{user.first_name}}primeiro nome
{{user.username}}@ username do usuário
{{message.text}}texto da mensagem
{{chat.id}}ID do chat
{{callback.data}}dado do callback
{{variables.nome}}O bloco da própria variável nome
Por que blocos em vez de {{ }}

Texto com {{ }} quebra silenciosamente quando você erra o nome do campo — vira literal na mensagem. Com blocos, um campo inexistente simplesmente não existe no toolbox. É menos flexível e muito mais seguro.

Segurança e variáveis

Nunca guarde segredos em variáveis do fluxo

Chave de API, token, senha de banco: nada disso vai num bloco de texto. Qualquer pessoa que abrir o projeto .json ou o bot.py exportado vê o valor. Use variável de ambiente ( ) — ele lê o valor em tempo de execução, sem que o segredo apareça em lugar nenhum do projeto.

❌ Segredo no fluxo
definir chave para
  [ "sk-live-a1b2c3d4e5f6" ]

baixar JSON da URL
  juntar("https://api.x.com?key=", chave)
✅ Segredo no ambiente
definir chave para
  [ variável de ambiente "API_KEY"
    padrão "" ]

se [ chave está vazia ] → parar por aqui

No servidor, defina API_KEY=... no arquivo .env (que nunca vai para o git) ou nas variáveis do serviço de hospedagem.

Listas e Dicionário 11 blocos

As duas formas de guardar vários valores. Escolher a certa deixa o fluxo simples; escolher a errada gera laços desnecessários.

Lista ou dicionário?

Explicação simples

A lista é uma fila de pessoas: existe a primeira, a segunda, a terceira. Você pega alguém pela posição. O dicionário é uma agenda de telefones: você não procura "o terceiro contato", procura pelo nome e recebe o número.

ListaDicionário
AcessoPor posição (1, 2, 3…)Por chave ("nome", "preco")
OrdemImportaNão importa
Exemplo["pão","leite","café"]{"nome":"Ana","idade":30}
Vem deargumentos do comando, buscar todas as linhas, listar arquivosbuscar 1 linha, baixar JSON
Use quandoSão vários itens do mesmo tipoÉ um item com vários campos

Combinando os dois: buscar todas as linhas devolve uma lista de dicionários — vários registros, cada um com seus campos. É a estrutura mais comum ao trabalhar com banco de dados.

lista de dicionários (resultado típico de uma busca no banco) [ { "id": 1, "nome": "Ana", "saldo": 120 }, { "id": 2, "nome": "Bruno","saldo": 80 } ] ├── para cada linha em lista │ └── enviar mensagem [ juntar(obter "nome" de linha, ": ", │ obter "saldo" de linha como texto) ]

Blocos de lista

BlocoFazObservação
criar lista comMonta uma lista literalExpanda pela engrenagem ⚙️
tamanho de ( )Quantos itensSempre teste antes de acessar por posição
( ) está vaziaSim/NãoGuarda contra listas sem resultado
item ( ) de ( )Pega por posiçãoComeça em 1, não em 0
definir item ( ) de ( ) como ( )Substitui um item
encontrar ( ) em ( )Posição do item0 se não encontrar
item aleatório de ( )Sorteia um itemFrases do dia, sorteios
Índice fora do intervalo

Pedir item 3 de uma lista com 2 itens derruba o fluxo. Antes de acessar por posição, verifique tamanho de ( ). Isso é especialmente crítico com argumentos do comando, porque o usuário decide quantos argumentos manda.

Blocos de dicionário

BlocoFaz
dicionário + chave ( ) valor ( )Monta um dicionário. Empilhe um par por campo
obter chave ( ) do dicionário ( )Lê um campo. Devolve vazio se a chave não existir
no dicionário ( ) definir chave ( ) como ( )Cria ou atualiza um campo. O 1º campo deve ser uma variável
├── definir pedido = dicionário │ • chave "usuario" valor [ ID do usuário ] │ • chave "item" valor [ "Bolo de cenoura" ] │ • chave "status" valor [ "aberto" ] │ ├── inserir na tabela "pedidos" dados [ pedido ] └── enviar mensagem [ juntar("Pedido de ", obter "item" de pedido, " registrado ✅") ]
Chave inexistente devolve vazio, não erro

Escrever obter "nomee" (com o erro de digitação) não gera erro — devolve vazio, e a mensagem sai com um buraco. Ao consumir JSON de uma API, confira o nome exato dos campos: eles distinguem maiúsculas.

Dica — JSON aninhado

Respostas de API costumam ter dicionários dentro de dicionários. Vá um nível por vez, guardando cada etapa numa variável — fica legível e você consegue inspecionar onde quebrou:

├── definir resp = baixar JSON da URL "https://api.exemplo.com/clima" ├── definir atual = obter "current" de resp └── definir temp = obter "temp_c" de atual

Banco de Dados 12 blocos

A memória permanente do bot. Se você precisa que algo sobreviva ao fim do fluxo e ao reinício do bot, é aqui.

O que é um banco de dados

Explicação simples

Um banco de dados é um caderno de tabelas. Cada tabela é uma folha com um título ("usuarios"), colunas no topo ("id", "nome", "saldo") e uma linha por registro. O bot escreve e lê nesse caderno, e o caderno continua ali mesmo se o bot for desligado.

Tecnicamente: o Dromornis gera código que usa SQLite — um banco relacional que vive num único arquivo ao lado do bot.py. Não exige servidor, instalação nem senha. É a escolha certa para a grande maioria dos bots.

Onde ficam os dados

Num arquivo .db na pasta onde o bot roda. Copiar só o bot.py para outro servidor não leva os dados. Inclua esse arquivo no seu backup — e trate-o como dado sensível, já que contém informações dos seus usuários.

Quando usar banco (e quando não)

SituaçãoFerramenta certa
Guardar resultado intermediário do fluxoVariável comum
Um valor global simples (contador, flag)salvar dado / carregar dado
Dados por usuário, com busca e filtroBanco de dados
Vários registros relacionados (pedidos, tickets)Banco de dados
Exportar planilha para humanos leremCSV
Segredo (chave de API)Variável de ambiente

Criando tabelas

criar tabela ( ) + coluna ( ) tipo ( ) Intermediário tg_db_create_table

Objetivo

Criar a tabela se ela ainda não existir. Não apaga dados de uma tabela já criada.

Entradas
Nome da tabela + blocos coluna empilhados dentro
Onde colocar
Dentro de quando o bot iniciar
Tipos de coluna
inteiro, texto, decimal — e "inteiro (chave)" para a chave primária

Regras

  • Toda tabela precisa de uma coluna inteiro (chave). Ela identifica cada linha de forma única.
  • Nomes de tabela e coluna: minúsculas, sem espaço, sem acento. Use _.
  • O bloco usa CREATE TABLE IF NOT EXISTS: rodar toda vez que o bot sobe é seguro.
Atenção — mudar a estrutura depois

Se a tabela já existe, adicionar uma coluna nova ao bloco não altera a tabela existente — o IF NOT EXISTS simplesmente não faz nada. Você precisa usar executar SQL com ALTER TABLE ... ADD COLUMN ..., ou apagar o arquivo .db (perdendo todos os dados). Planeje as colunas antes de colocar o bot no ar.

Exemplo — modelagem de um bot de pedidos

┌── quando o bot iniciar │ ├──── criar tabela "usuarios" │ • coluna "id" tipo inteiro (chave) ← o user.id do Telegram │ • coluna "nome" tipo texto │ • coluna "telefone" tipo texto │ • coluna "criado" tipo texto │ └──── criar tabela "pedidos" • coluna "id" tipo inteiro (chave) • coluna "usuario" tipo inteiro ← aponta para usuarios.id • coluna "item" tipo texto • coluna "valor" tipo decimal • coluna "status" tipo texto
Boa prática — modele antes

Escreva as tabelas no papel antes de montar. Uma tabela por "coisa" do seu domínio (usuário, pedido, ticket). Relacione com um campo que guarda o id da outra tabela. Mudar a estrutura depois que há dados reais é sempre mais caro do que pensar 15 minutos agora.

Operações

BlocoTipoFazDevolve
inserir na tabela ( ) dados ( )AçãoCria uma linha a partir de um dicionário
buscar todas as linhas da tabela ( )ValorLê a tabela inteiraLista de dicionários
buscar linhas ( ) onde ( ) = ( )ValorFiltra por igualdadeLista de dicionários
buscar 1 linha ( ) onde ( ) = ( )ValorPrimeira ocorrênciaDicionário ou vazio
atualizar ( ) definir ( ) = ( ) onde ( ) = ( )AçãoAltera linhas que casam
apagar da tabela ( ) onde ( ) = ( )AçãoRemove linhas
contar linhas da tabela ( )ValorTotal de registrosNúmero
executar SQL ( )AçãoSQL bruto de escrita
consultar SQL ( )ValorSELECT brutoLista de dicionários

O padrão que você vai repetir sempre

"Cadastra se ainda não existe" — a base de quase todo bot ┌── quando receber /start │ ├──── definir u = buscar 1 linha da tabela "usuarios" │ onde "id" = [ ID do usuário ] │ ├──── se [ u está vazio ] ← nunca esqueça este teste │ ├── inserir na tabela "usuarios" dados │ │ { id: ID do usuário, nome: primeiro nome, criado: agora } │ └── enviar mensagem [ "Cadastro criado! 🎉" ] │ └──── senão └── enviar mensagem [ juntar("Bem-vindo de volta, ", obter "nome" de u, "!") ]
O erro mais comum com banco

buscar 1 linha devolve vazio quando não encontra nada. Fazer obter "nome" de um resultado vazio quebra o fluxo. Sempre teste está vazio antes de ler os campos.

Filtrar, ordenar, paginar

Os blocos visuais filtram por igualdade simples. Para o resto, use consultar SQL:

sqlordenar e limitar
SELECT * FROM pedidos ORDER BY valor DESC LIMIT 10
sqlpaginação — página 3, 10 por página
SELECT * FROM pedidos ORDER BY id LIMIT 10 OFFSET 20
sqlagregação
SELECT status, COUNT(*) AS total, SUM(valor) AS soma
FROM pedidos GROUP BY status
Segurança — injeção de SQL

Nunca monte SQL concatenando texto que veio do usuário. Se alguém enviar '; DROP TABLE usuarios; -- como nome, você perde o banco.

❌ Vulnerável
consultar SQL [ juntar(
  "SELECT * FROM usuarios WHERE nome = '",
  texto da mensagem, "'") ]
✅ Seguro
buscar linhas da tabela "usuarios"
  onde "nome" = [ texto da mensagem ]

// os blocos visuais usam parâmetros,
// não concatenação

Se você precisa de SQL bruto com valor do usuário, valide antes com uma regra estrita (só dígitos, só letras, tamanho máximo) e rejeite tudo que não passar.

Desempenho e índices

SQLite é rápido, mas buscar todas as linhas lê a tabela inteira para a memória. Com 100 registros é instantâneo; com 500 mil, o bot congela para todo mundo.

  • Prefira buscar linhas onde a buscar tudo e filtrar no fluxo.
  • Para listas grandes, pagine com LIMIT e OFFSET.
  • Colunas usadas em filtros frequentes merecem um índice.
sqlcriar índice (dentro de "executar SQL")
CREATE INDEX IF NOT EXISTS idx_pedidos_usuario ON pedidos(usuario)
Boa prática

Coloque a criação de índices junto com a criação de tabelas, em quando o bot iniciar. O IF NOT EXISTS torna a operação repetível sem custo.

Validação antes de gravar

O banco aceita o que você mandar. Validar é responsabilidade do fluxo:

├── definir valor = [ item 1 de argumentos do comando ] como número │ ├── se [ valor está vazio ou valor ≤ 0 ] │ ├── enviar mensagem [ "Informe um valor válido. Ex.: /depositar 50" ] │ └── parar por aqui │ ├── se [ valor > 10000 ] │ ├── enviar mensagem [ "Valor máximo por operação: 10.000." ] │ └── parar por aqui │ └── inserir na tabela "movimentos" { usuario: ID do usuário, valor: valor }
Segurança e privacidade
  • Guarde o mínimo necessário. Dado que você não tem não vaza.
  • Nunca armazene senha em texto puro — na verdade, evite pedir senhas por bot.
  • O arquivo .db é dado pessoal: proteja o acesso ao servidor e aos backups.
  • Ofereça um comando para o usuário apagar os próprios dados.

Avançado 17 blocos

HTTP e APIs externas, JSON, agendamento de tarefas, armazenamento simples e a saída de emergência: código Python direto.

HTTP: falando com o resto do mundo

Explicação simples

Uma API é um garçom de restaurante. Você faz um pedido em um formato que ele entende ("me traga o clima de São Paulo") e ele volta com a resposta em uma bandeja padronizada. Seu bot é o cliente; o outro site é a cozinha.

Quatro blocos, do mais simples ao mais completo:

BlocoMétodoDevolveUse quando
baixar texto da URL ( )GETTexto puroA resposta não é JSON
baixar JSON da URL ( )GETDicionário/listaO caso mais comum
enviar POST para ( ) dados (JSON) ( ) cabeçalhos ( )POSTResposta convertidaCriar/enviar dados
requisição ( ) para ( )QualquerDicionário com status e corpoPUT, PATCH, DELETE, ou quando você precisa ver o status

Os verbos HTTP

VerboSignificaAnalogiaMuda dados?
GETBuscar informaçãoLer o cardápioNão
POSTCriar algo novoFazer um pedidoSim
PUTSubstituir por completoTrocar o pedido inteiroSim
PATCHAlterar um pedaço"Tira a cebola"Sim
DELETERemoverCancelar o pedidoSim

Códigos de status

FaixaSignificaExemplosO que fazer
2xxDeu certo200 OK, 201 CriadoSeguir o fluxo
3xxRedirecionamento301, 302Geralmente automático
4xxErro seu400, 401, 403, 404, 429Corrija a chamada; não adianta repetir
5xxErro do servidor500, 502, 503Tente de novo depois

Os quatro que você mais vai encontrar: 401 (chave de API errada ou ausente), 403 (chave válida sem permissão), 404 (URL errada) e 429 (excedeu o limite de chamadas).

Autenticação

A maioria das APIs exige uma chave. Dois blocos trabalham juntos e mantêm o segredo fora do projeto:

variável de ambiente ( ) padrão ( )
Lê um valor do ambiente — é onde a chave deve morar.
cabeçalho ( ) da variável de ambiente ( )
Monta o header Authorization a partir de uma variável de ambiente, no esquema escolhido (Bearer, API key…).
┌── quando receber comando /clima │ ├──── mostrar status [ digitando… ] │ ├──── definir chave = variável de ambiente "CLIMA_API_KEY" padrão "" ├──── se [ chave está vazia ] │ ├── enviar mensagem [ "Serviço de clima não configurado." ] │ ├── registrar no log [ "CLIMA_API_KEY ausente" ] │ └── parar por aqui │ ├──── tentar │ ├── definir r = baixar JSON da URL │ │ juntar("https://api.exemplo.com/v1/atual?cidade=", │ │ codificar URL [ "São Paulo" ], "&key=", chave) │ ├── definir temp = obter "temperatura" de r │ └── enviar mensagem [ juntar("🌡️ Agora: ", temp como texto, "°C") ] │ └──── se der erro ├── registrar no log [ "Falha na API de clima" ] └── enviar mensagem [ "Não consegui consultar o clima. Tente em instantes." ]
Segurança — chaves de API
  • Chave nunca dentro de um bloco de texto: ela vai parar no bot.py e no .json do projeto.
  • Prefira o header Authorization à querystring: URLs aparecem em logs de servidor e proxies.
  • Use a chave de menor privilégio possível (só leitura, se só lê).
  • Nunca envie ao usuário a resposta bruta da API — ela pode conter dados de outras pessoas ou detalhes internos.
Sempre codifique a URL

Acentos, espaços e & quebram a URL. Passe qualquer valor dinâmico por codificar URL. Sem isso, "São Paulo" vira uma requisição malformada — e se o valor vier do usuário, vira também um vetor de injeção de parâmetros.

Timeout e travamento

Uma API lenta segura o fluxo. Se ela demorar 30 segundos, o usuário fica 30 segundos sem resposta. Use mostrar status antes da chamada, envolva em tentar e, para APIs notoriamente instáveis, considere guardar o último resultado em cache com salvar dado.

JSON

Explicação simples

JSON é o idioma comum que programas usam para trocar informação. É só texto organizado com chaves e valores — o mesmo formato dos dicionários que você já usa nos blocos.

texto JSON ( ) para dados
Converte texto em dicionário/lista manipulável. Use quando receber JSON como texto puro.
dados ( ) para texto JSON
Converte dicionário/lista em texto. Use para salvar em arquivo ou enviar num POST.
JSON inválido

Se o texto não é JSON válido, a conversão falha e derruba o fluxo. Isso acontece com frequência quando a API devolve uma página de erro em HTML em vez do JSON esperado. Sempre dentro de tentar / se der erro.

Base64 ( ) ( )
Codifica/decodifica. Usado por algumas autenticações e para embutir dados binários.
( ) URL ( )
Codifica/decodifica para uso em URLs.
Base64 não é criptografia

Qualquer pessoa decodifica Base64 em um segundo. Não use para "esconder" senha, token ou dado sensível — é apenas uma forma de transporte, não de proteção.

Agendamento

BlocoFazUse para
a cada ( ) ( ) enviar ( )Repete em intervalo fixoLembretes periódicos, monitoramento
todo dia às ( )h ( )min enviar ( )Uma vez por dia no horárioBom dia, relatório diário, aviso de expediente
Como funciona

Estes blocos registram uma tarefa na fila do bot (JobQueue) para o chat onde foram executados. Por isso costumam ficar dentro de /start ou de um comando de ativação: é a execução daquele comando que "assina" o agendamento para aquele usuário.

┌── quando receber comando /lembrete │ ├──── a cada 1 hora enviar [ "⏰ Hora de alongar as costas!" ] └──── enviar mensagem [ "Combinado! Vou te lembrar de hora em hora." ]
Atenção
  • Os agendamentos vivem na memória do processo: reiniciar o bot cancela todos. Para persistir, guarde as assinaturas numa tabela e recrie-as em quando o bot iniciar.
  • Se o usuário mandar /lembrete cinco vezes, ele recebe cinco lembretes por hora. Registre quem já assinou e verifique antes.
  • O horário é o do servidor. Veja fuso horário.
  • O agendamento exige python-telegram-bot[job-queue] — o Dromornis já inclui isso automaticamente no requirements.txt quando o projeto usa agendamento.

Diferença entre delay e agendamento: esperar pausa o fluxo atual e morre com ele; agendamento cria uma tarefa independente que roda depois. Para "daqui a 5 segundos", delay; para "todo dia às 8h", agendamento.

Armazenamento simples

salvar dado chave ( ) valor ( )
Grava em data.json, no disco. Sobrevive a reinícios.
carregar dado chave ( ) padrão ( )
Lê o valor, ou devolve o padrão se a chave não existir.

É o meio-termo entre variável (some) e banco (estruturado). Bom para configurações globais, contadores e cache. Ruim para dados por usuário com busca e filtro — isso é banco.

Cache de resposta de API — evita chamar demais ├── definir cache = carregar dado "clima_cache" padrão "" ├── definir quando = carregar dado "clima_hora" padrão 0 │ ├── se [ (agora em minutos) − quando > 10 ] ← cache expirado │ ├── definir cache = baixar texto da URL "https://api.exemplo.com/clima" │ ├── salvar dado "clima_cache" valor [ cache ] │ └── salvar dado "clima_hora" valor [ agora em minutos ] │ └── enviar mensagem [ cache ]
Atenção

Sempre informe um valor padrão em carregar dado. Na primeira execução a chave não existe, e sem padrão você trabalha com vazio.

Código Python personalizado

linha de código Python ( )
Ação. Insere uma linha bruta no fluxo. Empilhe várias para um bloco maior.
expressão Python ( )
Valor. Insere uma expressão que devolve um resultado.

Quando usar código

  • Uma transformação que nenhum bloco cobre (cálculo específico, formatação incomum).
  • Uma biblioteca Python que você precisa e que não tem bloco equivalente.
  • Um algoritmo que ficaria com 40 blocos e 3 linhas de código.

Quando NÃO usar

  • Quando existe um bloco que faz aquilo. O bloco é validado, legível e migra junto com o projeto.
  • Quando você não entende exatamente o que a linha faz — copiar código da internet para dentro do bot é como assinar um contrato sem ler.
  • Para lógica central do bot: em seis meses ninguém vai lembrar por que aquela linha existe.
Segurança — código personalizado

O código é inserido literalmente no bot.py e roda com todos os privilégios do processo: acesso ao disco, à rede e ao banco. Não há sandbox. Isso significa:

  • Nunca construa a linha de código a partir de texto do usuário. eval(texto da mensagem) entrega o servidor de presente para qualquer pessoa.
  • Não cole código de origem desconhecida.
  • Não coloque segredos ali — use variável de ambiente.
  • Erro de indentação ou de sintaxe quebra o arquivo inteiro, não só aquele fluxo.
❌ Nunca faça isso
expressão Python [
  eval(update.message.text)
]

# executa o que o usuário digitar
# = controle total da máquina
✅ Uso legítimo
expressão Python [
  f"{valor:,.2f}".replace(",","·")
                 .replace(".",",")
                 .replace("·",".")
]

# formatação BR de número, determinística
Boa prática

Toda linha de código personalizado merece um comentário no bloco ao lado explicando por que ela existe e por que nenhum bloco serviu. Se você não consegue escrever esse porquê, provavelmente existe um bloco.

Download de arquivos

baixar arquivo de ( ) para ( ) salva o conteúdo de uma URL em um caminho local.

Segurança

Nunca baixe uma URL fornecida pelo usuário para um caminho fornecido pelo usuário. Um caminho como ../../bot.py sobrescreve o próprio bot. Fixe a pasta de destino no fluxo e valide a extensão.

Arquivos 8 blocos

Ler e escrever arquivos no disco do servidor onde o bot roda — incluindo CSV e mídia enviada por usuários.

Referência

BlocoTipoFaz
ler arquivo ( ) padrão ( )ValorLê o conteúdo inteiro como texto; devolve o padrão se não existir
( ) arquivo ( ) com ( )Açãoescrever substitui tudo; acrescentar adiciona ao final
arquivo ( ) existe?ValorSim/Não
listar arquivos da pasta ( )ValorLista de nomes
apagar arquivo ( )AçãoRemove; ignora se não existir
ler CSV ( )ValorLista de linhas, cada uma uma lista de colunas
adicionar linha ( ) ao CSV ( )AçãoAcrescenta uma linha ao final
salvar arquivo enviado pelo usuário em ( )AçãoBaixa a mídia da mensagem recebida

Arquivo, banco ou salvar dado?

Precisa de…Use
Buscar, filtrar, relacionar registrosBanco de dados
Guardar um punhado de configuraçõessalvar dado
Uma planilha que humanos vão abrir no ExcelCSV
Log em texto para leitura posteriorArquivo de texto
Guardar fotos/documentos enviadossalvar arquivo enviado

Trabalhando com CSV

Registro de presença exportável ┌── quando receber comando /presente │ ├──── se [ não (arquivo "presencas.csv" existe?) ] │ └── adicionar linha [ "data","usuario","nome" ] ao CSV "presencas.csv" ← cabeçalho │ ├──── adicionar linha [ criar lista: agora, ID do usuário, primeiro nome ] │ ao CSV "presencas.csv" │ └──── responder mensagem [ "✅ Presença registrada!" ] ┌── quando receber comando /relatorio │ ├── se [ não (usuário é administrador?) ] → parar por aqui │ └── enviar documento [ "presencas.csv" ]
Armadilhas do CSV
  • Vírgulas dentro de um valor quebram a estrutura das colunas. Limpe o texto antes de gravar.
  • Quebras de linha dentro de um campo também. Substitua por espaço.
  • Acentos podem aparecer errados no Excel se a codificação divergir. Se for para clientes, teste abrindo o arquivo antes.
  • CSV não é banco: buscar um registro exige ler o arquivo inteiro. Com milhares de linhas, use banco.

Recebendo arquivos de usuários

salvar arquivo enviado pelo usuário em ( ) baixa foto, documento, vídeo, áudio ou nota de voz da mensagem recebida.

┌── quando receber uma mensagem │ ├──── definir destino = juntar("uploads/", ID do usuário como texto, "_", │ ID da mensagem como texto, ".dat") │ ├──── tentar │ ├── salvar arquivo enviado pelo usuário em [ destino ] │ └── responder mensagem [ "📎 Arquivo recebido!" ] │ └──── se der erro └── responder mensagem [ "Não consegui salvar esse arquivo." ]
Segurança — arquivos de usuários são hostis por padrão
  • Nunca use o nome de arquivo enviado pelo usuário como caminho. Um nome como ../../bot.py sobrescreve o seu bot. Gere o nome você mesmo, a partir de ids.
  • Salve sempre em uma pasta dedicada (uploads/), nunca na raiz do projeto.
  • Nunca execute, importe ou interprete um arquivo recebido.
  • Limite o tamanho e o tipo aceitos. O bot baixa até 20 MB por arquivo; sem controle, o disco enche.
  • Arquivos de usuários são dados pessoais: proteja e tenha uma política de retenção.
Caminhos e permissões

Caminhos relativos partem da pasta onde o bot.py está rodando, que nem sempre é a pasta do arquivo. Crie a pasta de destino antes de gravar — escrever num diretório inexistente gera erro. E confirme que o processo tem permissão de escrita ali (em servidores, nem sempre tem).

Pagamentos, Figurinhas e Jogos 18 blocos

Três categorias especializadas. Cada uma exige uma configuração externa antes de qualquer bloco funcionar.

Pagamentos Avançado

O Telegram permite cobrar dentro da conversa, com o cartão salvo no app. O dinheiro não passa pelo Telegram: ele intermedia um provedor de pagamento que você conecta no BotFather.

Antes de qualquer bloco

  1. No BotFather: /mybots → seu bot → Payments.
  2. Escolha um provedor disponível no seu país e conecte a conta.
  3. Você recebe um provider token — que é um segredo, tratado como o token do bot.

O fluxo completo, em três atos

1. Você cobra enviar cobrança (título, descrição, valor em centavos) ↓ 2. O Telegram pergunta se pode cobrar → evento pré-checkout Você TEM que responder em até 10 segundos com responder pré-checkout (aceitar ou recusar com motivo) ↓ 3. O pagamento acontece → evento pagamento concluído Só AQUI o dinheiro é real. Entregue o produto agora.
BlocoPapel
enviar cobrança título ( ) descrição ( )Envia a fatura no chat. Valor em centavos: R$ 49,90 = 4990
criar link de pagamento título ( ) descrição ( )Mesma coisa, mas devolve um link para compartilhar
quando um pagamento estiver para ser confirmadoEvento de pré-checkout. Última chance de validar
responder pré-checkout ( )Aceita ou recusa. Obrigatório
quando um pagamento for concluídoEvento de sucesso. Entregue aqui
informação do pagamento ( )Lê valor, moeda, payload do pagamento concluído
Segurança — regras inegociáveis
  • Entregue o produto apenas no evento de pagamento concluído. Entregar no pré-checkout é entregar antes de receber.
  • Valide estoque e preço no pré-checkout, não antes. O tempo entre a fatura e o pagamento é suficiente para o item acabar ou o preço mudar.
  • Confira o valor recebido contra o esperado. Nunca confie no que veio no payload sem verificar contra o seu banco.
  • Guarde o registro da transação antes de entregar. Se o bot cair no meio, você precisa saber que a pessoa pagou.
  • Não responder o pré-checkout em 10 segundos cancela a venda.
┌── quando um pagamento estiver para ser confirmado │ ├── definir p = buscar 1 linha "produtos" onde "id" = [ payload ] │ ├── se [ p está vazio ou obter "estoque" de p ≤ 0 ] │ │ ├── responder pré-checkout [ recusar: "Produto esgotado." ] │ │ └── parar por aqui │ └── responder pré-checkout [ aceitar ] ┌── quando um pagamento for concluído │ ├── inserir na tabela "vendas" { usuario: ID do usuário, │ │ valor: informação do pagamento (total) } │ ├── atualizar "produtos" definir "estoque" = estoque − 1 onde "id" = payload │ └── enviar mensagem [ "✅ Pagamento aprovado! Seu pedido está a caminho." ]

Figurinhas Intermediário

Seis blocos para criar e gerenciar pacotes de figurinhas pelo bot.

BlocoFaz
enviar arquivo de figurinha dono ( ) arquivo ( ) formato ( )Sobe a imagem e devolve o file_id
criar pacote de figurinhas dono ( )Cria o pacote com a primeira figurinha
adicionar figurinha ao pacote dono ( )Acrescenta ao pacote existente
remover figurinha ( )Tira uma figurinha do pacote
definir pacote de figurinhas do grupo como ( )Pacote exclusivo do supergrupo
remover pacote de figurinhas do grupoDesfaz o anterior
Requisitos
  • O nome do pacote precisa terminar em _by_<username_do_bot>.
  • Imagens: PNG ou WebP, 512×512, fundo transparente, até 512 KB.
  • O "dono" é o user.id de uma pessoa real que já conversou com o bot.
  • Definir pacote do grupo só funciona em supergrupos com número mínimo de membros.

Jogos Avançado

Telegram Games permite abrir um jogo HTML5 dentro do app, com placar integrado. Você precisa hospedar a página do jogo por conta própria e registrá-lo no BotFather com /newgame.

BlocoPapel
enviar jogo ( )Envia o cartão do jogo pelo short name registrado
quando alguém abrir um jogoEvento disparado ao clicar em "Jogar"
nome do jogo abertoQual jogo foi aberto (para bots com vários)
abrir jogo na URL ( )Responde ao evento com a URL da sua página https
definir pontuação do jogo do usuário ( ) para ( )Registra o placar
recordes do jogo aberto do usuário ( )Lista o ranking ao redor do jogador
Segurança — placares

A pontuação vem do navegador do jogador, ou seja, do lado que o usuário controla. Qualquer pessoa com o console aberto pode enviar um placar inventado. Se o ranking tem valor (prêmio, competição), valide a pontuação no servidor com a lógica do jogo — nunca confie no número recebido.

Requisitos

A URL do jogo precisa ser https com certificado válido. O short name precisa ser exatamente o registrado no BotFather. Por padrão, o Telegram só atualiza o placar quando a pontuação nova é maior que a anterior.

Tutoriais essenciais

Oito projetos completos, do mais simples ao intermediário. Cada um segue a mesma estrutura: objetivo, blocos, fluxo, teste, erros possíveis e como melhorar.

1. Bot "Olá Mundo" Iniciante

Objetivo
O bot responde a /start com uma saudação.
Blocos
quando receber /start, enviar mensagem, primeiro nome, juntar
Tempo
5 minutos
┌── quando receber /start │ └──── enviar mensagem [ juntar("Olá, ", primeiro nome, "! 👋 Eu sou o seu primeiro bot.") ]

Teste: envie /start no Telegram. Resultado: a saudação com o seu nome, em menos de um segundo.

Erros possíveis: nenhuma resposta → confira se o bot está rodando no terminal e se você está falando com o @username certo. Nome aparece vazio → você usou sobrenome em vez de primeiro nome.

Como melhorar: adicione um /ajuda e um teclado com as opções principais.

2. Bot de menu Iniciante

Objetivo
Menu clicável que navega entre telas sem encher o chat.
Blocos
enviar mensagem com teclado, teclado inline, botão, quando o botão for pressionado, confirmar clique, editar mensagem do botão
┌── quando receber /start │ └── enviar mensagem [ "🥖 Padaria do Zé\nO que você quer ver?" ] com teclado inline: │ • "💰 Preços" → callback "menu:precos" │ • "🕘 Horário" → callback "menu:horario" ☑ mesma linha │ • "📍 Endereço" → callback "menu:endereco" ┌── quando o botão "menu:precos" for pressionado │ ├── confirmar clique do botão [ "" ] │ └── editar mensagem do botão para [ "💰 Preços\n\nPão: R$ 1,00\nBolo: R$ 35,00" ] │ com teclado inline: • "⬅️ Voltar" → callback "menu:inicio" ┌── quando o botão "menu:inicio" for pressionado │ ├── confirmar clique do botão [ "" ] │ └── editar mensagem do botão para [ "O que você quer ver?" ] com o menu principal

Teste: clique em cada botão e no "Voltar". A mesma mensagem deve se transformar, sem gerar mensagens novas.

Erros possíveis: botão gira e não faz nada → falta o evento correspondente ou o callback_data está diferente. Sem "Voltar" → usuário fica preso.

Como melhorar: extraia o menu principal para um único callback reutilizado por todas as telas.

3. FAQ automático Iniciante

Objetivo
Responder perguntas frequentes por palavra-chave, com fallback amigável.
Blocos
quando receber uma mensagem, contém, para minúsculas, se/senão
┌── quando receber uma mensagem │ ├──── definir t = [ texto da mensagem ] para minúsculas ← normaliza a caixa │ ├──── se [ t contém "horário" ou t contém "hora" ou t contém "aberto" ] │ ├── enviar mensagem [ "🕘 Seg a sex, 9h às 18h. Sábado, 9h às 13h." ] │ └── parar por aqui │ ├──── se [ t contém "entrega" ou t contém "frete" ] │ ├── enviar mensagem [ "🚚 Entregamos em toda a cidade. Frete grátis acima de R$ 50." ] │ └── parar por aqui │ └──── enviar mensagem [ "Não entendi 🤔\nTente: horário, entrega, preços — ou /ajuda." ]

Erros possíveis: a primeira condição captura tudo → coloque as regras mais específicas antes das genéricas. Não responde em grupos → modo privacidade (veja BotFather).

Como melhorar: guarde as perguntas não entendidas numa tabela; a lista vira o roteiro do que ensinar ao bot em seguida.

4. Sistema de cadastro Intermediário

Objetivo
Coletar nome e e-mail, validar e guardar no banco.
Blocos
quando o bot iniciar, criar tabela, aguardar próxima mensagem, contém, inserir na tabela, buscar 1 linha
┌── quando o bot iniciar │ └── criar tabela "usuarios" │ • "id" inteiro (chave) • "nome" texto • "email" texto ┌── quando receber comando /cadastro │ ├──── definir u = buscar 1 linha "usuarios" onde "id" = [ ID do usuário ] ├──── se [ não (u está vazio) ] │ ├── enviar mensagem [ "Você já está cadastrado. Use /perfil." ] │ └── parar por aqui │ ├──── enviar mensagem [ "Qual é o seu nome completo?" ] ├──── aguardar próxima mensagem → nome │ ├──── se [ tamanho de nome < 3 ] │ ├── enviar mensagem [ "Nome muito curto. Recomece com /cadastro." ] │ └── parar por aqui │ ├──── enviar mensagem [ "E o seu e-mail?" ] ├──── aguardar próxima mensagem → email │ ├──── se [ não (email corresponde ao padrão "[^@\s]+@[^@\s]+\.[a-z]{2,}") ] │ ├── enviar mensagem [ "E-mail inválido. Recomece com /cadastro." ] │ └── parar por aqui │ ├──── inserir na tabela "usuarios" { id: ID do usuário, nome: nome, email: email } └──── enviar mensagem [ juntar("✅ Cadastro concluído, ", nome, "!") ]

Erros possíveis: cadastro duplicado → falta o teste inicial. Fluxo perdido após reinício → a espera vive na memória; para cadastros longos, use máquina de estados no banco (veja Estados).

Como melhorar: aceite /cancelar em qualquer etapa e ofereça /apagarconta para o usuário remover os próprios dados.

5. Bot de atendimento Intermediário

Objetivo
Triagem por menu e encaminhamento para um humano quando necessário.
Blocos
teclado inline, copiar mensagem para o chat, salvar dado, banco
┌── quando receber /start │ └── menu: "🛒 Comprar" · "🔧 Suporte" · "💬 Falar com atendente" ┌── quando o botão "at:humano" for pressionado │ ├── confirmar clique do botão [ "" ] │ ├── inserir na tabela "fila" { usuario: ID do usuário, aberto: agora, status: "aberto" } │ ├── copiar mensagem recebida para o chat [ ID_DO_GRUPO_DA_EQUIPE ] │ └── editar mensagem do botão para [ "Você entrou na fila. Alguém já te chama. 👋" ] ┌── quando receber uma mensagem │ ├── definir t = buscar 1 linha "fila" onde "usuario" = ID do usuário │ └── se [ não (t está vazio) ] │ └── copiar mensagem recebida para o chat [ ID_DO_GRUPO_DA_EQUIPE ]
Dica

copiar mensagem (em vez de encaminhar) não mostra "encaminhado de" — melhor quando o encaminhamento é interno e você não quer expor o perfil do cliente para toda a equipe.

Como melhorar: horário de atendimento com agora (hora), e um aviso automático fora do expediente.

6. Bot de moderação Intermediário

Objetivo
Filtrar links, aplicar avisos escalonados e oferecer comandos de admin.
Blocos
é administrador?, apagar mensagem, adicionar aviso, silenciar, banir, tentar/se der erro
Pré-requisito
Bot admin com "Apagar mensagens" e "Banir usuários"; modo privacidade desligado
┌── quando receber uma mensagem │ ├──── se [ conversa é em grupo? e não (usuário é administrador?) │ e texto da mensagem contém "http" ] │ │ │ ├── tentar → apagar mensagem recebida │ │ se der erro → registrar no log [ "Sem permissão para apagar" ] │ │ │ ├── definir n = adicionar aviso ao usuário [ ID do usuário ] │ │ │ ├── se [ n = 1 ] → enviar [ "⚠️ Links não são permitidos. Aviso 1/3." ] │ ├── se [ n = 2 ] → silenciar usuário [ID do usuário] por 60 minutos │ │ → enviar [ "🔇 Aviso 2/3. Silenciado por 1 hora." ] │ └── se [ n ≥ 3 ] → banir usuário [ ID do usuário ] │ → enviar [ "🔨 Banido após 3 avisos." ] ┌── quando receber comando /perdoar │ ├── se [ não (usuário é administrador?) ] → parar por aqui │ ├── zerar avisos do usuário [ ID do usuário respondido ] │ └── responder mensagem [ "Avisos zerados. ✅" ]

Erros possíveis: o bot apaga mensagens de admin → falta a exceção. Nada acontece em grupo → modo privacidade ligado. Erro not enough rights → permissão específica desmarcada.

Como melhorar: lista de domínios permitidos, e envio de cada ação para um canal de auditoria.

7. Bot consumindo API externa Intermediário

Objetivo
Consultar um serviço externo e responder com o resultado.
Blocos
variável de ambiente, codificar URL, baixar JSON, obter chave, tentar/se der erro, mostrar status
Bot → API de clima → resposta → usuário ┌── quando receber comando /clima │ ├──── definir cidade = juntar(argumentos do comando) ← "/clima São Paulo" ├──── se [ cidade está vazia ] │ ├── enviar mensagem [ "Use assim: /clima São Paulo" ] │ └── parar por aqui │ ├──── mostrar status [ digitando… ] ├──── definir chave = variável de ambiente "CLIMA_API_KEY" padrão "" │ ├──── tentar │ ├── definir r = baixar JSON da URL │ │ juntar("https://api.exemplo.com/atual?q=", │ │ codificar URL [cidade], "&key=", chave) │ ├── definir atual = obter "current" de r │ └── enviar mensagem [ juntar("🌡️ ", cidade, ": ", │ obter "temp_c" de atual como texto, "°C") ] │ └──── se der erro ├── registrar no log [ juntar("Falha no clima para ", cidade) ] └── enviar mensagem [ "Não encontrei essa cidade ou o serviço está fora do ar." ]

Erros possíveis: 401 → chave ausente ou errada. Acentos quebrando a URL → falta codificar URL. Bot travando → API lenta; use cache.

Como melhorar: guarde o resultado por 10 minutos com salvar dado (veja cache) — economiza chamadas e responde instantaneamente.

8. Sistema de notificações Intermediário

Objetivo
Usuários assinam avisos e recebem uma mensagem diária.
Blocos
todo dia às ( ), banco, buscar todas as linhas, para cada, esperar
┌── quando receber comando /assinar │ ├── definir a = buscar 1 linha "assinantes" onde "id" = ID do usuário │ ├── se [ não (a está vazio) ] │ │ ├── enviar mensagem [ "Você já assina. Use /cancelar para sair." ] │ │ └── parar por aqui ← evita duplicar │ ├── inserir na tabela "assinantes" { id: ID do usuário } │ ├── todo dia às 8h 00min enviar [ "☀️ Bom dia! Confira as novidades: /novidades" ] │ └── enviar mensagem [ "Assinado! Você recebe o resumo às 8h. ✅" ]
Atenção

Agendamentos vivem na memória do processo. Depois de um reinício, o assinante deixa de receber. Para produção, releia a tabela de assinantes em quando o bot iniciar e recrie os agendamentos — esse é o mecanismo que torna a assinatura durável.

Como melhorar: para envios em massa, insira esperar 0.05 segundos entre mensagens e envolva cada envio em tentar / se der erro, marcando como inativo quem bloqueou o bot.

Sistemas completos Avançado

Nove projetos que combinam várias categorias. Aqui a dificuldade não está nos blocos, e sim na arquitetura.

9. Sistema de tickets

Objetivo
Usuário abre um chamado; a equipe responde por um grupo; o histórico fica registrado.
Modelo de dados
tickets: id, usuario, assunto, status, aberto_em, topico
┌── quando receber comando /ticket │ ├── enviar mensagem [ "Descreva seu problema em uma mensagem:" ] │ ├── aguardar próxima mensagem → assunto │ ├── definir topico = criar tópico chamado │ │ juntar("#", ID do usuário como texto, " — ", primeiro nome) cor azul │ ├── inserir "tickets" { usuario: ID do usuário, assunto: assunto, │ │ status: "aberto", topico: topico } │ └── enviar mensagem [ "🎫 Ticket aberto! Nossa equipe responde em breve." ] ┌── quando receber comando /fechar (usado pela equipe no grupo) │ ├── se [ não (usuário é administrador?) ] → parar por aqui │ ├── definir t = buscar 1 linha "tickets" onde "topico" = ID do tópico atual │ ├── se [ t está vazio ] → responder [ "Este tópico não é um ticket." ] → parar │ ├── atualizar "tickets" definir "status" = "fechado" onde "topico" = ID do tópico atual │ ├── fechar tópico [ ID do tópico atual ] │ └── responder mensagem [ "✅ Ticket encerrado." ]

Como melhorar: SLA com a cada 1 hora verificando tickets abertos há muito tempo; avaliação de atendimento por teclado inline ao fechar.

10. Vinculação de conta ("login")

Objetivo
Ligar a conta do Telegram a um cadastro que já existe no seu sistema.
O fluxo correto 1. No SEU site, o usuário logado pede "conectar Telegram" 2. Seu site gera um token aleatório, de uso único, válido por 10 minutos 3. Seu site mostra o link https://t.me/seu_bot?start=tok_ab12cd34 4. O usuário clica → o bot recebe /start tok_ab12cd34 5. O bot valida o token contra o banco (existe? não usado? não expirou?) 6. O bot grava a ligação user_id ↔ conta e INVALIDA o token
┌── quando receber /start │ ├──── definir args = argumentos do comando ├──── se [ args está vazia ] → mostrar menu normal → parar por aqui │ ├──── definir tok = buscar 1 linha "tokens" onde "valor" = [ item 1 de args ] │ ├──── se [ tok está vazio ou obter "usado" de tok = 1 ] │ ├── enviar mensagem [ "Link inválido ou já utilizado." ] │ └── parar por aqui │ ├──── atualizar "tokens" definir "usado" = 1 onde "valor" = [ item 1 de args ] ├──── inserir "vinculos" { telegram: ID do usuário, conta: obter "conta" de tok } └──── enviar mensagem [ "🔗 Conta vinculada com sucesso!" ]
Segurança — regras absolutas
  • Nunca peça senha pelo bot. Mensagens ficam no histórico dos dois lados e podem ser lidas por quem tiver acesso ao aparelho.
  • Token aleatório, longo, de uso único e com expiração curta.
  • Invalide o token antes de confirmar ao usuário.
  • Nunca deduza identidade de username ou de qualquer coisa que o usuário digite.

11. Níveis de usuário e permissões

Tabela "permissoes": id (user_id) · nivel ("admin" | "moderador" | "usuario") ┌── quando receber comando /promover │ ├── definir eu = buscar 1 linha "permissoes" onde "id" = ID do usuário │ ├── se [ eu está vazio ou obter "nivel" de eu ≠ "admin" ] │ │ ├── responder mensagem [ "❌ Sem permissão." ] │ │ └── parar por aqui │ ├── definir alvo = ID do usuário respondido │ ├── se [ alvo está vazio ] │ │ ├── responder [ "Responda à mensagem de quem você quer promover." ] │ │ └── parar por aqui │ ├── inserir "permissoes" { id: alvo, nivel: "moderador" } │ └── responder mensagem [ "✅ Promovido a moderador." ]
Boa prática

Crie um único fluxo de verificação por nível e repita o mesmo padrão em todos os comandos. Verificações inconsistentes são a origem clássica de escalada de privilégio: 20 comandos protegidos e um esquecido bastam.

12. Conversa com máquina de estados

Quando a conversa tem muitas etapas ou precisa sobreviver a reinícios, aguardar próxima mensagem não basta. Guarde a etapa no banco:

Tabela "estado": id (user_id) · etapa · dados ┌── quando receber uma mensagem │ ├──── definir e = buscar 1 linha "estado" onde "id" = ID do usuário ├──── se [ e está vazio ] → parar por aqui ← não está em conversa │ ├──── definir etapa = obter "etapa" de e │ ├──── se [ etapa = "aguardando_nome" ] │ ├── atualizar "estado" definir "dados" = texto da mensagem onde "id" = ID do usuário │ ├── atualizar "estado" definir "etapa" = "aguardando_email" onde "id" = ID do usuário │ ├── enviar mensagem [ "Agora o e-mail:" ] │ └── parar por aqui │ └──── se [ etapa = "aguardando_email" ] ├── inserir "usuarios" { id: ID do usuário, nome: obter "dados" de e, │ email: texto da mensagem } ├── apagar da tabela "estado" onde "id" = ID do usuário ← sempre limpe! └── enviar mensagem [ "✅ Pronto!" ]
Atenção

Sempre limpe o estado ao terminar e ofereça /cancelar. Um usuário preso numa etapa que nunca sai é o bug mais frustrante que existe — para ele, o bot simplesmente "parou de funcionar".

13. Painel administrativo

┌── quando receber comando /admin │ ├── se [ não é admin ] → parar por aqui │ ├── definir tot = contar linhas da tabela "usuarios" │ ├── definir ped = contar linhas da tabela "pedidos" │ └── enviar mensagem [ juntar("📊 Painel\n\n", │ "Usuários: ", tot como texto, "\n", │ "Pedidos: ", ped como texto) ] com teclado inline: │ • "📢 Enviar aviso a todos" → callback "adm:broadcast" │ • "📥 Exportar CSV" → callback "adm:export"

14. Broadcast para todos os usuários

┌── quando o botão "adm:broadcast" for pressionado │ ├── confirmar clique do botão [ "" ] │ ├── se [ não é admin ] → parar por aqui ← verifique DE NOVO no callback │ ├── enviar mensagem [ "Digite a mensagem que será enviada a todos:" ] │ ├── aguardar próxima mensagem → texto │ │ │ ├── definir todos = buscar todas as linhas da tabela "usuarios" │ ├── definir ok = 0 │ │ │ ├── para cada u em todos │ │ ├── tentar │ │ │ ├── enviar mensagem para [obter "id" de u] [ texto ] │ │ │ └── alterar ok em 1 │ │ │ se der erro │ │ │ └── atualizar "usuarios" definir "ativo" = 0 onde "id" = obter "id" de u │ │ └── esperar 0.05 segundos ← ~20 msg/s, dentro do limite │ │ │ └── enviar mensagem [ juntar("📢 Enviado para ", ok como texto, " usuários.") ]
Três erros que arruínam um broadcast
  1. Não verificar admin dentro do callback. Quem descobrir o callback_data dispara o broadcast.
  2. Não tratar erro por usuário. O primeiro que bloqueou o bot derruba a fila inteira.
  3. Não espaçar os envios. Erro 429 e mensagens perdidas.

15. Tarefas agendadas persistentes

┌── quando o bot iniciar │ ├── criar tabela "assinantes" ( "id" inteiro (chave) ) │ ├── definir lista = buscar todas as linhas da tabela "assinantes" │ └── para cada a em lista │ └── (recriar o agendamento diário para obter "id" de a)

Este é o padrão que transforma agendamentos voláteis em assinaturas duráveis: a tabela é a verdade; os agendamentos são reconstruídos a partir dela toda vez que o bot sobe.

16. Loja com pagamentos

Arquitetura mínima Tabelas: produtos (id, nome, preco_centavos, estoque) vendas (id, usuario, produto, valor, data) 1. /loja → lista produtos com um botão por item 2. callback "buy:ID" → confirma clique · consulta produto · enviar cobrança 3. pré-checkout → revalida preço e estoque · aceita ou recusa 4. pagamento ok → grava venda · baixa estoque · entrega
Segurança

O preço sempre vem do banco no momento da cobrança — nunca do callback_data. Um callback como buy:42:990 permite ao usuário editar o valor e comprar por qualquer preço.

17. Integrando um modelo de IA

O construtor não tem categoria de IA dedicada. A integração se faz pelos blocos de HTTP, o que na prática dá total liberdade de provedor.

┌── quando receber comando /perguntar │ ├──── definir pergunta = juntar(argumentos do comando) ├──── se [ pergunta está vazia ] │ ├── enviar mensagem [ "Use: /perguntar sua dúvida aqui" ] │ └── parar por aqui │ ├──── mostrar status [ digitando… ] │ ├──── tentar │ ├── definir r = enviar POST para "https://api.provedor.com/v1/chat" │ │ dados (JSON): { modelo: "...", entrada: pergunta } │ │ cabeçalhos: cabeçalho Bearer da variável de ambiente "IA_API_KEY" │ └── enviar mensagem [ obter "texto" de (obter "resposta" de r) ] │ └──── se der erro ├── registrar no log [ "Falha na API de IA" ] └── enviar mensagem [ "Não consegui responder agora. Tente de novo." ]
Segurança e custo
  • Chave sempre em variável de ambiente, nunca num bloco de texto.
  • Limite o uso. Sem controle, uma pessoa mal-intencionada consome sua cota inteira em minutos. Guarde um contador por usuário e por dia numa tabela.
  • Limite o tamanho da pergunta — o custo cresce com o texto enviado.
  • Trate a resposta como texto de terceiro. Não interprete como comando, não passe para código, não envie com Markdown sem escapar.
  • Não envie dados pessoais de usuários para serviços externos sem base legal e sem avisar.
Dica — memória de conversa

Para o modelo "lembrar" do que foi dito, guarde as últimas N mensagens numa tabela e envie-as junto na requisição. Limite o histórico (5 a 10 mensagens): cada mensagem extra aumenta o custo e a latência de toda pergunta seguinte.

Depuração e erros

Central de solução de problemas. Encontre o sintoma, entenda a causa, aplique a correção.

O método: isole antes de consertar

Quando algo não funciona, resista à tentação de mudar cinco coisas de uma vez. Siga a ordem:

  1. O bot está rodando? Olhe o terminal. Sem processo, nada funciona.
  2. A mensagem chega? Coloque registrar no log como primeira ação do fluxo. Se não aparece nada no terminal, o problema é o evento, não a lógica.
  3. A condição é verdadeira? Registre o valor comparado antes do se.
  4. A variável tem o que você acha? Registre-a. 90% dos bugs são "a variável estava vazia" ou "era texto, não número".
  5. A API respondeu? Registre o retorno bruto antes de extrair campos.
Dica — a técnica do log em três pontos

Coloque registrar no log no começo, no meio e no fim do fluxo, cada um com um texto distinto ("A", "B", "C"). Rode. O último que aparece no terminal marca exatamente onde o fluxo parou. Em dois minutos você reduz "não funciona" a "quebra entre B e C".

Suas quatro ferramentas

FerramentaOndeResponde
Aba ValidaçãoEditor"O que montei está estruturalmente errado?"
Aba Código PythonEditor"O bloco que adicionei virou código?"
Terminal do botOnde você rodou python bot.py"O que aconteceu de verdade em execução?" — a mais importante
Bloco registrar no logDentro dos fluxos"Quanto valia essa variável naquele instante?"
O terminal é a fonte da verdade

A aba Logs do editor registra o que você fez no editor. Os erros do bot em execução aparecem só no terminal, com o traceback completo. Se você não está olhando o terminal enquanto testa, está depurando às cegas.

Sintomas e correções

O bot não responde a nada

VerifiqueComo
O processo está rodando?O terminal deve estar "parado", esperando. Se voltou ao prompt, o bot caiu — leia a última linha
É o bot certo?Compare o @username com o que o BotFather criou
Existe algum evento?Aba Validação. Sem evento, o bot sobe e não faz nada
O token está certo?Erro InvalidToken aparece já na inicialização
Há outra instância rodando?Erro Conflict: terminated by other getUpdates

O bot responde em privado, mas não em grupo

Quase sempre é o modo privacidade. Por padrão, em grupos o bot só recebe comandos, menções e respostas diretas. Correção: BotFather → /setprivacyDisableremova e readicione o bot ao grupo. A última etapa é obrigatória: sem ela, a mudança não vale para o grupo atual.

"not enough rights" / a ação não acontece

O bot é membro comum, ou é admin sem a permissão específica. Vá em Grupo → Administradores → seu bot e marque exatamente a permissão da ação (Apagar mensagens, Banir usuários, Fixar mensagens, Alterar informações). Ser admin não concede tudo automaticamente.

O botão gira e nada acontece

  • Falta o evento quando o botão ( ) for pressionado com aquele callback_data.
  • O texto do callback_data do botão e do evento diferem (um espaço, um acento, uma maiúscula).
  • Falta confirmar clique do botão — o fluxo até funciona, mas o relógio nunca some.
  • O callback_data passou de 64 bytes e a mensagem com o teclado nunca foi enviada.

A mensagem sai com um buraco ("Olá, !")

Uma variável ou campo veio vazio. Causas frequentes:

  • Campo opcional do Telegram (username, sobrenome) que a pessoa não tem.
  • Variável definida em outro fluxo — variáveis não atravessam fluxos (veja escopo).
  • Bloco de leitura usado fora do evento certo (dado do callback num fluxo de comando).
  • Chave de dicionário com nome errado — devolve vazio, não erro.
  • Busca no banco sem resultado.

Correção padrão: teste está vazio e defina um valor padrão antes de usar.

A condição nunca executa (ou executa sempre)

CausaExemploCorreção
Comparando texto com número"10" = 10( ) como número nos dois lados
Maiúsculas"Sim" = "sim"Converta para minúsculas antes
ou no lugar de edia ≥ 1 ou dia ≤ 5Troque por e
Espaço invisível"sim "Compare com contém ou limpe o texto
Condição anterior já parou o fluxoUm parar por aqui acimaReordene as regras
Dica

Antes do se, registre no log o valor e o resultado da comparação. Ver "10" vs 10 → falso escrito na tela resolve o mistério na hora.

A API externa dá erro

StatusCausaCorreção
401Chave ausente ou inválidaConfirme a variável de ambiente e o .env
403Chave válida sem permissãoVerifique o plano/escopo da chave
404URL erradaTeste a URL no navegador
422 / 400Parâmetro faltando ou malformadoLeia a mensagem de erro da API no log
429Excedeu o limiteReduza a frequência; use cache
5xxProblema do outro ladoTente de novo depois; não é seu bug
JSON inválidoA API devolveu HTML de erroRegistre a resposta bruta antes de converter

O bot ficou lento ou travou

  • Laço infinito — trava tudo para todo mundo. Revise repetir enquanto.
  • API lenta sem tratamento — o fluxo espera. Use mostrar status e cache.
  • buscar todas as linhas numa tabela grande — filtre no banco, não no fluxo.
  • Muitos envios em sequência429 e enfileiramento.
  • esperar longo no meio de um fluxo comum.

Erros ao iniciar o bot

Mensagem no terminalCorreção
ModuleNotFoundError: No module named 'telegram'Ative o venv e rode pip install -r requirements.txt
telegram.error.InvalidTokenToken errado, com espaço, ou .env com nome de variável diferente do configurado
RuntimeError: There is no current event loopPython 3.14+. Use 3.12 ou 3.13
Conflict: terminated by other getUpdatesOutra instância do mesmo bot está rodando
IndentationError / SyntaxErrorBloco de código Python personalizado malformado
PermissionErrorO bot não tem permissão de escrita na pasta
UnicodeDecodeErrorArquivo lido com codificação diferente da esperada

Como testar um fluxo direito

  1. Caminho feliz: tudo certo, entrada válida.
  2. Entrada faltando: comando sem argumentos, resposta vazia.
  3. Entrada inválida: texto onde se espera número, e-mail sem @.
  4. Sem permissão: teste como usuário comum um comando de admin.
  5. Contexto errado: rode um comando de grupo numa conversa privada.
  6. Repetição: execute o comando duas vezes seguidas — duplica algo?
  7. Serviço fora: ponha uma URL inválida de propósito e veja se a mensagem de erro é boa.
Boa prática

Crie um grupo de testes só seu, com o bot como admin, e teste as ações de moderação ali. Testar banimento no grupo de produção é como testar o freio na estrada.

Boas práticas

O que separa um bot que funciona hoje de um bot que ainda funciona daqui a um ano — e que outra pessoa consegue manter.

Organização dos fluxos

  • Um evento, uma responsabilidade. Se o fluxo faz três coisas não relacionadas, são três fluxos.
  • Agrupe espacialmente no canvas: comandos numa região, callbacks noutra, eventos de grupo noutra. O canvas é infinito.
  • Comente os fluxos (botão direito → adicionar comentário) explicando o porquê, não o quê. "Verifica admin" é óbvio; "só admins porque isso apaga dados do cliente" não é.
  • Ordem interna: guardas → dados → decisões → resposta.
  • Fluxos curtos. Passou de ~20 blocos, divida.

Nomes

ElementoConvençãoExemplo
Variávelminusculas_com_underline, descrevendo o conteúdosaldo_usuario
TabelaPlural, minúsculas, sem acentousuarios, pedidos
ColunaSingular, minúsculanome, criado_em
ComandoCurto, óbvio, em português/precos, não /pl
callback_dataárea:ação:idpedido:cancelar:42
Chave de salvar dadoPrefixo por assuntocfg_horario_abertura

Reutilização e duplicação

Copiar e colar um trecho de fluxo é rápido, mas cada cópia é um lugar onde a correção precisa ser aplicada de novo — e um deles sempre fica esquecido.

  • Callbacks genéricos: um fluxo menu:* que decide pela leitura do dado do callback, em vez de um fluxo por botão.
  • Dados em tabela, não em blocos: uma tabela de produtos vale mais que 30 fluxos quase iguais.
  • Regra de três: na terceira vez que você copia o mesmo trecho, pare e generalize.

Tratamento de erros

OperaçãoPrecisa de tentar?Por quê
Chamada HTTP✅ SempreA rede falha; o serviço cai
Ação de moderação✅ SemprePermissão pode faltar
Envio em massa✅ SempreUsuários bloqueiam o bot
Leitura de arquivo✅ SimPode não existir
Conversão de entrada do usuário✅ SimO usuário digita qualquer coisa
Enviar mensagem simplesOpcionalRaramente falha em conversa ativa
Variáveis e cálculos internos❌ NãoEsconderia bugs seus
Boa prática — a regra dos dois destinatários

Todo erro capturado precisa informar duas pessoas: o usuário, com uma mensagem clara e sem jargão; e você, com um registrar no log contendo contexto suficiente para investigar.

Validação de dados

Toda entrada do usuário passa por três perguntas antes de ser usada:

  1. Existe? A lista de argumentos pode estar vazia.
  2. É do tipo certo? Texto que deveria ser número.
  3. Está na faixa aceitável? Valor negativo, data no passado, texto de 5.000 caracteres.
├── se [ argumentos do comando está vazia ] ← existe? │ └── enviar [ "Use: /depositar 50" ] → parar por aqui │ ├── definir v = [ item 1 de argumentos ] como número ← tipo certo? │ ├── se [ v está vazio ou v ≤ 0 ou v > 10000 ] ← faixa aceitável? │ └── enviar [ "Informe um valor entre 1 e 10.000." ] → parar por aqui │ └── ... a partir daqui, v é confiável ...

Limites de API e desempenho

  • Respeite ~30 mensagens/s no total e ~1/s por chat. Em massa, use esperar 0.05.
  • Prefira uma mensagem longa a cinco curtas.
  • Reaproveite file_id em vez de reenviar a mesma mídia por URL.
  • Filtre no banco (onde, LIMIT), não no fluxo.
  • Cacheie respostas de API que mudam devagar.
  • Evite trabalho pesado dentro de quando receber uma mensagem, que roda para tudo.

Logs

❌ Log inútil
registrar no log [ "erro" ]
registrar no log [ "aqui" ]
registrar no log [ "ok" ]
✅ Log útil
registrar no log [ juntar(
  "[pedido] usuario=", ID do usuário como texto,
  " item=", item,
  " status=falha_estoque") ]
Nunca registre

Token, chave de API, senha, conteúdo de mensagem privada, dado pessoal completo. Logs são copiados, enviados em pedidos de suporte e às vezes ficam em servidores de terceiros. Registre o user.id, não o nome; o código do erro, não o corpo inteiro da resposta.

Versionamento e backup

  • Exporte o .json ao fim de cada sessão de trabalho. O localStorage some ao limpar o navegador.
  • Nomeie com data: bot-padaria-2026-08-13.json. Poder voltar duas versões vale ouro.
  • Guarde os arquivos de dados do servidor (o .db, o data.json, a pasta de uploads) — o projeto sozinho não os contém.
  • Nunca faça commit do .env. Coloque-o no .gitignore antes do primeiro commit.
  • Teste o restore. Um backup que você nunca restaurou é uma esperança, não um backup.

Checklist de produção

ItemPor quê
Aba Validação limpaNenhum bloco órfão ou entrada vazia
/start e /ajuda existemPrimeiro contato e descoberta
Menu de comandos definidoDescoberta pelo botão "/"
Todo comando de admin tem guardaSegurança
Todo callback confirma o cliqueExperiência
Toda chamada externa em tentarResiliência
Nenhum segredo em bloco de textoSegurança
.env fora do gitSegurança
Supervisor reiniciando o processoDisponibilidade
Backup do banco automatizadoContinuidade
Testado com um usuário comumVocê não vê o que só o admin vê

Segurança

Um bot é um programa exposto à internet, que aceita entrada de qualquer pessoa do mundo. Estas são as regras que evitam que isso vire um problema.

Três princípios

Segredos fora do código

Token, chaves e senhas vivem em variáveis de ambiente. Nunca em blocos, nunca no repositório.

Não confie na entrada

Tudo que vem do usuário é suspeito até ser validado — inclusive o que parece inofensivo.

Menor privilégio

O bot só recebe as permissões que usa. Cada permissão extra é uma consequência a mais se algo der errado.

O token do bot

O token é a senha do bot

Quem tem o token pode ler tudo que chega ao bot, responder no lugar dele, banir pessoas nos grupos onde ele é admin e apagar mensagens. Não existe "acesso parcial".

❌ Nunca✅ Sempre
Escrever o token num bloco de textoDeixar no .env, lido por os.getenv()
Commitar o .envAdicionar .env ao .gitignore antes do 1º commit
Postar print do terminal com o tokenCobrir ou substituir por YOUR_BOT_TOKEN
Compartilhar o ZIP exportado com .envEnviar só o bot.py, ou o ZIP sem token
Reaproveitar o mesmo bot em dev e produçãoUm bot de teste separado, com token próprio

Se vazou: BotFather → /revoke → escolha o bot. O token antigo morre imediatamente. Atualize o .env e reinicie. Depois, descubra como vazou — senão vai vazar de novo.

Não confie em nada que vem do usuário

Tudo isto é controlado por quem está do outro lado e pode conter qualquer coisa:

  • Texto da mensagem, legendas, nome do arquivo enviado
  • Argumentos de comando e parâmetro do ?start=
  • Nome, sobrenome e username — podem conter <, *, emojis, 200 caracteres
  • callback_data — sim, é forjável por quem inspecionar a mensagem
  • Conteúdo de arquivos enviados
RiscoComo aconteceDefesa
Injeção de SQLTexto do usuário concatenado em consultar SQLUse os blocos visuais de banco, que parametrizam
Execução de códigoTexto do usuário dentro de expressão PythonJamais construir código a partir de entrada
Travessia de diretórioNome de arquivo com ../Gerar o nome você mesmo, a partir de ids
Quebra de formataçãoNome com * ou < em MarkdownV2/HTMLEscapar, ou usar texto puro
Escalada de privilégioPermissão baseada em username ou no ?start=Sempre user.id conferido no banco
Negação de serviçoMensagens enormes, spam de comandos carosLimite tamanho e frequência por usuário

Seguro × inseguro

❌ Permissão por username
se [ @username = "admin_joao" ]
   → apagar tabela usuarios
✅ Permissão por id no banco
a = buscar 1 linha "admins"
    onde "id" = ID do usuário
se [ não (a está vazio) ]
   → ação administrativa
❌ SQL concatenado
consultar SQL [ juntar(
 "SELECT * FROM u WHERE nome='",
 texto da mensagem, "'") ]
✅ Busca parametrizada
buscar linhas da tabela "usuarios"
  onde "nome" = [ texto da mensagem ]
❌ Caminho do usuário
salvar arquivo enviado em
  [ texto da mensagem ]

// "../../bot.py" sobrescreve o bot
✅ Caminho gerado
salvar arquivo enviado em
  juntar("uploads/",
    ID do usuário como texto, "_",
    ID da mensagem como texto, ".dat")
❌ Preço vindo do botão
callback "buy:42:990"
→ enviar cobrança valor 990

// o usuário edita e paga 1 centavo
✅ Preço vindo do banco
callback "buy:42"
p = buscar 1 linha "produtos" onde id=42
→ enviar cobrança valor
    obter "preco_centavos" de p

Controle de acesso

  • Verifique em todo ponto de entrada. Um comando protegido e o callback equivalente desprotegido é a mesma porta aberta.
  • Verifique de novo no callback. Quem descobre o callback_data pode disparar a ação sem passar pelo comando.
  • Registre ações sensíveis com quem, quando e o quê.
  • Confirme ações destrutivas com um passo extra de "tem certeza?".

Dados pessoais

Regras
  • Colete o mínimo. O dado que você não guarda não vaza.
  • Nunca peça senhas pelo bot. Use vinculação por token (veja login).
  • Não peça documento, cartão ou dado bancário por mensagem. Para cobrança, use o sistema de pagamentos do Telegram, onde os dados não passam pelo seu bot.
  • Diga o que você guarda — uma linha no /ajuda resolve.
  • Ofereça exclusão. Um /apagarconta que realmente apaga.
  • Proteja os backups. O .db tem dados de usuários; um backup em pasta pública é um vazamento.

Segredos e variáveis de ambiente

env.env — nunca vai para o git
TELEGRAM_BOT_TOKEN=YOUR_BOT_TOKEN
CLIMA_API_KEY=YOUR_API_KEY
PAGAMENTO_PROVIDER_TOKEN=YOUR_PROVIDER_TOKEN
gitignore.gitignore
.env
*.db
uploads/
data.json
.venv/
__pycache__/

No fluxo, leia com variável de ambiente ( ) padrão ( ) e sempre verifique se veio vazia — assim, um segredo mal configurado gera uma mensagem clara em vez de um erro obscuro no meio da operação.

Extensões

Extensões executam código

Um pacote .dpkg injeta blocos que geram Python no seu bot.py. Instalar uma extensão é confiar no autor com o mesmo nível de acesso que você tem. Prefira extensões com assinatura verificada, revise o código gerado na aba Código Python depois de instalar, e não instale nada de origem desconhecida em um projeto que roda em produção.

Conceitos avançados Avançado

Ideias de arquitetura que aparecem quando o bot cresce. Cada uma é explicada primeiro de forma simples, depois tecnicamente.

Estados e máquinas de estado

Explicação simples

Estado é lembrar em que parte da conversa vocês estão. É a diferença entre um atendente que pergunta "qual seu nome?" e depois entende que a próxima frase é o nome, e um que faz a mesma pergunta para sempre.

Tecnicamente: uma máquina de estados é um conjunto finito de estados, com transições disparadas por eventos. Para cada usuário você guarda o estado atual; a cada mensagem, o comportamento depende do par (estado, entrada).

inicio ──/cadastro──▶ aguardando_nome ──texto──▶ aguardando_email ▲ │ └──────────────── /cancelar ou conclusão ────────────────┘
aguardar próxima mensagemEstado no banco
ComplexidadeBaixaMédia
Sobrevive a reinício❌ Não✅ Sim
RamificaçõesDifícilNatural
Bom até~4 perguntas linearesQualquer tamanho
Regras de ouro do estado
  1. Sempre limpe o estado ao concluir.
  2. Sempre ofereça /cancelar.
  3. Considere expirar estados antigos — alguém que começou o cadastro há três semanas não deveria continuar do meio.

Contexto

Simples: contexto é tudo o que o bot sabe no momento em que executa um fluxo — quem falou, onde, o que disse, o que já estava guardado. Tecnicamente: parte do contexto vem do update (efêmero, só naquela execução) e parte da persistência (banco, salvar dado). Confundir os dois é a causa da maioria dos "por que a variável está vazia?".

Middleware

Simples: é um porteiro que examina toda mensagem antes de ela chegar ao fluxo certo — pode barrar, registrar ou enriquecer. Tecnicamente: uma camada que intercepta o processamento entre o recebimento do update e o handler.

No Dromornis não há um bloco "middleware", mas o padrão é reproduzível: um fluxo quando receber uma mensagem que faz verificações comuns (usuário banido? em manutenção? excedeu o limite?) e usa parar por aqui para barrar. Coloque as verificações no topo de cada fluxo, ou centralize o que der.

Idempotência

Explicação simples

Uma ação é idempotente quando fazer duas vezes dá o mesmo resultado que fazer uma. Apertar o botão do elevador dez vezes não chama dez elevadores.

Tecnicamente: em sistemas distribuídos, mensagens podem ser entregues mais de uma vez e usuários clicam duas vezes no mesmo botão. Operações que criam ou somam precisam de proteção.

❌ Não idempotente
quando /assinar
  → inserir "assinantes" { id }

// 3 cliques = 3 assinaturas
// = 3 mensagens diárias
✅ Idempotente
quando /assinar
  a = buscar 1 linha "assinantes"
      onde "id" = ID do usuário
  se [ não (a está vazio) ] → parar
  → inserir "assinantes" { id }

Onde isso importa mais: cadastros, assinaturas, créditos, pagamentos e agendamentos.

Concorrência

Simples: várias pessoas usam o bot ao mesmo tempo. Tecnicamente: o bot processa updates de forma assíncrona; dois handlers podem estar em execução simultânea e tocar os mesmos dados.

Condição de corrida

Dois fluxos leem "estoque = 1" ao mesmo tempo, ambos concluem que há estoque, e você vende duas unidades da última. A defesa é fazer a decisão e a alteração numa única operação no banco (UPDATE ... WHERE estoque > 0) em vez de ler, decidir e escrever em três passos separados.

Bloqueio do bot inteiro

Um esperar longo ou um laço pesado dentro de um handler afeta todos os usuários, não só quem disparou. Bots que "ficam lentos com muita gente" quase sempre têm um fluxo assim.

Filas e jobs

Simples: em vez de fazer o trabalho pesado na hora, você anota a tarefa numa lista e alguém executa depois. O usuário recebe a resposta na hora; o trabalho acontece em segundo plano.

Tecnicamente: o Dromornis usa a JobQueue do python-telegram-bot para agendamento. Para "fila" no sentido de trabalho pendente, o padrão prático é uma tabela tarefas com status, e um job periódico que processa os itens pendentes em lotes.

┌── quando receber /relatorio │ ├── inserir "tarefas" { tipo: "relatorio", usuario: ID do usuário, status: "pendente" } │ └── enviar mensagem [ "📊 Gerando… te aviso quando ficar pronto." ] ┌── (job) a cada 1 minuto │ ├── p = buscar linhas "tarefas" onde "status" = "pendente" │ └── para cada t em p → processar · marcar "concluida" · avisar o usuário

Rate limiting

Simples: um limite de quantas vezes cada pessoa pode usar um recurso num período. Tecnicamente: protege contra abuso, contra custo descontrolado (APIs pagas) e contra você mesmo bater no limite do Telegram.

├── definir chave = juntar("uso_", ID do usuário como texto, "_", agora (dia)) ├── definir n = carregar dado [ chave ] padrão 0 │ ├── se [ n ≥ 20 ] │ ├── enviar mensagem [ "Você atingiu o limite de 20 consultas hoje." ] │ └── parar por aqui │ ├── salvar dado [ chave ] valor [ n + 1 ] └── ... executar a operação cara ...

Cache

Simples: guardar a resposta de algo demorado para não precisar perguntar de novo tão cedo. Tecnicamente: troca-se atualidade por velocidade e custo. As duas perguntas do cache são por quanto tempo e quando invalidar.

DadoCache razoável
Cotação, clima5 a 15 minutos
Catálogo de produtos1 hora, invalidado ao editar
Saldo, estoqueNão cacheie
Textos fixos (regras, FAQ)Até mudar
Atenção

Cache é a fonte clássica de "o bot mostra informação errada". Se o dado mudou e o usuário vê o antigo, o cache precisa ser invalidado quando você altera a origem — não só pelo tempo.

Persistência

CamadaSobrevive ao fim do fluxoSobrevive ao reinícioSobrevive à troca de servidor
Variável do fluxo
aguardar próxima mensagem✅ (durante a espera)
Agendamento (JobQueue)
salvar dado (data.json)Só se copiar o arquivo
Banco de dados (.db)Só se copiar o arquivo
Boa prática

Escolha a camada pela pergunta "o que acontece se o bot reiniciar agora?". Se a resposta for inaceitável, suba de camada.

Arquitetura modular

Organize o projeto por domínio, não por tipo de bloco. Em vez de "todos os comandos aqui, todos os callbacks ali", agrupe: tudo de pedidos junto, tudo de moderação junto, tudo de cadastro junto. Cada módulo deve poder ser entendido — e removido — sem quebrar os outros. Use comentários no canvas como títulos de seção.

Plugins e extensões

O sistema de extensões (.dpkg) permite adicionar categorias e blocos ao construtor. É o caminho para encapsular integrações específicas do seu domínio e reutilizá-las entre projetos, em vez de recopiar fluxos.

Atenção ao remover

Remover uma extensão apaga do workspace os blocos que vieram dela, junto com o que estiver encaixado dentro. Exporte o projeto antes.

Webhooks e microserviços

Webhook do Telegram: o Telegram entrega updates numa URL sua em vez de você buscá-los. Veja polling × webhook — o código gerado usa polling, e migrar exige adaptar o bot.py.

Webhook de terceiros → seu bot: quando outro sistema quer avisar o bot (pagamento aprovado, chamado criado), o caminho usual é gravar o evento em algum lugar que o bot consulta, e ter um job periódico processando os pendentes. Isso evita expor um servidor HTTP junto do bot.

Microserviços: se uma parte do sistema é pesada (processar imagem, gerar relatório), separe-a em outro serviço e converse por HTTP. O bot fica responsável apenas pela conversa, e um pico de trabalho pesado não derruba o atendimento.

Referência rápida

Todos os blocos do construtor numa tabela pesquisável. Filtre por texto, categoria ou tipo.

BlocoCategoriaTipoDescriçãoIdentificador
Como ler

Evento = inicia um fluxo (topo). Ação = faz algo e não devolve valor. Valor = devolve um dado para encaixar dentro de outro bloco. O identificador é o nome interno do bloco, útil ao inspecionar o .json do projeto ou escrever extensões.

Além destes, o toolbox inclui os blocos nativos do Blockly nas categorias Controle, Operadores, Variáveis e Listasse, repetir, comparações, matemática, texto e manipulação de listas.

Glossário

Cada termo tem três partes: a explicação simples, a definição técnica e um exemplo concreto.

API

Simples: um balcão onde programas fazem pedidos uns aos outros.

Técnico: conjunto de operações que um sistema expõe para uso programático, com formato de entrada e saída definidos.

Exemplo: a Bot API do Telegram expõe sendMessage, que seu bot chama para falar.

Bloco

Simples: uma peça de LEGO com uma instrução escrita.

Técnico: nó de uma árvore sintática visual que gera uma expressão ou comando Python.

Exemplo: enviar mensagem ( ) vira uma chamada a send_message.

Boolean

Simples: uma resposta que só pode ser sim ou não.

Técnico: tipo de dado com dois valores, verdadeiro e falso; base de toda decisão condicional.

Exemplo: usuário é administrador? devolve um booleano.

Bot

Simples: um funcionário digital que mora no Telegram e faz só o que você ensinou.

Técnico: aplicação que se autentica na Bot API por token, recebe updates e responde chamando métodos da API.

Exemplo: @padaria_do_ze_bot.

Cache

Simples: guardar uma resposta para não precisar perguntar de novo tão cedo.

Técnico: armazenamento temporário de resultados, trocando atualidade por latência e custo. Ver Cache.

Exemplo: guardar o clima por 10 minutos em vez de chamar a API a cada mensagem.

Callback (callback query)

Simples: o recado invisível que o botão manda ao bot quando é clicado.

Técnico: update do tipo callback_query gerado por botão inline, contendo o callback_data (máx. 64 bytes). Exige answerCallbackQuery.

Exemplo: botão "Preços" com callback_data = "menu:precos".

CDN

Simples: uma rede de cópias de arquivos espalhadas pelo mundo, para baixar mais rápido de perto de você.

Técnico: rede de distribuição de conteúdo com cache geográfico.

Exemplo: hospedar as imagens do bot numa CDN acelera o envio por URL.

Chat ID

Simples: o número da conversa.

Técnico: identificador do chat. Em privado coincide com o user.id; em supergrupos é negativo e começa com -100.

Exemplo: -1001234567890.

CRON

Simples: um despertador que dispara tarefas em horários definidos.

Técnico: notação para agendamento recorrente. No Dromornis o equivalente são os blocos a cada ( ) e todo dia às ( ).

Exemplo: enviar o resumo diário às 8h.

Database (banco de dados)

Simples: o caderno de tabelas onde o bot anota o que precisa lembrar para sempre.

Técnico: o Dromornis gera código com SQLite — banco relacional em arquivo único, sem servidor. Ver Banco de Dados.

Exemplo: tabela usuarios com id, nome e saldo.

Deep link

Simples: um link que já avisa ao bot de onde a pessoa veio.

Técnico: https://t.me/bot?start=PARAM; o parâmetro chega em argumentos do comando no /start.

Exemplo: ?start=ref_987654321 para indicações.

Endpoint

Simples: o endereço exato de um serviço na internet.

Técnico: URL que aceita requisições de um método específico.

Exemplo: https://api.exemplo.com/v1/clima.

Evento / Trigger (gatilho)

Simples: a campainha que acorda o bot.

Técnico: condição de disparo de um handler. No construtor, os blocos em formato de chapéu. Ver Eventos.

Exemplo: quando receber /start.

Flow (fluxo)

Simples: a receita de bolo que o bot segue.

Técnico: pilha de blocos ancorada em um evento; vira uma função async no código gerado.

Exemplo: o fluxo do /start que cadastra e dá boas-vindas.

Handler

Simples: o funcionário responsável por um tipo de tarefa.

Técnico: função registrada na aplicação que trata um tipo de update. Um bloco de evento gera um handler.

Exemplo: CommandHandler("start", start_command).

HTTP

Simples: o idioma que os sites usam para conversar.

Técnico: protocolo de requisição/resposta com métodos (GET, POST…) e códigos de status. Ver HTTP.

Exemplo: GET https://api.exemplo.com/clima200 OK.

Idempotência

Simples: fazer duas vezes dá o mesmo resultado que fazer uma.

Técnico: propriedade de operações seguras contra repetição. Ver Idempotência.

Exemplo: verificar antes de inserir, para não duplicar o cadastro.

Inline keyboard

Simples: botões grudados na mensagem.

Técnico: teclado anexado à mensagem cujos botões geram callback, abrem URL ou Web App. Ver Teclado.

Exemplo: menu de opções com "Preços" e "Endereço".

JSON

Simples: o formato de texto que programas usam para trocar informação.

Técnico: notação de objetos com pares chave/valor, arrays, strings, números, booleanos e null.

Exemplo: {"nome":"Ana","idade":30}.

Middleware

Simples: um porteiro que vê toda mensagem antes de ela chegar ao destino.

Técnico: camada de interceptação entre o recebimento do update e o handler. Ver Middleware.

Exemplo: bloquear usuários banidos antes de qualquer fluxo.

Nó (node)

Simples: outro nome para um bloco no canvas.

Técnico: elemento da árvore do workspace, com id, tipo, campos e conexões.

Exemplo: um nó tg_send_message no .json do projeto.

Polling

Simples: o bot perguntando ao Telegram, sem parar, "chegou algo novo?".

Técnico: long polling via getUpdates. É o método usado pelo código gerado (run_polling). Ver Polling × Webhook.

Exemplo: um processo python bot.py sempre ligado.

Rate limit

Simples: um limite de quantas vezes você pode pedir algo num intervalo.

Técnico: restrição de frequência; ao exceder, a API devolve 429 com retry_after. Ver limites.

Exemplo: ~30 mensagens por segundo no Telegram.

Regex

Simples: um molde para descrever formatos de texto.

Técnico: expressão regular — linguagem de padrões para busca e extração.

Exemplo: ^[0-9]+$ aceita apenas dígitos.

Reply keyboard

Simples: botões que substituem o teclado do celular e digitam por você.

Técnico: teclado persistente cujos toques enviam mensagens de texto comuns.

Exemplo: um menu fixo com "Cardápio" e "Pedidos".

REST

Simples: um jeito organizado e comum de montar APIs.

Técnico: estilo arquitetural sobre HTTP com recursos identificados por URL e verbos semânticos.

Exemplo: GET /produtos/42.

State (estado)

Simples: lembrar em que ponto da conversa vocês estão.

Técnico: valor persistido por usuário que determina o comportamento da próxima entrada. Ver Estados.

Exemplo: etapa = "aguardando_email".

String

Simples: um texto.

Técnico: sequência de caracteres. "42" é string, não número.

Exemplo: texto da mensagem.

Token

Simples: a senha do bot.

Técnico: credencial <bot_id>:<segredo> emitida pelo BotFather. Ver Segurança.

Exemplo: 123456789:YOUR_BOT_TOKEN.

Update

Simples: o envelope com a novidade que o Telegram entrega ao bot.

Técnico: objeto que contém exatamente um tipo de acontecimento. Ver updates.

Exemplo: um update com message.text = "/start".

User ID

Simples: o número que identifica uma pessoa para sempre.

Técnico: identificador imutável do usuário. É a chave correta para relacionar dados — nunca o username.

Exemplo: 987654321.

Variável

Simples: uma caixinha com etiqueta onde você guarda um valor.

Técnico: nome ligado a um valor. No Dromornis, é local ao handler e não sobrevive ao fim do fluxo. Ver Variáveis.

Exemplo: saldo_usuario = 150.

Webhook

Simples: em vez de o bot perguntar, o Telegram avisa direto no endereço dele.

Técnico: URL HTTPS registrada para receber updates por POST. Ver Polling × Webhook.

Exemplo: https://meubot.com/telegram/updates.

WebSocket

Simples: uma linha telefônica sempre aberta entre dois programas.

Técnico: protocolo de comunicação bidirecional persistente. A Bot API não usa WebSocket — usa polling ou webhook.

Exemplo: comum em chats web em tempo real, não em bots do Telegram.

Perguntas frequentes

As dúvidas que mais aparecem, respondidas direto.

Começando

Preciso saber programar?

Não. O construtor foi feito para quem nunca escreveu código: você monta a lógica com blocos e o Dromornis escreve o Python. O que você precisa aprender é a pensar em passos ("quando acontecer X, faça Y") — e isso a Introdução ensina do zero.

Preciso saber Python?

Não para construir. Você precisa apenas ter o Python instalado (versão 3.10 a 3.13) para executar o bot que o Dromornis gera — como ter um leitor de PDF sem saber como PDFs funcionam. Saber Python ajuda a ler o código gerado e a usar blocos de código personalizado, mas é totalmente opcional.

Preciso saber PHP, JavaScript ou outra linguagem?

Não. O Dromornis gera apenas Python, usando a biblioteca python-telegram-bot. Não há suporte a outras linguagens de saída.

Posso criar bots sem escrever nenhuma linha de código?

Sim, e é o caso da maioria dos projetos. Os 192 blocos cobrem mensagens, botões, moderação, banco de dados, arquivos, HTTP, agendamento e pagamentos. Os blocos de código personalizado existem para casos excepcionais.

Posso misturar blocos visuais com código?

Sim. linha de código Python e expressão Python inserem código bruto no fluxo. Mas leia os avisos de segurança: o código roda sem sandbox, com todos os privilégios do processo, e um erro de sintaxe quebra o arquivo inteiro.

Telegram

Como conecto o bot ao Telegram?

Três passos: (1) crie o bot com /newbot no @BotFather; (2) cole o token no painel Projeto do Dromornis; (3) exporte o ZIP e rode python bot.py. O passo a passo detalhado está em Seu primeiro bot.

Como protejo meu token?

O Dromornis já faz a parte principal: o token nunca entra no bot.py — ele é lido de uma variável de ambiente em tempo de execução, e gravado apenas no .env do ZIP. O que cabe a você: não commitar o .env, não compartilhar o ZIP e não postar prints com o token. Se vazar, use /revoke no BotFather imediatamente.

Como faço o bot responder comandos?

Use o bloco quando receber comando /( ) e digite o nome sem a barra. Para o comando aparecer no menu "/" do app, adicione-o também em definir menu de comandos, dentro de quando o bot iniciar.

Meu bot não responde em grupos. Por quê?

Modo privacidade. Por padrão, em grupos o bot só recebe comandos, menções e respostas diretas a ele. Vá ao BotFather → /setprivacyDisable, e depois remova e readicione o bot ao grupo — sem esse último passo a mudança não vale.

Meu bot não bane / não apaga mensagens.

O bot precisa ser administrador do grupo com a permissão específica marcada. Ser admin genérico não basta: se "Apagar mensagens" estiver desmarcado, o bloco falha com not enough rights. Envolva ações de moderação em tentar / se der erro para descobrir isso no log em vez de em silêncio.

Posso usar mais de um bot?

Sim. Cada bot é um projeto separado, com seu próprio token e seu próprio processo rodando. O que não se pode é rodar duas instâncias do mesmo token em polling — isso gera Conflict: terminated by other getUpdates.

Dados e execução

Como armazeno usuários?

Com Banco de Dados. Crie a tabela usuarios em quando o bot iniciar, e no /start verifique com buscar 1 linha se a pessoa existe antes de inserir. Variáveis comuns não servem: elas somem ao fim do fluxo.

Por que minha variável está vazia no outro fluxo?

Porque variáveis vivem apenas dentro do fluxo que as definiu, durante aquela execução. Elas não são compartilhadas entre fluxos nem sobrevivem ao reinício do bot. Para persistir, use banco de dados ou salvar dado. Detalhes em escopo.

Como publico meu bot?

Publicar = manter o processo rodando 24 horas. Copie a pasta exportada para um servidor (VPS ou container), instale as dependências e execute sob um supervisor (systemd, pm2, supervisor) que reinicie o bot se ele cair. Bots em polling precisam de um processo sempre ligado — não funcionam em serverless que dorme. Veja publicar.

O Dromornis hospeda meu bot?

Não. Ele constrói e exporta o projeto; a execução é sua. Isso é intencional: o código gerado é Python legível e padrão, então você não fica preso a nenhuma plataforma.

Meus dados ficam salvos onde?

O projeto (os blocos) fica no localStorage do navegador — exporte o .json regularmente, porque limpar o navegador apaga tudo. Os dados do bot (banco, avisos, arquivos) ficam no servidor onde o bot roda, e precisam do seu próprio backup.

Posso agendar mensagens?

Sim, com a cada ( ) e todo dia às ( ). Atenção: os agendamentos vivem na memória do processo e somem ao reiniciar. Para assinaturas duráveis, guarde os assinantes numa tabela e recrie os agendamentos em quando o bot iniciar.

Posso usar IA no meu bot?

Sim, pelos blocos de HTTP: chame a API do provedor que preferir com enviar POST e a chave em variável de ambiente. Veja o tutorial em Integrando um modelo de IA, incluindo controle de custo e os cuidados de segurança.

Posso cobrar pagamentos?

Sim, pelo sistema de pagamentos do Telegram, depois de conectar um provedor no BotFather. Veja Pagamentos — em especial a regra de só entregar o produto no evento de pagamento concluído.

Quantos usuários o bot aguenta?

Muito mais do que a maioria dos projetos precisa. Os limites práticos aparecem primeiro nos limites da Bot API (~30 mensagens/s) e depois em fluxos mal construídos: laços pesados, buscar todas as linhas em tabelas grandes e chamadas de API sem cache. Um bot bem feito atende milhares de pessoas num servidor modesto.

Índice remissivo

Todos os assuntos do manual, em ordem alfabética.

A

B

C

D

E

F

G

H

I

J

L

M

N

P

R

S

T

U

V

W