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.
Related
I am trying to create a discord bot that gives peoples wins and then stores it into a json file. When I run the command ?win # I run into an error that displays their id within the error. I believe this is because their id is not in the json file. However I want my code to create an entry for their id and wins if it is not already inside the json file. Here are my functions and command.
open wins
async def open_wins(user):
users = await get_win_data()
if str(user.id) in users:
return False
users[str(user.id)] = {"Wins": 0}
with open('leaderboard.json',"w") as f:
json.dump(users,f)
return True
Get win data
async def get_win_data():
with open("leaderboard.json", "r") as f:
users = json.load(f)
return users
win code
#client.command()
#commands.has_role("Draft Manager")
async def win(ctx,member: discord.Member = None ):
if not member:
member = ctx.author
await open_wins(member)
users = await get_win_data()
user = member
onewin = 1
await ctx.send(f"You just gained {onewin} win ")
users[str(user.id)]["Wins"] += onewin
with open("leaderboard.json", "w") as f:
json.dump(users, f, indent=4)
Here is my json file
{
"325837218222440460": {
"Wins": 1
}
}
I would like to the note the win command only works when I do "?win" but when I do "?win <#user>" thats when I get an error.
You use await open_wins(member) only when member is None.
But if you use "?win <#user>", #user may be still not recorded to your file. So, you need to use await open_wins(member) function in all cases.
#client.command()
#commands.has_role("Draft Manager")
async def win(ctx,member: discord.Member = None ):
if not member:
member = ctx.author
await open_wins(member)
users = await get_win_data()
user = member
onewin = 1
await ctx.send(f"You just gained {onewin} win ")
users[str(user.id)]["Wins"] += onewin
with open("leaderboard.json", "w") as f:
json.dump(users, f, indent=4)
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()
So, I'm working on a Discord bot for my Discord server by following some tutorials, and I can't seem to figure out why both the reaction-role-giver and the leveling system isn't working.
For the reaction-role-giver, the point is to give the emoji the role to the person who reacts to that emoji as the descriptor implies. The error I'm getting for that is "Member not found." And the leveling system is supposed to store the user ID and the experience level in the users.json file (which it does). Still, it doesn't increase the level numbers like it's told to, and it duplicates the User ID in the users.json instead of adding the EXP points to the already-there User ID.
Here's the code:
import discord
from discord.ext import commands
import os
import json
client = commands.Bot(command_prefix="~")
#client.event
async def on_ready():
print("Bot is ready")
# Leveling System starts here!
# Needs to be fixed.
#client.event
async def on_member_join(member):
with open('users.json', 'r') as f:
users = json.load(f)
await update_data(users, member)
with open('users.json', 'w') as f:
json.dump(users, f)
#client.event
async def on_message(message):
with open('users.json', 'r') as f:
users = json.load(f)
await update_data(users, message.author)
await add_experience(users, message.author, 5)
await level_up(users, message.author, message.channel)
with open('users.json', 'w') as f:
json.dump(users, f)
async def update_data(users, user):
if not user.id in users:
users[user.id] = {}
users[user.id]['experience'] = 0
users[user.id]['level'] = 1
async def add_experience(users, user, exp):
users[user.id]['experience'] += exp
async def level_up(users, user, channel):
experience = users[user.id]['experience']
lvl_start = users[user.id]['level']
lvl_end = int(experience ** (1/4))
if lvl_start < lvl_end:
await client.send_message(channel, '{} has leveled up to level {}'.format(user.mention, lvl_end))
users[user.id]['level'] = lvl_end
# Reaction Code starts here
# Needs to be fixed...
#client.event
async def on_raw_reaction_add(payload):
message_id = payload.message_id
if message_id == ID_redacted: # ID Redacted but in the actual file it's there
guild_id = payload.guild_id
guild = discord.utils.find(lambda g : g.id == guild_id, client.guilds)
if payload.emoji.name == 'Toons':
role = discord.utils.get(guild.roles, name='Toons')
elif payload.emoji.name == 'Heroes':
role = discord.utils.get(guild.roles, name='Heroes')
elif payload.emoji.name == 'NotificationsSquad':
role = discord.utils.get(guild.roles, name='Notifications Squad')
else:
role = discord.utils.get(guild.roles, name=payload.emoji.name)
if role is not None:
member = discord.utils.find(lambda m : m.id == payload.user_id, guild.members)
if member is not None:
await member.add_roles(role)
print("done")
else:
print("Member not found.")
else:
print("Role not found.")
token = os.environ.get("Secrets")
client.run(token)
I've tried to get help from some friends but they're too busy. So, I have come here for help. I hope that this was enough information to go on! If not, ask me in the comments if I need more information.
I've been trying to make an economy system in discord.py. However, the system is not working whatsoever. Whenever I type in the !balance command it doesn't send an embed at all. It doesn't even give an error. Here is my code:
#client.command()
async def balance(ctx):
user = ctx.author
await open_account(ctx.author)
users = await get_bank_data()
wallet_amt = users[str(user.id)]["wallet"]
bank_amt = users[str(user.id)]["bank"]
embed=discord.Embed(title=f"{ctx.author.name}'s Balance")
embed.add_field(name="Wallet:", value=wallet_amt, inline=False)
embed.add_field(name="Bank:", value=bank_amt, inline=False)
await ctx.send(embed=embed)
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
Can anyone help me identify the error? I'm quite new to discord.py
Your open_account() function's logic will always return True even if the if condition passes because the indentation is backed off. If it is linked with the way you retrieve the data this might be your issue.
#client.command(aliases=['bal'])
async def balance(ctx):
await open_account(ctx.author)
user = ctx.author
users = await get_bank_data()
wallet_amt = users[str(user.id)]["wallet"]
bank_amt = users[str(user.id)]["bank"]
em = discord.Embed(title=f'{ctx.author.name} Balance',color = discord.Color.red())
em.add_field(name="Wallet Balance", value=wallet_amt)
em.add_field(name='Bank Balance',value=bank_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('mainbank.json','w') as f:
json.dump(users,f)
return True
async def get_bank_data():
with open('mainbank.json','r') as f:
users = json.load(f)
return users
This should work change the json file name to the correct one,
one important thing , check your saving functions and file content too
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)