Tendo começado a desenvolver bots para o Telegram há vários anos, descobri o desempenho, a simplicidade e a flexibilidade de trabalhar com eles como um caso especial da interface da linha de comando. Essas características, disponíveis para muitos hoje em dia, devem-se em grande parte à popular estrutura telegraf.js e similares, que fornecem métodos simplificados para trabalhar com a API do Telegram.A arquitetura do projeto, ao mesmo tempo, cabe inteiramente ao desenvolvedor e, a julgar pelo número modesto de bots complexos e multifuncionais, ainda temos espaço para crescer nesse sentido.Neste artigo, quero falar sobre uma pequena estrutura de roteamento de chatbot, sem a qual o desenvolvimento do nosso projeto seria impossível.Algumas informações básicas
Em chatbots e CLIs, a execução de uma única ação lógica geralmente consiste em várias etapas de refinamento ou etapas de ramificação. Isso requer que o programa armazene uma determinada coordenada para lembrar em que ponto do fluxo o usuário está localizado e executar seu comando de acordo com essa coordenada.A ilustração mais simples é a execução do comando npm init , durante o qual o programa solicita que você especifique um ou outro dado para package.json por vez.Na primeira etapa, ela diz ao usuário que está aguardando a entrada de texto do nome do pacote - e o que o usuário envia a ela com o próximo comando será salvo como o nome do pacote, graças à variável na qual essa espera é gravada.Chamamos esse caminho de variável - o caminho ao qual esse ou aquele código está logicamente conectado. É sempre necessário se o bot tiver navegação ou comandos emitidos em algumas etapas.Prática de hoje em arquitetura de bot
A abordagem que eu olhei pela primeira vez por outros desenvolvedores tinha a seguinte aparência: para qualquer atualização vinda do usuário, uma lista de verificações para um valor específico da variável path é gravada e a lógica de negócios e a navegação adicional são colocadas nessas verificações da forma mais elementar:onUserInput(ctx, input) {
switch(ctx.session.path) {
case 'firstPath':
if (input === '!') {
ctx.reply('!');
ctx.session.path = 'secondPath';
} else {
ctx.reply(' "!"');
}
break;
case '...':
}
}
Se você possui apenas algumas equipes e algumas etapas para cada equipe, esta solução é ideal. Chegar à terceira equipe e à sétima, se você começar a pensar que algo está errado.Em um dos bots com os quais tivemos a chance de trabalhar em um estágio tardio, o núcleo do funcional era uma folha de 4000 linhas e ~ 70 condições apenas do nível superior que cresceu em dois ifs, com uma verificação do que deu certo - às vezes caminhos, às vezes comandos, às vezes caminhos e comandos. Todas essas condições foram verificadas para cada ação do usuário e acessadas funções auxiliares de um objeto de planilha vizinho, que também cresceu de várias linhas. Escusado será dizer que quão lento e esquisito foi esse projeto?Quadro Hobot
Iniciando o ActualizeBot, já imaginávamos o tamanho, e nossa primeira tarefa foi preservar a extensibilidade e a velocidade do desenvolvimento.Para fazer isso, dividimos a lógica do cliente em controladores atribuídos aos caminhos e escrevemos uma pequena abstração para navegar entre esses controladores e processar as mensagens recebidas do usuário neles.Tudo isso, baseado em grandes projetos, foi escrito em TypeScript e recebeu o nome elegante Hobot, que sugere, é claro, pipelines de navegação.Um controlador é um objeto simples de três propriedades:- path - identificador de string do caminho usado para inicializar e navegar pelos caminhos inicializados
- get — , , hobot.gotoPath(ctx, path, data?). — data ,
- post — . , , . updateType — , : text, callback_query . updateType ctx
Exemplo de controlador:const anotherController = {
path: 'firstPath',
get: async (ctx, data) =>
await ctx.reply('Welcome to this path! Say "Hi"'),
post: async (ctx, updateType) => {
if (updateType === updateTypes.text && ctx.update.message.text === 'Hi') {
await ctx.reply("Thank you!");
this.hobot.gotoPath(ctx, 'secondPath', { userJustSaid: "Hi" });
} else {
await ctx.reply('We expect "Hi" text message here');
}
}
}
Parece um pouco mais complicado do que no começo, mas a dificuldade permanecerá a mesma quando você tiver 100 ou 200 caminhos.A lógica interna é trivial e é surpreendente que ninguém tenha feito isso ainda :esses controladores são adicionados a um objeto cujas chaves são os valores da propriedade path e são chamadas por essas chaves durante as ações do usuário ou ao navegar usando hobot.gotoPath (ctx, path, data? ) .A navegação é alocada em um método separado para não tocar no caminho variável e na lógica de navegação, mas pensar apenas na lógica de negócios, embora você sempre possa alterar ctx.session.path com as mãos, o que, é claro, não é recomendado.Tudo o que você precisa fazer para que seu novo bot com estrutura indestrutível funcione é iniciar um bot telegraf regular e passá-lo e o objeto de configuração ao construtor Hobot. O objeto de configuração consiste nos controladores que você deseja inicializar, no caminho padrão e nos pares comando / controlador.
const bot = new Telegraf('_');
export const hobot = new Hobot(bot, {
defaultPath: 'firstPath',
commands: [
{ command: 'start', path: 'firstPath' }
],
controllers: [
startController,
nextController
]
});
bot.launch();
Em conclusão
Vantagens implícitas de dividir uma planilha em controladores:- A capacidade de colocar funções, métodos e interfaces isolados que estão bloqueados na lógica deste controlador em arquivos separados ao lado dos controladores
- Risco significativamente reduzido de quebrar acidentalmente tudo
- Modularidade: ligar / desligar / atribuir a um determinado segmento da audiência esta ou aquela lógica pode ser feita simplesmente adicionando e removendo controladores da matriz, inclusive atualizando a configuração sem programação - para isso, é claro, precisamos escrever algumas letras, pois ainda não temos alcançado
- A capacidade de dizer claramente ao usuário o que exatamente é esperado dele quando ele (o que geralmente acontece) faz algo errado - e faz isso onde é esse o lugar - no final do processamento do método pós
Nossos planos :Este artigo descreve o script básico para trabalhar com as mensagens de texto Hobot.Se o tópico for relevante, compartilharemos outras sutilezas técnicas da estrutura e nossas observações da prática de desenvolver e usar bots de telegrama em artigos futuros.Links : Ainstalação do bot se parece com isso: npm i -s hobot
Repositório com explicação em README.MD e o bot sandboxReady trabalhando na produção com base no Hobot.Obrigado por sua atenção, ficarei feliz em ouvir suas perguntas, sugestões ou idéias para novos bots!