Bot para Twitter com Node.JS (Erros e acertos)
Tags: node, javascript, twitter, heroku
Published: 2021-01-18 04:30:31

# Resumo
Este artigo tem o intuito de apresentar o meu processo de desenvolvimento do [Surta Não Bot](https://github.com/flaviofilipe/surta-nao-bot/), apresentando tanto a criação quanto os problemas enfrentados.
A biblioteca utilizada para fazer a conexão com twitter foi a [Twit](https://github.com/ttezel/twit)
# Motivação
Após o lançamento da música de um amigo tive a ideia de criar um bot para o Twitter para divuga-lo. A proposta seria monitorar algumas hashtags específicas e comentar uma chamada para a música. O mesmo comportamento seria aplicado para quando o perfil fosse mencionado nos comentários.

# Problema
Após feito, testado e enviado para o servidor de "produção" não levou muito tempo para que a conta fosse suspensa e marcada como span. Isso aconteceu por não ter lido as [Regras e políticas do twitter](https://help.twitter.com/pt/rules-and-policies/twitter-reach-limited#spam).
Em uma de suas clausulas é alertado que será considerado span *"se uma conta estiver tweetando para não seguidores de forma repetida e não solicitada ou se estiver envolvida em padrões de comportamento abusivo"*.
Abaixo apresento as etapas de criação do bot.

# Etapa 1 - Credenciais no Twitter
### Twitter Developers
Após criar um novo perfil no twitter (ou utilizar um existente) é necessário criar um novo app no [Twitter Developers](https://developer.twitter.com/en/apps).
Nesta etapa será preciso informar o intuito do bot e fazer a descrição em inglês. Caso tenha dificuldade com o idioma, recorra ao bom e velho Google Tradutor.

# Etapa 2 - Criação do projeto
- Criei uma pasta chamada *surta-nao-bot*
- Foi necessário baixar duas bibliotecas: Twit e dotenv. A primeira será utilizada para conectar a API do Twitter e a segunda para carregar as variáveis de ambiente do arquivo .env. Segue o comando para instalar ambas:
```
npm i twit
npm i dotenv
```
- Neste primeiro momento será necessário criar três arquivos: **bot.js**, **config.js** e **.env**.

### .env
Dentro deste arquivo insira os códigos de acesso gerado no Twitter Developers
```
consumer_key=
consumer_secret=
access_token=
access_token_secret=
```
### config.js
Aqui será carregado as chaves de acesso criadas anteriormente para ser utilizada no projeto
```
require('dotenv').config();

module.exports = {
    consumer_key:process.env.consumer_key,
    consumer_secret:process.env.consumer_secret,
    access_token:process.env.access_token,
    access_token_secret:process.env.access_token_secret
}
```
### bot.js
Este é o arquivo onde terá o core do nosso bot. Para testar vamos criar um código simples onde será pesquisado no twitter posts com a hastag *"javascript"*

```
const config = require('./config');
const twit = require('twit')
const Twit = twit(config);

function searchTweets(params){

    Twit.get('search/tweets', params, (err, data, response) => {
        let tweets = data.statuses;
        if(!err) {
            for(let dat of tweets){
                let retweetId = dat.id_str;
                console.log(retweetId);
            }
        }else{
            console.log(err);
        }
    })
}

console.log('Aqui!')
searchTweets({q: '#javascript'}) 
```

Execute o arquivo **bot.js**
```
node bot.js
```

Caso tudo ocorra bem até aqui, podemos prosseguir para a próxima etapa.
Caso dê algum erro confira as mensagens que serão exibidas no console. Um dos principais motivos poderá ser as chaves de acesso do Twitter.

# Etapa 3 - Respondendo Tweets quando mencionado
Seguindo a documentação do [Twit](https://github.com/ttezel/twit), usaremos o `Twit.stream` para "escutar" um filtro. Neste caso, será feito um filtro no @ da conta, para quando for mecionado possa responder o tweet. Veja o código a baixo do arquivo [*bot.js*](https://github.com/flaviofilipe/surta-nao-bot/blob/13597fa404075e74a652c6f81bf292af06ba3d08/bot.js):

```
const config = require('./config');
const twit = require('twit')
const Twit = twit(config);

//Setting up a user stream
var stream = Twit.stream('statuses/filter', { track: '@surtanao_bb' });

stream.on('tweet', tweetEvent);

function tweetEvent(tweet) {
    // console.log(tweet)
    var reply_to = tweet.in_reply_to_screen_name;
    var text = tweet.text;
    var from = tweet.user.screen_name;
    var nameID = tweet.id_str;
    // params just to see what is going on with the tweets
    var params = { reply_to, text, from, nameID };
    console.log(params);

    let new_tweet = "It's me!!";
    if (from !== 'surtanao_bb') {
        new_tweet = 'Surta não @'+from+'!!! Ouça agora a nova música de Wander Luiz: https://bit.ly/SurtaNao';
    }

    var tweet = {
        status: new_tweet,
        in_reply_to_status_id: nameID
    }

    Twit.post('statuses/update', tweet, (err) => {
        if (err) {
            console.log("Something went wrong!", err);
        } else {
            console.log("It worked!");
        }
    });
}
```

No código a cima nós declaramos na variável `stream` com o filtro que queremos fazer, neste caso com o perfil *@surtanao_bb*

Logo a baixo no trecho `stream.on('tweet', tweetEvent);` é onde as coisas acontecem. A varíavel `stream` agora é uma instancia do Twit Stream, por isso possui o método *on* que recebe o evento que será "escutado" no primeiro parâmetro e a função que será executada no segundo, que recebe cada tweet encontrado.

O método `tweetEvent` recebe por parâmetero um objeto com os dados do tweet e logo no inicio colhe algumas informações, como o usuário que fez a postagem, o texto e o ID. Um pouco mais a baixo é motado a mensagem de resposta para enfim fazer a postagem utilizando o método post do Twit. Caso ocorra algum erro será exibido no console, se não, será impressa a mensagem "It worked!".

> Para que funcione é necessário conceder permissão ao app, dentro do Twitter Developers, para ler e escrever mensagens. Eu apliquei a última opção *"Read + Write + Direct Messages"*. Basta acessar seu app, na tela inicial acessar a opção *"App permissions"*.

Bom, até este momento o bot deverá executar com sucesso!
Após este passo decidi inserir as opções de monitorar *hashtags* específicas para responder automaticamente, o que não deu muito certo, pois pouco tempo depois a conta foi identificada como span. Confira o [commit](https://github.com/flaviofilipe/surta-nao-bot/commit/f123a87b11a29358462be8f7397701844d03e825) da atualização caso queira ver o resultado.

### Etapa 4 - Deploy na [Heroku](https://www.heroku.com/)
Para fazer o deploy será necessário ter uma conta na Heroku e criar um [novo app](https://dashboard.heroku.com/apps) e ter instalado o [heroku-cli](https://devcenter.heroku.com/articles/heroku-cli).

Após configurado, dentro da pasta *surta-nao-bot* será necessário adicionar um novo remote para o heroku, isso permitirá fazer o deploy utilizando o git.
```
heroku git:remote -a nome-do-app-heroku
```
> Ao exececutar o comando a cima será criado um novo origin do repositório. Você poderá conferir com o comando a baixo:
```
git remote -v
```

Para que o Heroku consiga executar a aplicação será necessário criar o arquivo `Procfile` na raiz do projeto. A principio criei este arquivo com o código a baixo:
```
worker: node bot.js
```

Além disso fiz a alteração no arquivo `package.json` para executar o comando `npm start` no arquivo `bot.js`. Segue o trecho de dentro do arquivo `package.json`:
```
...
"scripts": {
    "start": "node bot.js"
  },
...
```

E fiz o deploy utilizando o git
```
git push heroku master
```

> Antes de enviar para o servidor da Heroku já havia enviado para o github, por isso foi necessário criar o arquivo `.gitignore` para que não fosse enviado o arquivo `.env` com as credenciais de acesso. Por isso, ao fazer o push na heroku será necessário criar as variáveis de ambiente dentro da própria plataforma. Segue a baixo o código do .gitignore.

```
.env
node_modules
.vscode
package-lock.json
```
### Variáveis de ambiente na Heroku
- Acesse o app e abra as configurações no menu *settings*
- Dentro da sessão *Config Vars* crie as variáveis que estão no arquivo `.env` do projeto, sendo a key o nome da variável e o value as chave de acesso.

### Problema 2
Até aí tudo bem, o código executou na heroku e o bot já estava funcionado. Porém ao acompanhar o arquivo de logs da Heroku com o comando `heroku logs -a` percebi que após 60seg o bot era finalizado. Pensei que fosse a opção gratuita da plataforma até encontrar a solução.
Em vários tutoriais os passos executados a cima pareciam funcionar, porém para que este tivesse sucesso eu precisei adicionar uma nova linha ao `Procfile` informando a *web*. Aparentemente todo app da heroku precisa ter algo do tipo, foi o que funcionou para mim. Segue o código do arquivo *Procfile* final:
```
web: echo "whatever"
worker: node bot.js
```

Pronto! Agora sim o bot estará funcionando na heroku sem parar. Para fazer o deploy faça o push:
```
git push heroku master
```

### Testando
Para fazer o teste utilize uma outra conta e faça uma postagem mencionando o perfil do seu bot. Acompanhe os logs do servidor com `heroku logs --tail`.

### Melhorias
Algumas melhorias foram feitas, como criar mensagens aleatórias para que seja exibidas nas respostas e a remoção da pesquisa pelas tags.

Coisas como likes e retweets poderão ser exploradas dentro da documentação do [Twit](https://github.com/ttezel/twit)

Para acompanhar as atualizações do bot confira o [repositório no Github](https://github.com/flaviofilipe/surta-nao-bot).