Timing bot actions - making Discord bots with Discordpy 1.0.0 p.3




DiscordPy p3 Timing Actions

Welcome to part 3 of the Discordpy tutorials. In this tutorial, we're going to cover timing actions that you might want to do every n seconds, minutes, hours...etc. In my case, I would like to start tracking various statistics from my server, saving them to later graph over time.

Code up to this point:

import discord

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


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


@client.event
async def on_message(message):
    print(f"{message.channel}: {message.author}: {message.author.name}: {message.content}")
    sentdex_guild = client.get_guild(405403391410438165)

    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 = 0
        idle = 0
        offline = 0

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

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


client.run(token)

At least for now, I am just going to track the online, offline, and idle numbers. Later, I might also add something to track messages per hour something like that too.

Because of this, I am going to move the community report code to its own function, since we're going to need that same code again.

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

Now the actual community_report code is:

    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}```")

Next, let's add a background task:

async def user_metrics_background_task():
    await client.wait_until_ready()
    global sentdex_guild

    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)

Now, before our client.run, we do:

client.loop.create_task(user_metrics_background_task())

I also have employed the use of global to share the guild value across the async methods.

Full code now is:

import discord
import asyncio
import time

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

    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
    sentdex_guild = client.get_guild(405403391410438165)
    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)

Now, every 5 seconds, our bot saves some user stats. Now, what I'd like to do is collect these over time, then, replace the community_report function to include some charts generated by our bot from these stats so we can see trends over time.

The next tutorial:





  • 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