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.
É 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.
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 InicianteIntermediárioAvanç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:
Formato
Como parece
O que faz
Exemplo
Evento (chapéu)
Topo arredondado, nada encaixa acima
Começa um fluxo
quando receber /start
Ação (comando)
Encaixe em cima e embaixo
Faz alguma coisa acontecer
enviar mensagem ( )
Valor (reporter)
Bordas arredondadas, encaixa dentro de outro
Devolve um dado
id 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?.
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.
Entenda o que é um botConceitos: bot, fluxo, evento, bloco, variável. → Introdução aos conceitosIniciante
Crie seu bot no TelegramFale com o @BotFather, escolha o nome e receba o token. → Criando o bot no BotFatherIniciante
Conecte o token ao DromornisCole o token no painel Projeto e configure a variável de ambiente. → Configurando o tokenIniciante
Crie seu primeiro fluxoUm evento /start e uma mensagem de resposta. Exporte e rode. → Seu primeiro botIniciante
Aprenda variáveisGuarde o nome do usuário, monte textos dinâmicos, entenda escopo. → VariáveisIniciante
Aprenda condiçõesFaça o bot responder diferente conforme a situação. → Controle e OperadoresIntermediário
Aprenda teclados e botõesMenus clicáveis, callbacks e navegação. → TecladoIntermediário
Aprenda banco de dadosGuarde usuários, pedidos e histórico entre reinícios. → Banco de DadosIntermediário
Aprenda APIs e automação avançadaConsuma serviços externos, agende tarefas, trate erros. → AvançadoAvançado
Quanto tempo leva
Etapa
Tempo estimado
Você saberá fazer
1 a 4
~40 minutos
Um bot que responde comandos, rodando de verdade
5 a 7
~2 horas
Menus, respostas personalizadas, navegação por botões
8 a 9
~4 horas
Cadastro 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.
No Telegram, procure por @BotFather (com o selo azul de verificado).
Envie /newbot.
Ele pergunta o nome do bot — o nome de exibição, pode ter espaços e acentos. Ex.: Bot da Padaria.
Ele pergunta o username — precisa ser único no Telegram inteiro e terminar em bot. Ex.: padaria_do_ze_bot.
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 defstart_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.effective_chat.send_message("Olá! Eu sou o bot da padaria. 🥖")
defmain() -> 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ção
Conteúdo
Quando usar
Projeto completo (.zip)
bot.py, requirements.txt, README.md, .env.example e, se você preencheu o token, um .env já pronto
Para rodar o bot. É o que você quer agora.
Somente código (bot.py)
Só o arquivo Python
Para revisar o código ou colar num projeto existente
Projeto (.json)
Formato interno do Dromornis
Backup 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
Sintoma
Causa provável
Correção
Erro InvalidToken ao iniciar
Token errado, com espaço extra ou variável de ambiente vazia
Confira o .env: sem aspas, sem espaços, nome da variável igual ao configurado
RuntimeError: There is no current event loop
Python 3.14+
Instale Python 3.12 ou 3.13 e recrie o venv com py -3.12 -m venv .venv
ModuleNotFoundError: telegram
Venv não ativado ou dependências não instaladas
Ative o venv e rode pip install -r requirements.txt novamente
Bot inicia mas não responde
Nenhum evento no workspace, ou você está falando com o bot errado
Confira a aba Validação e o @username exato
Conflict: terminated by other getUpdates
Duas cópias do mesmo bot rodando ao mesmo tempo
Feche a outra instância. Um token só pode ter um processo em polling
PowerShell recusa Activate.ps1
Política de execução do Windows
Rode Set-ExecutionPolicy -Scope Process RemoteSigned e tente de novo
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:
Comando
O que faz
Observação
/newbot
Cria um bot e devolve o token
Username deve ser único e terminar em bot
/mybots
Lista seus bots e abre o painel de configuração
Caminho mais prático para tudo abaixo
/token
Mostra o token de um bot existente
Use quando você perdeu o token
/revoke
Invalida o token atual e gera outro
Use imediatamente se o token vazar
/setdescription
Texto exibido na conversa vazia
Também dá para fazer pelo bloco definir descrição do bot
/setabouttext
Texto curto no perfil do bot
Limite de 120 caracteres
/setuserpic
Foto de perfil do bot
Só pelo BotFather
/setcommands
Lista de comandos no menu "/"
Ou use o bloco definir menu de comandos
/setjoingroups
Permite ou bloqueia adicionar o bot a grupos
Desligue se seu bot é só para conversa privada
/setprivacy
Modo privacidade em grupos
Crucial — veja abaixo
/deletebot
Apaga o bot permanentemente
Irreversí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
Painel Projeto → campo Token do bot → cole o token.
Aba Configuração → Variá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.
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:
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:
Identificador da conversa. Em privado é igual ao user.id; em grupos é negativo (ex.: -1001234567890)
id do chat
from.id
Identificador único e permanente da pessoa. Nunca muda
id do usuário
message_id
Número da mensagem dentro daquele chat. Necessário para editar, apagar, fixar ou responder
id da mensagem
from.username
@apelido. Opcional — muita gente não tem — e pode mudar a qualquer momento
username do usuário
from.first_name
Nome de exibição. Sempre existe
nome do usuário
from.language_code
Idioma do app da pessoa (pt-br, en). Útil para respostas multilíngues
idioma do usuário
chat.type
private, group, supergroup ou channel
tipo do chat, é conversa privada?, é grupo?
callback_query.data
Texto escondido que você definiu no botão inline. Máx. 64 bytes
dado 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.
Polling
Webhook
Configuração
Nenhuma
Domínio + HTTPS + porta aberta
Latência
Ótima na prática (long polling, quase instantâneo)
Mínima
Custo em escala
Uma conexão sempre aberta por bot
Requisiçõ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 Dromornis
✅ application.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 update
↓
3. 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
Tipo
chat.type
Características
Cuidados
Privado
private
Um a um. chat.id = user.id. O bot recebe tudo
O bot não pode iniciar a conversa; a pessoa precisa mandar algo primeiro
Grupo
group
Até 200 membros, formato antigo
Pode virar supergrupo automaticamente e o chat.id muda
Supergrupo
supergroup
Até 200 mil membros, tópicos, permissões granulares. id começa com -100
Moderação exige o bot como admin com a permissão específica
Canal
channel
Difusão. Só admins publicam
Nã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ão
O que acontece ao clicar
Quando usar
callback_data
Envia um texto oculto ao bot (máx. 64 bytes)
Menus, navegação, confirmações
url
Abre um link no navegador
Site, documento, suporte externo
web_app
Abre uma Mini App dentro do Telegram
Formulários ricos, catálogos
switch_inline_query
Abre um chat com o bot já pré-digitado
Compartilhamento 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.
Deep linking e parâmetros do /start
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:
Modo
Vantagem
Desvantagem
Texto puro (None)
Nada quebra. Nenhum caractere é especial
Sem negrito, itálico ou links formatados
HTML
Tolerante e fácil de escapar (só & < >)
Só um subconjunto de tags é aceito
MarkdownV2
Sintaxe curta, cobre tudo
Rigoroso: 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
Limite
Valor
Consequê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/s
Idem
Mensagens por minuto, mesmo grupo
~20/min
Idem
Texto de uma mensagem
4096 caracteres
Erro message is too long
Legenda de mídia
1024 caracteres
Erro na chamada
callback_data
64 bytes
Mensagem rejeitada
Download de arquivo pelo bot
20 MB
Erro file is too big
Upload de arquivo pelo bot
50 MB
Erro na chamada
Botões por linha do teclado
8 (recomendado: 2 a 3)
Layout ilegível no celular
Apagar mensagem de outro usuário
Sem limite de tempo para o bot admin
—
Editar mensagem própria
48 horas
message 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
Erro
Significado
O que fazer
401 Unauthorized
Token inválido ou revogado
Verifique o .env; gere outro com /token
403 Forbidden: bot was blocked by the user
A pessoa bloqueou o bot
Marque como inativo no banco e pare de enviar
403 Forbidden: bot is not a member
O bot foi removido do grupo
Remova o grupo da sua lista de destinos
400 Bad Request: chat not found
chat_id errado, ou a pessoa nunca falou com o bot
Confirme o id; lembre que o bot não inicia conversas
400 message is not modified
Você editou a mensagem para o mesmo conteúdo
Compare antes de editar, ou ignore este erro
400 message to delete not found
Mensagem já apagada ou fora do alcance
Envolva em tentar / se der erro
400 not enough rights
O bot é admin mas falta a permissão específica
Marque a permissão exata nas configurações do grupo
400 can't parse entities
Markdown/HTML malformado
Escape o conteúdo dinâmico ou use texto puro
429 Too Many Requests
Rate limit
Aguarde 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.
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.
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
Atalho
Ação
Ctrl+Z
Desfazer
Ctrl+Shift+Z
Refazer
Ctrl+C / Ctrl+V
Copiar / colar bloco selecionado
Delete
Apagar bloco selecionado
Ctrl + roda
Zoom
Esc
Fechar 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:
O que dispara? — vira o bloco de evento.
O que precisa ser decidido? — vira as condições.
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:
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.
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
Tipo
O que é
Exemplo
Cuidado
String (texto)
Qualquer sequência de caracteres
"Olá", "42"
"42" é texto, não número
Number
Inteiro ou decimal
42, 3.14
Use ( ) como número para converter texto
Boolean
Verdadeiro 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 / vazio
Ausência de valor
Usuário sem sobrenome
Sempre 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.
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
Evento
Ação
Quem decide quando roda
O usuário / o Telegram
Você
Formato
Chapéu (nada encaixa acima)
Bloco empilhável
Quantos por fluxo
Exatamente 1, no topo
Quantos você quiser
No código gerado
Uma função async + um handler registrado
Uma 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 Iniciantetg_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 /( ) Iniciantetg_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 Iniciantetg_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 ( ) Iniciantetg_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áriotg_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áriotg_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áriotg_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
Bloco
Dispara quando…
Nível
quando receber /start
O usuário envia /start ou clica em Iniciar
Inic.
quando receber comando /( )
Chega o comando que você nomeou
Inic.
quando receber uma mensagem
Chega qualquer texto que não seja comando
Inic.
quando a mensagem contiver ( )
O texto contém o trecho informado
Inic.
quando o botão ( ) for pressionado
Alguém clica num botão inline com esse callback
Interm.
quando o bot iniciar
O processo do bot sobe (uma vez por execução)
Interm.
quando um usuário entrar no grupo
Um novo membro entra
Interm.
quando um usuário sair do grupo
Um membro sai ou é removido
Interm.
quando alguém pedir para entrar no grupo
Grupo com aprovação recebe um pedido
Interm.
quando alguém digitar @seubot …
Consulta inline em qualquer conversa
Avanç.
quando uma mensagem for editada
Alguém edita uma mensagem já enviada
Interm.
quando alguém responder uma enquete
Voto em enquete não anônima
Interm.
quando o status do bot no chat mudar
O bot é adicionado, removido ou promovido
Avanç.
quando chegar dado de um Web App
Uma Mini App envia dados ao bot
Avanç.
quando uma chamada de vídeo/voz começar
Videochamada iniciada no grupo
Interm.
quando uma chamada de vídeo/voz terminar
Videochamada encerrada
Interm.
quando o usuário compartilhar usuário(s)
Resposta a um botão "pedir usuários"
Avanç.
quando o usuário compartilhar um chat
Resposta a um botão "pedir chat"
Avanç.
quando um pagamento estiver para ser confirmado
Pré-checkout de pagamento
Avanç.
quando um pagamento for concluído
Cobrança paga com sucesso
Avanç.
quando alguém abrir um jogo
Clique em "Jogar" num Telegram Game
Avanç.
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 valor
Só faz sentido dentro de
Devolve
texto da mensagem editada
quando uma mensagem for editada
Texto novo
novo status do bot no chat
quando o status do bot mudar
member, administrator, left, kicked…
dados recebidos do Web App
quando chegar dado de um Web App
String enviada pela Mini App
resposta da enquete ( )
quando alguém responder uma enquete
Opções escolhidas / id da enquete
IDs dos usuários compartilhados
quando o usuário compartilhar usuário(s)
Lista de ids
ID do chat compartilhado
quando o usuário compartilhar um chat
Número
informação do pagamento ( )
quando um pagamento for concluído
Valor, moeda, payload…
nome do jogo aberto
quando alguém abrir um jogo
Short 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 ( ) Iniciantetg_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 ( ) Iniciantetg_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áriotg_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áriotg_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.
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.
Bloco
Formato
Aparece como
Observação
enviar foto ( )
JPG, PNG, WebP
Imagem no chat
Máx. 10 MB por URL
enviar vídeo ( )
MP4
Player de vídeo
Outros formatos podem virar documento
enviar documento ( )
Qualquer
Arquivo para download
Use para PDF, planilha, ZIP
enviar áudio ( )
MP3, M4A
Faixa de música
Mostra título e artista
enviar áudio de voz ( )
OGG/OPUS
Mensagem de voz
Onda sonora, como gravação
enviar nota de vídeo ( )
MP4 quadrado
Vídeo redondo
Máx. 1 minuto
enviar animação/GIF ( )
GIF, MP4 mudo
Loop 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
Bloco
Para 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 feedback
O índice da resposta correta começa em 0
encerrar enquete ( )
Fecha para novos votos
Precisa do id da mensagem da enquete — guarde numa variável ao enviar
enviar localização ( ) ( )
Ponto fixo no mapa
Latitude e longitude são números decimais
enviar localização ao vivo
Ponto que se move por até 24 h
Retorna um id necessário para atualizar/parar
enviar local ( ) (venue)
Estabelecimento com nome e endereço
Melhor que localização pura para lojas
enviar contato ( ) ( )
Cartão de contato
Telefone em formato internacional
enviar ( ) (dado/dardo)
Emoji animado com resultado aleatório
O resultado é do Telegram, não seu
Utilitários de conversa
mostrar status ( ) — "digitando…" Iniciantetg_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 Iniciantetg_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çadotg_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:
Bloco
Efeito
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 chat
Botão ao lado do campo de texto abre os comandos
definir botão de menu do chat como Web App
Botã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
Bloco
Para 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 recebida
Fixa no topo. Exige admin com permissão de fixar
desafixar mensagens do chat
Remove 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ão
Edita 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 inline
Teclado de respostas
Onde aparece
Preso abaixo da mensagem
No lugar do teclado do telefone
Ao clicar
Gera callback_query (invisível no chat)
Envia o texto do botão como mensagem normal
Evento que trata
quando o botão ( ) for pressionado
quando a mensagem contiver / quando receber uma mensagem
Fica no histórico
Sim, junto com a mensagem
Persiste até você removê-lo
Polui o chat
Não
Sim — cada toque vira uma mensagem
Permite editar a mensagem
Sim (menus navegáveis)
Não
Melhor para
Menus, paginação, confirmações
Menu 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 Appnã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:
Bloco
O que faz
Evento que trata a resposta
botão ( )
Envia o próprio texto como mensagem
quando a mensagem contiver
botão ( ) pedir usuário(s)
Abre a lista de contatos para escolher pessoas
quando o usuário compartilhar usuário(s)
botão ( ) pedir chat
Abre a lista de grupos/canais
quando 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:
Evento quando alguém digitar @seubot ….
Leia o texto com texto da consulta inline.
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)
Bloco
Devolve
Uso típico
usuário é administrador?
Sim/Não
Guarda no topo de comandos administrativos
autor é um bot?
Sim/Não
Evitar dar boas-vindas ou punir outros bots
quantidade de membros do grupo
Número
Estatísticas, regras por tamanho de grupo
ID do usuário respondido
Número
Aplicar ação em quem foi citado com "responder"
link de convite do grupo
Texto
Compartilhar convite. Exige admin
quantidade de boosts do usuário ( )
Número
Benefícios para quem impulsiona o canal
número de avisos do usuário ( )
Número
Consultar o histórico antes de punir
usuário é administrador? Intermediáriotg_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
Bloco
Efeito
Pode voltar?
Quando usar
expulsar usuário ( )
Remove do grupo
✅ Sim, na hora
Chamar atenção sem punir de verdade
banir usuário ( )
Remove e bloqueia a entrada
❌ Só com desbanimento
Spam, abuso, reincidência
silenciar usuário ( ) por ( ) min
Impede de escrever, continua no grupo
✅ Automático ao fim do prazo
Punição proporcional — a mais usada
remover silêncio do usuário ( )
Devolve a permissão de escrever
—
Perdão antecipado
desbanir usuário ( )
Remove o banimento
—
Revisã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
Bloco
Efeito
Permissão exigida
definir título do grupo ( )
Muda o nome
Alterar informações
definir descrição do grupo ( )
Muda a descrição
Alterar informações
definir foto do grupo (URL) ( )
Baixa a imagem e aplica
Alterar informações
promover usuário ( ) a administrador
Promove com permissões padrão
Adicionar admins
rebaixar administrador ( )
Remove todas as permissões
Adicionar admins
aprovar pedido de entrada
Aceita quem pediu para entrar
Convidar usuários
recusar pedido de entrada
Nega o pedido
Convidar usuários
sair do grupo
O 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:
Bloco
Efeito
criar tópico chamado ( ) cor ( )
Cria e devolve o id — guarde numa variável
ID do tópico atual
Em 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
Bloco
Devolve
Sempre existe?
Observação
ID do usuário
Número
✅ Sim
A forma correta de identificar alguém. Permanente
@ username do usuário
Texto
❌ Não
Opcional e mutável. Nunca use como chave
primeiro nome
Texto
✅ Sim
Ideal para saudações
sobrenome
Texto
❌ Não
Muita gente não preenche
nome completo
Texto
✅ Sim
Nome + sobrenome quando houver
ID do chat
Número
✅ Sim
Em DM é igual ao ID do usuário; em grupo é negativo
texto da mensagem
Texto
❌ Não
Vazio em foto sem legenda, sticker, áudio
ID da mensagem
Número
✅ Sim
Necessário para editar, apagar e fixar
argumentos do comando
Lista de textos
✅ (pode ser vazia)
/soma 2 3 → ["2","3"]
idioma do usuário
Texto
❌ Não
pt-br, en… Para bots multilíngues
tipo do chat
Texto
✅ Sim
private, group, supergroup, channel
conversa é privada (DM)?
Sim/Não
✅ Sim
Mais legível que comparar o tipo
conversa é em grupo?
Sim/Não
✅ Sim
Cobre grupo e supergrupo
texto da mensagem respondida
Texto
❌ Não
Só quando o usuário usou "responder"
texto da consulta inline
Texto
❌ Não
Só dentro do evento de consulta inline
foto de perfil do usuário ( )
file_id
❌ Não
Vazio se a pessoa não tem foto ou restringiu
agora (( ))
Data/hora ou número
✅ Sim
Horá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:
Operador
Verdadeiro quando…
Analogia
Exemplo
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:
A
B
A e B
A ou B
não A
V
V
✅ V
✅ V
❌ F
V
F
❌ F
✅ V
❌ F
F
V
❌ F
✅ V
✅ V
F
F
❌ 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 Iniciantecontrols_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. Simples2. Com senão3. 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
Bloco
Verdadeiro quando
Cuidado
( ) = ( )
Os valores são iguais
"10" ≠ 10 — converta antes
( ) ≠ ( )
São diferentes
Idem
( ) > ( ) / < / ≥ / ≤
Comparação numérica
Com texto, compara alfabeticamente
( ) contém ( )
O texto inclui o trecho
Casa no meio das palavras
( ) está vazio
Texto ou lista sem conteúdo
A forma segura de testar campos opcionais
( ) corresponde ao padrão ( )
Regex encontra algo
Poderoso 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
Bloco
Repete
Use para
repetir ( ) vezes
Um número fixo
Quando você sabe a quantidade
repetir enquanto / até
Enquanto a condição valer
Quando o fim depende de algo variável
para cada item ( ) na lista ( )
Uma vez por item
O mais usado: percorrer resultados do banco ou de uma API
sair / continuar
Interrompe ou pula uma volta
Sair 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áriotg_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.
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
Bloco
Faz
( ) + − × ÷ ^ ( )
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).
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ível
Como fazer
Dura
Use para
Temporária / de fluxo
Blocos de Variáveis
Uma execução do fluxo
Cálculos intermediários, resultados de API, contadores locais
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:
Tipo
Exemplo de origem
Como testar
String
texto da mensagem, juntar
( ) está vazio
Number
ID do usuário, ( ) como número, contas
Comparações >, <
Boolean
é administrador?, comparações
Direto no se
Array
argumentos do comando, buscar todas as linhas
tamanho de ( ), está vazia
Object
buscar 1 linha, baixar JSON
obter chave ( ) do dicionário ( )
Null / vazio
Campo 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úmeroantes de qualquer conta ou comparação numérica.
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 comum
Bloco 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.
Lista
Dicionário
Acesso
Por posição (1, 2, 3…)
Por chave ("nome", "preco")
Ordem
Importa
Não importa
Exemplo
["pão","leite","café"]
{"nome":"Ana","idade":30}
Vem de
argumentos do comando, buscar todas as linhas, listar arquivos
buscar 1 linha, baixar JSON
Use quando
Sã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
Bloco
Faz
Observação
criar lista com
Monta uma lista literal
Expanda pela engrenagem ⚙️
tamanho de ( )
Quantos itens
Sempre teste antes de acessar por posição
( ) está vazia
Sim/Não
Guarda contra listas sem resultado
item ( ) de ( )
Pega por posição
Começa em 1, não em 0
definir item ( ) de ( ) como ( )
Substitui um item
—
encontrar ( ) em ( )
Posição do item
0 se não encontrar
item aleatório de ( )
Sorteia um item
Frases 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
Bloco
Faz
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.
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
Bloco
Tipo
Faz
Devolve
inserir na tabela ( ) dados ( )
Ação
Cria uma linha a partir de um dicionário
—
buscar todas as linhas da tabela ( )
Valor
Lê a tabela inteira
Lista de dicionários
buscar linhas ( ) onde ( ) = ( )
Valor
Filtra por igualdade
Lista de dicionários
buscar 1 linha ( ) onde ( ) = ( )
Valor
Primeira ocorrência
Dicionário ou vazio
atualizar ( ) definir ( ) = ( ) onde ( ) = ( )
Ação
Altera linhas que casam
—
apagar da tabela ( ) onde ( ) = ( )
Ação
Remove linhas
—
contar linhas da tabela ( )
Valor
Total de registros
Número
executar SQL ( )
Ação
SQL bruto de escrita
—
consultar SQL ( )
Valor
SELECT bruto
Lista 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:
Bloco
Método
Devolve
Use quando
baixar texto da URL ( )
GET
Texto puro
A resposta não é JSON
baixar JSON da URL ( )
GET
Dicionário/lista
O caso mais comum
enviar POST para ( ) dados (JSON) ( ) cabeçalhos ( )
POST
Resposta convertida
Criar/enviar dados
requisição ( ) para ( )
Qualquer
Dicionário com status e corpo
PUT, PATCH, DELETE, ou quando você precisa ver o status
Os verbos HTTP
Verbo
Significa
Analogia
Muda dados?
GET
Buscar informação
Ler o cardápio
Não
POST
Criar algo novo
Fazer um pedido
Sim
PUT
Substituir por completo
Trocar o pedido inteiro
Sim
PATCH
Alterar um pedaço
"Tira a cebola"
Sim
DELETE
Remover
Cancelar o pedido
Sim
Códigos de status
Faixa
Significa
Exemplos
O que fazer
2xx
Deu certo
200 OK, 201 Criado
Seguir o fluxo
3xx
Redirecionamento
301, 302
Geralmente automático
4xx
Erro seu
400, 401, 403, 404, 429
Corrija a chamada; não adianta repetir
5xx
Erro do servidor
500, 502, 503
Tente 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
Bloco
Faz
Use para
a cada ( ) ( ) enviar ( )
Repete em intervalo fixo
Lembretes periódicos, monitoramento
todo dia às ( )h ( )min enviar ( )
Uma vez por dia no horário
Bom 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 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
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
Bloco
Tipo
Faz
ler arquivo ( ) padrão ( )
Valor
Lê o conteúdo inteiro como texto; devolve o padrão se não existir
( ) arquivo ( ) com ( )
Ação
escrever substitui tudo; acrescentar adiciona ao final
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
No BotFather: /mybots → seu bot → Payments.
Escolha um provedor disponível no seu país e conecte a conta.
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.
Bloco
Papel
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 confirmado
Evento de pré-checkout. Última chance de validar
responder pré-checkout ( )
Aceita ou recusa. Obrigatório
quando um pagamento for concluído
Evento 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.
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.
Bloco
Papel
enviar jogo ( )
Envia o cartão do jogo pelo short name registrado
quando alguém abrir um jogo
Evento disparado ao clicar em "Jogar"
nome do jogo aberto
Qual 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.
┌── 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
Não verificar admin dentro do callback. Quem descobrir o callback_data dispara o broadcast.
Não tratar erro por usuário. O primeiro que bloqueou o bot derruba a fila inteira.
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:
O bot está rodando? Olhe o terminal. Sem processo, nada funciona.
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.
A condição é verdadeira? Registre o valor comparado antes do se.
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".
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
Ferramenta
Onde
Responde
Aba Validação
Editor
"O que montei está estruturalmente errado?"
Aba Código Python
Editor
"O bloco que adicionei virou código?"
Terminal do bot
Onde você rodou python bot.py
"O que aconteceu de verdade em execução?" — a mais importante
Bloco registrar no log
Dentro 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
Verifique
Como
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 → /setprivacy → Disable → remova 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)
Causa
Exemplo
Correçã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 e
dia ≥ 1 ou dia ≤ 5
Troque por e
Espaço invisível
"sim "
Compare com contém ou limpe o texto
Condição anterior já parou o fluxo
Um parar por aqui acima
Reordene 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
Status
Causa
Correção
401
Chave ausente ou inválida
Confirme a variável de ambiente e o .env
403
Chave válida sem permissão
Verifique o plano/escopo da chave
404
URL errada
Teste a URL no navegador
422 / 400
Parâmetro faltando ou malformado
Leia a mensagem de erro da API no log
429
Excedeu o limite
Reduza a frequência; use cache
5xx
Problema do outro lado
Tente de novo depois; não é seu bug
JSON inválido
A API devolveu HTML de erro
Registre 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ência — 429 e enfileiramento.
esperar longo no meio de um fluxo comum.
Erros ao iniciar o bot
Mensagem no terminal
Correção
ModuleNotFoundError: No module named 'telegram'
Ative o venv e rode pip install -r requirements.txt
telegram.error.InvalidToken
Token errado, com espaço, ou .env com nome de variável diferente do configurado
RuntimeError: There is no current event loop
Python 3.14+. Use 3.12 ou 3.13
Conflict: terminated by other getUpdates
Outra instância do mesmo bot está rodando
IndentationError / SyntaxError
Bloco de código Python personalizado malformado
PermissionError
O bot não tem permissão de escrita na pasta
UnicodeDecodeError
Arquivo lido com codificação diferente da esperada
Como testar um fluxo direito
Caminho feliz: tudo certo, entrada válida.
Entrada faltando: comando sem argumentos, resposta vazia.
Entrada inválida: texto onde se espera número, e-mail sem @.
Sem permissão: teste como usuário comum um comando de admin.
Contexto errado: rode um comando de grupo numa conversa privada.
Repetição: execute o comando duas vezes seguidas — duplica algo?
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
Elemento
Convenção
Exemplo
Variável
minusculas_com_underline, descrevendo o conteúdo
saldo_usuario
Tabela
Plural, minúsculas, sem acento
usuarios, pedidos
Coluna
Singular, minúscula
nome, criado_em
Comando
Curto, óbvio, em português
/precos, não /pl
callback_data
área:ação:id
pedido:cancelar:42
Chave de salvar dado
Prefixo por assunto
cfg_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ção
Precisa de tentar?
Por quê
Chamada HTTP
✅ Sempre
A rede falha; o serviço cai
Ação de moderação
✅ Sempre
Permissão pode faltar
Envio em massa
✅ Sempre
Usuários bloqueiam o bot
Leitura de arquivo
✅ Sim
Pode não existir
Conversão de entrada do usuário
✅ Sim
O usuário digita qualquer coisa
Enviar mensagem simples
Opcional
Raramente falha em conversa ativa
Variáveis e cálculos internos
❌ Não
Esconderia 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:
Existe? A lista de argumentos pode estar vazia.
É do tipo certo? Texto que deveria ser número.
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
Item
Por quê
Aba Validação limpa
Nenhum bloco órfão ou entrada vazia
/start e /ajuda existem
Primeiro contato e descoberta
Menu de comandos definido
Descoberta pelo botão "/"
Todo comando de admin tem guarda
Segurança
Todo callback confirma o clique
Experiência
Toda chamada externa em tentar
Resiliência
Nenhum segredo em bloco de texto
Segurança
.env fora do git
Segurança
Supervisor reiniciando o processo
Disponibilidade
Backup do banco automatizado
Continuidade
Testado com um usuário comum
Você 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 texto
Deixar no .env, lido por os.getenv()
Commitar o .env
Adicionar .env ao .gitignore antes do 1º commit
Postar print do terminal com o token
Cobrir ou substituir por YOUR_BOT_TOKEN
Compartilhar o ZIP exportado com .env
Enviar só o bot.py, ou o ZIP sem token
Reaproveitar o mesmo bot em dev e produção
Um 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
Risco
Como acontece
Defesa
Injeção de SQL
Texto do usuário concatenado em consultar SQL
Use os blocos visuais de banco, que parametrizam
Execução de código
Texto do usuário dentro de expressão Python
Jamais construir código a partir de entrada
Travessia de diretório
Nome de arquivo com ../
Gerar o nome você mesmo, a partir de ids
Quebra de formatação
Nome com * ou < em MarkdownV2/HTML
Escapar, ou usar texto puro
Escalada de privilégio
Permissão baseada em username ou no ?start=
Sempre user.id conferido no banco
Negação de serviço
Mensagens enormes, spam de comandos caros
Limite 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.
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).
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.
Dado
Cache razoável
Cotação, clima
5 a 15 minutos
Catálogo de produtos
1 hora, invalidado ao editar
Saldo, estoque
Nã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
Camada
Sobrevive ao fim do fluxo
Sobrevive ao reinício
Sobrevive à 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.
Bloco
Categoria
Tipo
Descrição
Identificador
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 Listas — se, 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/clima → 200 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 → /setprivacy → Disable, 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.