Selecting My First Discord Bot Project

Having finished the RealPython Discord bot tutorial, I was anxious to build a discord bot. I’d recently complete my Pokémon Passport Service Alexa skill, so I wondered how hard it would be to adapt that data to a discord bot. Millions of Pokémon fans use Discord, so I hoped my bot would help the greater community.

Creating the Credentials

To start, I opened the Real Python tutorial up in one window and the Discord Developer Portal in another. I created a new application called “Pokémon Passport Service” and gave it a Pokéball image as an icon. Then, I attached a bot to the application. I copied my token from the bot and pasted it into a .env file. I set the variable “DISCORD_TOKEN.” This variable can be recalled in my code to make sure the bot is authorized to run.

Setting up my Database

For this project, I already had a data source ready. I’d created a Pokémon Passport Service Alexa skill a few weeks earlier. I copied the ‘pokemon.json’ file from my Alexa project file and moved that copy into my discord bot folder. I quickly inspected the file for inaccuracies and then proceeded to the next step.

Getting the Dependencies

To build a discord bot with Python you need access to the discord.py Python library. I installed the library in the command line by typing “pip install -U discord.py.” With the library installed, I created a Python file called pokepassport.py. I imported the libraries I needed with these lines of code.

import os
import discord
import json
import requests
from dotenv import load_dotenv
from discord.ext.commands import bot

I needed JSON to read my JSON file, and requests to retrieve images of my Pokémon. I needed dotenv to read my environmental variable file. With these dependencies imported into my code, I knew I’d be able to build my bot.

Using Credentials to Connect my Python Code to Discord

To pull my token from the .env file, I typed the following two lines of code.

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')

Then, I typed the next three lines of code to make sure the bot will connect and run.

bot.run(TOKEN)
client = discord.Client()
client.run(TOKEN)

With these pieces of code added, I was confident that my bot would be able to connect to Discord.

Creating the Pokémon Passport Command

For this bot, I only wanted one command. I wanted to be able to request a Pokémon and find out the last game the Pokémon was available. To start, I created a command prefix with the following line of code.

bot = commands.Bot(command_prefix='!')

Then, I defined my command with the following line.

@bot.command(name='passport', help='Tells you the last game a pokemon is obtainable in and playable in')

To build the functionality of the command, I looked at the code I used for my Pokémon Passport Service Alexa skill. In my Alexa skill, I looked for Pokémon by their name. From there, I was able to pull out their Pokédex number, last game catchable in, and the last game they were playable. After considering how I designed the Alexa skill, I was ready to code the command.

First, I created the command function with the following line of code.

async def pokemon(ctx, pokemon_name):

The bot would react to the command “!passport” and would take in a single argument “pokemon_name.” Next, I opened the JSON file as a Python dictionary with the following lines.

with open('pokemon.json') as f:
    data = json.load(f)

With the data loaded, I was able to pull up the Pokédex number and the games the Pokémon was last catchable and playable in with these three lines.

DexNumber = data[pokemon_name]['Pokedex Number']
LastCatchable = data[pokemon_name]['Last Game Catchable In']
LastPlayable = data[pokemon_name]['Last Game Playable in']

Now that I knew how to pull the data, I had a new issue. How would I address the issue of people using the command improperly? There is nothing forcing people to give a valid argument. I wanted the bot to inform users of their mistakes, so I decided to use an exception handler. If users used a valid Pokémon value, then they would get the standard response. If they used an invalid value, then they would receive an error message. The code below executed that vision.

try:
   DexNumber = data[pokemon_name]['Pokedex Number']
   LastCatchable = data[pokemon_name]['Last Game Catchable In']
   LastPlayable = data[pokemon_name]['Last Game Playable in']
except:
    response = "You didn't enter a valid pokemon name"
await ctx.send(response)

For one last bit of polish, I wanted to add an image of the Pokémon in question to the discord bot’s response. I used this website for my pictures. I created a variable called URL in this line.

url='https://pokeres.bastionbot.org/images/pokemon/{}.png'.format(DexNumber)

Then I embedded the image and set the bot to respond with the picture. Finally, I had the block of code below.

@bot.command(name='passport', help='Tells you the last game a pokemon is obtainable in and playable in')
async def pokemon(ctx, pokemon_name):
    with open('pokemon.json') as f:
        data = json.load(f)
    try:
        DexNumber = data[pokemon_name]['Pokedex Number']
        LastCatchable = data[pokemon_name]['Last Game Catchable In']
        LastPlayable = data[pokemon_name]['Last Game Playable in']
        url='https://pokeres.bastionbot.org/images/pokemon/{}.png'.format(DexNumber)
        e = discord.Embed()
        response = "{} was last catchable in {} and last useable in {}".format(pokemon_name,LastCatchable,LastPlayable)
    except:
        response = "You didn't enter a valid pokemon name"
    await ctx.send(response)
    e.set_image(url=url)
    await ctx.send(url)

When I tested this command in my test server, I ended up with the response below.

A sample response from the bot

I was happy with these results, so I left the bot on for my friends to enjoy. If you’d like to check out the code, visit the GitHub Repository here.