The Creation of RocketBot

A journey into creating practical discord bots in python and discord.py

Theo
6 min readOct 1, 2020

Discord is a widely used messaging platform, with a seemingly unlimited amount of community’s and topics. The allure of bots within in this network is to do what computers do best: repetitive and info heavy tasks. Today, we are going to embody this and create “RocketBot”. RocketBot’s objective is to fulfill his name and fetch Rocket-related information from a multitude of free APIs, then format them before posting in discord — all on command.

Let’s begin by outlining what tasks we want RocketBot to Accomplish:

  • Get the next Rocket launch
  • Get the next couple of Rocket launches
  • Get the astronauts in space
  • Get the ISS position (and map it)

What library's we are going to use:

  • Discord.py | a easy to use wrapper for the discord API
  • Requests | for getting data
  • Plotly | advanced plotting (for some advanced features)
  • Kaleido | for static image export for the above-mentioned graphs

To install, simply use pip:

pip install discord requests plotly kaleido

Now, lets begin by actually connecting discord to our bot. First, we need a discord account, this can be made here. Once this is completed, we can go to The Discord Developer Portal. This is where we can actually make our bot and get the needed credentials to begin development on our bot.

Afterwards, we can begin creating our bot. Lets begin with a simple goal: get the next Rocket Launch. Lets begin with the most minimal code and explore what it means:

import discord

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('your token here')

When run, and once a discord user says the command $hello, the bot will respond with its own greeting. This is pretty explanatory but first lets examine what is going on.

These lines define our client, set it to a variable and let it wait for events.

client = discord.Client()

@client.event

Then, once the bot is ready (logged in, etc) it tells us:

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

Lastly, this is where the true logic occurs. Once we get a message (of any kind) we insure its not from us (so the bot does not infinitely trigger itself) and if the message starts with the command $hello, then we send a message back. This basic logic is what will define our following features.

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

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

client.run('your token here')

Now, we can actually begin getting information. For our Rocket Launch information we will use the RocketLaunch API. The great thing about this API is that its really accurate, real time and you can get the next 5 launches for free, forever!

We will begin by making a simple GET request to the API, and turning it into JSON so we can handle it with ease:

response = requests.get('https://fdo.rocketlaunch.live/json/launches/next/5')
w = response.json()

Then, we navigate the JSON until we find our desired result, before sending it to discord:

nlmsg = "cant find it..."

nlmsg = str(y[0]["launch_description"])

await message.channel.send(nlmsg)

This is our final code for this segment, using the try, and except as basic error handling.

if message.content.startswith('$nl'):
try:
response = requests.get('https://fdo.rocketlaunch.live/json/launches/next/5')
w = response.json()
y = w["result"]
nlmsg = "cant find it..."

nlmsg = str(y[0]["launch_description"])

await message.channel.send(nlmsg)

except:
await message.channel.send("Whoa we encountered an Error")

Now, lets expand on this and make a method to fetch the the next 5 launches. `This will be the same prior logic, we will just iterate over the 5 data points at the end:


for x in range(0, 5):
nlmsg = str(y[x]["launch_description"])

await message.channel.send("• {}".format(nlmsg))

Thus, our final result would be a slightly modified version of the prior code:

if message.content.startswith('$ll'):
try:
response = requests.get('https://fdo.rocketlaunch.live/json/launches/next/5')
w = response.json()
y = w["result"]
llmsg = "cant find it..."

for x in range(0, 5):
nlmsg = str(y[x]["launch_description"])

await message.channel.send("• {}".format(nlmsg))
except:
await message.channel.send("Whoa we encountered an Error")

Now, lets work on some other features. One I would like to add is information about the astronauts in space, so lets do that. The open-notify API has us covered, it has a easy method to find the astronauts in space and get the ISS (an advanced feature we will add soon). This will essentially be the same logic as the Rocket Launch list we made earlier just changed to correlate with the API.

We begin by making a request, parsing it into JSON, navigating to the information we need, then iterating around it, then print the info.

if message.content.startswith('$nauts'):
try:
response = requests.get('http://api.open-notify.org/astros.json')
w = response.json()
if w["message"] == "success":
nautmsg = "cant find it..."
nautnum = w["number"]
nautmsg = str(w["number"])
await message.channel.send("There are {} Astronauts in space!".format(nautmsg))
await message.channel.send("-------")
y = w["people"]
namemsg = "cant find it..."

for x in range(0, nautnum):
nautmsg = str(y[x]["name"])

await message.channel.send("• {}".format(nautmsg))

except:
await message.channel.send("Whoa we encountered an Error")

Lastly, lets visit the most advanced feature: the ISS position. While its pretty easy to simply fetch the coordinates from the aforementioned API, that's lame and no one knows where it actually is. Thats why we are going to plot it on a map, and upload the Image.

To plot, we are going to use Plotly, it has super easy mapping abilities and is over-all super easy to use. Now, the user must need to see the map, so we will use kaleido to export, and save the image so we can upload it to discord. For the sake of simplicity, we will make this function separate, on a new script called “maptest.py”.

Lets begin by outlining what we need to do:

  • Get the latitude and longitude of the ISS
  • Geo-locate that data
  • Plot that data on a map
  • Export the map (kaleido)
  • Upload the Image to discord

Lets start by getting the mapping done. Luckily Plotly makes this super easy (this requires a key for mapbox):

import plotly.graph_objects as go
import kaleido
mapbox_access_token = "your token goes here"def map(latitude, longitude): fig = go.Figure(go.Scattermapbox(
lat=[latitude],
lon=[longitude],
mode='markers',
marker=go.scattermapbox.Marker(
size=14
),
text=['Montreal'],
))
fig.update_layout(
hovermode='closest',
mapbox=dict(
accesstoken=mapbox_access_token,
bearing=0,
center=go.layout.mapbox.Center(
lat=latitude,
lon=longitude
),
pitch=0,
zoom=5
)
)

fig.write_image("fig1.jpg", width=900, height=800, scale=3)
return
An example of this at work

In explanation, we feed the function the coordinates, it creates a basic plot, styles it then exports it. Thus, we can make this process call-able and so we can use it at will.

Going back to our main script, lets create a section that manages this and does the work for discord:

if message.content.startswith('$iss'):
try:
response = requests.get('http://api.open-notify.org/iss-now.json')
w = response.json()
if w["message"] == "success":
issmsg = "cant find it..."

y = w["iss_position"]
lat = float(y["latitude"])
long = float(y["longitude"])


await message.channel.send("Loading Image (will take a moment)")
map(lat, long)
await message.channel.send(file=discord.File('fig1.jpg'))

await message.channel.send("ISS Postion: Lat: {0}, Long: {1}".format(str(lat),str(long)))
os. remove("fig1.jpg")
except:
await message.channel.send("Whoa we encountered an Error")

What this does is: Fetches the coordinates, sends it too our mapping script, waits before uploading the image and such. Plus a bunch of basic orginization and handling features.

Now we can test!

$nl
$ll
$nauts
$iss

Full code is available here

Invite the Full Featured bot here

--

--