Bot do Telegram no WordPress avisa cada artigo publicado

Celular apoiado de bruços sobre uma mesa de madeira, ao lado de uma xícara branca e de um par de óculos, com luz da manhã vindo da janela

Quatro etapas separam um bot recém-criado de um grupo do Telegram que recebe o aviso a cada artigo publicado no WordPress: criar o bot, descobrir o chat_id, mandar a primeira mensagem e prender o disparo ao momento da publicação. A terceira etapa é onde a maioria das tentativas trava, com a mensagem que some sem explicação, e é ali que a resposta da API precisa ser lida inteira.

Crie o bot no BotFather e trate o token como senha

O bot nasce dentro do próprio Telegram, em conversa com o @BotFather. O comando é /newbot. Ele pede um nome de exibição e um nome de usuário, que precisa ser único e terminar em bot, e devolve o token: uma cadeia parecida com 110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw, o exemplo que a própria documentação de recursos de bots usa. O número antes dos dois-pontos identifica o bot; o resto é a chave.

A documentação de introdução é direta sobre o peso desse valor: quem tem o token tem controle total sobre o bot. Ele não deve ficar no repositório do tema, nem colada em um campo de plugin qualquer que grave em texto puro na tabela de opções. Uma constante no wp-config.php, fora do controle de versão, resolve para a maioria dos sites.

Antes de montar qualquer mensagem, confirme que o token responde. Toda chamada à Bot API tem a mesma forma, sempre por HTTPS:

curl -s "https://api.telegram.org/bot<TOKEN>/getMe"

A resposta boa traz "ok": true e, dentro de result, o id, o first_name e o username do bot. Se vier "ok": false com error_code 401, existem três causas, nesta ordem de frequência. A primeira é a palavra bot ter ficado de fora da URL: a documentação pede explicitamente que o token seja prefixado por ela, e /110201543:AAHdq.../getMe não é um endereço válido. A segunda é um espaço ou uma quebra de linha que veio junto na hora de copiar do aplicativo, o que costuma acontecer quando o token passa por um bloco de anotações. A terceira é alguém ter rodado /token no BotFather em algum momento: esse comando gera um token novo e derruba o anterior no mesmo instante, sem aviso para quem estava usando.

Descubra o chat_id do grupo antes de escrever qualquer código

O bot precisa de um chat_id para saber o destino, e esse número não aparece em lugar nenhum da interface do Telegram. Ele sai de uma mensagem que o bot tenha recebido.

Adicione o bot ao grupo, mande ali uma mensagem qualquer e chame o método que devolve as atualizações pendentes:

curl -s "https://api.telegram.org/bot<TOKEN>/getUpdates"

Dentro de result, cada item traz message.chat.id. Copie o valor exatamente como veio, sinal incluído, e guarde junto o message.chat.type, porque o tipo do chat muda o que é possível fazer depois:

  • Em conversa privada, o identificador é o da pessoa, e a mensagem só sai se ela já tiver falado com o bot antes.
  • Em grupo comum, o identificador vale enquanto o grupo for grupo. Se ele for promovido a supergrupo, o Telegram envia uma mensagem de serviço com o campo migrate_to_chat_id, o identificador novo, e o antigo para de valer.
  • Em supergrupo e em canal com nome público, dá para usar @nomedocanal no lugar do número: o parâmetro chat_id do sendMessage aceita o nome de usuário de supergrupo ou canal nesse formato.
  • Em canal, o bot precisa ser administrador com a permissão de publicar. É o campo can_post_messages, que a documentação descreve como o direito do administrador de publicar mensagens no canal, e que só existe para canais.

Quando o getUpdates volta com result vazio, quase sempre é um destes quatro casos. O mais comum é o modo de privacidade, ligado por padrão em todo bot que não entrou no grupo como administrador: nessa configuração o bot recebe apenas os comandos endereçados a ele, no formato /comando@nomedobot, as respostas às mensagens dele, as mensagens de serviço e o /start quando ele foi o último bot a falar no grupo. Uma mensagem solta como “oi” não chega nele. Mande /start@nomedobot no grupo e a atualização aparece. Para desligar o modo de privacidade de vez existe o /setprivacy no BotFather, com a ressalva que a própria documentação faz: o bot precisa ser removido e adicionado de novo ao grupo para a mudança valer.

Se a mensagem de comando também não aparecer, procure um webhook configurado para esse bot. A documentação é categórica: o getUpdates não funciona quando existe um webhook ativo, porque os dois são formas mutuamente exclusivas de receber atualizações. Cheque com getWebhookInfo, que devolve o campo url vazio quando o bot está em long polling, e use deleteWebhook para voltar ao método manual. Sobram dois motivos, ambos ligados ao tempo. As atualizações ficam guardadas no servidor por no máximo 24 horas, então a mensagem que você mandou no grupo ontem já não está mais lá. E uma chamada anterior com offset confirma e descarta tudo o que veio antes daquele ponto, o que costuma acontecer quando duas pessoas depuram o mesmo bot ao mesmo tempo.

Dispare a primeira mensagem e leia a resposta inteira

Com token e chat_id em mãos, o envio é um POST:

curl -s -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" -H "Content-Type: application/json" -d '{"chat_id":"-1001234567890","text":"Artigo novo no ar"}'

O campo text aceita de 1 a 4096 caracteres depois da interpretação das entidades. Folgado para um aviso de artigo, apertado para quem decide mandar o excerto inteiro junto: um resumo de 500 palavras passa do limite e a chamada volta com erro.

A parte que mais gente pula é ler a resposta. A Bot API sempre devolve um objeto com o campo booleano ok; quando ele é false, vem também description, em texto legível, e error_code. Alguns erros trazem ainda um objeto parameters feito justamente para tratamento automático. É a mesma disciplina de leitura de erro que vale para os códigos 401, 404 e 429 de um webhook: o número diz a categoria, a descrição diz o caso.

O erro que trava a maior parte dos primeiros testes é o Forbidden: bot can't initiate conversation with a user. Ele aparece quando alguém tenta mandar a primeira mensagem para uma pessoa, e não para um grupo. A regra está na introdução aos bots: bots não podem começar conversas com usuários, e o usuário precisa adicionar o bot a um grupo ou mandar uma mensagem para ele antes. Não existe contorno. Em uma discussão no repositório do servidor da Bot API, um desenvolvedor descreve o caso preciso: recebeu o evento de pedido de entrada em um grupo, tentou responder à pessoa no privado e levou o mesmo 403, porque clicar num link de convite não conta como iniciar conversa. Para avisar uma equipe, isso deixa de ser obstáculo: o destino é um grupo ou um canal, onde a restrição não se aplica.

Duas outras respostas aparecem cedo. Uma é o erro de formatação: se você mandar parse_mode como MarkdownV2, todo caractere reservado precisa vir escapado, e títulos de artigo costumam ter parênteses, hífen e ponto de exclamação. Mande a primeira mensagem sem parse_mode nenhum, confirme que ela chega, e só depois acrescente formatação. A outra é o 429, que vem acompanhado de parameters.retry_after, definido na documentação como o número de segundos que faltam para o pedido poder ser repetido. O FAQ oficial dá os limites que provocam isso: cerca de uma mensagem por segundo em um mesmo chat, no máximo 20 mensagens por minuto em um grupo e algo em torno de 30 por segundo em envio em massa. Um blog que publica cinco artigos por dia não encosta nesses números; uma rotina que reprocessa o arquivo inteiro do site encosta na primeira execução.

Ligue o disparo à publicação e confirme que ele continua vivo

No WordPress, o gancho certo é o transition_post_status, que dispara quando um post muda de status e recebe o status novo, o status antigo e o objeto do post. A verificação obrigatória é comparar os dois: o gancho também roda quando o post é salvo sem mudança de status, e sem essa comparação a equipe recebe um aviso novo a cada correção de vírgula em um artigo já publicado. Interessa apenas a transição em que o status novo é publish e o antigo não era.

O envio em si é um wp_remote_post. O tempo limite padrão de uma requisição HTTP no WordPress é de 5 segundos, e ela é bloqueante por padrão: enquanto o Telegram não responde, o editor fica esperando a tela de publicação voltar. Passar blocking como false devolve o controle na hora, mas a documentação avisa o que se perde: a chamada não recebe resposta nenhuma do servidor remoto. Você troca cinco segundos de espera pela descrição do erro. Para um aviso de artigo, o caminho que se sustenta é manter a chamada bloqueante com tempo limite curto e gravar o par error_code e description em log, porque é esse texto que responde, três semanas depois, por que o grupo ficou em silêncio. Quando a lógica de fila e de repetição começa a crescer, releia o que um webhook faz e o que ele não faz antes de seguir montando peça por peça.

Montar isso à mão dá trabalho de uma tarde. A manutenção é que se estende: token trocado, grupo promovido a supergrupo, log que ninguém lê. O Post Ping cobre essa parte com uma mensagem automática no grupo ou canal a cada artigo publicado, pela Bot API, com o token guardado criptografado no banco do site, e roda uma verificação diária dos artigos que ficaram sem notificação, que é justamente quando a falha passa despercebida. A lista completa do que o plugin faz está em recursos.

Feita a ligação, publique um post de teste de verdade, com status publish, e confira se a mensagem entra no grupo no mesmo minuto. Depois edite esse mesmo post, salve de novo e confirme que nenhuma segunda mensagem chegou: é o que prova que a comparação de status está correta. Faltando isso, provoque a falha de propósito. Rode /token no BotFather para invalidar o token em uso e publique outro post de teste. O aviso está sob controle se o seu log registrar o 401 com a descrição vinda da API. Se a única forma de descobrir a pane for alguém da equipe comentar que faz tempo que não vê nada no grupo, falta o registro do erro, não o bot.

Este blog publica sozinho nas redes

Cada artigo daqui vira post no Instagram e no Facebook automaticamente: legenda escrita por IA, card visual gerado na hora. Quem faz isso é o Post Ping, o mesmo plugin que você pode instalar no seu WordPress.

Ver planos do Post Ping