Bots In Python #2 | Creating Ticker And Notes Commands | DiscordSteem | Part #3

in #utopian-io6 years ago (edited)


Image Source

Hi Everyone! Welcome back to another part of DiscordSteem. Today's post will be a bit more complex/productive. We are going to be creating two new commands in this tutorial. The first one will be "Ticker" command. With the help of this command, we will be easily able to check the price of any coin. We will be also able to get the price of the coin in many other currencies than just USD. To create this command, we will use coinmarketcap api (version 1). The reason why we are going to be using version 1 is that because in version two we need the id of the cryptocurrency of which we want the information but in version 1 we can just use the name of the coin instead of the id. The second one will be the notes command. Using this command, users will be able to create notes which can be called at any point. I know both of these commands are not related to Steem but I think they are useful and thus I decided to add them.

Repository

Other Related Repositories

What Will I Learn?

In this tutorial, you will learn a lot of very useful things like how you can send requests to api and then use the response you get to send messages to the discord server. You will also learn how you can code your bot to send direct messages. This will be done in my beloved language Python.

Requirements

In order to follow along with this tutorial you will need to have these things installed.

  • Python 3.6
  • Discord.py

Difficulty

  • Intermediate

Tutorial Contents

Creating Ticker Command

The first command that we are going to be creating is Ticker. Here's how people will be able to use this command !ticker <coin> <currency>. There are going to be two things that people will need to assign. The first one is going to be the coin, this will be the name of coin (i.e bitcoin).

The second parameter is currency which is optional. If user hasn't selected any currency than it will show USD by default. We will use short forms of currencies. Here are the currencies that coinmarketcap support. Our bot will support these currencies as well.

"AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR"

The first step is going to be importing requests which can be done by typing import requests. This will be used to send requests to api and then get the response. Create a new command after the last one. The name of the command will be "ticker" and it will have three parameters.

The first one - of course - is going to be "ctx", second one will be "coin" and third one will be "currency" which by default will be set to "usd". Here's what our first two lines will look like.

# Ticker Command
@bot.command(pass_context=True)
async def ticker(ctx, coin, currency="usd") # Defining Command

We want our currency to be lowercase because even though we can get the price of the coin even if it is upper case, it will make it easy for us to grab the price in that currency from JSON. In the third line, we are going to make the currency lower case so our bot doesn't break if some accidentally writes currency in upper case. Now our command will look like this. I have created the variable called "cur" which is currency in lower case. I couldn't come up with better name.

# Ticker Command
@bot.command(pass_context=True)
async def ticker(ctx, coin, currency="usd"): # Defining Command
  cur = currency.lower() # "cur" will have lower cased currency

The next thing will be to send the request to the api and then get the response. We have already imported requests library to do this. Create a variable called "r" under the "cur" variable. Assign it this value requests.get("https://api.coinmarketcap.com/v1/ticker/%s/?convert=%s" % (coin, cur)).

So what Python will do is that it will get the response from this api and then store it in the "r" variable. You can learn more about coinmarketcap api from here. Now our command will look like this.

# Ticker Command
@bot.command(pass_context=True)
async def ticker(ctx, coin, currency="usd"): # Defining Command
  cur = currency.lower() # "cur" will have lower cased currency
  r = requests.get("https://api.coinmarketcap.com/v1/ticker/%s/?convert=%s" % (coin, cur)) # Getting the response

Now we have the response but it is not JSON. To turn it into JSON we will need to import it and you can do that by typing import json at the top of the file. Now create a new variable under the last one called "r_json" and assign it the value r.json(). Now our command will look like this.

# Ticker Command
@bot.command(pass_context=True)
async def ticker(ctx, coin, currency="usd"): # Defining Command
  cur = currency.lower() # "cur" will have lower cased currency
  r = requests.get("https://api.coinmarketcap.com/v1/ticker/%s/?convert=%s" % (coin, cur)) # Getting the response
  r_json = r.json() # Getting JSON from the request

Now we have to take a step. Users won't be always writing correct names of coins or currency. We have to separate correct requests from the incorrect ones. Let's take a look at both and find the difference which will allow us to stop the bot from breaking on wrong requests. When users uses correct name of coin and currency then the response looks like this.

[{'id': 'bitcoin', 'name': 'Bitcoin', 'symbol': 'BTC', 'rank': '1', 'price_usd': '6434.45411862', 'price_btc': '1.0', '24h_volume_usd': '3908251781.19', 'market_cap_usd': '110840389231', 'available_supply': '17226075.0', 'total_supply': '17226075.0', 'max_supply': '21000000.0', 'percent_change_1h': '-0.15', 'percent_change_24h': '-3.73', 'percent_change_7d': '0.76', 'last_updated': '1535019742'}]

This is the response when the user types wrong coin or currency.

{'error': 'id not found'}

The main difference that we can easily use is that when the request is correct, the response is inside a list. When the request is invalid or incorrect then the response will be a dictionary. We can use this information to identify which request was correct and which was wrong because in the wrong requests the price won't be found causing the bot to break. A simple if statement will do the trick.

The if statement will allow us to check whether the response is a dict or list. If the response is a list we will allow the bot to get the price and if it is a dict then we will send a message telling that request was incorrect. Under the last line, create an if statement that will look like if type(r_json) is list:. This will check if the response is a list. Let's focus first on creating the part where user has sent correct request.

Create a variable under the if statement called "price", in this variable we will be storing the price of the coin. To get the dict we will type r_json[0]. The normal way to get the price from the dict is by typing r_json[0]["price_usd"] if the user requested USD price. We will use get() function to get the price, we will do this because it can take a optional parameter in which we set default string or integer if the key is no present. Assign this value to the variable r_json[0].get(("price_" + cur), ("Incorrect Currency Or Price")). Our code now looks like this.

@bot.command(pass_context=True)
async def ticker(ctx, coin, currency="usd"): # Defining Command
  cur = currency.lower() # "cur" will have lower cased currency
  r = requests.get("https://api.coinmarketcap.com/v1/ticker/%s/?convert=%s" % (coin, cur)) # Getting the response
  r_json = r.json() # Getting JSON from the request
  if type(r_json) is list: # Checking if response is list
    price = r_json[0].get(("price_" + cur), ("Invalid currency")) # Adding Price If Exists

Now "price" variable will either be the price of the coin or it will be the string "Invalid currency". We need to check this because if the user has used incorrect currency then there will not be price in the dictionary. Now we will assign the bot to send different message if the "price" variable contains the price of the coin and different if it does not.

To do this we will create another if statement. The statement will look like this if price[0] == "I":. Surely, price won't have alphabets in it so if the first letter is "I" - which will be the case when user requests wrong currency - then it will run the code inside the block.

The code inside the will be very generic. If the if statement is true then that means that user input wrong currency. This block of code will be inside if statement.

embed = discord.Embed(title=("DiscordSteem"), description=("Invalid currency"))
await bot.say(embed=embed)

Now our command will be looking something like this.

@bot.command(pass_context=True)
async def ticker(ctx, coin, currency="usd"): # Defining Command
  cur = currency.lower() # "cur" will have lower cased currency
  r = requests.get("https://api.coinmarketcap.com/v1/ticker/%s/?convert=%s" % (coin, cur)) # Getting the response
  r_json = r.json() # Getting JSON from the request
  if type(r_json) is list: # Checking if response is list
    price = r_json[0].get(("price_" + cur), ("Invalid currency")) # Adding Price If Exists
    if price[0] == "I": # If the price contains string
        embed = discord.Embed(title=("DiscordSteem"), description=("Invalid currency")) # Creating embed message
        await bot.say(embed=embed) # Sending embed message

Now that we have two if statement, let's build the else statement. First we are going to be creating inner else statement. Code inside this else statement will run when the correct price is present inside "price" variable. Type else: with the inner if statement's indentation and inside it type

embed = discord.Embed(title=("Price Of " + coin.upper()), description=("%.2f %s" % (float(price), cur.upper())))
await bot.say(embed=embed)

Now our command will be looking like this.

@bot.command(pass_context=True)
async def ticker(ctx, coin, currency="usd"): # Defining Command
  cur = currency.lower() # "cur" will have lower cased currency
  r = requests.get("https://api.coinmarketcap.com/v1/ticker/%s/?convert=%s" % (coin, cur)) # Getting the response
  r_json = r.json() # Getting JSON from the request
  if type(r_json) is list: # Checking if response is list
    price = r_json[0].get(("price_" + cur), ("Invalid currency")) # Adding Price If Exists
    if price[0] == "I": # If the price contains string
      embed = discord.Embed(title=("DiscordSteem"), description=("Invalid currency")) # Creating embed message
      await bot.say(embed=embed) # Sending embed message
    else:
      embed = discord.Embed(title=("Price Of " + coin.upper()), description=("%.2f %s" % (float(price), cur.upper()))) # Creating Embed Message
      await bot.say(embed=embed) # Sending Message

Let's now create the second else statement which will be triggered when the response isn't dict which means that user has typed incorrect coin. The same embed message will be displayed but this time the coin is invalid. Type else: with the indentation of the outer if and then inside it type this.

embed = discord.Embed(title=("DiscordSteem"), description=("Invalid coin"))
await bot.say(embed=embed)

Here's what our command looks finally. Our command is now complete.

@bot.command(pass_context=True)
async def ticker(ctx, coin, currency="usd"): # Defining Command
  cur = currency.lower() # "cur" will have lower cased currency
  r = requests.get("https://api.coinmarketcap.com/v1/ticker/%s/?convert=%s" % (coin, cur)) # Getting the response
  r_json = r.json() # Getting JSON from the request
  if type(r_json) is list: # Checking if response is list
    price = r_json[0].get(("price_" + cur), ("Invalid currency")) # Adding Price If Exists
    if price[0] == "I": # If the price contains string
      embed = discord.Embed(title=("DiscordSteem"), description=("Invalid currency")) # Creating embed message
      await bot.say(embed=embed) # Sending embed message
    else:
      embed = discord.Embed(title=("Price Of " + coin.upper()), description=("%.2f %s" % (float(price), cur.upper()))) # Creating Embed Message
      await bot.say(embed=embed) # Sending Message
  else:
    embed = discord.Embed(title=("DiscordSteem"), description=("Invalid coin")) # Creating Embed Message
    await bot.say(embed=embed) # Sending Message

How Ticker Command Works

Let's now see our bot in action. First check the simple command !ticker steem. This should return the price of Steem and it will be in USD. This is because the default currency is USD.
Screenshot from 2018-08-23 21-43-19.png
Now let's check another command and that is !ticker steem PHP. This will let us know the price of Steem in Philippines Pesos. Here's how it works.
Screenshot from 2018-08-23 21-51-34.png
Now let's see some errors. Here's what the bot replies when we type wrong coin.
Screenshot from 2018-08-23 21-53-31.png
Here's what the bot replies when we type wrong currency.
Screenshot from 2018-08-23 21-55-46.png

Creating Notes Commands

Our second command which we will be making in this tutorial is notes commands. It's actually gonna be three commands, one is going to used for adding note, one for reading notes and the last one will be for deleting notes.

Creating "Add Note" Command (!addnote)

The first command which we are going to be making out of those three commands is add note command and it will be used like this !addnote <note>. The purpose of this command is pretty self explanatory. Here's what our workflow is going to look like.

- User uses command
- Bot stores the note in a dictionary where the key will be name ofhttps://discordpy.readthedocs.io/en/latest/api.html#embed the user and value will be his notes in a list
- Bot sends message that note has been added

Let's start by defining the command. Here's what our first two lines are going to be.

@bot.command(pass_context=True)
async def addnote(ctx, note): # Defining Command

The function will have two parameters, first is ctx or context which in our case is going to be !addnote and then the second parameter is going to be the actual note. In the dictionary we the key will be the name of the user so first let's store it in a variable. We can do this by typing author = ctx.message.author.

There is going to be if statement checking whether author already has some notes or not because we are going to be performing different actions according to that. If the author is not present in the dictionary then we are first going to create key and add value. If the author is present then we are just going to append the value after the previous values (notes). Before creating the if statement we will be creating an empty dictionary outside the function because this same dictionary will be used in other functions as well. In the variables section, type notes = {} after the last variable. Type if author in notes: on the fourth line of the command. Now our command will look like this.

@bot.command(pass_context=True)
async def addnote(ctx, note):
  author = ctx.message.author # Author variable will have name of the person who used the command
  if author in notes: # Checking if author already has previous notes

Code inside the if statement will only run if the author already has previous notes. In this case, we will add new note after the last one instead of creating new key and value. Type this command inside the if statement.

notes[author] += [note] # Adding note after last one
embed = discord.Embed(title="Note Added!", color=0x00ff00) # Creating embed message
await bot.say(embed=embed) # Sending embed message

First we are going to be adding this note after the last one by using +=. Then we are creating embed message with the title "Note Added!" to let user know that we have added the note. The color is going to be a hex code in our case it is green. After this we are just going to send the message. Here's what our command looks like now.

@bot.command(pass_context=True)
async def addnote(ctx, note): # Defining Command
  author = ctx.message.author # Author variable will have name of the person who used the command
  if author in notes: # Checking if author already has previous notes
    notes[author] += [note] # Adding note after last one
    embed = discord.Embed(title="Note Added!", color=0x00ff00) # Creating embed message
    await bot.say(embed=embed) # Sending embed message

Now that we have coded what to do if the author already has any previous note, we will code if the author does not. This will be done in else statement so type else: with the indentation of the if statement. The code in the else statement will run if the author does not has any previous note. Type this inside the else statement.

notes[author] = [note]
embed = discord.Embed(title="Note Added!", color=0x00ff00)
await bot.say(embed=embed)

You can see that this code is also pretty much same but there is one difference and that is = instead of +=. This is because this time we are assigning value and last time we were adding value after the existing one. We have now successfully created add note command. Notes can now be added to the dictionary. Here's complete code for the command.

@bot.command(pass_context=True)
async def addnote(ctx, note): # Defining Command
  author = ctx.message.author # Author variable will have name of the person who used the command
  if author in notes: # Checking if author already has previous notes
    notes[author] += [note] # Adding note after last one
    embed = discord.Embed(title="Note Added!", color=0x00ff00) # Creating embed message
    await bot.say(embed=embed) # Sending embed message
  else:
    notes[author] = [note] # Creating key with the name of author and value is the note
    embed = discord.Embed(title="Note Added!", color=0x00ff00) # Creating embed message
    await bot.say(embed=embed) # Sending embed message

Here's what our command looks like when it is working. There's one thing to make sure and that is that note should be in quotation marks (" ").

Creating Recall Note Command (!note)

Now that users can add notes, let's allow them to read the notes. I don't want anybody to read any other ones notes. To prevent this, whenever someone uses this command, the bot will DM him listing all the notes that he created. This way no one will be able to check others notes. Let's start by simply defining the command. You can do this by typing or copying the code below.

@bot.command(pass_context=True)
async def note(ctx):

You can see that name of the command is note. There is only one parameter and that is context. User will only to type !note and then bot will DM the user. To DM the user we need the name of the user so we will get this out of the way first. Create a variable called "author" and assign it the value ctx.message.author just like we did before.

We will use the old tactic. First we will create an embed message and then run the four loop in which we will be adding fields. After the for loop, we will be sending the message. First thing is to create an embed message. You can do that by typing embed = discord.Embed(title=("DiscordSteem"), description=("Your Notes"), color=0x00ff00, inline=True). Now we will run the for loop and then after the for loop we will send the DM. Here's the complete code of the command.

@bot.command(pass_context=True)
async def note(ctx): # Defining command
  author = ctx.message.author # Author variable will have name of author
  embed = discord.Embed(title=("DiscordSteem"), description=("Your Notes"), color=0x00ff00, inline=True) # Creating embed message
  for note in notes[author]: # for loop to get each note
    embed.add_field(name=note, value="Note") # Adding each note as a field
  await bot.send_message(author, embed=embed) # Sending direct message

I didn't wanted to add value to the field but since it is necessary, I had to do so. To send a direct message we use bot.send_message(). It uses two parameters, the first one to whom do we want to send the message. The second parameter is what message to send. Let's how the bot works right now. Once anyone types !note he get's a discord DM by the bot with the notes that he/she created.

Creating Delete Notes Command (!delnote)

Now that users can add and read notes, let's allow them to delete the notes. I won't allow them to delete individual notes for now. This command will delete all the existing notes that user has created. This is also one of the easiest commands that we are going to be creating in this tutorial. The first step will always be to define the command. You can do that by typing the code below.

@bot.command(pass_context=True)
async def delnote(ctx):

Now we need the author to delete the key which is present in the dictionary because each key is the author who wrote the message. We have already done it multiple times. After that we will delete the key using ```del````. Then create a embed message and then send it. Here's the full code for this command.

@bot.command(pass_context=True)
async def delnote(ctx): # Defining Command
  author = ctx.message.author # Name of the author will be assigned to this variable
  del notes[author] # Deleting the key with the name of author
  embed = discord.Embed(title="Notes Deleted!", color=0x00ff00) # Creating embed message
  await bot.say(embed=embed) # Sending the message

Final Thoughts

I know that in this tutorial we learned some very important stuff that can be used in other applications as well if we take this tutorial as a base. This tutorial was not related to Steem in any way because I wanted to show and add some other features to my bot as well that are not related to Steem. Hope you liked my tutorial. See Ya!

Curriculum

Here's a list of tutorials that are related to building bots in Python.

Proof Of Work Done

Note: There are some changes in the code of the repository. The name of the file is changed from "bot.py" to "DiscordSteem.py". I have not added the token to my code and to be able to use the code you must add the token of your bot. Thanks!

Sort:  

Thank you for your contribution.

  • Excellent tutorial with very well explained code. Thank you very much for your contribution.

We are waiting for more tutorials of this quality.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Thank you for your review, @portugalcoin!

So far this week you've reviewed 12 contributions. Keep up the good work!

Hey @rodus
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.19
TRX 0.13
JST 0.028
BTC 64970.70
ETH 3238.82
USDT 1.00
SBD 2.64