SelectMenu keeping selection after message edit - discord.py

This is using the discord.py 2.0 alpha SelectMenu. After editing the original message the selections clears any idea on how to keep the selection after the edit?
I think this is how it should be done but I can't implement that in-code:
Create a variable that stores play_status_view()
Save the user choice within the object
Use the new object when editing
class play_status_view(discord.ui.View):
def __init__(self):
super().__init__(timeout=None)
#discord.ui.select(custom_id="squad_play_status", placeholder="Signup status",
min_values=1, max_values=1,
options=[discord.SelectOption(label="A"),
discord.SelectOption(label="B"),
discord.SelectOption(label="C")])
async def signup_callback(self, select: discord.ui.select, interaction: discord.Interaction):
print(select.values[0])
await interaction.response.edit_message(embed=discord.Embed(title='Test'))
# or
await interaction.response.edit_message(embed=discord.Embed(title='Test'), view=self)

Related

Does not work Context Menus in cog - Discord.py

I wanted to create a context menu in my bot. For example, he took the documentation code.
#app_commands.context_menu(name='react')
async def react_(self, interaction: discord.Interaction, message: discord.Message):
await interaction.response.send_message('Very cool message!', ephemeral=True)
But when the code was launched in the console, the following error appeared:TypeError: context menus cannot be defined inside a class. How can I fix this?
You cannot use the decorator to create a context menu in Cogs, as explained by Danny here.
The quick way to create them in cogs is to create them by using the app_commands.ContextMenu class. Like so:
class MyCog(commands.Cog):
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
self.ctx_menu = app_commands.ContextMenu(
name='Cool Command Name',
callback=self.my_cool_context_menu, # set the callback of the context menu to "my_cool_context_menu"
)
self.bot.tree.add_command(self.ctx_menu) # add the context menu to the tree
async def my_cool_context_menu(self, interaction, message):
...
You can check out Danny's explanation for more information.

How can I edit an embed field with a button in Discord.py?

Basically, I'm making a poll command. I want it to put the user's vote in the embed field, and obviously make it so they can't vote again, again, and again. No idea how to do this.
Command Preview Here
Here's the code:
class Buttons(discord.ui.View):
def __init__(self, *, timeout = 180):
super().__init__(timeout=timeout)
#discord.ui.button(label="Yes", style=discord.ButtonStyle.success, emoji='👍')
async def upvote_button(self, button : discord.ui.Button, interaction : discord.Interaction):
pass
#discord.ui.button(label="No", style=discord.ButtonStyle.danger, emoji='👎')
async def downvote_button(self, button : discord.ui.Button, interaction : discord.Interaction):
pass
class Poll(commands.Cog):
def __init__(self, bot):
self.bot = bot
#app_commands.command(name="poll", description="Ask a question and receive upvotes or downvotes (Yes/No)")
async def poll(self, interaction : discord.Interaction, *, question : str):
embed = discord.Embed(
description=question,
color = 0xb037e1
)
embed.set_author(name="Poll", icon_url=self.bot.user.avatar)
embed.set_footer(text="Asked by: " + str(interaction.user), icon_url=interaction.user.avatar)
embed.add_field(name="Upvotes", value="X", inline=True)
embed.add_field(name="Downvotes", value="X", inline=True)
await interaction.response.send_message(embed=embed, view=Buttons())
Probably very simple, although I still haven't thought about a way
The easiest way to do this is by using dictionaries, lists, or even sets to store the users that have clicked.
For this example, I will use one list to store the users who have clicked the upvote and downvote buttons. Like here:
class Buttons(discord.ui.View):
def __init__(self, *, timeout=None):
super().__init__(timeout=timeout or 180)
self.voted_users = []
self.upvote_count = 0
self.downvote_count = 0
#discord.ui.button(
label="Yes", style=discord.ButtonStyle.success, emoji="👍"
)
async def upvote_button(
self, interaction: discord.Interaction, button: discord.ui.Button
):
if (
interaction.user in self.voted_users
): # check if the user has already voted or not and return if true
return await interaction.response.send_message(
content="You've already voted!", ephemeral=True
)
self.upvote_count += 1
self.voted_users.append(interaction.user) # add the user to voted list
await interaction.response.send_message("Upvoted!", ephemeral=True)
#discord.ui.button(label="No", style=discord.ButtonStyle.danger, emoji="👎")
async def downvote_button(
self, interaction: discord.Interaction, button: discord.ui.Button
):
if (
interaction.user in self.voted_users
): # check if the user has already voted or not and return if true
return await interaction.response.send_message(
content="You've already voted!", ephemeral=True
)
self.downvote_count += 1
self.voted_users.append(interaction.user) # add the user to voted list
await interaction.response.send_message("Downvoted!", ephemeral=True)
By the way, interaction always comes before the button in the button callback argument if you're using discord.py.

How to add Select Table to the response?

How can I add a select option to the bot's response so when I select one, it edits the message with another content?
Please help me. I do not have a example code just to add this to.
Those select menus or dropdowns are available in discord.py 2.0
Example
import discord
from discord import commands, ui
class Dropdown(ui.Select):
def __init__(self):
# The options which can be chosen inside the dropdown
options = [
discord.SelectOption(label="Moderation", description="Shows all moderation commands", emoji="🚨"),
discord.SelectOption(label="Config", description="Shows all config commands", emoji="⚙️"),
discord.SelectOption(label="Games", description="Shows all meme commands", emoji="🎮")
]
# placeholder: Will be shown when no option is chosen
# options: The dropdown options which can be chosen
# min_values, max_values: Indicates that only one of the three options can be picked
super().__init__(placeholder="Choose a section", options=options, min_values=1, max_values=1)
async def callback(self, interaction: discord.Interaction):
# This function is called when the user has chosen an option
# With the interaction parameter, you can send a response message.
# With self.values you get a list of the user's selected options. We only want the first one.
await interaction.response.send_message(f"You've chosen {self.values[0]}")
#bot.command()
async def dropdown(ctx: commands.Context):
# Create a view
view = ui.View()
# Add the dropdown to this view
view.add_item(Dropdown())
# Send a message containing the view
await ctx.send(f"**Dropdown**", view=view)
If you want to disable the ui.Select you can do that with the disabled kwarg. See more in the docs.
References:
ui.Select
ui.View
discord.SelectOption
discord.Interaction

Check if a user id is in a list of user ids to allow command access - discord.py

im fairly new to python and have been writing a discord bot for a server with me and a few friends as practice. I've added a few commands that could be pretty disruptive if all users had access to them at all times and have been trying to add a method to enable and disable access to them without using discord roles but a python list instead. I added a command that i can use to to add their user id to a list, but when the user tries to use the command they receive a permission error.
the code below is the problematic code from a cog
modlist = []
def ListCheck():
async def IsInList(ctx):
member = ctx.message.author.id
return member in modlist
return commands.check(IsInList)
#commands.command()
async def AddUser(self, ctx, *, question):
modlist.append(question)
#commands.command()
async def ViewModList(self, ctx):
await ctx.send('User ids that have access:')
for i in range(0, len(modlist)):
await ctx.send(modlist[i])
#commands.command()
async def ClearModList(self, ctx):
modlist.clear()
await ctx.send('Modlist cleared')
#commands.command()
#ListCheck()
async def PermTest(self, ctx):
await ctx.send('user is in modlist')
Any help would be hugely appreciated
One way you could do it would be to use commands.check(). The function in the code below checks if the message author, ctx.author, is in the given list, modList. If the author is in this list when the command 'test' is invoked, then the bot will send "You are a mod!", otherwise it will raise a check error.
modList = [394506589350002688, 697725862128386048] # just some ids as examples
def check_Mod(ctx):
if ctx.author.id in modList:
return ctx.author.id in modList
#commands.command()
#commands.check(check_Mod)
async def test(ctx):
await ctx.send("You are a mod!")
The above working is shown in the image below:
To answer is there is a way to add user ids to the list via a command?, you may either use a json or text file. In this case, I will use a text file since it's simpler, and also because my json isn't as fluent as others.
Here's an example of how your text file may look like:
394506589350002688
697725862128386048
First you need to read the file and check whether the author's id is in the file. We will be changing the check_Mod function for this. If you want to know more about reading through a text file, or feel that you may need a more efficient way, you may have a look at this: How to search for a string in text files?
def check_Mod(ctx):
with open('Mod.txt') as f: # do change the 'Mod.txt' to the name that suits you. Ensure that this file is in the same directory as your code!
if str(ctx.author.id) in f.read(): # this is reading the text file and checking if there's a matching string
return ctx.author.id
Now for writing the author id into your text file.
#commands.command()
#commands.check(check_Mod)
async def add_Mod(ctx, user:discord.Member=None):
if user == None:
await ctx.send("Please provide a user to add as a Mod!")
return
# First we'll make some functions for cleaner, more readable code #
def is_Mod(user_id):
## This function will check if the given id is already in the file. True if in file, False if not ##
with open('Mod.txt', 'r') as f:
if str(user_id) in f.read():
return True
else:
return False
def add_Mod(user_id):
## This function will add the given user id into the given text file, Mod.txt ##
with open('Mod.txt', 'a') as f: # 'a' is used for appending, since we don't want to overwrite all the ids already in the file
f.write(f"{str(user_id)}\n")
f.close()
# Now we put those functions to use #
if is_Mod(user.id) == True:
await ctx.send(f"The user {user} is already a Mod!")
else:
add_Mod(user.id)
await ctx.send(f"{user} added as a Mod!")
The above should work as seen in the image below.
p.s. If you have any other questions, please post them as a new question, thank you!

How to make a Reaction role command with discord.py

So I'm trying to make a Reaction role via discord.py but i don't know how to do it. Also there is no tutorial on YT, I want the bot to send a message and react to that message so the user when react to that get the role.
Okay,
This uses cogs and a bot instead of a client.
message = await ctx.send(" Click the ✅ reaction to verify yourself")
This piece sends the message (You can change it to whatever you want).
if not get(ctx.guild.roles, name="Verified"):
perms = discord.Permissions()
perms.general()
perms.text()
perms.voice()
await ctx.guild.create_role(name="Verified", permissions=perms, colour=discord.Colour(0x00bd09))
This piece will just check whever the role exists or not. If it doesnt it will create one.
await message.add_reaction("✅")
This adds this reaction to the message(So the user doesnt have to scroll around for it). If you dont want the bot have the role, you can add a check (If the user is a bot or not) in the on_raw_reaction_add() function.
#commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
guild = self.bot.get_guild(payload.guild_id)
role = discord.utils.get(guild.roles, name="Verified")
await payload.member.add_roles(role, reason="Verified Self", atomic=True)
This is the function to check when someone adds a reaction to the message. We want to use on_raw_reaction_add() instead of on_reaction_add() because the second one check the cache for adding a reaction, and if the thing isnt in chache it wont work. Thus the 1st one is less probable to create problems.
Here's the whole piece.
from discord.ext import commands
from discord.utils import get
import discord
from discord.ext.commands import has_permissions, CheckFailure
class Verification(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.command(name='generate_verification', help='Generates verification message')
async def generate_verification(self, ctx):
message = await ctx.send(" Click the ✅ reaction to verify yourself")
verification_message_id = message.id
# Check if Verification role already exists
if not get(ctx.guild.roles, name="Verified"):
perms = discord.Permissions()
perms.general()
perms.text()
perms.voice()
await ctx.guild.create_role(name="Verified", permissions=perms, colour=discord.Colour(0x00bd09))
await message.add_reaction("✅")
#commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
guild = self.bot.get_guild(payload.guild_id)
role = discord.utils.get(guild.roles, name="Verified")
await payload.member.add_roles(role, reason="Verified Self", atomic=True)
def setup(bot):
bot.add_cog(Verification(bot))
If you're creating a verification command, then you probably would like to store the amount of verification messages sent in a file. This would stop people from generating the message a lot of times. To this you also probably want a command to clear the data from the file, so if the adming wants to create the message again he just needs to clear the file.

Resources