Neste tutorial vou mostrar o passo a passo de como criar seu bot de telegram usando javascript e o serviço serverless netlify functions. Ao final do post vou deixar nas disposições finais as desvantagens de usar este método, apesar de que o serviço ser gratuíto é bem interessante de usar.
Sumário
- Criando Seu Bot
- Iniciando o Projeto
- Codando o Bot
- Subindo o Código Para o Netlify
- Configurando Webhook
- Disposições finais
Criando Seu Bot
Primeiro vamos criar nosso bot usando o @BotFather.
Clique em Start/Iniciar para iniciar o processo de criação do bot. Use o comando /newbot
para criarmos o nosso bot.
Em seguida ele irá perguntar pelo nome do bot, só para tests, vou dar o nome de Olá Usuário.
E então devemos escolher o nome de usuário do bot, que deve terminar com “bot”, vou dar o nome de ola_usuario_bot.
Ele irá devolver as informações do bot e seu token de acesso como a seguir.
Configurando inline mode.
Acesse seus bots usando o comando /mybots
e selecione o bot que acabou de criar.
Em seguida clique em Bot Settings
.
E então em Inline Mode
e então em Turn On.
Agora podemos ir para o código.
Iniciando o Projeto
Como é um projeto simples em javascript, esta é a configuração básica para podermos começar.
Sendo que precisamos do arquivo netlify.toml para definir onde nossas funções serveless estarão, e uma pasta functions para colocar as tais funções.
Abra a pasta raíz do projeto na ide que preferir.
Arquivo netlify.toml
Neste arquivo precisamos informar qual será a pasta com as funções serverless, como neste projeto ela está na pasta /functions/ podemos atribuir como a seguir:
[build]
functions = "./functions"
Criando as Funções
De acordo com a documentação da API de bots.
Para este bot vamos usar estas 3 funções. Sendo o bot o nosso endpoint para receber as requests e as outras duas para responder essas requests.
A estrutura do nosso projeto deve estar assim:
Vamos estar usando o axios
, então podemos usar o npm init para iniciar um projeto node e instalar esta dependencia com npm install axios
Codando o Bot
O repositório com o código deste bot poderá se encontrado aqui.
Função Principal
Está função será responsável por receber os updates sempre que o bot for contatado no telegram, mais a frente quando formos configurar nosso webhook será para essa função que iremos redirecionar.
const axios = require("axios"); //Usando axios para realizar as requests.
exports.handler = async (event) => {
const body = event.body; //Recebendo o body da request com as informações.
const req = JSON.parse(body); //Convertendo a body em JSON.
//Desestruturando apenas os objetos necessários para as funções. Nosso bot irá receber mensagens e comandos de inline query.
const { message, inline_query } = req;
let response = {}; //Inicializando nosso objeto de resposta. Que servirá para os dois tipos de resposta.
//Respondendo comandos inline.
if (inline_query) {
//Comandos inline esperam um array com várias respostas, então vamos armazenar na variável results.
const results = [];
//O formato desta resposta pode ser referida aqui https://core.telegram.org/bots/api#answerinlinequery
//Por hora apenas uma resposta é o suficiente então vou inserir apenas:
results.push({
type: "Article",
id: results.length, //O ID da resposta.
title: `Olá ${inline_query.from.first_name} ${inline_query.from.last_name}`, //O título da resposta.
thumb_url:
"https://raw.githubusercontent.com/Jorgen-Jr/Jorgen-Jr.github.io/main/src/assets/image/logo.png", //Imagem que irá aparecer na request.
description: `Olá ${inline_query.from.first_name} ${inline_query.from.last_name}`,
// Em seguida a resposta da inline query, que será entregue caso o usuário escolha esta resposta.
input_message_content: {
parse_mode: "HTML",
message_text: `Olá ${inline_query.from.first_name} ${inline_query.from.last_name}, ${inline_query.query}`,
},
});
response = {
inline_query_id: inline_query.id,
results,
};
await answerInlineQuery(response);
}
//Respondendo mensagens.
else if (message) {
const chatId = message.chat.id;
const parse_mode = "HTML";
//O formato da resposta pode ser referida aqui: https://core.telegram.org/bots/api#sendmessage
response = {
chat_id: chatId,
text: `Olá ${message.from.first_name} ${message.from.last_name}.`,
parse_mode,
};
await sendMessage(response);
}
//Função para enviar as mensagens
async function sendMessage(response) {
return await axios.post(
"https://ola-usuario-bot.netlify.app/.netlify/functions/sendMessage",
response,
);
}
//Função para responder comandos inline
async function answerInlineQuery(response) {
return await axios.post(
"https://ola-usuario-bot.netlify.app/.netlify/functions/answerInlineQuery",
response,
);
}
return {
statusCode: 200,
body: JSON.stringify(response),
};
};
Função answerInlineQuery
Vamos fazer uma função genérica para responder as inline queries respeitando a documentação do Telegram, sendo assim como já estamos recebendo a estrutura corretamente da função principal, nosso código pode ficar assim.
const axios = require("axios");
exports.handler = async (event) => {
console.log("Enviando o resultado da inline query.");
const body = event.body; //Receber o resultado da request resultante do bot.js
const response = JSON.parse(body);
//Criar URL de acesso usando as variáveis de ambiente.
const bot_url = "https://api.telegram.org/bot" + process.env.BOT_TOKEN;
//Enviar a resposta de volta para o telegram.
const res = await axios.post(bot_url + "/answerInlineQuery", response);
return {
statusCode: res.status,
body: JSON.stringify({ message: "Query finalizada." }),
};
};
Função sendMessage
Partindo da mesma idéia da função anterior, nossa request só irá alterar o método usado na chamada da api do telegram.
const axios = require("axios");
exports.handler = async (event) => {
console.log("Respondendo ao usuário.");
const body = event.body; //Receber o resultado da request resultante do bot.js
const response = JSON.parse(body);
//Criar URL de acesso usando as variáveis de ambiente.
const bot_url = "https://api.telegram.org/bot" + process.env.BOT_TOKEN;
//Enviar a resposta de volta para o telegram.
const res = await axios.post(bot_url + "/sendMessage", response);
return {
statusCode: res.status,
body: JSON.stringify({ message: "Message sent." }),
};
};
Subindo o Código Para o Netlify
Faça o upload do seu código no github, gitlab ou bitbucket, no meu caso estarei usando o github. Este passo é necesário pois o netlify requere que seja ativado a integração de implantação contínua para usar as cloud functions.
Acesse o netlify e indique qual repositório ele vai usar.
Com as configurações padrão, ele irá fazer o primeiro deploy, sem misterios, eu apenas troquei o nome padrão do site para ficar mais fácil de acessar. Em “Site Settings” e em Site information deve ter um botão chamado “Change Site Name”.
E também precisamos adcionar o TOKEN do bot nas variáveis de ambiente, para que não fiquem expostas no nosso repositório.
Configurando WebHook
Estarei usando as variáveis de ambiente do insonmia e elas estão configuradas assim:
Então podemos ir na pasta Telegram e executar a rota setWebhook
como a seguir:
Com isso nosso bot já deve estar funcionando, mas podemos confirmar que o webhook foi aceito executando o getWebhookInfo.
Bot em Funcionamento
Agora se acessarmos o bot ele já deve nos responder com “Olá, primeiro nome
e ultimo nome
” para qualquer mensagem que enviarmos no chat dele, ou para qualquer comando inline.
Claro, este é apenas um projeto simples demonstrando como o netlify functions pode ser usada com os bots do telegram, mas é possível fazer bastante coisa com apenas isso. Você pode ler sobre alguns de meus bots que funcionam usando este mesmo método aqui sobre o meu bot de dicionário e aqui sobre outros bots que desenvolvi. Todos estão com o código fonte liberados no meu perfil no github.
Disposições Finais
As cloud functions são ótimas pra isso por não precisarem de um servidor dedicado rodando 24/7 para que o bot funcione, mas são limitadas as funções de resposta rápida pois elas devem respeitar o limite de resposta em 10 segundos, o mesmo limite também é usado para respostas de inline_query então esse limite é aceitável, mas devemos ter em mente de que podemos experienciar as cold starts e a depender do tamanho da função isso pode impactar negativamente o funcionamento do bot causando problemas de resposta e time-outs, por isso para aplicações mais complexas ou de longa execução essa abordagem é de longe a melhor indicada.
Porem, para aplicações simples até que isso cumpre sua função e é completamente gratuíto então pode ser uma alternativa atraente para alguns. E caso contrário por estar em javascript é bem fácil de reescrever tudo em nodejs e abrir um servidor dedicado somente para isso.