Adam
Adam

Reputation: 23

Discord Bot Looping Task from Command

Good afternoon all, I should preface this post with the statement that I am pretty new to Python and know enough to get myself into trouble, but not enough to always get out of trouble...this is one of those instances I can't get out.

I am attempting to create a Discord Bot using discord.py that has the ultimate goal of being started from a command in Discord, starts counting the days since the start command was issued, and sends a message to Discord every morning with the count. I also would have a command to reset the count and another to cancel the counter. For testing purposes, I have created code to count minutes instead of days and a task loop of only 10 seconds rather than a full day.

My problem is that I am attempting to use a discord.ext task for the loop, but I clearly don't know how to use it properly, and my research online has not made it any more clear to me. I'm hoping some folks here can steer me in the right direction. I have supplied my code below.

What I expect to happen when I execute my code:

What actually happens:

The code is currently being hosted on replit.com, hence the keep_alive function using UpTimeRobot to keep the bot alive. Something to note, I originally used asyncio.sleep() to just wait to send the new message. This worked fine for shorter periods (like a few minutes to hours) but even with UpTimeRobot doing its thing, I can't get 100% uptime of the Bot and I think whenever the bot went offline for a few minutes, it stopped my counter loop and that was that. This is what lead me to look into Tasks, as I read they can be used to continue loops on reconnection if the bot goes offline for a bit.

import discord
import os
import keep_alive
from datetime import date, datetime, time, timedelta
from discord.ext import commands, tasks

bot = commands.Bot(command_prefix="$")
init_count = 0
count = 0
channel_id = My_Channel_ID_Here

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

@tasks.loop(seconds=10, count=None, reconnect=True)
async def min_counter():
    global count
    global init_count
    count = init_count
    today = datetime.today()
    now = datetime.today()
    channel = bot.get_channel(channel_id)

    if today.minute != now.minute:
            # increment count by 1
      count = count + 1
      today = now
      print('Count: ' + str(count))
      await channel.send('It Has Been ' + str(count) + ' Minutes.') # Send message of minutes count

@bot.command() # Command to start the counter
async def start (ctx, arg: int): # arg is initial number of minutes to start with
    global init_count
    init_count = arg
    await ctx.send('It Has Been ' + str(init_count) + ' Minutes.') # Send message of minutes count
    min_counter.start()

@bot.command() # Command to reset counter
async def reset (ctx):
    global count
    count = 0
    await ctx.send('It Has Been 0 Minutes.') # Send message that count is not zero

@bot.command() # Command to stop the counter
async def stop (ctx):
    min_counter.cancel()
    await ctx.send('Counter Stopped.')

keep_alive.keep_alive() # keep alive function call
bot.run(os.getenv('TOKEN')) # Discord bot private token call

Upvotes: 2

Views: 1371

Answers (1)

Richard
Richard

Reputation: 425

Upon quick inspection you are looping the method min_counter() but every time it is called you are changing the values of now and today to datetime.today().

So when you go to compare the values here:

if today.minute != now.minute:
  # increment count by 1
  count = count + 1
  today = now
  print('Count: ' + str(count))
  await channel.send('It Has Been ' + str(count) + ' Minutes.') # Send message of minutes count

This will always evaluate to False.

Easy Fix

Although I don't like the use of globals too much, here is how we could fix this keeping the style you have now.

Start the counter and initialize a variable named start_time in global scope:

@bot.command() # Command to start the counter
async def start (ctx, arg: int): # arg is initial number of minutes to start with
    global init_count, start_time
    start_time = datetime.today()
    await ctx.send('It Has Been ' + str(init_count) + ' Minutes.') # Send message of minutes count
    min_counter.start()

Then check if start_time is equal to now in the loop:

@tasks.loop(seconds=10, count=None, reconnect=True)
async def min_counter():
    global count, start_time, init_count
    now = datetime.today()
    channel = bot.get_channel(channel_id)

    if start_time != now:
      # increment count by 1
      count = count + 1
      print('Count: ' + str(count))
      await channel.send('It Has Been ' + str(count) + ' Minutes.') # Send message of minutes count

Let me know if everything works for you, I'd be happy to help further if needed.

Upvotes: 1

Related Questions