Embedding and Attaching w/ Image Example - making Discord bots with Discordpy 1.0.0 p.4




DiscordPy P.4 - bot sharing attachments

Greetings and welcome to part 4 of the DiscordPy tutorials. In this tutorial, we're going to add the functionality for our bot to share attachments. In this case, it will be an image of a graph of one of our user metrics, users online, over time.

In the previous tutorial, we covered how to schedule a task to run automatically every n seconds. In our case, we did every 5, just to make sure it was working, and that's saving a CSV file of our users. That CSV file is called usermetrics.csv and looks like:

1539367729,332,609,7588
1539367734,332,608,7589
1539367739,332,607,7590
1539367744,331,607,7590
1539367749,331,608,7589
1539367754,330,607,7590
1539367759,330,607,7590
1539367764,329,606,7591
1539367769,329,606,7591
1539367774,329,608,7589
1539367779,329,608,7589
1539367784,331,608,7589
1539367789,332,608,7589
1539367794,329,606,7591
1539367799,328,608,7589
1539367804,328,608,7589
1539367809,326,606,7591
1539367814,325,606,7591
(1539367814, 325, 606, 7591)

Feel free to copy that and save it if you don't have your own metrics to test with or something.

First, we need to graph and save this stuff. Here's a quick script for that, using pandas and matplotlib (py -3.6 -m pip install pandas matplotlib if you need them).

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import style
style.use("fivethirtyeight")

df = pd.read_csv("usermetrics.csv", names=['time', 'online', 'idle', 'offline'])
df['date'] = pd.to_datetime(df['time'],unit='s')
df['total'] = df['online'] + df['offline'] + df['idle']
df.drop("time", 1,  inplace=True)
df.set_index("date", inplace=True)

print(df.head())

df['online'].plot()
plt.legend()
plt.show()
plt.savefig("online.png")
                     online  idle  offline  total
date
2018-10-12 18:08:49     332   609     7588   8529
2018-10-12 18:08:54     332   608     7589   8529
2018-10-12 18:08:59     332   607     7590   8529
2018-10-12 18:09:04     331   607     7590   8528
2018-10-12 18:09:09     331   608     7589   8528
<Figure size 432x288 with 0 Axes>

Good enough, and now we have the figure saved. We can take the above code, and add it to our background task, to make sure the image stays updated. Our code so far is:

import discord
import time
import asyncio

client = discord.Client()
token = open("token.txt", "r").read()


def community_report(guild):
    online = 0
    idle = 0
    offline = 0

    for m in guild.members:
        if str(m.status) == "online":
            online += 1
        if str(m.status) == "offline":
            offline += 1
        else:
            idle += 1

    return online, idle, offline


async def user_metrics_background_task():
    await client.wait_until_ready()
    global sentdex_guild
    sentdex_guild = client.get_guild(405403391410438165)
    while not client.is_closed():
        try:
            online, idle, offline = community_report(sentdex_guild)
            with open("usermetrics.csv","a") as f:
                f.write(f"{int(time.time())},{online},{idle},{offline}\n")
            await asyncio.sleep(5)

        except Exception as e:
            print(str(e))
            await asyncio.sleep(5)


@client.event  # event decorator/wrapper
async def on_ready():
    global sentdex_guild

    print(f"We have logged in as {client.user}")


@client.event
async def on_message(message):
    global sentdex_guild

    print(f"{message.channel}: {message.author}: {message.author.name}: {message.content}")

    if "sentdebot.member_count()" == message.content.lower():
        await message.channel.send(f"```py\n{sentdex_guild.member_count}```")

    elif "sentdebot.logout()" == message.content.lower():
        await client.close()

    elif "sentdebot.community_report()" == message.content.lower():
        online, idle, offline = community_report(sentdex_guild)

        await message.channel.send(f"```Online: {online}.\nIdle/busy/dnd: {idle}.\nOffline: {offline}```")

client.loop.create_task(user_metrics_background_task())
client.run(token)

So now we modify the background task:

async def user_metrics_background_task():
    await client.wait_until_ready()
    global sentdex_guild
    sentdex_guild = client.get_guild(405403391410438165)
    while not client.is_closed():
        try:
            online, idle, offline = community_report(sentdex_guild)
            with open("usermetrics.csv","a") as f:
                f.write(f"{int(time.time())},{online},{idle},{offline}\n")
            plt.clf()
            df = pd.read_csv("usermetrics.csv", names=['time', 'online', 'idle', 'offline'])
            df['date'] = pd.to_datetime(df['time'],unit='s')
            df['total'] = df['online'] + df['offline'] + df['idle']
            df.drop("time", 1,  inplace=True)
            df.set_index("date", inplace=True)
            df['online'].plot()
            plt.legend()
            plt.savefig("online.png")

            await asyncio.sleep(5)

        except Exception as e:
            print(str(e))
            await asyncio.sleep(5)

Note I added the plt.clf(), otherwise the bot will plot separate graphs of online user every time. Next, add the matplotlib and pandas imports:

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import style
style.use("fivethirtyeight")

Now, we just want to add the online.png image to be shared, so, in the community report:

    elif "sentdebot.community_report()" == message.content.lower():
        online, idle, offline = community_report(sentdex_guild)
        await message.channel.send(f"```Online: {online}.\nIdle/busy/dnd: {idle}.\nOffline: {offline}```")

        file = discord.File("online.png", filename="online.png")
        await message.channel.send("online.png", file=file)

Full code up to this point:

import discord
import time
import asyncio

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import style
style.use("fivethirtyeight")

client = discord.Client()
token = open("token.txt", "r").read()


def community_report(guild):
    online = 0
    idle = 0
    offline = 0

    for m in guild.members:
        if str(m.status) == "online":
            online += 1
        if str(m.status) == "offline":
            offline += 1
        else:
            idle += 1

    return online, idle, offline


async def user_metrics_background_task():
    await client.wait_until_ready()
    global sentdex_guild
    sentdex_guild = client.get_guild(405403391410438165)
    while not client.is_closed():
        try:
            online, idle, offline = community_report(sentdex_guild)
            with open("usermetrics.csv","a") as f:
                f.write(f"{int(time.time())},{online},{idle},{offline}\n")

            plt.clf()
            df = pd.read_csv("usermetrics.csv", names=['time', 'online', 'idle', 'offline'])
            df['date'] = pd.to_datetime(df['time'],unit='s')
            df['total'] = df['online'] + df['offline'] + df['idle']
            df.drop("time", 1,  inplace=True)
            df.set_index("date", inplace=True)
            df['online'].plot()
            plt.legend()
            plt.savefig("online.png")

            await asyncio.sleep(5)

        except Exception as e:
            print(str(e))
            await asyncio.sleep(5)


@client.event  # event decorator/wrapper
async def on_ready():
    global sentdex_guild
    print(f"We have logged in as {client.user}")


@client.event
async def on_message(message):
    global sentdex_guild

    print(f"{message.channel}: {message.author}: {message.author.name}: {message.content}")

    if "sentdebot.member_count()" == message.content.lower():
        await message.channel.send(f"```py\n{sentdex_guild.member_count}```")

    elif "sentdebot.logout()" == message.content.lower():
        await client.close()

    elif "sentdebot.community_report()" == message.content.lower():
        online, idle, offline = community_report(sentdex_guild)
        await message.channel.send(f"```Online: {online}.\nIdle/busy/dnd: {idle}.\nOffline: {offline}```")

        file = discord.File("online.png", filename="online.png")
        await message.channel.send("online.png", file=file)

client.loop.create_task(user_metrics_background_task())
client.run(token)

Testing:

Woo hoo!





  • Introduction and basic bot - making Discord bots with Discordpy 1.0.0 p.1
  • Building out Bot Commands - making Discord bots with Discordpy 1.0.0 p.2
  • Timing bot actions - making Discord bots with Discordpy 1.0.0 p.3
  • Embedding and Attaching w/ Image Example - making Discord bots with Discordpy 1.0.0 p.4