What I've tried
StackOverflow articles
Need help to fix a certain bug in rock, scissor and paper.
discord.py documentation
How can I add reaction to a message
(checked plenty of articles, but I'm not going to add them in order to get into the point)
I've managed to retrieve the emoji from the user in the check method, but still the issue with timeouterror appears, so there is something I'm missing which I can't get a hold on.
In this senario im using Cog,
The issue
Even though the wait_for passes the check method, I'm not sure how to send it forward to the logical conditon. The result I get is passed in the exception.
bot.py
import os
from dotenv import load_dotenv
from discord import Intents
# lib
from lib.cog.miniGames import miniGames # Games
from lib.client.krigjo25 import krigjo25
# Importing .evn file
load_dotenv()
def botSetup ():
# necsessary values from .env
botKey = os.getenv('Token')
server = os.getenv('server')
# discord configs
intents= Intents.all()
#retrieving the module command prefix is created in the bot module
bot=krigjo25(intents=intents)
# adding cogs into the bot
bot.add_cog(miniGames(bot))
bot.run(botKey)
if __name__ == '__main__':
botSetup()
miniGames.py
class miniGames(Cog, name='miniGames module'):
def __init__(self, bot):
self.bot = bot
#command(name='rsp')
#Cog.listener()
# 1: Creating a game where the user has three choices to choose between from
# 2: The bot adds 3 reactions, where you have to choose one of them as an answer
# 3: The bot checks wheter the conditions is true or false, in order to send the correct messge:
async def RockscissorsPaper(self, message:Message):
# Declearing variables
# Rock, Scissors, Paper
rock,scissor, paper = '\U0001FAA8', '\U00002702','\U0001F4C4'
answer = { 0:'\U0001FAA8',
1:'\U00002702',
2:'\U0001F4C4'}
# Randomizing the answer and assign it to the variable
shuffle(answer)
x = randrange(0,2)
answer = '\U0001FAA8' #answer.get(x)
print(answer)
# Creating Embed message
embed = Embed(color = Color.blurple())
embed.title = 'Rock, Scissors & Paper'
embed.description = ' In order to play the game, please click one one of the following reactions'
# Sending & adding reactions
ch = await message.channel.send(embed=embed)
await ch.add_reaction(rock)
await ch.add_reaction(scissor)
await ch.add_reaction(paper)
# passes the check method
def check( user, reaction):
print('check function', reaction, user)
return user == message.author and reaction == str(reaction.emoji)
# Checks the user's input¨'
# Questions : Is it possible to use dictionaries in order to find the given emoji?
try :
# How does this work? If wait_for is a "asynico" function how can it work like this?
reaction, user = await self.bot.wait_for('reaction_add', timeout=30, check=check)
except TimeoutError as e:
print(e)
else:
reaction = str(reaction.emoji)
# Checking if its the bots reaction
if user == self.bot.user:
return None
else:
if reaction == '\U0001F4C4' and answer == '\U0001FAA8':
await message.channel.send(' you won')
Add this at the beginning of your code:
intents = discord.Intents.all()
Then add this into your discord.Bot's declaration:
bot = discord.commands.Bot(prefixes=['!'], intents=intents)
Then go to Discord Developer Portal and enable all the intents writing from the Application's "bot" page.
can u remove this line?
#command(name='rsp')
--> #Cog.listener()
# 1: Creating a game where the user has three choices to choose between from
What is this listener supposed to answer?
It appears that i had to change :
def check( user, reaction):
print('check function', reaction, user)
return user == message.author and reaction == str(reaction.emoji)
New
def emojiCheck(reaction, member):
reaction = str(reaction)
member == ctx.author.name
# Do not return the bot's last reaction
return member !=self.bot.user and reaction
--
I also had to add the code block in the try - except statement.
** OLD **
try :
# How does this work? If wait_for is a "asynico" function how can it work like this?
reaction, user = await self.bot.wait_for('reaction_add', timeout=30, check=check)
except TimeoutError as e:
print(e)
else:
reaction = str(reaction.emoji)
# Checking if its the bots reaction
if user == self.bot.user:
return None
else:
if reaction == '\U0001F4C4' and answer == '\U0001FAA8':
await message.channel.send(' you won')
New
try:
# Timer Check
reaction, member = await self.bot.wait_for('reaction_add', timeout=60.0, check=emojiCheck)
print(member, reaction, answer)
# Dictionaries
# Tie
tie = {
0:f'{self.bot.user} draws a **tie** for {member}',
1:'Sir, lets **tie** a **tie**',
2:'What did the **tie** say to the bowtie? You\'re a weirdo',
3:'lets have a wii-match',
}
reactionRock = {
}
# Randomize the dictionary
shuffle(tie)
x = randrange(0,3)
tie = tie.get(x)
# If the situation is a draw / tie
if str(reaction) == answer:
self.embed.description = tie
await ctx.send(embed = self.embed)
The point is if-elif or else statement has to be inside the try-except statement.
Hope this is for help for other developers which has the same issue.
Thanks for the assistance which I received. Its appreciated.
Related
sorry that this is probably rather easy to fix (I don't really understand the docs)
But, I want a channel to be opened, so only the user and mods can see it. It will be opened when the user adds a reaction to the message, the reaction will then be deleted (leaving just the bot's reaction)
Currently, my code is:
#commands.Cog.listener()
async def on_reaction_add(self, reaction, user):
if reaction.emoji == 'π©':
if user.channel.id == 850622999577231370:
await message.remove_reaction("π©", user)
overwrites = {
guild.default_role: discord.PermissionOverwrite(read_messages=False),
guild.me: discord.PermissionOverwrite(read_messages=True)
}
n = random.randomint(1, 1000)
await guild.create_text_channel(f'Ticket {n}', overwrites=overwrites, categoty="Tickets")
else:
pass
else:
pass
It doesn't seem to run, since there is no error message, but no channels are created either
A few things I noticed:
random.randomint does not exist, it must be random.randrange.
You didn't define guild in the provided code, we'll just use user.guild instead.
To get the channel we use if reaction.message.channel.id and not if user.channel.id, that makes little sense here.
Here is a possible new code:
#commands.Cog.listener()
async def on_reaction_add(self, reaction, user):
if reaction.emoji == 'π©':
print("Reaction")
if reaction.message.channel.id == Your_Channel_ID:
await reaction.message.remove_reaction("π©", user)
guild = user.guild # We get the guild
overwrites = {
guild.default_role: discord.PermissionOverwrite(read_messages=False),
guild.me: discord.PermissionOverwrite(read_messages=True)
}
n = random.randrange(1, 1000) # We choose a number from randrange 1-1000
await guild.create_text_channel(f'Ticket {n}', overwrites=overwrites, categoty="Tickets")
else:
pass
else:
pass
I am trying to make a function whenever the user reacts with the check emoji or if they react with the x. So basically I have a check if the user has a phone, if the user has a phone then you can call them. The bot sends a message in dms and reacts with a check and a x, if you click the check I want a function to happen, how can I do this?
#commands.command()
#commands.cooldown(1,120,commands.BucketType.user)
async def call(self, ctx, member : discord.Member):
phone = False
users = await bank_data()
user = ctx.author
client = discord.Client
try:
bag=users[str(user.id)]["bag"]
except:
bag=[]
for item in bag:
name = item["item"]
if name == "phone":
phone = True
if phone == False:
await ctx.reply("You must buy a phone to call someone idiot.")
if phone == True:
try:
targetBag = users[str(target.id)]["bag"]
except:
targetBag=[]
targetPhone = False
if target:
for item in targetBag:
name = item["item"]
if name =="phone":
targetPhone = True
if targetPhone == False:
await ctx.reply("Attempted user to call does not have a phone.")
if targetPhone == True:
channel = await target.create_dm()
emojis=['β
', 'β']
confirmEmoji = 'β
'
message=await channel.send(f"{ctx.author} is calling you. Do you want to answer?")
for emoji in emojis:
await message.add_reaction(emoji)
You can use the wait_for() for this case. Create a response variable so that you can check and use the response to answer the phone or decline it.
You can read more about it here-
https://discordpy.readthedocs.io/en/stable/api.html#discord.Client.wait_for
There are different types of events which you can pass into wait_for() in your case it's reaction_add
Hope this helped :D
I've been having an issue with my attempt to make a embed that reveals a list of characters that a play owns. I basically want to have the :left_arrow: and :right_arrow: emojis as reaction to pan left and right between the long database of characters a user owns.
I did in fact manage to do that however I noticed that if I use the command which shows an embed with all the characters twice before the timeout of one and then i click the left arrow to see the next page it goes to the next page on both embeds instead of just the one i reacted to.
Here's my source code.
#commands.command(aliases=["collection", "c"])
#commands.cooldown(1, 5, commands.BucketType.user)
async def characters(self, ctx):
if not await bot.check_channel_permissions(ctx.author, ctx.channel): # Checks if the bot can send messages in the channel.
return
playerData = await bot.get_player_data(ctx.author, ctx.channel) # Returns the user's data.
if playerData:
if len(playerData["Characters"]) > 0: # Checks amount of characters the user owns.
if len(playerData["Characters"]) > 20: # Player owns 20 so it will create an embed with reactions.
global pageNumber
global maxPages
pageNumber = 1
maxPages = math.ceil(len(playerData["Characters"]) / 20)
embed = await characters.setupEmbed(self, playerData, ctx.author, pageNumber)
currentEmbed = await ctx.channel.send(ctx.author.mention, embed=embed)
await currentEmbed.add_reaction("β¬
")
await currentEmbed.add_reaction("β‘")
async def listenForRection(self, mainUser): # Waits until the user reacts with the left or right arrow in order to edit the current embed.
global pageNumber # the current page (20 characters are shown a page)
global maxPages # the max amount of pages the user can pan between.
def check(reaction, reactionuser):
if reactionuser != mainUser:
return False
elif str(reaction.emoji) == 'β¬
' and pageNumber > 1:
return True
elif str(reaction.emoji) == 'β‘' and pageNumber < maxPages:
return True
else:
return False
try:
reaction, reactionuser = await self.client.wait_for(
"reaction_add",
timeout=60,
check=check
)
except:
asyncio.TimeoutError
await currentEmbed.edit(content="Timed out.")
else:
if str(reaction.emoji) == 'β¬
':
pageNumber += -1
elif str(reaction.emoji) == "β‘":
pageNumber += 1
newEmbed = await characters.setupEmbed(self, playerData, mainUser, pageNumber) # Returns an embeded list given the page number and info on the user.
await currentEmbed.edit(embed=newEmbed) # re-edits the current embed to show the next or previous page.
await listenForRection(self, mainUser) # re-runs the function again to listen for the next reaction.
await listenForRection(self, ctx.author) # runs the listener once so that it can begin detecting and re-running on its own.
else: # player will get an embeded list with no reactions since they only have 1 page of characters.
embed = await characters.setupEmbed(self, playerData, ctx.author, 1)
await ctx.channel.send(ctx.author.mention, embed=embed)
else:
await ctx.reply("You don't have any characters.")
What would happen is I run the command once and it works perfectly fine. Then when I run it again before the timeout and click a reaction it either wouldn't work or run both on the new embed and the old. I spent a ton of hours trying multiple things and searching but I didn't manage to do much apart from creating a new bug right after i fix the current.
There is actually an ext library for making paginated menus called discord-ext-menus. It does most of the work for you so you don't have to create your own.
I'm basically trying to make an application/questionnaire discord bot. When triggered, the bot is supposed to notify the user via dm and asks if user wants to proceed. User replies with "proceed" and bot starts with the first question and wait for a response from user. Bot then takes response and store it and proceeds with the next question and it loops until it finishes the last question. I'm stuck at the part where the bot sends the first question and I get no response when I answer. Any help would be appreciated.
PS. I'm fairly new to python and discord py
#bot.command()
async def apply(ctx):
Name = ''
Age = ''
user = ctx.author
name = ctx.author.display_name
q = ['What is your full real name?',
'What is your age?']
i = 0
#embed
message = 'Thank you ' + name + \
' for taking interest in Narcos City Police Department. You will be asked a series of question which you are required to answer to the fullest of your ability. Please reply with *proceed* to start your application.'
embedmsg =discord.Embed(title = 'NARCOS CITY POLICE DEPARTMENT', color = Police)
embedmsg.set_thumbnail(url = thumbnail)
embedmsg.add_field(name = 'Human Resources:', value = message)
#initial bot response
dmchannel = await ctx.author.send(embed=embedmsg)
#check
def check(m):
return m.author == ctx.author and m.channel == ctx.author.dm_channel
msg = await bot.wait_for('message', check=check)
if msg.content == 'proceed':
async def askq(q):
await ctx.author.send(q)
msg
return msg.content
Name = await askq(q[0])
Age = await askq(q[1])
Your check function is a bit messed up. You can try the following:
#bot.command
async def apply(ctx):
await ctx.author.send("Filler") # Sends a message to the author
questions = ["", "", ""] # Create your list of answers
answers = [] # Empty list as the user will input the answers
def check(m):
return ctx.author == m.author and isinstance(m.channel, discord.DMChannel) # Check that the messages were sent in DM by the right author
for i in questions:
await ctx.author.send(i) # Sends the questions one after another
try:
msg = await bot.wait_for('message', timeout=XXX, check=check)
except asyncio.TimeoutError:
await ctx.author.send("Timeout message.")
return # Will no longer proceed, user has to run the command again
else:
answers.append(msg) # Appends the answers, proceed
[OPTIONAL PART]
channel = bot.get_channel(ChannelID)
e = discord.Embed(color=ctx.author.color)
e.title = "New application"
e.description = f"First answer: {answers[0].content} [...]"
await channel.send(embed=e)
What did I do in the code?
Built in a different check to make sure the answers were sent in a DM channel by the author of the command
Put the answers into a list (answers.append(msg))
Sent the answers in an embed to a channel of your choice
!! Note that with this method the bot will directly start to ask your questions !!
If you want to first ask if the user wants to start with the questions you can build in something like:
try:
preply = await self.bot.wait_for("message", check=check, timeout=43200)
while preply.content != "proceed": # If answer is not "proceed"
await ctx.author.send("If you want to proceed please run the command again and type `proceed`")
preply = await self.bot.wait_for("message", check=check, timeout=XXX)
await ctx.author.send("We will now proceed.") # If answer is "proceed"
[Rest of the code above in the right indentation.)
I currently have a function which is polling a message for reactions and adding users to a list based on that using Discord.py. Here is the code below:
#commands.Cog.listener()
async def on_message(self, message):
editMessage = message
tankBoosters = []
healBoosters = []
dpsBooster = []
boosters = []
# we do not want the bot to reply to itself
if message.author != self.bot.user:
return
if len(message.embeds) > 0:
for embed in message.embeds:
if "Boost is Ready" in embed.fields:
return
else:
pass
for x in message.embeds:
if '<:tank:801416324306829312>' and '<:healer:801416334243921971>' and '<:dps:801416343848615947>' in x.description:
await message.add_reaction('<:tank:801416324306829312>')
await message.add_reaction('<:healer:801416334243921971>')
await message.add_reaction('<:dps:801416343848615947>')
embedToEdit = x
def check(reaction, user):
return str(reaction.emoji) in ['<:tank:801416324306829312>', '<:healer:801416334243921971>', '<:dps:801416343848615947>'] and user != self.bot.user
boosters = tankBoosters + healBoosters + dpsBooster
while len(boosters) != 4:
if len(boosters) != 4:
reaction, user = await self.bot.wait_for('reaction_add', check=check)
print(message.reactions)
if reaction.emoji.name == 'tank' and len(tankBoosters) == 0:
tankBoosters.append(user)
if reaction.emoji.name == 'healer' and len(healBoosters) == 0:
healBoosters.append(user)
if reaction.emoji.name == 'dps' and len(dpsBooster) < 2:
dpsBooster.append(user)
if len(tankBoosters) == 1 and len(healBoosters) == 1 and len(dpsBooster) == 2:
message = f"<:tank:801416324306829312> {tankBoosters[0].mention} \n <:healer:801416334243921971> {healBoosters[0].mention} \n <:dps:801416343848615947> {dpsBooster[0].mention} \n <:dps:801416343848615947> {dpsBooster[1].mention}"
embedToEdit.add_field(name="Boost is Ready", value=message, inline=False)
await editMessage.edit(embed=embed)
This is working fine, but what I need to do be able to do is remove users from the respective lists (tank, heal and dps) when a reaction is removed from the message.
I.e. a message is posted and 3 tanks, 2 healers and 6 DPS "sign up" to the message by posting reactions to the message and they are appended to their respective lists. Then 1 tank and 2 DPS "unsign" by removing their reaction to the message. I need to remove those users from the list when they remove their reaction. I have looked into using message.reactions[0].users() but according to the VS Code debug terminal, message.reactions[0].users() is <discord.iterators.ReactionIterator object at 0x011B5DF0>
, which I unfortunately don't know enough about python or discord to understand!
I think you can try this:
#bot.event
async def on_raw_reaction_remove(payload):
reaction = str(payload.emoji)
msg_id = payload.message_id
user_id = payload.user_id
guild_id = payload.guild_id
exists = db.exists(msg_id)
When someone remove his reaction, you know what reaction, the user ID...
For those interested, or frustratedly searching for answers on how to do this, I ended up changing my approach slightly. Instead of doing all the logic inside the on_message function, I removed that and used on_raw_reaction_add and on_raw_reaction_remove instead, and I am tracking users by storing them in global arrays which are emptied once certain conditions are met.
This does mean that the code can only handle looking at one post at a time, but thats all I need it to do for now :)