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.)
Related
I am making a command that will randomly pick a user from a message's reaction (similar to giveaway command). I tried to add a feature that will remove the chosen user's reaction once the user is chosen by the bot but it didn't work. I am new to the world of discord bot, please help me.
Here are the codes:
#client.command()
async def king(ctx):
time = 10
embed = nextcord.Embed(title="King's Game", description="React to join.")
embed.set_footer(text=f"Ends in {time}s")
kmsg = await ctx.send(embed=embed)
await kmsg.add_reaction("👑")
await asyncio.sleep(time)
new_msg = await ctx.channel.fetch_message(kmsg.id)
players = await new_msg.reactions[0].users().flatten()
players.pop(players.index(client.user))
print (players)
king = random.choice(players)
tembed = nextcord.Embed(title="Time is up!", description="Get ready to obey the king.")
kembed = nextcord.Embed(title="King", description=f"{king.mention} is the king!")
await ctx.send(embed=tembed)
ktime = 2
await asyncio.sleep(ktime)
await ctx.send(embed=kembed)
kingid = get(king.user.id)
await kmsg.remove_reaction(kingid)
You should read about await remove_reaction(emoji, member) from the official documentation.
You have to simply specify the member of which remove the reaction.
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.
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 am trying to make it where when the user initiates the command $createprofile the bot will dm the user with the questions provided in the list, one after the other then post the answers in an embed to a specified channel. Now I have already built the list, but I am unsure of how to build in asking each question one after another, I am possibly thinking of using asyncio wait_for
import discord
from discord.ext import commands
import platform
import cogs._json
class Profile(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.Cog.listener()
async def on_ready(self):
print("Profile Cog has been loaded\n-----")
# #commands.command(aliases=['pm'])
# async def dm(self, ctx, user: discord.User, *, message=None):
# message = message or "This message is sent via dm"
# await user.send(message)
# await ctx.message.delete()
#commands.command()
async def createprofile(ctx, member: discord.Member = None):
userName = ""
userAge = ""
questions = [
"Please input your name/nickname:",
"Please input your age:"
]
dmChannel = await ctx.author.send(
"You will be receiving two questions. One involving your name and the other your age.")
def check(message):
return message.author == ctx.author and message.channel == dmChannel.channel
async def askQuestion(question):
await ctx.author.send(question)
print("Waiting for reply...")
userReply = await client.wait_for('message', check=check)
print("User replied")
return userReply.content
userName = await askQuestion(questions[0])
userAge = await askQuestion(questions[1])
e = discord.Embed(title=str(userName) + "'s Profile", description=f"""
Age: `{str(userAge)}`
""")
await ctx.send(embed=e)
def setup(bot):
bot.add_cog(Profile(bot))
#client.command()
async def createprofile(self, ctx, member: discord.Member = None):
userName = ""
userAge = ""
questions = [
"Please input your name/nickname:",
"Please input your age:"
]
dmChannel = await ctx.author.send(
"Yo will be receiving two questions. One involving your name and the other your age.")
def check(message):
return message.author == ctx.author and message.channel == dmChannel.channel
async def askQuestion(question):
await ctx.author.send(question)
print("Waiting for reply...")
userReply = await client.wait_for('message', check=check)
print("User replied")
return userReply.content
userName = await askQuestion(questions[0])
userAge = await askQuestion(questions[1])
e = discord.Embed(title=str(userName) + "'s Profile", description=f"""
Age: `{str(userAge)}`
""")
await ctx.send(embed=e)
First, you need to send the user a question, so how you do that is through await ctx.author.send("this is a question"). You then store that message into a variable. You then create a check function that makes sure that the user who replied is in fact the user who sent the $createprofile message in the first place. You will also need to check if the message's channel is the dm channel. So that's where the message you stored earlier takes action. It will make sure that the channel of the message is the same as the channel of the dm message you sent to the user earlier. After that, you create an async function to ask a question. From there on it's pretty straightforward. Feel free to optimize your code using for loops. Hope this helps.
I've been trying to create a bot command that listens for a message after the command is sent. This is meant to be used to create a profile of the command author to later store in a JSON file.
#client.command() #creates player profile
async def create(ctx):
await ctx.send("You will be asked a series of questions to create your Profile. If you accidentally typed this wait 15 seconds for it to cancel.")
message = await client.wait_for('message',check=None,timeout=15)
await ctx.send(message) #used to check what message is holding
While the code above works, it doesn't work as I want it to. It sends back the id's of the server, member, channel, message, and the author, along with other information, instead of holding the reply by the command author.
So, what you want is to store the user's input data and append them as like an interview form:
It takes the user's input messages and appends them to the message they answered like:
q_list = [
'Your question 1',
'Your question 2',
'Your question 3']
a_list = []
#client.command()
async def create(ctx):
await ctx.send("You will be asked a series of questions to create your Profile. If you accidentally typed this wait 15 seconds for it to cancel.")
a_list = []
submit_channel = client.get_channel(CHANNEL_ID_FOR_SUBMISSIONS)
channel = await ctx.author.create_dm()
def check(m):
return m.content is not None and m.channel == channel
for question in q_list:
await asyncio.sleep(3)
await channel.send(question)
msg = await client.wait_for('message', check=check)
a_list.append(msg.content)
submit_wait = True
while submit_wait:
await channel.send(
'You have completed the interview, type ``submit`` to confirm')
msg = await client.wait_for('message', check=check)
if "submit" in msg.content.lower():
submit_wait = False
answers = "\n".join(f'{a}. {b}' for a, b in enumerate(a_list, 1))
submit_msg = f'''**Submission - Created by {msg.author.mention}** \n{answers}'''
await submit_channel.send(submit_msg)