Artigo original: https://www.freecodecamp.org/news/create-a-discord-bot-with-javascript-nodejs/

Este tutorial mostrará para você como usar o JavaScript e o Node.js para criar seu próprio bot do Discord totalmente na nuvem.

Você não precisa instalar nada em seu computador e não precisa pagar nada para hospedar seu bot.

Vamos usar diversas ferramentas, incluindo a API do Discord, bibliotecas do Node.js e uma plataforma de computação na nuvem chamada Repl.it.

Se preferir programar seu bot do Discord em Python em vez de JavaScript, leia este outro tutorial.

Também há uma versão em vídeo deste tutorial escrito. O vídeo está integrado abaixo e a versão escrita vem logo após o vídeo.

Como criar uma conta de bot no Discord

Para trabalhar com a biblioteca do Node.js e com a API do Discord, precisamos criar uma conta de bot no Discord.

Esses são os passos para criar uma conta de bot no Discord.

1. Certifique-se de estar logado no site do Discord.

2. Navegue até a página de aplicações.

3. Clique no botão "New Application" (Nova aplicação).

image-117

4. Dê um nome à sua aplicação e clique em "Create" (Criar).

image-118

5. Vá até a guia "Bot" e clique em "Add Bot" (Adicionar bot). Você terá que confirmar, clicando em "Yes, do it!" (Sim, faça isso!).

image-119

Mantenha as configurações padrão para Public Bot (Bot público - marcada) e Require OAuth2 Code Grant (Exigir autorização de código OAuth2 - desmarcada).

Seu bot foi criado. A próxima etapa é copiar o token.

image-122

Este token é a senha do seu bot. Por isso, não o compartilhe com ninguém. Isso pode permitir que alguém faça o login com seu bot e faça o que quiser com ele.

Você pode gerar novamente o token se ele for compartilhado por acidente.

Como convidar seu bot para participar de um servidor

Agora, você precisa colocar seu usuário bot em um servidor. Para fazer isso, crie um URL de convite para ele.

Vá para a guia "OAuth2" e selecione "bot" na seção "scopes" (Escopos).

image-123

Em seguida, escolha as permissões que você quer que o bot tenha. O bot usará, principalmente, mensagens de texto. Assim, não precisamos de muitas permissões. Você pode precisar de mais, dependendo do que você quer que o bot possa fazer. Cuidado com a permissão de "Administrator" (Administrador).

image-124

Depois de selecionar as permissões apropriadas, clique no botão 'copy' (Copiar) acima das permissões. Isso copiará o URL, que pode ser usado para adicionar o bot ao servidor.

Cole o URL em seu navegador, selecione um servidor para o qual você convidará o bot e clique em "Authorize" (Autorizar).

Para adicionar o bot, sua conta precisa da permissão "Manage Server" (Gerenciar servidor).

Agora que você criou o usuário bot, vamos começar a escrever o código em JS para ele.

Como programar um bot básico do Discord com a biblioteca discord.js

Usaremos a biblioteca discord.js do Node para escrever o código para o bot. O discord.js é um wrapper de API para o Discord que facilita criar um bot do Discord em Node.js/JavaScript.

Como criar um Repl e instalar o discord.js

Você pode desenvolver o bot em seu computador local em qualquer editor de código. No entanto, neste tutorial, usaremos o Repl.it, pois ele torna mais simples acompanhar o processo para quem quiser. O Repl.it é uma IDE on-line que você pode usar em seu navegador na web.

Para começar, vá para Repl.it. Crie um novo Repl e selecione "Node.js" como a linguagem. Isso significa que a linguagem será o JavaScript.

Para usar a biblioteca discord.js, você só precisa adicionar const Discord = require("discord.js"); na parte superior do main.js. O Repl.it automaticamente instalará esta dependência quando você pressionar o botão "Run" (Executar).

Como configurar eventos do Discord para o seu bot

O discord.js trabalha a partir do conceito de eventos. Um evento é algo que você escuta e responde. Por exemplo, quando há uma mensagem, você receberá um evento a respeito e poderá responder a ele.

Vamos fazer um bot que responde a uma mensagem específica. Este código de bot simples é retirado diretamente da documentação do discord.js (em inglês). Ainda adicionaremos mais recursos ao bot mais tarde.

Adicione este código ao main.js. Explicarei em breve o que ele faz.

const Discord = require("discord.js")
const client = new Discord.Client()

client.on("ready", () => {
  console.log(`Logged in as ${client.user.tag}!`)
})

client.on("message", msg => {
  if (msg.content === "ping") {
    msg.reply("pong");
  }
})

client.login(process.env.TOKEN)

Quando você criou um usuário bot no Discord, você copiou um token. Agora, vamos criar um arquivo .env para armazenar o token.

Os arquivos .env são usados para declarar variáveis de ambiente. No Repl.it, a maioria dos arquivos que você cria são visíveis para todos, mas os arquivos .env são visíveis apenas para você. Outras pessoas que virem um repl público não poderão ver o conteúdo do arquivo .env.

Então, se estiver desenvolvendo no Repl.it, inclua informações privadas, como tokens ou chaves, somente em um arquivo .env.

Clique no botão "Adicionar arquivo" e crie um arquivo chamado .env. Dentro do arquivo, adicione a linha abaixo, com o token que você criou anteriormente:

TOKEN=[cole o token aqui]

Agora, vamos examinar o que faz cada linha do código do seu bot do Discord.

A primeira linha importa a biblioteca do discord.js.  Em seguida, criamos uma instância de um Client. Esta é a conexão com o Discord.

A instrução client.on() é usada para verificar eventos.  Ela aceita um nome de evento. Então, uma função de callback será chamada quando o evento ocorrer. Neste código, o evento ready (pronto) é chamado quando o bot estiver pronto para começar a ser usado. Depois, quando o servidor do Discord receber uma nova mensagem, o evento message é chamado.

O código verifica se o msg.content (o que está escrito na mensagem) é igual a 'ping'. Se for, o bot responde no canal com 'pong'.

Agora que o bot está configurado, a linha final roda o bot com o token de login. Ela recebe o token do arquivo .env.

Temos o código para o bot. Agora, só precisamos rodá-lo.

Como rodar o bot

Agora, clique no botão "Run" (Executar) na parte superior do seu código do bot no repl.it.

Em seguida, vá para a sua sala no Discord e digite "ping". Seu bot deve retornar "pong".

image

Como melhorar o bot

Agora que temos um bot básico funcionando, vamos melhorá-lo. Ele é chamado de "Bot encorajador" por algum motivo.

Este bot responderá com uma mensagem de encorajamento sempre que alguém enviar uma mensagem contendo uma palavra triste ou depressiva.

Qualquer um poderá adicionar mensagens encorajadoras para que o bot as use e as mensagens enviadas pelo usuário serão armazenadas no banco de dados do Repl.it.

O bot também retornará uma citação inspiradora aleatória de uma API quando alguém digitar a mensagem "$inspire" no chat.

Vamos começar adicionando o recurso de "$inspire".

Como adicionar citações inspiradoras ao bot

Usaremos as citações inspiradoras de uma API chamada zenquotes.io. Precisaremos importar o módulo node-fetch, adicionar uma função getQuote() e atualizar o código do nosso bot para chamar a função.

Aqui está um código atualizado. Após o código, explicarei as partes novas.

const Discord = require("discord.js")
const fetch = require("node-fetch")
const client = new Discord.Client()

function getQuote() {
  return fetch("https://zenquotes.io/api/random")
    .then(res => {
      return res.json()
      })
    .then(data => {
      return data[0]["q"] + " -" + data[0]["a"]
    })
}

client.on("ready", () => {
  console.log(`Logged in as ${client.user.tag}!`)
})

client.on("message", msg => {
  if (msg.author.bot) return
    
  if (msg.content === "$inspire") {
    getQuote().then(quote => msg.channel.send(quote))
  }
})

client.login(process.env.TOKEN)

Agora, temos de importar o módulo node-fetch. Esse módulo permite que nosso código faça uma solicitação de HTTP para obter dados da API.

A função getQuote() é bastante direta. Primeiro, ela usa o módulo node-fetch para solicitar dados do URL da API. A API retorna uma citação inspiradora aleatória. Essa função pode ser facilmente reescrita para buscar citações de uma API diferente se a atual parar de funcionar.

Em seguia, a função converte a resposta da API em JSON e cria uma string para ser retornada. Por tentativa e erro, descobri como pegar a citação do JSON em formato de string que eu queria. A citação é retornada pela função como string.

A parte final e atualizada do código está bem abaixo. Anteriormente, o código buscava a mensagem "ping". Agora, ele busca a mensagem "$inspire". Em vez de retornar "pong", ele busca uma citação com getQuote() e a retorna. Usamos msg.channel.send() para enviar a mensagem para o canal. Além disso, o código verifica se a mensagem vem do próprio bot. Se vier, ele deixa a função e não faz nada.

Neste ponto, você pode rodar seu código e testá-lo.

Como adicionar mensagens encorajadoras ao bot

Agora, implementaremos o recurso em que o bot responde com mensagens encorajadoras quando um usuário enviar uma mensagem com uma palavra triste.

Como adicionar palavras tristes ao bot

Primeiro, precisamos criar um array que contenha as palavras tristes as quais o bot responderá.

Adicione a linha abaixo após a variável client ter sido criada:

sadWords = ["sad", "depressed", "unhappy", "angry", "miserable"]

Fique à vontade para adicionar mais palavras à lista.

Como adicionar mensagens encorajadoras ao bot

Em seguida, adicionaremos um array de mensagens encorajadoras com as quais o bot responderá às palavras tristes.

Adicione o array a seguir após a lista de sadWords (palavras tristes) que você criou:

encouragements = [
  "Cheer up!",
  "Hang in there.",
  "You are a great person / bot!"
]

Como antes, fique à vontade para adicionar mais expressões de sua escolha ao array . Estou usando apenas três itens por enquanto, porque, mais tarde, adicionaremos a habilidade de os usuários adicionarem mais expressões de encorajamento para que o bot as use.

Como responder às mensagens

Agora, precisamos atualizar nosso bot para que use as duas listas que criamos.

Vamos atualizar a função message para verificar todas as mensagens e ver se elas contêm uma palavra da lista de sadWords. Se uma das palavras tristes for encontrada, o bot enviará uma mensagem aleatória de encorajamento.

Aqui está o código atualizado:

client.on("message", msg => {
  if (msg.content === "$inspire") {
    getQuote().then(quote => msg.channel.send(quote))
  }

  if (sadWords.some(word => msg.content.includes(word))) {
    const encouragement = encouragements[Math.floor(Math.random() * encouragements.length)]
    msg.reply(encouragement)
  }

})

Essa é uma boa hora de testar o bot. Você, nesse momento, sabe o suficiente para criar seu próprio bot. Em seguida, porém, você aprenderá a implementar mais recursos avançados e a armazenar dados usando o banco de dados do Repl.it.

Como ativar as mensagens enviadas por usuários

O bot está totalmente funcional, mas vamos tornar possível a atualização do bot diretamente pelo Discord. Um usuário poderá adicionar mais mensagens de encorajamento para que o bot as use quando detectar uma palavra triste.

Vamos usar o banco de dados integrado do Repl.it para armazenar mensagens enviadas pelo usuário. Este banco de dados armazena pares chave-valor e é integrado em cada repl.

Na parte superior do código, abaixo das instruções de import, adicionamos:

const Database = require("@replit/database")
const db = new Database()

Isso permitirá o uso do banco de dados do Repl.it. Ao rodar o código, o Repl.it deve instalar o módulo de banco de dados automaticamente. Se, por algum motivo, isso não acontecer, você pode precisar ir na guia Shell (não no Console) e digitar "npm install @replit/database".

Após o local onde o array encouragements foi criado, insira o trecho de código a seguir para adicionar mensagens de encorajamento ao banco de dados quando necessário:

db.get("encouragements").then(encouragements => {
  if (!encouragements || encouragements.length < 1) {
    db.set("encouragements", starterEncouragements)
  }  
})

Além disso, renomeie o array encouragements na parte superior, chamando-o de starterEncouragements.

Os usuários poderão adicionar mensagens personalizadas de encorajamento ao bot para que ele as use diretamente do chat do Discord. Antes de adicionarmos novos comandos ao bot, vamos criar duas funções de ajuda que adicionarão as mensagens personalizadas ao banco de dados e as excluirão.

Adicione o trecho de código a seguir após a função getQuote():

function updateEncouragements(encouragingMessage) {
  db.get("encouragements").then(encouragements => {
    encouragements.push([encouragingMessage])
    db.set("encouragements", encouragements)
  })
}

function deleteEncouragment(index) {
  db.get("encouragements").then(encouragements => {
    if (encouragements.length > index) {
      encouragements.splice(index, 1)
      db.set("encouragements", encouragements)
    }
  })
}

A função updateEncouragements() aceita uma mensagem de encorajamento como argumento.

Primeiro, ela recebe os "encorajamentos" do banco de dados. Em seguida, adiciona o novo encorajamento ao array e armazena o array atualizado no banco de dados sob a chave "encouragements".

A função deleteEncouragement() aceita um índice como argumento.

Ela recebe a lista de encorajamentos do banco de dados, armazenada sob a chave "encouragements". Se o tamanho do array for maior que o valor do índice, o item da lista naquele índice é excluído. Por fim, a lista atualizada é armazenada de volta no banco de dados sob a chave "encouragements".

Este é o código atualizado para a função message. Após o código, explicarei as novas seções.

client.on("message", msg => {
  if (msg.content === "$inspire") {
    getQuote().then(quote => msg.channel.send(quote))
  }

  
  if (sadWords.some(word => msg.content.includes(word))) {
    db.get("encouragements").then(encouragements => {
      const encouragement = encouragements[Math.floor(Math.random() * encouragements.length)]
      msg.reply(encouragement)
    })
  }

  if (msg.content.startsWith("$new")) {
    encouragingMessage = msg.content.split("$new ")[1]
    updateEncouragements(encouragingMessage)
    msg.channel.send("New encouraging message added.")
  }

  if (msg.content.startsWith("$del")) {
    index = parseInt(msg.content.split("$del ")[1])
    deleteEncouragment(index)
    msg.channel.send("Encouraging message deleted.")
  }
})

A seção de palavras tristes foi atualizada e usa mensagens de encorajamento do banco de dados de modo que as mensagens enviadas pelo usuário também possam ser utilizadas.

A próxima seção do código é usada para adicionar uma nova mensagem enviada pelo usuário ao banco de dados. Se uma mensagem do Discord começar com "$new", o texto após "$new" será usado como uma nova mensagem de encorajamento.

O código msg.content.split('$new ')[1] divide a mensagem a partir do comando "$new" e armazena a mensagem em uma variável. Naquela linha de código, observe o espaço em '$new '. Queremos tudo o que estiver após o espaço.

Chamamos a função de auxílio updateEncouragements com a nova mensagem. Em seguida, o bot envia uma mensagem ao chat do Discord confirmando que a mensagem foi adicionada.

A terceira seção nova (ao final do código acima) verifica se uma nova mensagem do Discord começa com "$del". Este é o comando para excluir um item da lista "encouragements" no banco de dados.

O índice é adquirido na mensagem do Discord começando com "$del" depois do split. Em seguida, a função deleteEncouragement() é chamada, passando o índice a ser excluído. A lista de encorajamento atualizada é carregada na variável encouragements e o bot envia uma mensagem ao chat do Discord com a lista atual.

Recursos finais do bot

O bot deve estar funcionando. Por isso, é um bom momento para fazer testes. Agora, adicionaremos alguns recursos finais.

Vamos adicionar a capacidade de obter uma lista de mensagens enviadas pelo usuário diretamente do Discord e a capacidade de ativar e desativar o modo de responder às palavras tristes.

Aqui vai o código do programa final e completo. Logo após, discutiremos as atualizações abaixo do código.

const Discord = require("discord.js")
const fetch = require("node-fetch")
const Database = require("@replit/database")

const db = new Database()
const client = new Discord.Client()

const sadWords = ["sad", "depressed", "unhappy", "angry", "miserable"]

const starterEncouragements = [
  "Cheer up!",
  "Hang in there.",
  "You are a great person / bot!"
]

db.get("encouragements").then(encouragements => {
  console.log(encouragements)
  if (!encouragements || encouragements.length < 1) {
    db.set("encouragements", starterEncouragements)
  }  
})

db.get("responding").then(value => {
  if (value == null) {
    db.set("responding", true)
  }  
})

function getQuote() {
  return fetch("https://zenquotes.io/api/random")
    .then(res => {
      return res.json()
      })
    .then(data => {
      return data[0]["q"] + " -" + data[0]["a"]
    })
}

function updateEncouragements(encouragingMessage) {
  db.get("encouragements").then(encouragements => {
    encouragements.push([encouragingMessage])
    db.set("encouragements", encouragements)
  })
}

function deleteEncouragment(index) {
  db.get("encouragements").then(encouragements => {
    if (encouragements.length > index) {
      encouragements.splice(index, 1)
      db.set("encouragements", encouragements)
    }
  })
}

client.on("ready", () => {
  console.log(`Logged in as ${client.user.tag}!`)
})

client.on("message", msg => {
  if (msg.content === "$inspire") {
    getQuote().then(quote => msg.channel.send(quote))
  }

  db.get("responding").then(responding => {
    if (responding && sadWords.some(word => msg.content.includes(word))) {
      db.get("encouragements").then(encouragements => {
        const encouragement = encouragements[Math.floor(Math.random() * encouragements.length)]
        msg.reply(encouragement)
      })
    }
  })

  if (msg.content.startsWith("$new")) {
    encouragingMessage = msg.content.split("$new ")[1]
    updateEncouragements(encouragingMessage)
    msg.channel.send("New encouraging message added.")
  }

  if (msg.content.startsWith("$del")) {
    index = parseInt(msg.content.split("$del ")[1])
    deleteEncouragment(index)
    msg.channel.send("Encouraging message deleted.")
  }

  if (msg.content.startsWith("$list")) {
    db.get("encouragements").then(encouragements => {
      msg.channel.send(encouragements)
    })
  }
    
  if (msg.content.startsWith("$responding")) {
    value = msg.content.split("$responding ")[1]

    if (value.toLowerCase() == "true") {
      db.set("responding", true)
      msg.channel.send("Responding is on.")
    } else {
      db.set("responding", false)
      msg.channel.send("Responding is off.")
    }
  }
})

client.login(process.env.TOKEN)

A primeira seção adicionada ao código está logo abaixo da lista starterEncouragements:

db.get("responding").then(value => {
  if (value == null) {
    db.set("responding", true)
  }  
})

Criamos uma nova chave no banco de dados chamada "responding" e a definimos como "true". Usaremos isso para determinar se o bot deve ou não responder às palavras tristes. Como o banco de dados é salvo mesmo após o programa parar de rodar, somente criamos uma nova chave se ela já não existir.

O próximo trecho novo de código está na seção que responde às palavras tristes e está dentro de uma instrução if. O bot somente responderá às palavras tristes se db.get("responding") = true. A capacidade de atualizar esse valor vem na próxima seção.

Em seguida, após o código que faz o bot responder ao comando "$del", há um novo trecho de código para responder ao comando "$list" quando enviado como mensagem do Discord.

O bot envia a lista de encorajamentos como mensagem do Discord.

A nova seção final vem a seguir. Este código faz o bot responder ao comando "$responding". Este comando recebe como argumento "true" ou "false". Exemplo de uso: "$responding true".

O código pega o argumento com value = msg.content.split("$responding ")[1] (como antes, observe o espaço em "$responding "). Depois, há uma instrução if/else que define apropriadamente a chave "responding" no banco de dados e envia a mensagem de notificação de volta ao Discord. Se o argumento for diferente de "true", o código assume que é "false".

O código do bot está completo! Agora, você pode rodar o bot e experimentá-lo. Porém, ainda há mais um passo importante que discutiremos a seguir.

Como configurar o bot para que rode continuamente

Se você rodar seu bot no repl.it e fechar a guia na qual ele está rodando, em breve o bot vai parar.

Há duas maneiras pelas quais você pode manter o bot em execução continuamente, mesmo depois de fechar seu navegador da web.

A primeira e mais simples é se inscrever para um plano pago no Repl.it. O plano pago deles mais barato é chamado de Hacker Plan e inclui cinco Repls em funcionamento contínuo.

Você pode ter três meses de graça usando este link (limitado às primeiras 1000 pessoas):  https://repl.it/claim?code=tryalwayson2103

Depois de haver se inscrito para um plano, abra seu Repl e clique no nome na parte superior. Depois, selecione a opção "Always On" (Sempre ativo).

image-36

Há outra maneira de manter seu código funcionando mesmo no plano gratuito, mas é um pouco mais complicado. O Repl.it continuará a rodar o servidor da web mesmo depois de a guia estar fechada. No entanto, o servidor da web somente rodará por até uma hora em utilização.

Isso é o que a documentação do repl.it diz:

Uma vez implantado, o servidor continuará a rodar em segundo plano, mesmo depois de você ter fechado a guia do navegador. O servidor permanecerá ligado e ativo após sua última solicitação. Depois disso, ele entrará em modo de hibernação. Repls em hibernação serão despertados quando receberem outra solicitação; não há a necessidade de rodar o repl novamente. Porém, se você fizer alterações ao servidor, precisará reiniciar o repl para ver as mudanças refletidas na versão ativa.

Para manter o bot rodando continuamente, usaremos outro serviço gratuito chamado Uptime Robot, em https://uptimerobot.com/.

O Uptime Robot pode ser definido para fazer o ping no servidor da web do bot no repl.it a cada 5 minutos. Com pings constantes, o bot nunca entrará em modo de hibernação e seguirá em execução.

Então, temos apenas duas coisas a fazer ainda para que nosso bot siga rodando continuamente:

  1. criar um servidor da web no repl.it e
  2. configurar o Uptime Robot para fazer o ping continuamente no servidor da web.

Como criar um servidor da web no repl.it

Criar um servidor da web é mais simples do que você imagina.

Para fazer isso, crie um novo arquivo em seu projeto, chamado server.js.

Em seguida, adicione o código abaixo:

const express = require("express")

const server = express()

server.all("/", (req, res) => {
  res.send("Bot is running!")
})

function keepAlive() {
  server.listen(3000, () => {
    console.log("Server is ready.")
  })
}

module.exports = keepAlive

Neste código, usaremos o express para iniciar um servidor da web. O servidor retorna "Bot is running!" (O bot está em execução!) para quem o visitar. O servidor vai rodar em uma thread separada daquela do nosso bot. Não discutiremos essa questão aqui, pois o resto não é relevante para o nosso bot de fato.

Agora, somente precisamos que o bot rode esse servidor da web.

Adicione a linha a seguir na parte superior do index.js para importar o servidor.

const keepAlive = require("./server")

Para iniciar o servidor da web quando o index.js é executado, adicione a linha a seguir como a penúltima linha, logo antes da linha que executa o bot.

keepAlive()

Ao rodar o bot no repl.it após adicionar este código, uma nova janela de servidor da web se abrirá. Há um URL visível para o servidor da web. Copie o URL para poder usá-lo na próxima seção.

image-1

Como configurar o Uptime Robot

Agora, precisamos configurar o Uptime Robot para fazer o ping no servidor da web a cada cinco minutos. Isso fará com que o bot rode continuamente.

Crie uma conta gratuita em https://uptimerobot.com/.

Quando estiver logado em sua conta, clique em "Add New Monitor" (Adicionar novo monitor).

image-21

Para o novo monitor, selecione "HTTP(s)" como o Monitor Type (Tipo de monitor) e dê a ele o nome que quiser. Depois, cole o URL do seu servidor da web do repl.it. Por fim, clique em "Create Monitor" (Criar monitor).

image-22

Pronto! O bot agora rodará continuamente para que as pessoas possam sempre interagir com ele no Repl.it.

Conclusão

Você agora sabe como criar um bot do Discord com JavaScript, bem como rodá-lo continuamente na nuvem.

Há muitas outras coisas que a biblioteca discord.js pode fazer. Se você quiser dar mais recursos ao seu bot do Discord, seu próximo passo é conferir a documentação do discord.js (em inglês).