Artigo original: Python Discord Bot Tutorial – Code a Discord Bot And Host it for Free, escrito por Beau Carnes

Traduzido e adaptado por: Daniel Rosa

Neste tutorial, você verá como construir seu próprio bot para o Discord totalmente na nuvem.

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

Vamos usar várias ferramentas, incluindo a API do Discord, bibliotecas do Python e uma plataforma de computação em nuvem chamada Repl.it.

Também temos uma versão em vídeo do tutorial deste artigo. O vídeo está integrado abaixo. A versão escrita vem logo após o link.

Como criar uma conta de bot no Discord

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

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

1. Verifique se está logado no site do Discord.

2. Navegue até a página dos desenvolvedores.

3. Clique no botão “New Application”.

image-117

4. Dê um nome à aplicação e clique em “Create”.

image-118

5. Vá até a aba “Bot” e clique em “Add Bot”. Você terá de confirmar clicando em "Yes, do it!"

image-119

Mantenha as configurações padrão para Public Bot (marcado) e Require OAuth2 Code Grant (desmarcado).

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

image-122

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

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

Como convidar seu bot para que entre em um servidor

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

Vá até a guia "OAuth2" e selecione "bot" na seção "scopes".

image-123

Em seguida, escolha as permissões que deseja ter para o bot. Nosso bot usará mensagens de texto, principalmente. Assim, não precisamos de muitas permissões. Talvez você precise de mais, dependendo do que deseja que seu bot faça. Cuidado com a permissão "Administrator".

image-124

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

Cole o URL em seu navegador, escolha o servidor para onde quer enviar o bot e clique em “Authorize”.

Para adicionar o bot, sua conta precisa das permissões de "Manage Server".

Agora que você criou o usuário bot, é hora de começar a escrever o código em Python para ele.

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

Usaremos a biblioteca do Python discord.py para escrever o código para o bot. A discord.py é um wrapper de API para o Discord que facilita a criação de um bot do Discord em Python.

Como criar um Repl e instalar o discord.py

Você pode desenvolver o bot em seu computador local com qualquer editor de código. Neste tutorial, usaremos o Repl.it, pois ele simplificará o acompanhamento do processo para todos. O Repl.it é um IDE on-line que você pode usar em seu navegador da web.

Comece acessando Repl.it. Crie um novo repl e selecione a linguagem "Python" .

Para usar a biblioteca do discord.py, escreva import discord na parte superior do main.py. O Repl.it automaticamente instalará essa dependência quando você pressionar o botão "run".

Se preferir programar o bot em seu computador, você pode usar esse comando em um MacOS para instalar a biblioteca discord.py:

python3 -m pip install -U discord.py

Pode ser que você precise usar pip3 em vez de pip.

Se estiver usando o Windows, porém, use a linha a seguir:

py -3 -m pip install -U discord.py

Como configurar eventos do Discord para o seu bot

A biblioteca discord.py tem como princípio o conceito de eventos. Um evento é algo que você escuta para, depois, responder a ele. Por exemplo, quando uma mensagem ocorre, você receberá um evento a respeito e poderá responder.

Vamos criar um bot que responde a uma mensagem específica. Este código simples para um bot, junto com a explicação do código, foi retirado da documentação da discord.py. Adicionaremos mais recursos ao bot mais tarde.

Adicione este código ao main.py (você pode dar outro nome ao arquivo se quiser, apenas evite "discord.py"). Em breve, explicarei o que o código faz.

import discord
import os

client = discord.Client()

@client.event
async def on_ready():
    print('We have logged in as {0.user}'.format(client))

@client.event
async def on_message(message):
    if message.author == client.user:
        return

    if message.content.startswith('$hello'):
        await message.channel.send('Hello!')

client.run(os.getenv('TOKEN'))

Ao criar seu usuário bot no Discord, você copiou um token. Agora, vamos criar o arquivo .env para armazenar o token. Se estiver rodando o código na sua máquina, não é preciso usar um arquivo .env. Apenas substitua os.getenv('TOKEN') pelo token.

Os arquivos .env são usados para declarar variáveis de ambiente. No Repl.it, a maioria dos arquivos que você cria é visível 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 os conteúdos do arquivo .env.

Assim, se estiver desenvolvendo no Repl.it, inclua apenas informações privadas, como tokens ou chaves em um arquivo .env.

Clique no botão "Add file" e crie um arquivo chamado .env.

image-19-1

Dentro do arquivo, adicione a linha a seguir, incluindo o token que você copiou anteriormente no local demarcado:

TOKEN=[insira o token aqui]

Vamos examinar o que faz cada linha do código do seu bot do Discord.

  1. A primeira linha importa a biblioteca do discord.py.
  2. A segunda linha importa a biblioteca os, mas ela é usada apenas para pegar a variável TOKEN no arquivo .env. Se você não estiver usando um arquivo .env, não precisa desta linha.
  3. Em seguida, criamos uma instância de um Client. Esta é a conexão com o Discord.
  4. O decorator @client.event() é usado para registrar um evento. É uma biblioteca assíncrona. Por isso, tudo é feito a partir de callbacks. Uma callback é uma função que é chamada quando algo acontece. Neste código, o evento on_ready() é chamado quando o bot está pronto para começar a ser usado. Depois, quando o bot recebe uma mensagem, o evento on_message() é chamado.
  5. O evento on_message() é disparado sempre que uma mensagem é recebida, mas não queremos fazer nada se a mensagem vem de nós mesmos. Por isso, se o Message.author for igual a Client.user, o código apenas retorna.
  6. Depois, verificamos se o Message.content começa com '$hello'. Se começar, o bot responde com um 'Hello!' para o canal em que foi usado.
  7. Agora que o bot está configurado, a linha final executa o bot com o token de login. Esta linha obtém o token do arquivo .env.

Já temos o código para o bot. Agora, só precisamos executá-lo.

Como executar o bot

Agora, clique no botão "run", na parte superior, para executar seu bot no repl.it.

Se estiver escrevendo o bot em sua máquina, pode usar os seguintes comando no terminal/prompt de comando para executar o bot:

No Windows:

py -3 main.py

Em outros sistemas:

python3 main.py

Agora, vá para sua sala do Discord e digite "$hello". O bot deve retornar "Hello!".

image-141

Como melhorar o bot

Agora que temos um bot básico funcionando, vamos melhorá-lo. Chamamos o bot de "Bot encorajador" por um motivo.

Este bot responderá com uma mensagem de encorajamento quando alguém mandar uma mensagem que tenha uma palavra triste ou que pareça depressiva.

Qualquer um poderá adicionar mensagens de encorajamento 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 vinda de uma API quando alguém digitar a mensagem "$inspire" no chat.

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

Como adicionar citações inspiradoras no bot

Vamos pegar as citações inspiradoras em uma API chamada zenquotes.io. Precisamos importar mais alguns módulos de Python, adicionar uma função get_quote() e atualizar nosso código do bot para que ele chame a função.

Abaixo vemos o código atualizado. Depois do código, eu explicarei as partes novas.

import discord
import os
import requests
import json

client = discord.Client()

def get_quote():
  response = requests.get("https://zenquotes.io/api/random")
  json_data = json.loads(response.text)
  quote = json_data[0]['q'] + " -" + json_data[0]['a']
  return(quote)

@client.event
async def on_ready():
  print('We have logged in as {0.user}'.format(client))

@client.event
async def on_message(message):
  if message.author == client.user:
    return

  if message.content.startswith('$inspire'):
    quote = get_quote()
    await message.channel.send(quote)

client.run(os.getenv('TOKEN'))

Importamos o módulo requests. Ele permite que nosso código faça uma solicitação HTTP para obter dados da API. A API retorna um JSON e o módulo json facilita o trabalho com os dados retornados.

A função get_quote() é bastante clara. Primeiro, ela usa o módulo requests para solicitar dados do URL da API. A API retorna uma citação inspiradora aleatória. Essa função pode ser reescrita facilmente para obter citações de APIs diferentes, caso a atual pare de funcionar.

Em seguida, dentro da função, usamos json.loads() para converter a resposta de uma API para JSON. Depois de muita tentativa e erro, eu descobri como obter a citação do JSON no formato de string que eu desejava. A citação é, então, retornada pela função como uma string.

A parte final atualizada do código está mais para o final. Anteriormente, o bot verificava se a mensagem começava com "$hello". Agora, ele busca por "$inspire". Em vez de retornar "Hello!", ele pega uma citação com quote = get_quote() e a retorna.

Neste ponto, você pode rodar seu código e experimentar.

Como adicionar mensagens encorajadoras ao bot

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

Como adicionar palavras tristes ao bot

Primeiro, precisamos criar uma lista em Python que contenha as palavras tristes as quais o bot responderá.

Adicione a linha a seguir após a linha que cria a variável client no seu código:

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

Fique à vontade para adicionar mais palavras à lista.

Como adicionar as mensagens encorajadoras

Vamos adicionar uma lista de mensagens encorajadoras para as respostas do bot.

Adicione a lista a seguir após a lista de sad_words que você criou:

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

Da mesma forma que antes, fique à vontade para adicionar mais frases de sua preferência à lista. Estou usando apenas três itens por enquanto porque, mais tarde, adicionaremos a funcionalidade que permite aos usuários inserir mais frases de encorajamento para que o bot as utilize.

Como responder às mensagens

Precisamos atualizar nosso bot para que use as duas listas que criamos. Primeiro, importamos o módulo random, pois o bot escolherá mensagens de encorajamento aleatoriamente. Adicione a linha a seguir às declarações de import na parte superior do código: import random.

Vamos atualizar a função on_message() para verificar todas as mensagens e vermos se ela contém uma das palavras da lista de sad_words. Se uma palavra triste for encontrada, o bot enviará uma mensagem aleatória de encorajamento.

Aqui está o código atualizado:

async def on_message(message):
  if message.author == client.user:
    return

  msg = message.content

  if msg.startswith('$inspire'):
    quote = get_quote()
    await message.channel.send(quote)
    
  if any(word in msg for word in sad_words):
    await message.channel.send(random.choice(starter_encouragements))

É um bom momento para testar o bot. Você já sabe o bastante para criar seu próprio bot. Em seguida, você aprenderá a implementar recursos mais avançados e armazenar dados usando o banco de dados do Repl.it.

Como permitir mensagens enviadas pelo usuário

O bot está totalmente funcional, mas vamos permitir a atualização do bot diretamente do Discord. Um usuário deve poder adicionar mais mensagens encorajadoras ao bot para que ele as use ao 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 é um armazenamento do tipo chave-valor integrado a cada repl.

Na parte superior do código, abaixo das outras declarações de import, adicione from replit import db. Isso nos permitirá o uso do banco de dados do Repl.it.

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

Adicione o código a seguir após a função get_quote():

def update_encouragements(encouraging_message):
  if "encouragements" in db.keys():
    encouragements = db["encouragements"]
    encouragements.append(encouraging_message)
    db["encouragements"] = encouragements
  else:
    db["encouragements"] = [encouraging_message]

def delete_encouragment(index):
  encouragements = db["encouragements"]
  if len(encouragements) > index:
    del encouragements[index]
  db["encouragements"] = encouragements

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

Primeiro, ela verifica se "encouragements" é uma chave no banco de dados. Se for, ela busca a lista de encorajamentos que já está no banco de dados, adiciona a nova mensagem à lista e armazena a lista atualizada no banco de dados na chave "encouragements".

Se o banco de dados já não tiver a chave "encouragements", uma nova chave com esse nome será criada e a nova mensagem de encorajamento será adicionada como o primeiro elemento da lista.

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

Ela pega a lista de encorajamentos no banco de dados, armazenada sob a chave "encouragements". Se o número de itens da lista for maior do que o índice, o item da lista que está naquele índice é excluído.

Por fim, a lista atualizada é armazenada de volta no banco de dados na chave "encouragements".

Aqui está o código atualizado para a função on_message(). Depois do código, explicarei as novas seções.

async def on_message(message):
  if message.author == client.user:
    return

  msg = message.content
 
  if msg.startswith("$inspire"):
    quote = get_quote()
    await message.channel.send(quote)

  options = starter_encouragements
  if "encouragements" in db.keys():
    options = options + db["encouragements"]

  if any(word in msg for word in sad_words):
    await message.channel.send(random.choice(options))

  if msg.startswith("$new"):
    encouraging_message = msg.split("$new ",1)[1]
    update_encouragements(encouraging_message)
    await message.channel.send("New encouraging message added.")

  if msg.startswith("$del"):
    encouragements = []
    if "encouragements" in db.keys():
      index = int(msg.split("$del",1)[1])
      delete_encouragment(index)
      encouragements = db["encouragements"]
    await message.channel.send(encouragements)

A primeira linha do código acima é options = starter_encouragements. Fizemos uma cópia de starter_encouragements, pois vamos adicionar as mensagens enviadas pelo usuário àquela lista antes de escolher uma mensagem aleatória para que o bot a envie.

Verificamos se "encouragements" já está nas chaves do banco de dados (o que significa que um usuário enviou, pelo menos, uma mensagem personalizada). Em caso positivo, adicionamos as mensagens do usuário aos encorajamentos iniciais.

Então, em vez de enviar uma mensagem aleatória de starter_encouragements, o bot agora envia uma mensagem aleatória de options.

A próxima seção de código nova é 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 depois de "$new" será usado como uma nova mensagem de encorajamento.

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

Chamamos a função assistente update_encouragements com a nova mensagem. O bot, então, envia uma mensagem para o 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" do banco de dados.

Primeiro, uma nova variável chamada encouragements é inicializada em um array vazio. O motivo para isso está no fato de que esta seção de código enviará uma mensagem com um array vazio se o banco de dados não incluir uma chave "encouragement".

Se a chave "encouragement" estiver no banco de dados, o índice será removido da mensagem do Discord começando com "$del". Então, a função delete_encouragement() é chamada passando o índice da mensagem a ser excluída. A lista atualizada de encorajamentos é carregada na variável encouragements e o bot envia uma mensagem ao Discord com a lista atual.

Recursos finais do bot

O bot deve estar funcionando. É uma boa hora de testar. Adicionaremos agora 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 ou desativar a resposta do bot a palavras tristes.

Abaixo, eu forneço o código final completo do programa. Em seguida, discutiremos as atualizações feitas ao código.

import discord
import os
import requests
import json
import random
from replit import db

client = discord.Client()

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

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

if "responding" not in db.keys():
  db["responding"] = True

def get_quote():
  response = requests.get("https://zenquotes.io/api/random")
  json_data = json.loads(response.text)
  quote = json_data[0]["q"] + " -" + json_data[0]["a"]
  return(quote)

def update_encouragements(encouraging_message):
  if "encouragements" in db.keys():
    encouragements = db["encouragements"]
    encouragements.append(encouraging_message)
    db["encouragements"] = encouragements
  else:
    db["encouragements"] = [encouraging_message]

def delete_encouragment(index):
  encouragements = db["encouragements"]
  if len(encouragements) > index:
    del encouragements[index]
  db["encouragements"] = encouragements

@client.event
async def on_ready():
  print("We have logged in as {0.user}".format(client))

@client.event
async def on_message(message):
  if message.author == client.user:
    return

  msg = message.content

  if msg.startswith("$inspire"):
    quote = get_quote()
    await message.channel.send(quote)

  if db["responding"]:
    options = starter_encouragements
    if "encouragements" in db.keys():
      options = options + db["encouragements"]

    if any(word in msg for word in sad_words):
      await message.channel.send(random.choice(options))

  if msg.startswith("$new"):
    encouraging_message = msg.split("$new ",1)[1]
    update_encouragements(encouraging_message)
    await message.channel.send("New encouraging message added.")

  if msg.startswith("$del"):
    encouragements = []
    if "encouragements" in db.keys():
      index = int(msg.split("$del",1)[1])
      delete_encouragment(index)
      encouragements = db["encouragements"]
    await message.channel.send(encouragements)

  if msg.startswith("$list"):
    encouragements = []
    if "encouragements" in db.keys():
      encouragements = db["encouragements"]
    await message.channel.send(encouragements)
    
  if msg.startswith("$responding"):
    value = msg.split("$responding ",1)[1]

    if value.lower() == "true":
      db["responding"] = True
      await message.channel.send("Responding is on.")
    else:
      db["responding"] = False
      await message.channel.send("Responding is off.")

client.run(os.getenv("TOKEN"))

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

if "responding" not in db.keys():
  db["responding"] = True

Criamos uma nova chave no banco de dados chamada "responding" e definida como "True". Usaremos isso para determinar se o bot deve ou não responder a palavras tristes. Como o banco de dados é salvo mesmo após o programa parar de executar, criamos a nova chave apenas se ela ainda não existe.

A próxima parte nova do código é a seção que responde às palavras tristes, agora dentro dessa declaração de if: if db["responding"]:. O bot somente responderá às palavras tristes se db["responding"] = True. A capacidade de atualizar esse valor vem depois desta próxima seção.

Em seguida, após o código fazer com que o bot responda ao comando "$del", criamos um código novo para responder ao comando "$list" quando enviado como uma mensagem do Discord.

A seção começa criando uma lista vazia chamada encouragements. Então, se já houver encorajamentos no banco de dados, eles substituem a linha vazia recém-criada.

Por fim, o bot envia a lista de encorajamentos como uma mensagem do Discord.

A nova seção final vem logo após. Este código faz o bot responder ao comando "$responding". Esse comando recebe um argumento, "true" ou "false". Aqui, vemos um exemplo de uso: "$responding true".

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

O código do bot está completo! Agora, você pode executar o bot e experimentá-lo. Há, no entanto, mais um passo importante que discutiremos a seguir.

Como configurar o bot para que execute continuamente

Se estiver executando o bot no Repl.it e fechar a guia em que ele está executando, o bot vai parar de executar.

No entanto, há duas maneiras pelas quais você pode manter seu bot continuamente em execução, mesmo que você feche seu navegador.

A primeira forma e mais simples é se registrar em um plano pago no Repl.it. O plano mais barato que eles têm é chamado de Hacker Plan e inclui cinco repls sempre em atividade.

Depois de se registrar para o plano, abra seu repl e clique no nome na parte superior. Depois, selecione a opção "Always On".

image-35-1

Existe outra maneira de manter seu código em execução mesmo na camada gratuita, mas é um pouco mais complicada. O Repl.it seguirá em execução em um servidor web, mesmo se a guia estiver fechada. Mesmo um servidor web, no entanto, executará por, no máximo, uma hora se não estiver em uso.

Abaixo vemos o que diz a documentação do Repl.it:

Depois de implantado, o servidor continuará a executar em segundo plano, mesmo depois de você ter fechado a guia do navegador. O servidor seguirá habilitado e ativo por uma hora após a última execução. Depois disso, ele entrará em estado de hibernação. Os repls em hibernação despertam assim que receberem outra solicitação. Não há a necessidade de executar novamente o repl. Porém, se você fizer alterações ao servidor, precisará reiniciar o repl para ver essas mudanças refletidas na versão em execução.

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

O Uptime Robot pode ser configurado para lançar um ping para o servidor de web do bot no Repl.it a cada 5 minutos. Com pings constantes, o bot nunca entrará em estado de hibernação e continuará em execução infinitamente.

Desse modo, temos duas outras coisas para fazer com que nosso bot fique em execução continuamente:

  1. criar um servidor web no Repl.it e
  2. configurar o Uptime Robot para enviar pings continuamente para o servidor web.

Como criar um servidor web no Repl.it

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

Para fazer isso, crie um novo arquivo em seu projeto chamado keep_alive.py.

Depois, adicione nele o código abaixo:

from flask import Flask
from threading import Thread

app = Flask('')

@app.route('/')
def home():
    return "Hello. I am alive!"

def run():
  app.run(host='0.0.0.0',port=8080)

def keep_alive():
    t = Thread(target=run)
    t.start()

Neste código, usamos o Flask para iniciar um servidor web. O servidor retorna "Hello. I am alive." para quem o visitar. Ele estará em execução em uma thread separada daquela do nosso bot. Não discutiremos tudo aqui, pois, na verdade, o resto não é relevante para o nosso bot.

Agora, só precisamos fazer com que o bot execute nesse servidor web.

Adicione a linha a seguir na parte superior do main.py  para importar o servidor.

from keep_alive import keep_alive

Para iniciar o servidor web quando main.py é executado, adicione a linha abaixo como a penúltima linha, logo antes de o bot ser executado.

keep_alive()

Ao rodar o bot no Repl.it após adicionar esse código, uma nova janela do servidor web será aberta. Um URL é exibido para o servidor web. Copie esse URL para usá-lo na próxima seção.

image-20-1

Como configurar o Uptime Robot

Agora, precisamos configurar o Uptime Robot para fazer o ping para o servidor web a cada cinco minutos. Isso fará com que o bot fique em execução continuamente.

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

Quando estiver logado em sua conta, clique em "Add New Monitor".

image-21-1

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

image-22-1

Era isso! O bot, agora, estará em execução continuamente. Qualquer pessoa poderá interagir com ele no Repl.it.

Conclusão

Agora, sabemos como criar um bot do Discord com Python e colocá-lo em execução continuamente na nuvem.

Há muitas outras coisas que a biblioteca discord.py pode fazer. Se você quiser que seu bot do Discord tenha ainda mais recursos, seu próximo passo será conferir a documentação da discord.py.