eptaccio

Construindo o chatbot da Bienal da Quebrada

June 16, 2020

quebradinha, o personagem do bot

A Bienal do Livro da Quebrada tem como missão a democracia de oportunidades. Levar para as favelas e periferias a literatura e um evento literário que dialogue na linguagem de quem teve e tem o acesso negado. As Bienais ocorrem sempre nos grandes centros, a distância geográfica e social afasta desses eventos as pessoas que moram nas periferias, ainda que haja uma tentativa de inclusão, ela é feita de maneira que quem vem das periferias não se sentem pertencentes a esses espaços.

Felizmente a Bienal recebe várias solicitações de pessoas querendo doar livros, então o criador do movimento, @alamoju me chamou e perguntou se seria possível automatizar um pouco o processo de doações via DM do Twitter.

Já tinha feito algumas coisas utilizando a API do Twitter mas o de Direct Messages era novidade para mim até então. Ainda estamos trabalhano no bot mas vou mostrar como foi essa saga até agora.

Conseguindo keys da API do Twitter

O Twitter trabalha com APPS (aplicações) dentro da plataforma. Essas aplicações podem ser distribuídas ou não. Cada aplicação tem seus tokens de acesso entre outras coisas para organização.

Apenas contas de desenvolvimento no Twitter podem criar APPS. Essas contas são, perfils normais do Twitter, que fizeram a solicitação para o modo de desenvolvimento.

Eu já tinha minha conta pessoal ativada no modo desenvolvedor. Uma opção seria criar o APP no meu perfil e posteriormente instalar o mesmo na conta da Bienal. Esse modo de instalação é o mesmo que você faz quando autoriza um APP de terceiros na sua conta.

Eu preferi solicitar o acesso das features de desenvolvimento para a conta da própria Bienal, para que no futuro não distante as pessoas não ficassem dependentes de mim caso quisessem fazer alguma alteração no chatbot ou no próprio APP.

Solicitando o acesso

dev account

O primeiro passo foi solicitar acesso a uma conta de desenvolvimento no Twitter. Essa parte é fácil mas um pouco chata. Respondi os questionários até o final.

O Twitter demorou um pouco para autorizar a solicitação, talvez pelo número de seguidores da conta, quem eram muitos.

Se você for criar o seu chatbot, se prepare para um formulário bem chato. Recomendo que você descreva exatamente o que irá fazer com os poderes que irá conseguir, já que o pessoal está meio embaçado com as autorizações.

Se você está apenas estudando, coloque isso lá, assim, a autorização costuma ser mais rápida, quase instantanea.

Ah, as respostas (e as perguntas) são em inglês, então se você não estiver confortavel, vai na fé com o tradutor

Depois do processo feito, você deve ter uma tela como essa:

dev account

No meu caso criei um APP chamado “xiaomi é melhor que apple”.

É necessário configurar o APP para ter acesso a Direct Messages do Twitter.

dm-permition

Webhook do twitter

Para a parte de comunicação, parti para pesquisar como o twitter trabalhava com a parte de chats. Felizmente encontrei uma parte da API deles chamada de Account Activity API. Essa API permite que você cadastre webhooks para ações de uma conta, uma nova DM, novos seguidores e coisas do tipo.

imagem da api de ativaves

Para ativar a API, acesse esse link e configure um novo ambiente. Eu achei a documentação do Twitter bem fraca nesse sentido, tive que dar várias voltas para encontrar o que queria.

Após o registro do ambiente da Account Activity API o próximo passo foi de fato cadastrar uma API minha de Webhook no Twitter. Cada passo é uma chamada HTTP na API do twitter, sendo assim são dois passos:

  • Registrar Webhook
  • Adicionar uma “subscription” na conta para ativar o Webhook

Registrar webhook

A API registrada como Webhook deve ter uma rota com mapeamento GET e POST. No meu caso usei /webhooks/twitter. Inicialmente o twitter irá fazer um GET nessa rota, para validar se sua API está nos conformes deles.

No GET eles te informan um crc_token, você tem que gerar um HASH maluco com essa informação e um dos tokens do twitter (consumer secret) e devolver para eles. Se você fizer o cálculo certo, o twitter irá assumir sua API como um Webhook válido.

Segue um pedaço da implementação:

const getChallengeResponse = ({ crcToken, consumerSecret }) => { const hmac = crypto .createHmac('sha256', consumerSecret) .update(crcToken) .digest('base64') return hmac } app.get('/webhooks/twitter', (req, res) => { const crcToken = req.query.crc_token const hmac = getChallengeResponse({ crcToken, consumerSecret: TWITTER_AUTH.consumer_secret }) res.send({ response_token: `sha256=${hmac}` }) })

Eu criei minha API usando express e subi utilizando o now.sh, que agora é vercel. A configuração de deploy foi quase a mesma de um post anterior meu.

Com a MINHA API no ar, fiz o cadastro do Webhook no twitter.

print do postman

POST https://api.twitter.com/1.1/account_activity/all/{NOME_DO_SEU_AMBIENTE}/webhooks.json

A requisição deve ser feita utilizando a autenticação OAuth 1.0. Dentro do APP no twitter é possível encontrar todos os tokens.

Registrando Inscrição (subscription)

Após o cadastro do Webhook é necessário ativar a inscrição daquele Webhook para o seu usuário na API de Account Activity API.

print do postman

POST https://api.twitter.com/1.1/account_activity/all/{NOME_DO_SEU_AMBIENTE}/subscriptions.json

Depois disso, o twitter passa a enviar alguns eventos na API. Os que eu utilizei foram os de DM, as informações chegam num objeto assim:

{ "direct_message_events": [{ "type": "message_create", "id": "1299858332", "created_timestamp": "1333079023603", "message_create": { "target": { "recipient_id": "10948585902" }, "sender_id": "38051043333174", "message_data": { "text": "eae ma broda", "entities": { "hashtags": [], "symbols": [], "urls": [], "user_mentions": [] } } } }] }

Contruindo o fluxo do chatbot

bot bienal

Se você abrir o chatbot da Bienal hoje, vai receber uma mensagem de boas vindas (como na imagem acima). Essa mensagem de entrada é um recurso do Twitter chamado “Welcome Messages”. Com essa mensagem, dois botões cada passo é uma chamada HTTP na API do Twitter irão aparecer no chat.

Na primeira versão eu estava utilizando o callback desses botões para triggar ações e mensagens. Entretanto atualmente estamos melhorando os fluxos com a utilização de NLP.

NLP ou Processamento de Linguagem Natural em português, é uma subárea da inteligência artificial, bem conhecida no meio de chatbots. O chatbot recebe uma mensagem, faz o processamento usando as ferramentas de NLP e à partir disso, consegue saber qual é a “intenção” do usuário, para que programaticamente você tomar possa tomar uma ação. É necessário mapear as possíveis intenções para que não ocorram erros.

explicação nlp

Essas intenções não saem do nada, é necessário fazer um “treinamento” prévio, passando exemplos de frases que o usuário pode enviar em cada intenção. Esse treinamento gera um MODEL, que no final das contas são vários processamentos salvos para que, quando seja necessário o processamento de intenção seja mais rápido.

Akamaru Framework

Recentemente iniciei o Akamaru framework escrito em TypeScript, uma ferramenta para facilitar a parte do gerenciamento de estados e intenções, além da parte de NLP. É baseado na minha experiência com soluções para chatbot como: IBM Watson, Dialog Flow, entre outros.

Com o Akaramu consigo fazer o treinamento dos modelos de intenções e definir os fluxos à partir de um objeto javascript. Nessa organização temos estados, intenções, ações e resolvers.

  • Estado
const botDefinition = { allIntents: , language: 'pt', states: [ { name: 'START', startTexts: [ 'Oi, como posso te ajudar?', 'Vamos lá, como posso te ajudar?' ], actions: startActions, unknownIntentAction: { responses: [ 'Não te entendi muito bem :/' ] } } ], resolvers: { getSession, saveSession } }

Configurações de textos iniciais, nome do estado e uma ação definida, para caso o processo de NLP não identifique nenhuma intenção mapeada para aquele estado. Um estado também agrupa ações.

  • Ações
const startActions = [ { onIntent: 'SAUDACAO', responses: [ 'Olá, tudo bem?', 'oi oi, estou aqui' ] }, { onIntent: 'GERENTE', handler: async session => { await sendNotification(session.userId) return { responses: [ 'Eita, vou chamar o brabo. Pera só um pouco' ], nextState: { name: 'SILENCIO' } } } }, { onIntent: 'DOAR-GENERICO', goToState: { name: 'DOACAO' } } ]

As ações mapeam as respostas ou redirecionamentos para outros estados. As respostam são sempre um array, pois é possível declarar várias. O Akamaru vai escolher uma aleatória para devolver ao usuário, deixando assim o bot um pouco mais humanizado.

No caso de intenções onde ação é mais complexa que só retornar uma resposta, é possível declarar um handler e posteriormente retornar as possíveis respostas.

As ações são baseada em intenções.

  • Intenções
const allIntents = [ { name: 'SIM', training: [ 'sim', 'pode', 'quero', 'preciso', 'manda ve' ] } ]

As intenções tem seu nome e exemplos de digitação do usuário. Os exemplos são utilizados no treinamento do MODEL. Quanto mais exemplos, mais acertivo fica a classificação de intenção.

  • resolvers / gerenciamento da sessão

Na definição do bot, temos uma parte destinada a resolvers. Esses são os métodos getSession e saveSession. Como os nomes dizem esses métodos recuperam e salvam a sessão do usuário, assim o usuário do framework (no caso eu mesmo) posso definir qual estratégia irei usar em cada bot.

Nesse bot estou utilizando o Redis, um banco de dados open source que armazena dados em memória, para que a leitura e escrita seja mais rápida. É conhecido como um banco de cache.

const THIRTY_MINUTES = 1800000 const saveSession = async (userId, session) => { return redis.set( userId, session, 'ex', THIRTY_MINUTES ) } const getSession = async userId => { return redis.get(userId) }

As sessões morrem depois de 30 minutos sem interação do usuário.

Resultado final

resultado final

Iremos programar mais fluxos no bot conforme as necessidades surgirem. Por enquanto, ele consegue ajudar as pessoas à fazer doações de livros ou financeiras. Em qualquer fluxo é possível solicitar para falar com um humano, sendo assim uma notificação é disparada para o @alamoju no Telegram.

Obrigado por ler até o final. Participe da discussão no twitter


Se ficou interessado em chatbots, confira esse episódio do Quebradev, movimento onde faço parte. Falamos sobre a história, presente e implicações sociais de automatizações desse tipo.


dev, parte do @afrotechbr, @quebradev e akatsuki. chaotic good (ele/he/el)