I am creating a ticket system for my bot so that members can tell their issue to mods and I can create multiple ticket channels without any limit by reacting again and again. But I want that there should be a limit so that when there is an ongoing ticket so that a specific user cannot create one more ticket and the bot should yell at him.
Code:
#bot.event
async def on_raw_reaction_add(payload):
if payload.member.id != bot.user.id and str(payload.emoji)== u"\U0001F3AB":
msg_id, channel_id, category_id= bot.ticket_configs[payload.guild_id]
if payload.message_id == msg_id:
guild= bot.get_guild(payload.guild_id)
for category in guild.categories:
if category.id == category_id:
break
channel = guild.get_channel(channel_id)
ticket_num= 1 if len(category.channels) == 0 else int(category.channels[-1].name.split("-")[1]) + 1
ticket_channel= await category.create_text_channel(f"Ticket {ticket_num}", topic= f"Channel for ticket number {ticket_num}.", permission_synced= True)
await ticket_channel.set_permissions(payload.member, read_messages= True, send_messages= True)
message= await channel.fetch_message(msg_id)
await message.remove_reaction(payload.emoji, payload.member)
await ticket_channel.send(f"{payload.member.mention} Thank You! for creating this ticket staff will contact you soon. Type **-close** to close the ticket.")
try:
await bot.wait_for("message", check=lambda m: m.channel== ticket_channel and m.author== payload.member and m.content == "-close", timeout= 3600)
except asyncio.TimeoutError:
await ticket_channel.delete()
else:
await ticket_channel.delete()
TL;DR: My bot can currently create multiple ticket channels without any limits. However, I want to enforce a limit where if a user had an existing ticket but started a new channel, the bot would remind him that he already has an existing ticket.
try this
if discord.utils.get(guild.channels, name=f"Ticket {ticket_num}") != None:
await payload.member.send(content=f"Ticket {ticket_num} is already an existing channel!")
Related
Im trying to make a bot that send me a message with a reaction button, when I click it, give me a role in the server. Im trying to use on_raw_reaction_add event but I cant reach a solution to make it, Im always getting errors at getting guild roles and this stuff.
In this case, guild is none, I dont know what Im doing wrong.
My code:
#client.command()
async def test(ctx):
global member
global message__id
global channel_id
channel_id = (ctx.channel.id)
member = ctx.message.author
embed = discord.Embed(title="Verify your account", color=0x03fc14)
embed.add_field(name=f"Verification!", value=('React to this message to get verified!'), inline=False)
embed.set_footer(text=ctx.author, icon_url=ctx.author.avatar_url)
mesg = await member.send(embed=embed)
await mesg.add_reaction("✅")
message__id = mesg.id
print("EXECUTED")
#client.event
async def on_raw_reaction_add(payload):
print("reacted")
print("messageid accepted")
guild = client.get_guild(payload.guild_id)
if guild is not None:
print("messageid accepted")
reactor = payload.guild.get_member(payload.member.id)
role = discord.utils.get(guild.roles, name="Member")
if payload.emoji.name == '✅':
print("emoji accepted")
await reactor.add_roles(role)
EDIT:
I changed my on_raw_reaction_add event:
#client.event
async def on_raw_reaction_add(payload):
print("reacted")
print("messageid accepted")
guild = client.get_guild(payload.guild_id)
if guild is not None:
print("guild not none ")
reactor = payload.guild.get_member(payload.member.id)
role = discord.utils.get(guild.roles, name="Verified")
if payload.emoji.name == '✅':
print("emoji accepted")
await reactor.add_roles(role)
else:
print("guild none")
And this is what happen when I try the method:
First three words are the prints of the bot reacting his own message, the "EXECUTED" is the print of test method and the last three messages. are when I react the bot message
In this case, I suggest using a wait_for rather than on_raw_reaction_add.
Example, as seen in the docs:
#client.command(aliases=["t"])
async def test(ctx):
embed = discord.Embed(title="Verify your account", color=0x03fc14)
embed.add_field(name=f"Verification!", value=('React to this message to get verified!'), inline=False)
embed.set_footer(text=ctx.author, icon_url=ctx.author.avatar_url)
# sends message to command author
mesg = await ctx.author.send(embed=embed)
await mesg.add_reaction("✅")
#check if the reactor is the user and if that reaction is the check mark
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) == '✅'
try:
#wait for the user to react according to the checks
reaction, user = await client.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
# 60 seconds without reaction
await ctx.send('Timed out')
else:
await ctx.send('✅')
docs- https://discordpy.readthedocs.io/en/stable/api.html?highlight=wait_for#discord.Client.wait_for
async def test(ctx):
global member
global message__id
global channel_id
channel_id = (ctx.channel.id)
member = ctx.message.author
embed = discord.Embed(title="Verify your account", color=0x03fc14)
embed.add_field(name=f"Verification!", value=('React to this message to get verified!'), inline=False)
embed.set_footer(text=ctx.author, icon_url=ctx.author.avatar_url)
mesg = await member.send(embed=embed)
await mesg.add_reaction("✅")
message__id = mesg.id
print("EXECUTED")
role = get(ctx.guild.roles, id=role id here)
done = False
while done == False:
reaction, user = await client.wait_for(“reaction_add”, check=check)
if user == client.user:
continue
await member.add_roles(role)
done = True
I am pretty sure something like this would work :D
When you do guild is not None, it will never fire because you are reacting in a DM channel context - where the guild is None. To access the guild information from the DM, you'll need to save it somewhere in the message (or save it yourself internally). Then, you have the guild object and you're able to add roles or do things to it.
#client.command()
async def testrole(ctx):
member = ctx.author
# ...
embed = discord.Embed(title="Verify your account", color=0x03fc14)
embed.add_field(name=f"Verification!", value=('React to this message to get verified!'), inline=False)
embed.set_footer(text=ctx.author, icon_url=ctx.author.avatar_url)
mesg = await member.send(str(ctx.guild.id), embed=embed) # we need the guild information somewhere
await mesg.add_reaction("✅")
#client.event
async def on_reaction_add(reaction, user): # there's really no need to use raw event here
if user.id == client.user.id:
return
if reaction.message.guild is None:
if reaction.emoji == '\u2705':
# we have to get the guild that we saved in the other function
# there is no other way to know the guild, since we're reacting in the DM
# that's why it was saved inside of the message that was sent to the user
guild_id = int(reaction.message.content)
guild = client.get_guild(guild_id)
member = guild.get_member(user.id)
await member.add_roles(...) # change this with your roles
I am creating ticket tool for my server. So whenever a user will react to the message a new channel will be created, but I want the bot to send a message in that new channel saying "React below to delete the ticket.". But can I do that without storing msg_id and other data in a global variable?
Code:
#bot.event
async def on_raw_reaction_add(payload):
if payload.member.id != bot.user.id and str(payload.emoji)== u"\U0001F3AB":
msg_id, channel_id, category_id= bot.ticket_configs[payload.guild_id]
if payload.message_id == msg_id:
guild= bot.get_guild(payload.guild_id)
for category in guild.categories:
if category.id == category_id:
break
guild = await bot.fetch_guild(460654239094669332)
channels = guild.text_channels
channel = guild.get_channel(842807548968042536)
duplicate = False
topic = f"<#!{payload.member.id}>'s ticket"
for channel in channels:
if topic == channels.topic:
duplicate = True
break
if duplicate:
return
else:
ticket_num= 1 if len(category.channels) == 0 else int(category.channels[-1].name.split("-")[1]) + 1
ticket_channel= await category.create_text_channel(f"Ticket-{ticket_num}", topic= topic, permission_synced= True)
await ticket_channel.set_permissions(payload.member, read_messages= True, send_messages= True)
print(msg_id)
embed= discord.Embed(title= "New Ticket!!!", description= f"{payload.member.mention} Thank You! for creating this ticket staff will contact you soon. Type **-close** to close the ticket.")
message= await ticket_channel.send(embed=embed)
await message.add_reaction("❌")
Help will be really appreciated
You can use bot.wait_for with a simple check
message = await ctx.send("React below to delete the ticket")
def check(reaction, user):
return reaction.message == message and str(reaction) == "✅" # you can use another emoji, or don't use it at all
try:
await bot.wait_for("reaction_add", check=check, timeout=60.0) # timeout is optional
except asyncio.TimeoutError:
...
else:
await channel.delete() # where `channel` is the channel to delete
suggestEmbed.add_field(name='Ticket ID: ', value = f'{uniqueID}', inline=False)
message = await channel.send(embed = suggestEmbed)
await message.add_reaction('✅')
await message.add_reaction('❌')
sendEmbed.set_author(name = f'suggested by {ctx.message.author}', icon_url = f'{ctx.author.avatar_url}')
sendEmbed.timestamp = datetime.utcnow()
def check (reaction, user):
return not user.bot and message == reaction.message
try:
reaction, user = await bot.wait_for('reaction_add',check=check,timeout=604800)
while reaction.message == message:
if str(reaction.emoji) == "✅":
await ctx.send("🚀🚀🚀 Yay! Your suggestion has been approved, We thank you for your valuable time!")
await channel.send(f'suggestion of {ctx.message.author}, with ID: {uniqueID} has been approved, this post will no longer be active')
return
if str(reaction.emoji) == "❌":
await ctx.send("🙇♀️🙇♀️ Sorry! Your suggestion has not been approved, We thank you for your valuable time!")
message1 = await ctx.send(embed = sendEmbed)
await channel.send(f'suggestion of {ctx.message.author}, with ID: {uniqueID} has not been approved, this post will no longer be active')
return
except asyncio.TimeoutError:
await ctx.send("Your suggestion was timed out. Please try again!")
return
I want to know the role/Username of the person who reacts to my embed. (made this using dicord.py). is there a way I can access it using ctx? or any other way that I don't know of.
Since you are getting user from the bot.wait_for, you can simply access the username using one of the two:
username = user.display_name
#or
username = user.name
and the roles you can access using:
roles = user.roles #user.roles returns a list because the user can have multiply roles.
#Attention! This will raise an error if the reaction was in a dmchannel
References
user.nameS
user.display_name
member.roles
I have been using the following two methods to get a user input from a discord embed. The first I use to get a text based message input and the second I use to get a reaction input (used to paginate the embed). What I would like to know is if I can merge these two to create a method to wait for either a reaction or a message input?
#message input
try:
answer = await self.bot.wait_for(
"message",
timeout=60,
check=lambda message: message.author.id == ctx.author.id
and isinstance(message.channel, discord.channel.DMChannel)
)
#reaction input
try:
reaction, user = await self.bot.wait_for(
"reaction_add",
timeout=60,
check=lambda reaction, user: user.id == ctx.author.id
and reaction.emoji in buttons
#and isinstance(reaction.channel, discord.channel.DMChannel),
)
UPDATE:
So I have tried to implement the method duckboycool linked to (Many thanks man!).
The issue I am having now is that when I react to the paginated embed it works perfectly, the reaction is noted and the embed is updated accordingly. But if I input a message instead it get the following error:
"reaction, emoji = await task
TypeError: cannot unpack non-iterable Message object"
here is my code:
finished =0
while finished == 0:
done_tasks = None
check1=lambda reaction, user: user.id == ctx.author.id and reaction.emoji in buttons
check2=lambda message: message.author.id == ctx.author.id and isinstance(message.channel, discord.channel.DMChannel)
pending_tasks = [self.bot.wait_for('reaction_add',check=check1),self.bot.wait_for('message',check=check2)]
done_tasks, pending_tasks = await asyncio.wait(pending_tasks, return_when=asyncio.FIRST_COMPLETED)
#print(done_tasks)
for task in pending_tasks:
task.cancel()
for task in done_tasks:
reaction, emoji = await task
message = await task
if reaction:
print(reaction)
previous_page = current
if reaction.emoji == u"\u23EA":
current = 0
elif reaction.emoji == u"\u25C0":
if current > 0:
current -= 1
elif reaction.emoji == u"\u25B6":
if current < len(pages)-1:
current += 1
elif reaction.emoji == u"\u23E9":
current = len(pages)-1
if current != previous_page:
await msg.edit(embed = pages[current])
else:
print(message.content)
In the updated code, you need to check what kind of awaitable the event is so that you can unpack with the correct number of values and complete the intended behavior. With your examples of message and reaction_add, this would probably look something like this.
for task in done_tasks:
taskobj = await task
if isinstance(taskobj, discord.Message):
message = taskobj
#Message logic
else:
reaction, user = taskobj
#Reaction logic
You might have to do different things depending on which events you're using. (Here, it relies on a tuple being recognized as not an instance of discord.Message.)
so right now I'm trying to create a type of application bot and right now the check I used for the bot doesn't allow the user to move onto the next question. There is no traceback so it's something with the check I defined.
#commands.command()
#commands.has_role("Realm OP")
async def block(self, ctx, user: discord.User):
DMchannel = await ctx.author.create_dm()
channel = ctx.message.channel
author = ctx.message.author
mentions = [role.mention for role in ctx.message.author.roles if role.mentionable]
channel2 = str(channel.name)
channel = channel2.split('-')
if len(channel2) == 2: # #real-emoji
realm, emoji = channel
else: # #realm-name-emoji
realm, emoji = channel[0], channel[-1]
realmName = realm.replace("-" , " ")
realmName1 = realmName.lower()
rolelist = []
print(realmName1)
a = solve(realmName1)
print(a)
realmName2 = str(a) + " OP"
print(realmName2)
check_role = discord.utils.get(ctx.guild.roles, name= realmName2)
if check_role not in ctx.author.roles:
return await ctx.send('You do not own this channel')
else:
await ctx.send("You own this channel!")
def check(m):
return m.channel == channel and m.author != self.bot.user
await DMchannel.send("Please fill out the questions in order to block the user!")
await DMchannel.send("User's Gamertag: (If you don't know, try using the >search command to see if they have any previous records!) ")
blocka1 = await self.bot.wait_for('message', check=check)
await DMchannel.send("Reason for block:")
blocka2 = await self.bot.wait_for('message', check=check)
Right now the bot doesn't do anything after the user responds to the first question and there is no traceback.
Any help or suggestions would help greatly!
Your check isn't returning anything. It's supposed to return either True or False. This means your wait_for will never end (as the check never returns True), so it will never ask the next question.
def check(m):
return m.channel == channel and m.author != self.bot.user
EDIT:
Your channel is the ctx.channel, while your bot waits for answers in DM. Assuming you answer your bot in DM as well, this means your check will never pass, as m.channel never equals channel.
def check(m):
return m.guild is None and m.author == ctx.author
This checks if a message was sent in DM (Guild is None), and if it was a DM by the original author.