tasks.loop timer is repeatedly executing in discord.py - discord

So I'm making a bot that'll post the Wednesday Frog meme every Wednesday. To do this, I'm using discord.py's tasks.loop function which in theory should run once every 24 hours in the finished version. Thing is though no matter how many hours, minutes or seconds I set it to, It spams my chat with either the meme or "It's not Wednesday," almost every second.
The code:
import discord
import datetime
from discord.ext import tasks, commands
client = commands.Bot(command_prefix = ')')
#client.event
async def on_ready():
wednesdayCheck.start()
print("Lets see if it's wednesday.")
#tasks.loop(hours=2)
async def wednesdayCheck():
if datetime.datetime.today().weekday() == 2:
for guild in client.guilds:
for channel in guild.text_channels:
await channel.send(file=discord.File('Meme.jpg'))
elif datetime.datetime.today().weekday() != 2:
for guild in client.guilds:
for channel in guild.text_channels:
await channel.send("It's not Wednesday")
Any help with making the function follow the timer as intended will be much apreciated.

Try putting the .start() outside of the on_ready() function, just above the bot.run().
There is no guarantee that the on_ready() function only runs once.
https://discordpy.readthedocs.io/en/latest/api.html?highlight=on_ready#discord.on_ready

Here is a possible solution to your problem:
Create a global boolean variable that ensures the block is only run once in on_ready
ON_READY_FIRST_RUN = True
#client.event
async def on_ready():
global ON_READY_FIRST_RUN
if ON_READY_FIRST_RUN:
wednesdayCheck.start()
print("Lets see if it's Wednesday.")
ON_READY_FIRST_RUN = False
I have been trying to set up a bot to send a message every weekday morning but for some reason can't get the #tasks.loop to execute. Hope this helps you though!
Edit: I was trying to use #tasks.before_loop but I just threw that code in my #tasks.loop and now this works perfectly!

Related

SyntaxError: 'await' outside async function discord.py

import discord.ext
import discord
from discord.ext import commands
from discord.ext import tasks
from keep_alive import keep_alive
import os
import schedule
client = discord.Client()
client = commands.Bot(command_prefix="")
channel_id = 927272717227528262
channel = client.get_channel(channel_id)
#tasks.loop(seconds=1.0)
#client.event
async def on_message(message):
if message.author == client.user:
return
def job2():
await message.channel.send("message")
schedule.every(10).seconds.do(job2)
while True:
schedule.run_pending()
time.sleep(0)
keep_alive()
client.run(os.getenv('TOKEN'))
Every time I run it I get :
SyntaxError: 'await' outside async function
I am also having the issue where message is undefined the definition of job2 even though it was just defined in the line above.
You should probably be using discord.ext.tasks. Async functions aren't really supported by schedule.
In fact, your bot can never run because your schedule.run_pending() in the infinite loop will never end.
In order to keep your message there you'll need to put it in a global of some type.
the_message = None
#tasks.loop(seconds=1.0)
#client.event
async def on_message(message):
global the_message
if message.author == client.user:
return
the_message = message
async def job2():
if the_message is None:
return
await the_message.channel.send("message")
#tasks.loop(seconds=10.0) # you can do other things like `minutes=5` or `hours=1.75`
async def my_task():
await client.wait_until_ready()
await job2()
my_task.start()
# then the bot runs fine after
keep_alive()
client.run(os.getenv('TOKEN'))
Be careful that this might run before your client is not ready and initializing. You may also want to check wait_until_ready if you're going to do anything with the API, like in your case. You can also find other examples here.
await is used in asynchronous functions to wait for the function to do its thing. However, you have used it in a synchronous function (def job2)
message is not a variable. message is the parameter of the on_message function. A parameter can only be used in the function it is provided to.
Hope this answered your question.

Discord.py doesn't update on member remove

I am making a discord bot that changes a voice channels' name when a user joins or leaves to the amount of members on the server. My issue is that when a user leaves, it doesn't update. Any help is appreciated.
#bot.event
async def on_raw_member_remove(member):
channel = discord.utils.get(member.guild.channels, id=973603264639668248)
await channel.edit(name=f'Member Count: {member.guild.member_count}')
The right event for this would be on_member_remove.
You can also get the channel in a much easier way and edit it.
See a possible new code:
#bot.event
async def on_member_remove(member: discord.Member):
channel = bot.get_channel(Channel_ID_here)
await channel.edit(name=f"Member Count: {len(member.guild.members)}")
The reason it doesn't work is because you are using on_raw_member_remove
You should be using on_member_leave as the member is not getting removed.

Discord.py Reaction Events

My bot sends an embed every time a new member joins. Then the bot adds the little 👋🏽 reaction to it. I want members to be able to welcome the new member by reacting. If they react, then they will be rewarded in some way. So onto my question, how would I make my bot watch for reactions for 60 seconds after the embed is sent, and what event would I use? I've found a few things in the documentation but none of which seem to make sense to me. A code example and explanation of how it works would be amazing. Thanks in advance!
In order to do this, you'd need to make use of the on_reaction_add event, assuming you already have all your imports:
#bot.event
async def on_member_join(member): # when a member joins
channel = discord.utils.get(member.guild.channels, name="welcome") #getting the welcome channel
embed = discord.Embed(title=f"Welcome {member.mention}!", color=member.color) #creating our embed
msgg1 = await channel.send(embed=embed) # sending our embed
await msgg1.add_reaction("👋🏽") # adding a reaction
#bot.event
async def on_reaction_add(reaction, user):
message = reaction.message # our embed
channel = discord.utils.get(message.guild.channels, name="welcome") #our channel
if message.channel.id == channel.id: # checking if it's the same channel
if message.author == bot.user: #checking if it's sent by the bot
if reaction.emoji.name == "👋🏽": #checking the emoji
# enter code here, user is person that reacted
I think this would work. I might have done the indentations wrong since I'm on another device. Let me know if there are any errors.
You can use the on_reaction_add() event for that. The 60 second feature might be complicated.
If you can get the user object from the joined user the on_reaction_add() event, you could check if the user joined less than 60 seconds ago.
You can check the time a user joined with user.joined_at, and then subtract this from the current time.
Here is how to check how many seconds a user is already on the server.
from datetime import datetime
seconds_on_server = (datetime.now() - member.joined_at).total_seconds()
A rather hacky solution is to retrieve the original user who joined through the message on which the reaction is added. Members have the joined_at attribute, which is a datetime object, with it you can just snap current datetime and subtract the former from it. The resulting is a timedelta object which you can use to calculate the time difference.

Toggling a server limited command

#f spam
async def on_message(message):
if message.author.bot:
return
elif message.content.lower() == 'f':
await message.channel.send('f')
await bot.process_commands(message)
this is my current code for an f spam or sends an f each time a user does.
I was wanting to make this command toggleable and also server/guild limited if possible.
for example someone says !fspam and it gets toggled and switched off and when done the same again it gets turned on. OR it could be !fspam on/ !fspam off
You can use Command.enable, where you can use command.update to. This will raise the DisabledCommand error.
Also please dont just copy and paste the code, try understanding what I did.This will disable it for all the guilds to. If you want it per server then you will need to use a database.
For example:
#client.command()
async def enable(ctx,*,command):
command = client.get_command(command)
command.update(enabled=True)
await ctx.send(f"{command} enabled!")
#client.command()
async def disable(ctx,*,command):
command = client.get_command(command)
command.update(enabled=False)
await ctx.send(f"{command} disabled!")

discord.py restart the command

I am making a discord bot and I tried to make a status command that will make my bot's status start changing, so I thought its working pretty well until I realized that I need it to restart and that I have no idea how to do that, so here is my code without the restart part:
#client.command()
async def status(ctx):
await client.change_presence(activity=discord.Streaming(name='firststatus', url='https://www.twitch.tv/my-channel-name'))
await asyncio.sleep(5)
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name='Secondstatus'))
so I did that but I don't know what to put at the end so it'll restart I guess it will be something like client.command.restart.
You can use a simple while loop or the built-in discord.py extension tasks
from discord.ext import tasks
#tasks.loop(seconds=5)
async def change_status():
await client.change_presence(activity=discord.Streaming(name='firststatus', url='https://www.twitch.tv/my-channel-name'))
await asyncio.sleep(5)
await client.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name='Secondstatus'))
#client.command()
async def status(ctx):
change_status.start()
The change_status function will loop every 5 seconds and change the presence. You can stop it with change_status.stop()
Reference:
tasks.loop
Loop.start
Loop.stop

Resources