Timer/countdown with custom duration? - python

This chunk of code is a timer with a set duration of 90 seconds, using the command '!t90' to start.
How would I write the option to use command '!t' with any number of seconds?
(or 30, 45, 60, 90, 120 in one file (as these are the only timers that my server will use 99.9% of the time))
Thanks
import discord
from discord.ext import commands
import asyncio
bot = commands.Bot(command_prefix="!")
counter_channel = None
task = None
async def ex(message):
global counter_channel
if counter_channel is not None:
await bot.send_message(
message.channel,
embed=discord.Embed("There is a counter in {}".format(counter_channel.mention), color=discord.Color.red() ))
return
counter_channel = message.channel
await bot.send_message(message.channel, "1:30")
await asyncio.sleep(30)
await bot.send_message(message.channel, "1:00")
await asyncio.sleep(30)
await bot.send_message(message.channel, "0:30")
await asyncio.sleep(20)
await bot.send_message(message.channel, "0:10")
await asyncio.sleep(10)
await bot.send_message(message.channel, "time")
counter_channel = None
#bot.command(pass_context=True)
async def t90(ctx):
global task
task = bot.loop.create_task(ex(ctx.message))
#bot.command(pass_context=True)
async def cancel(ctx):
global task, counter_channel
await bot.send_message(message.channel, "timer reset")
task.cancel()
task = None
counter_channel = None
bot.run('###token###')

Here's one way you could make such a command
from datetime import timedelta
from asyncio import sleep
def time_repr(td: timedelta) -> str:
"Time formatter with optional dates/hours"
minutes, seconds = divmod(int(td.total_seconds()), 60)
hours, minutes = divmod(minutes, 60)
days , hours = divmod(hours, 24)
res = f"{minutes:>02}:{seconds:>02}"
if hours or days:
res = f"{hours:>02}:" + res
if days:
res = f"{td.days} days, " + res
return res
#bot.command(pass_context=True)
async def countdown(ctx, seconds: int):
td = timedelta(seconds=seconds)
while True:
await bot.say(time_repr(td))
if td.total_seconds() > 30:
td -= timedelta(seconds=30)
await sleep(30)
elif td.total_seconds > 10:
td -= timedelta(seconds=10)
await sleep(10)
elif td.total_seconds > 1:
td -= timedelta(seconds=1)
await sleep(1)
else:
break
Note that this won't always make the best timer: sleep(10) guarantees the event loop will wait at least 10 seconds, but it may wait longer. It'll usually be pretty close though.

Related

Python discord bot not respoding to on_message event

I'm trying to make a discord bot but the bot is not responding to any of my commands.
He is writing the command when going online but nothing more.
I have given him every permission and intent but it did not help.
import discord
import time
intents = discord.Intents.default()
intents.members = True
client = discord.Client(intents=intents)
# Dictionary to store the brake times for each user
brb_times = {}
#client.event
async def on_message(message):
# Only process messages from other users, not the bot itself
if message.author == client.user:
return
if message.content.startswith("!brb 5"):
brb_times[message.author.name] = time.time() + 5 * 60
await message.channel.send(f"{message.author.mention} has taken a 5-minute brake.")
elif message.content.startswith("!brb 10"):
brb_times[message.author.name] = time.time() + 10 * 60
await message.channel.send(f"{message.author.mention} has taken a 10-minute brake.")
elif message.content.startswith("!brb 15"):
brb_times[message.author.name] = time.time() + 15 * 60
await message.channel.send(f"{message.author.mention} has taken a 15-minute brake.")
elif message.content.startswith("!back"):
if message.author.name in brb_times:
brake_time = brb_times[message.author.name]
del brb_times[message.author.name]
elapsed_time = time.time() - brake_time
if elapsed_time > 0:
await message.channel.send(f"{message.author.mention} is back, but was late by {int(elapsed_time)} seconds.")
else:
await message.channel.send(f"{message.author.mention} is back.")
else:
await message.channel.send(f"{message.author.mention} was not on a brake.")
#client.event
async def on_ready():
print("Bot is ready!")
await client.get_channel(CENSORED).send("Bot is ready to track breaks!")
client.run("CENSORED")
You need message_content to be True in intents.

How to let the bot delete your command after it reply me in python

I am doing a giveaway command call =gstart <time> <prize and the giveaway will be held in the channel I use this command and After sending the embed giveaway message it will delete the gstart command. Here is the code:
#bot.command()
async def gstart(ctx,time,*,prize):
time = convert(time)
if time == -1:
await ctx.send("Error, can't identify time unit")
if time == -2:
await ctx.send("Error, can't identify time value")
embed = discord.Embed(title=prize, description = f"React with :tada: to enter\nHosted by: {ctx.author.mention}", color=discord.Color.red())
embed.set_footer(text = f"Ends at {time} from now!")
#delete-command
await ctx.send(":tada: **GIVEAWAY** :tada:")
my_msg = await ctx.send(embed = embed)
await my_msg.add_reaction("🎉")
await asyncio.sleep(time)
new_msg = await ctx.fetch_message(my_msg.id)
users = await new_msg.reactions[0].users().flatten()
users.pop(users.index(bot.user))
winner = random.choice(users)
await ctx.send(f"Congratulations {winner.mention}! You won the {prize}!")
msg_link = my_msg.jump_url
winner_embed = discord.Embed(title=f"Hey {winner}",description=f"You just won a [giveaway]({msg_link}) in {ctx.guild}",color=discord.Color.red())
winner_embed.add_field(name=prize,value=f"Hosted by: {ctx.author.mention}",inline=True)
await winner.send(embed=winner_embed)
By the way the convert() function looks like this:
def convert(time):
pos = ["s","m","h","d"]
time_dict = {"s" : 1, "m" : 60, "h" : 3600, "d" : 3600*24}
unit = time[-1]
if unit not in pos:
return -1
try:
val = int(time[:-1])
except:
return -2
return val * time_dict[unit]
Reply you with the discord.Context object and delete the message user sent.
Example:
import asyncio
#bot.command()
async def deleteCommandAfterSent(ctx):
await ctx.send("blablabla ") #REPLY MESSAGE
await asyncio.sleep(1)
await ctx.message.delete()

Adding a timer to giveaway command discord.py

I was trying to create a discord bot with giveaway command which updates time left every 1-2 mins in giveaway embed . I have been trying it since 3 days but wasn't able to find a solution. I managed to make it update seconds, but if I specify time more than 1m i.e. 60 seconds it automatically converts it to seconds and starts giveaway with time left in just seconds . I want it to keep time in given unit and update time in --days, --hours, --minutes, --seconds left.
Here are a few images what i mean:
What it currently do:
What i want it to do:
Its just Ends In Or Time remaining what i want to be changed!
My current code:
#commands.command()
#commands.guild_only()
async def gstart(self, ctx, duration, *, prize):
time = self.convert(duration)
if time == -1:
await ctx.send(f'Answer Time With A Proper Unit (s, m, h, d)')
return
elif time == -2:
await ctx.send(f'Time Must Be A Integer!')
return
giveawayembed = discord.Embed(
title="🎉 New Giveaway! 🎉",
description=f"**Prize:** {prize}\n"
f"**Hosted By:** {ctx.author.mention}\n"
f"**Ends In:** {time} Seconds",
colour=discord.Color.green()
)
msg = await ctx.send(embed=giveawayembed)
reactions = await msg.add_reaction("🎉")
while time:
await sleep(10)
time -= 10
giveawayembed.description= f"**Prize:** {prize}\n**Hosted By:** {ctx.author.mention}\n**Ends In:** {time} Seconds"
await msg.edit(embed=giveawayembed)
new_msg = await ctx.fetch_message(msg.id)
users = await new_msg.reactions[0].users().flatten()
users.pop(users.index(self.client.user))
winner = random.choice(users)
endembed = discord.Embed(
title="Giveaway ended!",
description=f"Prize: {prize}\nWinner: {winner.mention}")
await msg.edit(embed=endembed)
await ctx.send(f"🎉 Giveaway Winner: {winner.mention} | Prize: {prize}")
For Converting Time I Have:
class Giveaway(commands.Cog):
def __init__(self, client):
self.client = client
def convert(self, time):
pos = ["s", "m", "h", "d"]
time_dict = {"s" : 1, "m" : 60, "h" : 3600, "d" : 3600*24}
unit = time[-1]
if unit not in pos:
return -1
try:
val = int(time[:-1])
except:
return -2
return val * time_dict[unit]
Any help is highly appreciated! 😁
And I m sorry if I wasn't able to make you understand my problem. 😅
I understand your problem as it is actually a very simple fix, you can either import time, or use await asyncio.sleep(time)
Before using the code I am providing, make sure to import asyncio in your imports.
Your code:
#commands.command()
#commands.guild_only()
async def gstart(self, ctx, duration, *, prize):
time = self.convert(duration)
if time == -1:
await ctx.send(f'Answer Time With A Proper Unit (s, m, h, d)')
return
elif time == -2:
await ctx.send(f'Time Must Be A Integer!')
return
giveawayembed = discord.Embed(
title="🎉 New Giveaway! 🎉",
description=f"**Prize:** {prize}\n"
f"**Hosted By:** {ctx.author.mention}\n"
f"**Ends In:** {time} Seconds",
colour=discord.Color.green()
)
msg = await ctx.send(embed=giveawayembed)
reactions = await msg.add_reaction("🎉")
while time:
await sleep(10)
time -= 10
giveawayembed.description= f"**Prize:** {prize}\n**Hosted By:** {ctx.author.mention}\n**Ends In:** {time} Seconds"
await msg.edit(embed=giveawayembed)
new_msg = await ctx.fetch_message(msg.id)
users = await new_msg.reactions[0].users().flatten()
users.pop(users.index(self.client.user))
winner = random.choice(users)
endembed = discord.Embed(
title="Giveaway ended!",
description=f"Prize: {prize}\nWinner: {winner.mention}")
await msg.edit(embed=endembed)
await ctx.send(f"🎉 Giveaway Winner: {winner.mention} | Prize: {prize}")
In both fixes I will fix a slight problem that could easily be fixed, you have it so while time, it will go down by 10, I recommend doing it so it says while time > 0: so that once it hits 0 it wont keep counting down.
Easy fix using await asyncio.sleep(time):
#commands.command()
#commands.guild_only()
async def gstart(self, ctx, duration, *, prize):
time = self.convert(duration)
if time == -1:
await ctx.send(f'Answer Time With A Proper Unit (s, m, h, d)')
return
elif time == -2:
await ctx.send(f'Time Must Be A Integer!')
return
giveawayembed = discord.Embed(
title="🎉 New Giveaway! 🎉",
description=f"**Prize:** {prize}\n"
f"**Hosted By:** {ctx.author.mention}\n"
f"**Ends In:** {time} Seconds",
colour=discord.Color.green()
)
msg = await ctx.send(embed=giveawayembed)
reactions = await msg.add_reaction("🎉")
while time >= 0:
if time <= 60:
giveaway.remove_field(index=1)
giveaway.insert_field_at(index=1, name='Ends:', value=f'{time} second(s) from now')
await my_msg.edit(embed=giveaway)
time -= 10
await asyncio.sleep(10)
elif 60 <= time < 3600:
giveaway.remove_field(index=1)
giveaway.insert_field_at(index=1, name='Ends:', value=f'{time/60} minute(s) from now')
await my_msg.edit(embed=giveaway)
time -= 6
await asyncio.sleep(6)
elif 3600 <= time < 86400:
giveaway.remove_field(index=1)
giveaway.insert_field_at(index=1, name='Ends:', value=f'{time/3600} hour(s) from now')
await my_msg.edit(embed=giveaway)
time -= 360
await asyncio.sleep(360)
elif time >= 86400:
giveaway.remove_field(index=1)
giveaway.insert_field_at(index=1, name='Ends:', value=f'{time/86400} day(s) from now')
await my_msg.edit(embed=giveaway)
time -= 8640
await asyncio.sleep(8640)
if time <= 0:
giveaway.remove_field(index=1)
giveaway.insert_field_at(index=1, name='Ends:', value=f'Ended at {datetime.datetime.now().strftime("%B %d, %I:%M %p")}') # noqa
await my_msg.edit(embed=giveaway)
await asyncio.sleep(time)
new_msg = await ctx.fetch_message(msg.id)
users = await new_msg.reactions[0].users().flatten()
users.pop(users.index(self.client.user))
winner = random.choice(users)
endembed = discord.Embed(
title="Giveaway ended!",
description=f"Prize: {prize}\nWinner: {winner.mention}")
await msg.edit(embed=endembed)
await ctx.send(f"🎉 Giveaway Winner: {winner.mention} | Prize: {prize}")
If you are still confused or have any more questions you can contact me on discord at killrebeest#7187 or comment on this answer. Have a good day!

discord.py: function wait until a specific date and time

i have a question again :)
i plan a bot that will give a message output to a channel after a user has tell the bot when.
i will show you the code and then i will tell you my problem:
import discord
import datetime
import time
import asyncio
import random
from discord.ext import commands
from discord import Embed
TOKEN = 'mytoken'
intents = discord.Intents().all()
client = discord.Client(intents=intents)
#client.event
async def on_ready():
print(client.user.name)
print(client.user.id)
#client.event
async def on_message(message):
def check(m):
return m.channel == message.channel and m.author != client.user
if message.author == client.user:
return
if message.content.startswith("!start"):
await message.channel.send('Please type in the Enddate (TT.MM.JJ):')
enddateinput = await client.wait_for('message', check=check, timeout=30)
enddate = enddateinput.content
await message.channel.send('Please type in the Endtime (HH:MM):')
endtimeinput = await client.wait_for('message', check=check, timeout=30)
endtime = endtimeinput.content
# *********************************************
# I dont know how to check the time with the subfunction time_check :(
# The optimal result is the function wait at this point until datetime.now(now) = enddate & endtime.
# and then i want the function to continue working.
# *********************************************
await message.channel.send('Enddate & Endtime reached.')
await message.channel.send('Go on with the rest of the code :).')
async def time_check():
await client.wait_until_ready()
while not client.is_closed():
nowdate = datetime.strftime(datetime.now(), '%d.%m.%y')
nowtime = datetime.strftime(datetime.now(), '%H:%M')
if (nowdate == enddate) and (nowtime == endtime):
await asyncio.sleep(1)
# *********************************************
# BACK TO FUNKTION
# *********************************************
else:
await asyncio.sleep(60)
client.run(TOKEN)
The problem is, i need time_check() only to check every minute the current time with endtime/enddate.
If the endtime and enddate reached, time_check() can stop working and go back to on_message() to continue working the funktion.
I absolutely don't know how to integrate this check_time () function.
I hope someone of you can help me.
Thank you guys.
These are the functions you need to change, instead of checking every minute to see if the end time is reached I simply calculated the total seconds it will take to wait from the current time to the end time. I also added a check to see if the end time is in the past which is very important. In general I believe this is the best way to implement youre desired outcome.
#client.event
async def on_message(message):
def check(m):
return m.channel == message.channel and m.author != client.user
if message.author == client.user:
return
if message.content.startswith("!start"):
# Get end date.
await message.channel.send("Please type in the Enddate (DD.MM.YYYY):")
enddateinput = await client.wait_for("message", check=check, timeout=30)
enddate = enddateinput.content
# Get end time.
await message.channel.send("Please type in the Endtime (HH:MM):")
endtimeinput = await client.wait_for("message", check=check, timeout=30)
endtime = endtimeinput.content
# Get the two datetime objects, current and endtime.
endtime_obj = datetime.datetime.strptime(
".".join([enddate, endtime]), "%d.%m.%Y.%H:%M"
)
currtime_obj = datetime.datetime.now()
if not (endtime_obj > currtime_obj): # Check if end time is in the past.
await message.channel.send("End time can not be in the past!")
# Send the seconds to wait into the time check function
time_to_wait = endtime_obj - currtime_obj # The time delta (difference).
await time_check(message, time_to_wait.total_seconds())
async def time_check(message_obj, secs_to_wait: float):
await client.wait_until_ready()
await asyncio.sleep(secs_to_wait)
await message_obj.channel.send("Enddate & Endtime reached.")
await message_obj.channel.send("Go on with the rest of the code :).")

How to restart a loop in discord.py?

I am making a discord bot using discord.py. I want to make a command to purge all messages inside a channel every 100 seconds. Here is my code:
autodeletetime = -100
autodeletelasttime = 1
#client.command()
#commands.has_permissions(manage_messages=True)
async def autodelete(ctx):
global autodeleterunning
if autodeleterunning == False:
autodeleterunning = True
asgas = True
while asgas:
message = await ctx.send(f'All messages gonna be deleted in 100 seconds')
await message.pin()
for c in range(autodeletetime,autodeletelasttime):
okfe = abs(c)
await message.edit(content=f"All messages gonna be deleted in {okfe} seconds" )
await asyncio.sleep(1)
if c == 0:
await ctx.channel.purge(limit=9999999999999999999999999999999999999999999999999999999999999999999)
await time.sleep(1)
autodeleterunning = False
else:
await ctx.send(f'The autodelete command is already running in this server')
I want the loop to restart every 100 seconds after the purge is complete.
You should use tasks instead of commands for these kind of commands.
import discord
from discord.ext import commands, tasks
import asyncio
#tasks.loop(seconds=100)
async def autopurge(channel):
message = await channel.send(f'All messages gonna be deleted in 100 seconds')
await message.pin()
try:
await channel.purge(limit=1000)
except:
await channel.send("I could not purge messages!")
#client.group(invoke_without_command=True)
#commands.has_permissions(manage_messages=True)
async def autopurge(ctx):
await ctx.send("Please use `autopurge start` to start the loop.")
# Start the loop
#autopurge.command()
async def start(ctx):
task = autopurge.get_task()
if task and not task.done():
await ctx.send("Already running")
return
autopurge.start(ctx.channel)
# Stop the loop
#autopurge.command()
async def stop(ctx):
task = autopurge.get_task()
if task and not task.done():
autopurge.stop()
return
await ctx.send("Loop was not running")

Categories

Resources