fotddict = {}
#client.event
async def on_ready():
global fotddict
with open("factoftheday.json", "r") as f:
fotddict = json.load(f)
#client.command()
#commands.has_permissions(administrator=True)
async def fotd(ctx, channel : discord.TextChannel=None):
if channel is None:
embe=discord.Embed(title="<:redcross:781952086454960138>Error", description="**Please pass in all required arguments!**\nNeed help?** https://dsc.gg/otaysupport**", color=0x7289da)
await ctx.send(embed=embe)
else:
#are you sure embed
msg = await ctx.send(embed=embed)
def checkifnotbotfact(reaction, user):
return user != client.user
await msg.add_reaction('💡')
reaction, user = await client.wait_for("reaction_add", timeout=60.0, check=checkifnotbotfact)
if str(reaction.emoji) == "💡":
#confirm embed
global fotddict
fotddict[str(ctx.guild.id)] = channel.id
with open("factoftheday.json", "w") as f:
json.dump(fotddict, f)
#tasks.loop(seconds=10)
async def factsend(member):
x = randfacts.getFact()
channel_id = fotddict[str(member.guild.id)]
embed = discord.Embed(title="💡Fact of the day!", description=x, color=0x7289da)
await client.get_channel(channel_id).send(embed=embed)
#factsend.before_loop
async def before():
factsend.start()
await client.wait_until_ready()
Problem: This is my fact of the day command, it adds the channel id + guild id in a json file (so that isnt the problem). I think the problem is the loop, since that is the part that im not sure of if thats correct.
Goal: Bot sends a message with a fact every 24 hours (Task is set to 10 seconds for test purposes)
Firstly your #factsend.before_loop function is called just before the loop execution, so you have to start the loop in other place, not in the function. So you have to deplace factsend.start() outside of this function.
The corriged code will be:
#client.event
async def on_ready():
global fotddict
with open("factoftheday.json", "r") as f:
fotddict = json.load(f)
#client.command()
#commands.has_permissions(administrator=True)
async def fotd(ctx, channel : discord.TextChannel=None):
if channel is None:
embe=discord.Embed(title="<:redcross:781952086454960138>Error", description="**Please pass in all required arguments!**\nNeed help?** https://dsc.gg/otaysupport**", color=0x7289da)
await ctx.send(embed=embe)
else:
#are you sure embed
msg = await ctx.send(embed=embed)
def checkifnotbotfact(reaction, user):
return user != client.user
await msg.add_reaction('💡')
reaction, user = await client.wait_for("reaction_add", timeout=60.0, check=checkifnotbotfact)
if str(reaction.emoji) == "💡":
#confirm embed
global fotddict
fotddict[str(ctx.guild.id)] = channel.id
with open("factoftheday.json", "w") as f:
json.dump(fotddict, f)
#tasks.loop(seconds=10)
async def factsend(member):
x = randfacts.getFact()
channel_id = fotddict[str(member.guild.id)]
embed = discord.Embed(title="💡Fact of the day!", description=x, color=0x7289da)
await client.get_channel(channel_id).send(embed=embed)
#factsend.before_loop
async def before():
await client.wait_until_ready()
factsend.start() #deplaced outside of the function
Have a nice day!
If you are going to leave the bot running locally (or host it somewhere), then you should use Advance Python Scheduler
from apscheduler.schedulers.blocking import BlockingScheduler
#Code goes Here
scheduler = BlockingScheduler()
scheduler.add_job(function_name_you_want_to_run(), 'interval', hours=24)
scheduler.start()
Related
I am having troubles gettimg my commands to work, it keeps telling me that they do not exist when they clearly do. I've been stumped on this for hours and can't figure out why its not working. I've tried doing commands.command() instead of #commands.command(), but no luck, i tried explicitly defining the name of the command #commands.command(name="play") but still no luck
import youtube_dl
import pafy
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.members = True
bot = commands.Bot(command_prefix="!", intents=intents)
#bot.event
async def on_ready():
print(f"{bot.user.name} is ready.")
bot.run("....")
class Player(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.song_queue = {}
self.setup()
def setup(self):
for guild in self.bot.guilds:
self.song_queue[guild.id] = []
async def check_queue(self, ctx):
if len(self.song_queue[ctx.guild.id]) > 0:
await self.play_song(ctx, self.song_queue[ctx.guild.id][0])
self.song_queue[ctx.guild.id].pop(0)
async def search_song(self, amount, song, get_url=False):
info = await self.bot.loop.run_in_executor(None, lambda: youtube_dl.YoutubeDL({"format" : "bestaudio", "quiet" : True}).extract_info(f"ytsearch{amount}:{song}", download=False, ie_key="YoutubeSearch"))
if len(info["entries"]) == 0: return None
return [entry["webpage_url"] for entry in info["entries"]] if get_url else info
async def play_song(self, ctx, song):
url = pafy.new(song).getbestaudio().url
ctx.voice_client.play(discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(url)), after=lambda error: self.bot.loop.create_task(self.check_queue(ctx)))
ctx.voice_client.source.volume = 0.5
#commands.command()
async def join(self, ctx):
if ctx.author.voice is None:
return await ctx.send("You are not connected to a voice channel, please connect to the channel you want the bot to join.")
if ctx.voice_client is not None:
await ctx.voice_client.disconnect()
await ctx.author.voice.channel.connect()
#commands.command()
async def leave(self, ctx):
if ctx.voice_client is not None:
return await ctx.voice_client.disconnect()
await ctx.send("I am not connected to a voice channel.")
#commands.command(name="play")
async def play(self, ctx, *, song=None):
if song is None:
return await ctx.send("You must include a song to play.")
if ctx.voice_client is None:
return await ctx.send("I must be in a voice channel to play a song.")
# handle song where song isn't url
if not ("youtube.com/watch?" in song or "https://youtu.be/" in song):
await ctx.send("Searching for song, this may take a few seconds.")
result = await self.search_song(1, song, get_url=True)
if result is None:
return await ctx.send("Sorry, I could not find the given song, try using my search command.")
song = result[0]
if ctx.voice_client.source is not None:
queue_len = len(self.song_queue[ctx.guild.id])
if queue_len < 10:
self.song_queue[ctx.guild.id].append(song)
return await ctx.send(f"I am currently playing a song, this song has been added to the queue at position: {queue_len+1}.")
else:
return await ctx.send("Sorry, I can only queue up to 10 songs, please wait for the current song to finish.")
await self.play_song(ctx, song)
await ctx.send(f"Now playing: {song}")
#commands.command()
async def search(self, ctx, *, song=None):
if song is None: return await ctx.send("You forgot to include a song to search for.")
await ctx.send("Searching for song, this may take a few seconds.")
info = await self.search_song(5, song)
embed = discord.Embed(title=f"Results for '{song}':", description="*You can use these URL's to play an exact song if the one you want isn't the first result.*\n", colour=discord.Colour.red())
amount = 0
for entry in info["entries"]:
embed.description += f"[{entry['title']}]({entry['webpage_url']})\n"
amount += 1
embed.set_footer(text=f"Displaying the first {amount} results.")
await ctx.send(embed=embed)
#commands.command()
async def queue(self, ctx): # display the current guilds queue
if len(self.song_queue[ctx.guild.id]) == 0:
return await ctx.send("There are currently no songs in the queue.")
embed = discord.Embed(title="Song Queue", description="", colour=discord.Colour.dark_gold())
i = 1
for url in self.song_queue[ctx.guild.id]:
embed.description += f"{i}) {url}\n"
i += 1
embed.set_footer(text="Thanks for using me!")
await ctx.send(embed=embed)
#commands.command()
async def skip(self, ctx):
if ctx.voice_client is None:
return await ctx.send("I am not playing any song.")
if ctx.author.voice is None:
return await ctx.send("You are not connected to any voice channel.")
if ctx.author.voice.channel.id != ctx.voice_client.channel.id:
return await ctx.send("I am not currently playing any songs for you.")
poll = discord.Embed(title=f"Vote to Skip Song by - {ctx.author.name}#{ctx.author.discriminator}", description="**80% of the voice channel must vote to skip for it to pass.**", colour=discord.Colour.blue())
poll.add_field(name="Skip", value=":white_check_mark:")
poll.add_field(name="Stay", value=":no_entry_sign:")
poll.set_footer(text="Voting ends in 15 seconds.")
poll_msg = await ctx.send(embed=poll) # only returns temporary message, we need to get the cached message to get the reactions
poll_id = poll_msg.id
await poll_msg.add_reaction(u"\u2705") # yes
await poll_msg.add_reaction(u"\U0001F6AB") # no
await asyncio.sleep(15) # 15 seconds to vote
poll_msg = await ctx.channel.fetch_message(poll_id)
votes = {u"\u2705": 0, u"\U0001F6AB": 0}
reacted = []
for reaction in poll_msg.reactions:
if reaction.emoji in [u"\u2705", u"\U0001F6AB"]:
async for user in reaction.users():
if user.voice.channel.id == ctx.voice_client.channel.id and user.id not in reacted and not user.bot:
votes[reaction.emoji] += 1
reacted.append(user.id)
skip = False
if votes[u"\u2705"] > 0:
if votes[u"\U0001F6AB"] == 0 or votes[u"\u2705"] / (votes[u"\u2705"] + votes[u"\U0001F6AB"]) > 0.79: # 80% or higher
skip = True
embed = discord.Embed(title="Skip Successful", description="***Voting to skip the current song was succesful, skipping now.***", colour=discord.Colour.green())
if not skip:
embed = discord.Embed(title="Skip Failed", description="*Voting to skip the current song has failed.*\n\n**Voting failed, the vote requires at least 80% of the members to skip.**", colour=discord.Colour.red())
embed.set_footer(text="Voting has ended.")
await poll_msg.clear_reactions()
await poll_msg.edit(embed=embed)
if skip:
ctx.voice_client.stop()
#commands.command()
async def pause(self, ctx):
if ctx.voice_client.is_paused():
return await ctx.send("I am already paused.")
ctx.voice_client.pause()
await ctx.send("The current song has been paused.")
#commands.command()
async def resume(self, ctx):
if ctx.voice_client is None:
return await ctx.send("I am not connected to a voice channel.")
if not ctx.voice_client.is_paused():
return await ctx.send("I am already playing a song.")
ctx.voice_client.resume()
await ctx.send("The current song has been resumed.")
async def setup():
await bot.wait_until_ready()
bot.add_cog(Player(bot))
bot.loop.create_task(setup())
You can try replacing :
async def setup():
await bot.wait_until_ready()
bot.add_cog(Player(bot))
bot.loop.create_task(setup())
With just :
bot.add_cog(Player(bot))
Moreover, put you run statement after all setup operations, so you have :
# Cog declaration
bot.add_cog(Player(bot))
bot.run('....')
run() should be executed as last function because it starts bot and it runs until you stop bot.
And all code which you have after run() is executed after you stop bot.
bot.loop.create_task(setup())
bot.run(TOKEN)
#commands.command()
Write this down where you wrote it down.
#bot.command()
Because:
bot = commands.Bot(command_prefix="!", intents=intents)
Since you have assigned a "bot" variable here, the name of the variable is added first, and then the "command" is added.
Im programing a Discord bot and and have some functions depending on the method. I have one bot.event that search for all messages and apply its function, and one bot command that only search for the command !balance and apply its function. The problem is that the bot.command is not working as the method on_message is acting first. How can I write that on the method on message the program exclude the commands??
#bot.event
async def on_message(ctx):
await open_account(ctx.author)
users = await get_bank_data()
user = ctx.author
earnings_1 = 1
users[str(user.id)]['wallet'] += earnings_1
with open('bank.json', 'w') as f:
json.dump(users, f)
#bot.command(pass_context=True)
async def balance(ctx):
await open_account(ctx.author)
user = ctx.author
users = await get_bank_data()
wallet_amt = users[str(user.id)]['wallet']
em = discord.Embed(
title = f"{ctx.author.name}´s balance:",
color = 0xf3e577
)
em.add_field(name = 'Wallet Balance', value = wallet_amt)
await ctx.send(embed = em)
async def open_account(user):
users = await get_bank_data()
if str(user.id) in users:
return False
else:
users[str(user.id)]= {}
users[str(user.id)]['wallet'] = 0
users[str(user.id)]['bank'] = 0
with open('bank.json', 'w') as f:
json.dump(users, f)
return True
async def get_bank_data():
with open('bank.json','r') as f:
users = json.load(f)
return users
should I use other method?
When you have a #bot.event decorated function and you want the bot to process commands too, you have to add this line at the end of your on_message function, making sure that this line is always reached by the program.
await bot.process_commands(message)
This will process the commands like if there was no event listener for message event.
I'm trying to make a random fact send at certain times. It worked before I implemented a dict to hold the random facts and it should work now, the only problem I'm running into is I can't access the guild id inside the on_ready() event. Here is what I have:
async def func(self):
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
------> guild_id = NEED
channel = bot.get_channel(guild_settings[guild_id]["random_facts_channel_id"])
random_messages = guild_settings[guild_id]["random_facts"].value()
if random_messages is None:
await channel.send(f"You need to add a fact first.")
else:
random_messages = random.choice(random_messages)
await channel.send(random_messages)
#commands.Cog.listener()
async def on_ready(self):
print("Bot is ready.")
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
# Initializing scheduler
scheduler = AsyncIOScheduler()
hour = 12
minute = 0
for guild in bot.guilds:
guild_id = str(guild.id)
try:
hour = guild_settings[str(guild_id)]["random_facts_send_time"]["hour"]
minute = guild_settings[str(guild_id)]["random_facts_send_time"]["minute"]
except KeyError:
print(f"{guild_id} has a KeyError with the random fact feature.")
continue
# Sends "Your Message" at 12PM and 18PM (Local Time)
scheduler.add_job(self.func, CronTrigger(hour=hour, minute=minute, second="0"))
# Starting the scheduler
scheduler.start()
If I were able to somehow get the guild id then I'm sure it would work correctly, I just can't figure out how.
EDIT: This is what I have working so far
async def func(self):
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
for guild_id in guild_settings:
channel = bot.get_channel(guild_settings[guild_id]["random_facts_channel_id"])
if guild_settings[guild_id]["random_facts"] is not None:
random_facts = []
for values in guild_settings[guild_id]["random_facts"].values():
random_fact = values.split("\n")
random_facts += random_fact
try:
random_choice = random.choice(random_facts)
except IndexError:
continue
await channel.send(random_choice)
#commands.Cog.listener()
async def on_ready(self):
print("Bot is ready.")
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
# Initializing scheduler
scheduler = AsyncIOScheduler()
hour = 12
minute = 0
for guild in bot.guilds:
guild_id = str(guild.id)
try:
hour = guild_settings[str(guild_id)]["random_facts_send_time"]["hour"]
minute = guild_settings[str(guild_id)]["random_facts_send_time"]["minute"]
except KeyError:
print(f"{guild_id} has a KeyError with the random fact feature.")
continue
# Sends "Your Message" at 12PM and 18PM (Local Time)
scheduler.add_job(self.func, CronTrigger(hour=hour, minute=minute, second="0"))
# Starting the scheduler
scheduler.start()
If your bot is on only one server, you can simply get the guild object by using discord.utils.get.
async def func(self):
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
guild = discord.utils.get(bot.guilds, name="guild's name")
channel = bot.get_channel(guild_settings[guild.id]["random_facts_channel_id"])
random_messages = guild_settings[guild.id]["random_facts"].value()
if random_messages is None:
await channel.send(f"You need to add a fact first.")
else:
random_messages = random.choice(random_messages)
await channel.send(random_messages)
If it's on multiple servers, you have to loop through the guilds and send the messages one by one.
async def func(self):
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
for guild in bot.guilds:
channel = bot.get_channel(guild_settings[guild.id]["random_facts_channel_id"])
random_messages = guild_settings[guild.id]["random_facts"].value()
if random_messages is None:
await channel.send(f"You need to add a fact first.")
else:
random_messages = random.choice(random_messages)
await channel.send(random_messages)
As #ŁukaszKwieciński said, you can just do this:
async def func():
for guild in client.guilds:
print(guild.id) #Do something with each ID
Or you can waste time and memory doing this (which was my initial approach 😔):
guildIDs = set()
#client.event
async def on_ready():
for guild in client.guilds:
guildIDs.add(guild.id)
#client.event
async def on_guild_join(guild):
guildIDs.add(guild.id)
#client.event
async def on_guild_remove(guild):
guildIDs.remove(guild.id)
guildIDs is an integer set that will contain all the IDs of all the guilds that the bot is present in. The reason I used a set is to prevent repetition of IDs.
You can then use each ID in a function like this:
async def func():
for guildID in guildIDs:
print(guildID) #Do something with each ID
I recommend using tasks extension of discord.py
If you'd like to get the code, here it is.
#tasks.loop(hours=10)
async def func():
await bot.wait_until_ready()
with open('guild_settings.json', 'r') as file:
guild_settings = json.loads(file.read())
for guild_id in guild_settings:
channel = bot.get_channel(guild_settings[guild_id]["random_facts_channel_id"])
if guild_settings[guild_id]["random_facts"] is not None:
random_facts = []
for values in guild_settings[guild_id]["random_facts"].values():
random_fact = values.split("\n")
random_facts += random_fact
try:
random_choice = random.choice(random_facts)
except IndexError:
continue
await channel.send(random_choice)
#tasks.loop(minutes=15)
async def update_interval():
hour = 12
minute = 0
for guild in bot.guilds:
guild_id = str(guild.id)
try:
hour = guild_settings[str(guild_id)]["random_facts_send_time"]["hour"]
minute = guild_settings[str(guild_id)]["random_facts_send_time"]["minute"]
except KeyError:
print(f"{guild_id} has a KeyError with the random fact feature.")
continue
func.cancel()
func.change_interval(hours=hour, minutes=minute)
I hope this is helpful.
I'm currently learning python (slowly) and have started writing a discord bot. However, when i call a #client.command function nothing happens in discord. For example .ping should reply with "pong!" but nothing happens and I do not get an error.
My code is below
import discord
from discord.ext import commands
import asyncio
import time
import discord.client
client = commands.Bot(command_prefix=".")
messages = joined = 0
bad_words = ["example"]
channel = ["general"]
#client.command()
async def kick(ctx, userName: discord.Member):
await client.kick(userName)
#client.event(message)
async def on_ready():
id = client.get_guild(705455096749752323)
print("hello")
async def update_stats():
await client.wait_until_ready()
global messages, joined
while not client.is_closed():
try:
with open("stats.txt", "a") as f:
f.write(f"""Messages: {messages}, Members Joined {joined}, time: {time.time()}\n""")
messages = 0
joined = 0
await asyncio.sleep(5)
except Exception as e:
print(e)
#client.event
async def on_member_update(before, after):
n = after.nick
if n:
if n.lower().count("Crysis Wolfie") > 0:
last = before.nick
if last:
await after.edit(nick=last)
else:
await after.edit(nick="NO STOP THAT!")
#client.event
async def on_member_update(before, after):
n = after.game
if n:
if n.lower.count(f"""{before.n}""") > 0:
discord.message.channel.send()
#client.event
async def on_member_join(member):
global joined
joined += 1
for channel in member.server.channels:
if str(channel) == "general":
await discord.message.channel_send(f"""Welcome to the server {member.mention}""")
#client.event
async def on_message(message, yes=None):
# chat commands
id = client.get_guild(705455096749752323)
channels = ["general", "apply"]
global messages
messages = + 1
if str(message.channel) in channels:
if message.content.find("!hello") != -1:
await message.channel.send(f"""What's cooking {message.author.mention} looking good today :wink:""")
elif message.content == "!users":
await message.channel.send(f"""Number of members {id.member_count}""")
if message.content.find("NHS") != -1:
await message.channel.send("https://tenor.com/view/goodjob-clap-nicework-great-gif-7248435")
if message.content.find("wolfie") != -1:
await message.channel.purge(limit=1)
await message.channel.send("https://tenor.com/view/facepalm-really-stressed-mad-angry-gif-16109475")
if message.content.find("crysis") != -1:
await message.channel.purge(limit=1)
await message.channel.send("https://tenor.com/view/facepalm-really-stressed-mad-angry-gif-16109475")
if message.content.find("!apply") != -1:
await message.channel.send(f"""TESTING""")
for word in bad_words:
if message.content.count(word) > 0:
await message.channel.purge(limit=1)
await message.channel.send(f"""User {message.author.mention} watch your language!""")
try:
with open("badwordlog.txt", "a") as n:
n.write(f"""member: {message.author}, said {message.content}, at {time.time()}\n""")
print(f"""member: {message.author}, said {message.content}""");
except Exception as e:
print(e)
await client.process_commands(message)
#client.event
async def on_member_update(before, after): # needs fixing
n = before.status
if n != before:
await discord.message.channel.send(f"""have fun on {after} """)
#client.command()
async def create_invite(self, ctx): # needs fixing
self = bot
"""Create invite"""
link = await ctx.channel.create_invite(max_age=300)
await ctx.send("Here is an instant invite to your server: " + link)
#client.command()
async def clear(ctx, amount=5):
await ctx.channel.purge(limit=amount)
#client.command()
async def ping(ctx):
await ctx.send('pong!')
clint.run('my token is here but I have removed')
#client.event functions run fine and work great! But client.command does nothing :(.
thanks
I have a background loop that will spit out an emoji every X amount of minutes with a reaction attached to it. I want for when someone presses on the reaction of the message, it will delete the message and then send another message saying "messageauthor has grabbed the loot" and then add the amount to the cash json file.
Right now, my code is making the background loop work, but I am not sure how to grab the message.author.id in regards to the background loop so I can reference it in on_reaction_add. The current code is making the bot react once when it spits out the background loop and then again in on_reaction_add. I'm trying to make it wait for a user to react to the background loop message with the same emoji and not the bot.
client = discord.Client()
emoji_msg_grab = {}
try:
with open("cash.json") as fp:
cash = json.load(fp)
except Exception:
cash = {}
def save_cash():
with open("cash.json", "w+") as fp:
json.dump(cash, fp, sort_keys=True, indent=4)
def add_dollars(user: discord.User, dollars: int):
id = user.id
if id not in cash:
cash[id] = {}
cash[id]["dollars"] = cash[id].get("dollars", 0) + dollars
print("{} now has {} dollars".format(user.name, cash[id]["dollars"]))
save_cash()
async def background_loop():
await client.wait_until_ready()
while not client.is_closed:
channel = client.get_channel("479919577279758340")
emojigrab = '💰'
emojimsgid = await client.send_message(channel, emojigrab)
await client.add_reaction(emojimsgid, "💵")
user_id = emojimsgid.author.id
emoji_msg_grab[user_id] = {"emoji_msg_id": emojimsgid.id, "emoji_user_id": user_id}
await asyncio.sleep(600)
#client.event
async def on_reaction_add(reaction, user):
msgid = reaction.message.id
chat = reaction.message.channel
if reaction.emoji == "💵" and msgid == emoji_msg_grab[user.id]["emoji_msg_id"] and user.id == emoji_msg_grab[user.id]["emoji_user_id"]:
emoji_msg_grab[user.id]["emoji_msg_id"] = None
await client.send_message(chat, "{} has grabbed the loot!".format(user.mention))
await client.delete_message(reaction.message)
add_dollars(user, 250)
client.loop.create_task(background_loop())
I would use Client.wait_for_reaction instead of on_reaction_add:
async def background_loop():
await client.wait_until_ready()
channel = client.get_channel("479919577279758340")
while not client.is_closed:
emojigrab = '💰'
emojimsg = await client.send_message(channel, emojigrab)
await client.add_reaction(emojimsg, "💵")
res = await client.wait_for_reaction(emoji="💵", message=emojimsg, timeout=600,
check=lambda reaction, user: user != client.user)
if res: # not None
await client.delete_message(emojimsg)
await client.send_message(channel, "{} has grabbed the loot!".format(res.user.mention))
await asyncio.sleep(1)
add_dollars(res.user, 250)