From 61b46e1f21f035796485137198d384c70f8e6b5e Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:17:05 +0300 Subject: [PATCH 01/32] Fixed alignment in the rank card and improved trunacation --- bot/extensions/levelling/commands.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bot/extensions/levelling/commands.py b/bot/extensions/levelling/commands.py index eae67cb3..60d0e0be 100644 --- a/bot/extensions/levelling/commands.py +++ b/bot/extensions/levelling/commands.py @@ -230,14 +230,13 @@ def convert_int(integer): xp_offset_x -= xp_text_size[2] - xp_text_size[0] draw.text((xp_offset_x, xp_offset_y), text, font=self.small_font, fill="#fff") - if len(username) >= 15: + if len(username) >= 18: # Truncating the name username = username[:15] + "..." text_bbox = draw.textbbox((0, 0), username, font=self.medium_font) - text_offset_x = bar_offset_x - 10 text_offset_y = bar_offset_y - (text_bbox[3] - text_bbox[1]) - 20 - draw.text((text_offset_x, text_offset_y), username, font=self.medium_font, fill="#fff") + draw.text((bar_offset_x, text_offset_y), username, font=self.medium_font, fill="#fff") # create copy of background background = self.background.copy() @@ -274,8 +273,6 @@ async def rank(self, interaction: core.InteractionType, member: discord.Member = record = await LevellingUser.pool.fetchrow(query, interaction.guild.id, member.id) - log.info(record) - if record.total_xp is None: if member.id == interaction.user.id: return await interaction.response.send_message( From b5a2e6af4b7d032bd696eafca27bd4458b9c0f00 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Wed, 25 Oct 2023 23:54:18 +0300 Subject: [PATCH 02/32] Migrated poll cog to new extension format --- bot/cogs/poll.py | 151 ------------------------ bot/extensions/poll/__init__.py | 9 ++ bot/extensions/poll/commands.py | 203 ++++++++++++++++++++++++++++++++ bot/extensions/poll/events.py | 34 ++++++ cli.py | 2 +- 5 files changed, 247 insertions(+), 152 deletions(-) delete mode 100644 bot/cogs/poll.py create mode 100644 bot/extensions/poll/__init__.py create mode 100644 bot/extensions/poll/commands.py create mode 100644 bot/extensions/poll/events.py diff --git a/bot/cogs/poll.py b/bot/cogs/poll.py deleted file mode 100644 index 95feb86c..00000000 --- a/bot/cogs/poll.py +++ /dev/null @@ -1,151 +0,0 @@ -import discord -from discord.ext import commands - - -class Polls(commands.Cog): - def __init__(self, bot: commands.AutoShardedBot): - self.__bot = bot - - @property - def reactions(self): - return { - 1: "1️⃣", - 2: "2️⃣", - 3: "3️⃣", - 4: "4️⃣", - 5: "5️⃣", - 6: "6️⃣", - 7: "7️⃣", - 8: "8️⃣", - 9: "9️⃣", - 10: "🔟", - } - - def poll_check(self, message: discord.Message): - try: - embed = message.embeds[0] - except Exception: - return False - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.__bot.user - return False - - @commands.Cog.listener() - async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): - channel: discord.TextChannel = self.__bot.get_channel(payload.channel_id) - message: discord.Message = await channel.fetch_message(payload.message_id) - - if payload.user_id == self.__bot.user.id: - return - - if not self.poll_check(message): - return - - emojis = list(self.reactions.values()) - if str(payload.emoji) not in emojis: - return - - for reaction in message.reactions: - if str(reaction) not in emojis: - return - - if str(reaction.emoji) != str(payload.emoji): - user = self.__bot.get_user(payload.user_id) - await message.remove_reaction(reaction.emoji, user) - - @commands.group() - async def poll(self, ctx): - """Polls""" - if ctx.invoked_subcommand is None: - return await ctx.send_help(self.__bot.get_command("poll")) - - @poll.command() - @commands.cooldown(1, 10, commands.BucketType.channel) - async def new(self, ctx, desc: str, *choices): - """Create a new poll""" - await ctx.message.delete() - - if len(choices) < 2: - ctx.command.reset_cooldown(ctx) - if len(choices) == 1: - return await ctx.send("Can't make a poll with only one choice") - return await ctx.send("You have to enter two or more choices to make a poll") - - if len(choices) > 10: - ctx.command.reset_cooldown(ctx) - return await ctx.send("You can't make a poll with more than 10 choices") - - embed = discord.Embed( - description=f"**{desc}**\n\n" - + "\n\n".join(f"{str(self.reactions[i])} {choice}" for i, choice in enumerate(choices, 1)), - timestamp=discord.utils.utcnow(), - color=discord.colour.Color.gold(), - ) - embed.set_footer(text=f"Poll by {str(ctx.author)}") - msg = await ctx.send(embed=embed) - for i in range(1, len(choices) + 1): - await msg.add_reaction(self.reactions[i]) - - @poll.command() - async def show(self, ctx, message: str): - """Show a poll result""" - await ctx.message.delete() - - try: - *_, channel_id, msg_id = message.split("/") - - try: - channel = self.__bot.get_channel(int(channel_id)) - message = await channel.fetch_message(int(msg_id)) - except Exception: - return await ctx.send("Please provide the message ID/link for a valid poll") - except Exception: - try: - message = await ctx.channel.fetch_message(message) - except Exception: - return await ctx.send("Please provide the message ID/link for a valid poll") - - if self.poll_check(message): - poll_embed = message.embeds[0] - reactions = message.reactions - reactions_total = sum( - [reaction.count - 1 if str(reaction.emoji) in self.reactions.values() else 0 for reaction in reactions] - ) - - options = list( - map( - lambda o: " ".join(o.split()[1:]), - poll_embed.description.split("1️")[1].split("\n\n"), - ) - ) - desc = poll_embed.description.split("1️")[0] - - embed = discord.Embed( - description=desc, - timestamp=poll_embed.timestamp, - color=discord.Color.gold(), - ) - - for i, option in enumerate(options): - reaction_count = reactions[i].count - 1 - indicator = "░" * 20 - if reactions_total != 0: - indicator = "█" * int(((reaction_count / reactions_total) * 100) / 5) + "░" * int( - (((reactions_total - reaction_count) / reactions_total) * 100) / 5 - ) - - embed.add_field( - name=option, - value=f"{indicator} {int((reaction_count / (reactions_total or 1)*100))}%" - f" (**{reaction_count} votes**)", - inline=False, - ) - - embed.set_footer(text="Poll Result") - return await ctx.send(embed=embed) - - return await ctx.send("Please provide the message ID/link for a valid poll") - - -async def setup(bot): - await bot.add_cog(Polls(bot)) diff --git a/bot/extensions/poll/__init__.py b/bot/extensions/poll/__init__.py new file mode 100644 index 00000000..998921e0 --- /dev/null +++ b/bot/extensions/poll/__init__.py @@ -0,0 +1,9 @@ +from bot.core import DiscordBot + +from .commands import Polls +from .events import PollsEvents + + +async def setup(bot: DiscordBot) -> None: + await bot.add_cog(Polls(bot=bot)) + await bot.add_cog(PollsEvents(bot=bot)) diff --git a/bot/extensions/poll/commands.py b/bot/extensions/poll/commands.py new file mode 100644 index 00000000..6dc21729 --- /dev/null +++ b/bot/extensions/poll/commands.py @@ -0,0 +1,203 @@ +import discord +from discord import app_commands, ui +from discord.ext import commands + +from bot import core + + +class PollModal(ui.Modal): + name = ui.TextInput(label="Option name", placeholder="Enter poll option name", max_length=50, required=True) + + def __init__(self, var: discord.Interaction): + self.var = var + super().__init__(title="Poll options") + + @property + def reactions(self): + return { + 0: "1️⃣", + 1: "2️⃣", + 2: "3️⃣", + 3: "4️⃣", + 4: "5️⃣", + 5: "6️⃣", + 6: "7️⃣", + 7: "8️⃣", + 8: "9️⃣", + 9: "🔟", + } + + async def on_submit(self, interaction: discord.Interaction) -> None: + message = await self.var.followup.fetch_message(self.var.message.id) + + # Determine the emoji to use based on the number of options + num = str(message.embeds[0].description).count("\n\n") + message.embeds[0].description += f"\n\n{str(self.reactions[num])} {self.name}" + + await message.edit(embed=message.embeds[0]) + await interaction.response.defer() + + +class Buttons(ui.View): + def __init__(self, *, timeout=180): + super().__init__(timeout=timeout) + + @property + def reactions(self): + return { + 0: "1️⃣", + 1: "2️⃣", + 2: "3️⃣", + 3: "4️⃣", + 4: "5️⃣", + 5: "6️⃣", + 6: "7️⃣", + 7: "8️⃣", + 8: "9️⃣", + 9: "🔟", + } + + @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕") + async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): + # Count the number of options + num = str(interaction.message.embeds[0].description).count("\n\n") + # If there are more than 10 options, return + if num >= 10: + return await interaction.response.send_message( + "You can't make a poll with more than 10 choices", ephemeral=True + ) + + modal = PollModal(var=interaction) + await interaction.response.send_modal(modal) + + @discord.ui.button(label="Remove Choice", style=discord.ButtonStyle.gray, emoji="➖") + async def remove_choice(self, interaction: discord.Interaction, _button: ui.Button): + embed = interaction.message.embeds[0] + + # If there are no options, return + if str(embed.description).count("\n\n") == 0: + return await interaction.response.send_message( + "You can't remove a choice from a poll with no choices", ephemeral=True + ) + + # Remove the last option + embed.description = "\n\n".join(embed.description.split("\n\n")[:-1]) + await interaction.response.edit_message(embed=embed) + + @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.gray, emoji="📝") + async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): + embed = interaction.message.embeds[0] + + # If there are less than 2 options, return + if str(embed.description).count("\n\n") < 2: + return await interaction.response.send_message("You can't create a poll with no choices", ephemeral=True) + + message = await interaction.channel.send(embed=embed) + + # Add reactions + for i in range(0, str(embed.description).count("\n\n")): + await message.add_reaction(self.reactions[i]) + + +class Polls(commands.GroupCog, group_name="poll"): + def __init__(self, bot: commands.AutoShardedBot): + self.__bot = bot + + @property + def reactions(self): + return { + 1: "1️⃣", + 2: "2️⃣", + 3: "3️⃣", + 4: "4️⃣", + 5: "5️⃣", + 6: "6️⃣", + 7: "7️⃣", + 8: "8️⃣", + 9: "9️⃣", + 10: "🔟", + } + + def poll_check(self, message: discord.Message): + try: + embed = message.embeds[0] + except Exception: + return False + if str(embed.footer.text).count("Poll by") == 1: + return message.author == self.__bot.user + return False + + @app_commands.command() + @app_commands.checks.cooldown(1, 10) + async def new(self, interaction: core.InteractionType, desc: str): + """Create a new poll""" + + embed = discord.Embed( + description=f"**{desc}**\n\n", + timestamp=discord.utils.utcnow(), + color=discord.colour.Color.gold(), + ) + embed.set_footer(text=f"Poll by {str(interaction.user.display_name)}") + await interaction.response.send_message(embed=embed, ephemeral=True, view=Buttons()) + + @app_commands.command() + async def show(self, interaction: core.InteractionType, message: str, ephemeral: bool = True): + """Show a poll result""" + try: + *_, channel_id, msg_id = message.split("/") + + try: + channel = self.__bot.get_channel(int(channel_id)) + message = await channel.fetch_message(int(msg_id)) + except Exception: + return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + except Exception: + try: + message = await interaction.channel.fetch_message(int(message)) + except Exception: + return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + + if self.poll_check(message): + poll_embed = message.embeds[0] + reactions = message.reactions + reactions_total = sum( + [reaction.count - 1 if str(reaction.emoji) in self.reactions.values() else 0 for reaction in reactions] + ) + + options = list( + map( + lambda o: " ".join(o.split()[1:]), + poll_embed.description.split("1️")[1].split("\n\n"), + ) + ) + desc = poll_embed.description.split("1️")[0] + + embed = discord.Embed( + description=desc, + timestamp=poll_embed.timestamp, + color=discord.Color.gold(), + ) + + for i, option in enumerate(options): + reaction_count = reactions[i].count - 1 + indicator = "░" * 20 + if reactions_total != 0: + indicator = "█" * int(((reaction_count / reactions_total) * 100) / 5) + "░" * int( + (((reactions_total - reaction_count) / reactions_total) * 100) / 5 + ) + + embed.add_field( + name=option, + value=f"{indicator} {int((reaction_count / (reactions_total or 1)*100))}%" + f" (**{reaction_count} votes**)", + inline=False, + ) + + embed.set_footer(text="Poll Result") + return await interaction.response.send_message(embed=embed, ephemeral=ephemeral) + + return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + + +async def setup(bot: commands.Bot): + await bot.add_cog(Polls(bot=bot)) diff --git a/bot/extensions/poll/events.py b/bot/extensions/poll/events.py new file mode 100644 index 00000000..9ecc98c0 --- /dev/null +++ b/bot/extensions/poll/events.py @@ -0,0 +1,34 @@ +import discord +from discord.ext import commands + +from bot import core + + +class PollsEvents(commands.Cog): + """Events for polls in discord.""" + + def __init__(self, bot: core.DiscordBot): + self.bot = bot + + @commands.Cog.listener() + async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): + channel: discord.TextChannel = self.bot.get_channel(payload.channel_id) + message: discord.Message = await channel.fetch_message(payload.message_id) + + if payload.user_id == self.bot.user.id: + return + + if not self.poll_check(message): + return + + emojis = list(self.reactions.values()) + if str(payload.emoji) not in emojis: + return + + for reaction in message.reactions: + if str(reaction) not in emojis: + return + + if str(reaction.emoji) != str(payload.emoji): + user = self.bot.get_user(payload.user_id) + await message.remove_reaction(reaction.emoji, user) diff --git a/cli.py b/cli.py index f90ae121..6878c734 100644 --- a/cli.py +++ b/cli.py @@ -137,10 +137,10 @@ async def main(ctx): "bot.extensions.tags", "bot.extensions.levelling", "bot.extensions.persistent_roles", + "bot.extensions.poll", "bot.cogs._help", "bot.cogs.clashofcode", "bot.cogs.roles", - "bot.cogs.poll", ) intents = discord.Intents.all() From ff933c8b7470a11367864afe0c5e3f112fb9ff93 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 08:27:12 +0300 Subject: [PATCH 03/32] Migrated poll cog to new extension format --- bot/cogs/poll.py | 151 -------------------- bot/extensions/levelling/commands.py | 7 +- bot/extensions/poll/__init__.py | 9 ++ bot/extensions/poll/commands.py | 203 +++++++++++++++++++++++++++ bot/extensions/poll/events.py | 34 +++++ cli.py | 2 +- 6 files changed, 249 insertions(+), 157 deletions(-) delete mode 100644 bot/cogs/poll.py create mode 100644 bot/extensions/poll/__init__.py create mode 100644 bot/extensions/poll/commands.py create mode 100644 bot/extensions/poll/events.py diff --git a/bot/cogs/poll.py b/bot/cogs/poll.py deleted file mode 100644 index 95feb86c..00000000 --- a/bot/cogs/poll.py +++ /dev/null @@ -1,151 +0,0 @@ -import discord -from discord.ext import commands - - -class Polls(commands.Cog): - def __init__(self, bot: commands.AutoShardedBot): - self.__bot = bot - - @property - def reactions(self): - return { - 1: "1️⃣", - 2: "2️⃣", - 3: "3️⃣", - 4: "4️⃣", - 5: "5️⃣", - 6: "6️⃣", - 7: "7️⃣", - 8: "8️⃣", - 9: "9️⃣", - 10: "🔟", - } - - def poll_check(self, message: discord.Message): - try: - embed = message.embeds[0] - except Exception: - return False - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.__bot.user - return False - - @commands.Cog.listener() - async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): - channel: discord.TextChannel = self.__bot.get_channel(payload.channel_id) - message: discord.Message = await channel.fetch_message(payload.message_id) - - if payload.user_id == self.__bot.user.id: - return - - if not self.poll_check(message): - return - - emojis = list(self.reactions.values()) - if str(payload.emoji) not in emojis: - return - - for reaction in message.reactions: - if str(reaction) not in emojis: - return - - if str(reaction.emoji) != str(payload.emoji): - user = self.__bot.get_user(payload.user_id) - await message.remove_reaction(reaction.emoji, user) - - @commands.group() - async def poll(self, ctx): - """Polls""" - if ctx.invoked_subcommand is None: - return await ctx.send_help(self.__bot.get_command("poll")) - - @poll.command() - @commands.cooldown(1, 10, commands.BucketType.channel) - async def new(self, ctx, desc: str, *choices): - """Create a new poll""" - await ctx.message.delete() - - if len(choices) < 2: - ctx.command.reset_cooldown(ctx) - if len(choices) == 1: - return await ctx.send("Can't make a poll with only one choice") - return await ctx.send("You have to enter two or more choices to make a poll") - - if len(choices) > 10: - ctx.command.reset_cooldown(ctx) - return await ctx.send("You can't make a poll with more than 10 choices") - - embed = discord.Embed( - description=f"**{desc}**\n\n" - + "\n\n".join(f"{str(self.reactions[i])} {choice}" for i, choice in enumerate(choices, 1)), - timestamp=discord.utils.utcnow(), - color=discord.colour.Color.gold(), - ) - embed.set_footer(text=f"Poll by {str(ctx.author)}") - msg = await ctx.send(embed=embed) - for i in range(1, len(choices) + 1): - await msg.add_reaction(self.reactions[i]) - - @poll.command() - async def show(self, ctx, message: str): - """Show a poll result""" - await ctx.message.delete() - - try: - *_, channel_id, msg_id = message.split("/") - - try: - channel = self.__bot.get_channel(int(channel_id)) - message = await channel.fetch_message(int(msg_id)) - except Exception: - return await ctx.send("Please provide the message ID/link for a valid poll") - except Exception: - try: - message = await ctx.channel.fetch_message(message) - except Exception: - return await ctx.send("Please provide the message ID/link for a valid poll") - - if self.poll_check(message): - poll_embed = message.embeds[0] - reactions = message.reactions - reactions_total = sum( - [reaction.count - 1 if str(reaction.emoji) in self.reactions.values() else 0 for reaction in reactions] - ) - - options = list( - map( - lambda o: " ".join(o.split()[1:]), - poll_embed.description.split("1️")[1].split("\n\n"), - ) - ) - desc = poll_embed.description.split("1️")[0] - - embed = discord.Embed( - description=desc, - timestamp=poll_embed.timestamp, - color=discord.Color.gold(), - ) - - for i, option in enumerate(options): - reaction_count = reactions[i].count - 1 - indicator = "░" * 20 - if reactions_total != 0: - indicator = "█" * int(((reaction_count / reactions_total) * 100) / 5) + "░" * int( - (((reactions_total - reaction_count) / reactions_total) * 100) / 5 - ) - - embed.add_field( - name=option, - value=f"{indicator} {int((reaction_count / (reactions_total or 1)*100))}%" - f" (**{reaction_count} votes**)", - inline=False, - ) - - embed.set_footer(text="Poll Result") - return await ctx.send(embed=embed) - - return await ctx.send("Please provide the message ID/link for a valid poll") - - -async def setup(bot): - await bot.add_cog(Polls(bot)) diff --git a/bot/extensions/levelling/commands.py b/bot/extensions/levelling/commands.py index eae67cb3..60d0e0be 100644 --- a/bot/extensions/levelling/commands.py +++ b/bot/extensions/levelling/commands.py @@ -230,14 +230,13 @@ def convert_int(integer): xp_offset_x -= xp_text_size[2] - xp_text_size[0] draw.text((xp_offset_x, xp_offset_y), text, font=self.small_font, fill="#fff") - if len(username) >= 15: + if len(username) >= 18: # Truncating the name username = username[:15] + "..." text_bbox = draw.textbbox((0, 0), username, font=self.medium_font) - text_offset_x = bar_offset_x - 10 text_offset_y = bar_offset_y - (text_bbox[3] - text_bbox[1]) - 20 - draw.text((text_offset_x, text_offset_y), username, font=self.medium_font, fill="#fff") + draw.text((bar_offset_x, text_offset_y), username, font=self.medium_font, fill="#fff") # create copy of background background = self.background.copy() @@ -274,8 +273,6 @@ async def rank(self, interaction: core.InteractionType, member: discord.Member = record = await LevellingUser.pool.fetchrow(query, interaction.guild.id, member.id) - log.info(record) - if record.total_xp is None: if member.id == interaction.user.id: return await interaction.response.send_message( diff --git a/bot/extensions/poll/__init__.py b/bot/extensions/poll/__init__.py new file mode 100644 index 00000000..998921e0 --- /dev/null +++ b/bot/extensions/poll/__init__.py @@ -0,0 +1,9 @@ +from bot.core import DiscordBot + +from .commands import Polls +from .events import PollsEvents + + +async def setup(bot: DiscordBot) -> None: + await bot.add_cog(Polls(bot=bot)) + await bot.add_cog(PollsEvents(bot=bot)) diff --git a/bot/extensions/poll/commands.py b/bot/extensions/poll/commands.py new file mode 100644 index 00000000..6dc21729 --- /dev/null +++ b/bot/extensions/poll/commands.py @@ -0,0 +1,203 @@ +import discord +from discord import app_commands, ui +from discord.ext import commands + +from bot import core + + +class PollModal(ui.Modal): + name = ui.TextInput(label="Option name", placeholder="Enter poll option name", max_length=50, required=True) + + def __init__(self, var: discord.Interaction): + self.var = var + super().__init__(title="Poll options") + + @property + def reactions(self): + return { + 0: "1️⃣", + 1: "2️⃣", + 2: "3️⃣", + 3: "4️⃣", + 4: "5️⃣", + 5: "6️⃣", + 6: "7️⃣", + 7: "8️⃣", + 8: "9️⃣", + 9: "🔟", + } + + async def on_submit(self, interaction: discord.Interaction) -> None: + message = await self.var.followup.fetch_message(self.var.message.id) + + # Determine the emoji to use based on the number of options + num = str(message.embeds[0].description).count("\n\n") + message.embeds[0].description += f"\n\n{str(self.reactions[num])} {self.name}" + + await message.edit(embed=message.embeds[0]) + await interaction.response.defer() + + +class Buttons(ui.View): + def __init__(self, *, timeout=180): + super().__init__(timeout=timeout) + + @property + def reactions(self): + return { + 0: "1️⃣", + 1: "2️⃣", + 2: "3️⃣", + 3: "4️⃣", + 4: "5️⃣", + 5: "6️⃣", + 6: "7️⃣", + 7: "8️⃣", + 8: "9️⃣", + 9: "🔟", + } + + @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕") + async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): + # Count the number of options + num = str(interaction.message.embeds[0].description).count("\n\n") + # If there are more than 10 options, return + if num >= 10: + return await interaction.response.send_message( + "You can't make a poll with more than 10 choices", ephemeral=True + ) + + modal = PollModal(var=interaction) + await interaction.response.send_modal(modal) + + @discord.ui.button(label="Remove Choice", style=discord.ButtonStyle.gray, emoji="➖") + async def remove_choice(self, interaction: discord.Interaction, _button: ui.Button): + embed = interaction.message.embeds[0] + + # If there are no options, return + if str(embed.description).count("\n\n") == 0: + return await interaction.response.send_message( + "You can't remove a choice from a poll with no choices", ephemeral=True + ) + + # Remove the last option + embed.description = "\n\n".join(embed.description.split("\n\n")[:-1]) + await interaction.response.edit_message(embed=embed) + + @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.gray, emoji="📝") + async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): + embed = interaction.message.embeds[0] + + # If there are less than 2 options, return + if str(embed.description).count("\n\n") < 2: + return await interaction.response.send_message("You can't create a poll with no choices", ephemeral=True) + + message = await interaction.channel.send(embed=embed) + + # Add reactions + for i in range(0, str(embed.description).count("\n\n")): + await message.add_reaction(self.reactions[i]) + + +class Polls(commands.GroupCog, group_name="poll"): + def __init__(self, bot: commands.AutoShardedBot): + self.__bot = bot + + @property + def reactions(self): + return { + 1: "1️⃣", + 2: "2️⃣", + 3: "3️⃣", + 4: "4️⃣", + 5: "5️⃣", + 6: "6️⃣", + 7: "7️⃣", + 8: "8️⃣", + 9: "9️⃣", + 10: "🔟", + } + + def poll_check(self, message: discord.Message): + try: + embed = message.embeds[0] + except Exception: + return False + if str(embed.footer.text).count("Poll by") == 1: + return message.author == self.__bot.user + return False + + @app_commands.command() + @app_commands.checks.cooldown(1, 10) + async def new(self, interaction: core.InteractionType, desc: str): + """Create a new poll""" + + embed = discord.Embed( + description=f"**{desc}**\n\n", + timestamp=discord.utils.utcnow(), + color=discord.colour.Color.gold(), + ) + embed.set_footer(text=f"Poll by {str(interaction.user.display_name)}") + await interaction.response.send_message(embed=embed, ephemeral=True, view=Buttons()) + + @app_commands.command() + async def show(self, interaction: core.InteractionType, message: str, ephemeral: bool = True): + """Show a poll result""" + try: + *_, channel_id, msg_id = message.split("/") + + try: + channel = self.__bot.get_channel(int(channel_id)) + message = await channel.fetch_message(int(msg_id)) + except Exception: + return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + except Exception: + try: + message = await interaction.channel.fetch_message(int(message)) + except Exception: + return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + + if self.poll_check(message): + poll_embed = message.embeds[0] + reactions = message.reactions + reactions_total = sum( + [reaction.count - 1 if str(reaction.emoji) in self.reactions.values() else 0 for reaction in reactions] + ) + + options = list( + map( + lambda o: " ".join(o.split()[1:]), + poll_embed.description.split("1️")[1].split("\n\n"), + ) + ) + desc = poll_embed.description.split("1️")[0] + + embed = discord.Embed( + description=desc, + timestamp=poll_embed.timestamp, + color=discord.Color.gold(), + ) + + for i, option in enumerate(options): + reaction_count = reactions[i].count - 1 + indicator = "░" * 20 + if reactions_total != 0: + indicator = "█" * int(((reaction_count / reactions_total) * 100) / 5) + "░" * int( + (((reactions_total - reaction_count) / reactions_total) * 100) / 5 + ) + + embed.add_field( + name=option, + value=f"{indicator} {int((reaction_count / (reactions_total or 1)*100))}%" + f" (**{reaction_count} votes**)", + inline=False, + ) + + embed.set_footer(text="Poll Result") + return await interaction.response.send_message(embed=embed, ephemeral=ephemeral) + + return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + + +async def setup(bot: commands.Bot): + await bot.add_cog(Polls(bot=bot)) diff --git a/bot/extensions/poll/events.py b/bot/extensions/poll/events.py new file mode 100644 index 00000000..9ecc98c0 --- /dev/null +++ b/bot/extensions/poll/events.py @@ -0,0 +1,34 @@ +import discord +from discord.ext import commands + +from bot import core + + +class PollsEvents(commands.Cog): + """Events for polls in discord.""" + + def __init__(self, bot: core.DiscordBot): + self.bot = bot + + @commands.Cog.listener() + async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): + channel: discord.TextChannel = self.bot.get_channel(payload.channel_id) + message: discord.Message = await channel.fetch_message(payload.message_id) + + if payload.user_id == self.bot.user.id: + return + + if not self.poll_check(message): + return + + emojis = list(self.reactions.values()) + if str(payload.emoji) not in emojis: + return + + for reaction in message.reactions: + if str(reaction) not in emojis: + return + + if str(reaction.emoji) != str(payload.emoji): + user = self.bot.get_user(payload.user_id) + await message.remove_reaction(reaction.emoji, user) diff --git a/cli.py b/cli.py index f90ae121..6878c734 100644 --- a/cli.py +++ b/cli.py @@ -137,10 +137,10 @@ async def main(ctx): "bot.extensions.tags", "bot.extensions.levelling", "bot.extensions.persistent_roles", + "bot.extensions.poll", "bot.cogs._help", "bot.cogs.clashofcode", "bot.cogs.roles", - "bot.cogs.poll", ) intents = discord.Intents.all() From 8d9c5e204e1eb03686e5822cb637ad1f70146c18 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 09:01:34 +0300 Subject: [PATCH 04/32] Fixed events and delete message after poll creation --- bot/extensions/poll/commands.py | 14 +++++--------- bot/extensions/poll/events.py | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/bot/extensions/poll/commands.py b/bot/extensions/poll/commands.py index 6dc21729..a335bca4 100644 --- a/bot/extensions/poll/commands.py +++ b/bot/extensions/poll/commands.py @@ -86,6 +86,7 @@ async def remove_choice(self, interaction: discord.Interaction, _button: ui.Butt @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.gray, emoji="📝") async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): + # Get the embed embed = interaction.message.embeds[0] # If there are less than 2 options, return @@ -98,6 +99,10 @@ async def create_poll(self, interaction: discord.Interaction, _button: ui.Button for i in range(0, str(embed.description).count("\n\n")): await message.add_reaction(self.reactions[i]) + # Delete the original message + await interaction.response.defer() + await interaction.delete_original_response() + class Polls(commands.GroupCog, group_name="poll"): def __init__(self, bot: commands.AutoShardedBot): @@ -118,15 +123,6 @@ def reactions(self): 10: "🔟", } - def poll_check(self, message: discord.Message): - try: - embed = message.embeds[0] - except Exception: - return False - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.__bot.user - return False - @app_commands.command() @app_commands.checks.cooldown(1, 10) async def new(self, interaction: core.InteractionType, desc: str): diff --git a/bot/extensions/poll/events.py b/bot/extensions/poll/events.py index 9ecc98c0..718e1e3b 100644 --- a/bot/extensions/poll/events.py +++ b/bot/extensions/poll/events.py @@ -9,6 +9,16 @@ class PollsEvents(commands.Cog): def __init__(self, bot: core.DiscordBot): self.bot = bot + self.emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "9️⃣", "🔟"] + + def poll_check(self, message: discord.Message): + try: + embed = message.embeds[0] + except Exception: + return False + if str(embed.footer.text).count("Poll by") == 1: + return message.author == self.bot.user + return False @commands.Cog.listener() async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): @@ -21,12 +31,11 @@ async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): if not self.poll_check(message): return - emojis = list(self.reactions.values()) - if str(payload.emoji) not in emojis: + if str(payload.emoji) not in self.emojis: return for reaction in message.reactions: - if str(reaction) not in emojis: + if str(reaction) not in self.emojis: return if str(reaction.emoji) != str(payload.emoji): From 003e2c04126cc038b5c5a166e42f82037267dc7b Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 09:11:12 +0300 Subject: [PATCH 05/32] Update commands.py --- bot/extensions/poll/commands.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bot/extensions/poll/commands.py b/bot/extensions/poll/commands.py index a335bca4..2f11fc4b 100644 --- a/bot/extensions/poll/commands.py +++ b/bot/extensions/poll/commands.py @@ -86,12 +86,13 @@ async def remove_choice(self, interaction: discord.Interaction, _button: ui.Butt @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.gray, emoji="📝") async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): - # Get the embed embed = interaction.message.embeds[0] # If there are less than 2 options, return if str(embed.description).count("\n\n") < 2: - return await interaction.response.send_message("You can't create a poll with no choices", ephemeral=True) + return await interaction.response.send_message( + "You can't create a poll with less than 2 choices", ephemeral=True + ) message = await interaction.channel.send(embed=embed) From 4de940b7d8de937af050d3940730fdc7ade9acb7 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 16:02:44 +0300 Subject: [PATCH 06/32] Rename extension to polls --- bot/extensions/{poll => polls}/__init__.py | 0 bot/extensions/{poll => polls}/commands.py | 0 bot/extensions/{poll => polls}/events.py | 0 cli.py | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename bot/extensions/{poll => polls}/__init__.py (100%) rename bot/extensions/{poll => polls}/commands.py (100%) rename bot/extensions/{poll => polls}/events.py (100%) diff --git a/bot/extensions/poll/__init__.py b/bot/extensions/polls/__init__.py similarity index 100% rename from bot/extensions/poll/__init__.py rename to bot/extensions/polls/__init__.py diff --git a/bot/extensions/poll/commands.py b/bot/extensions/polls/commands.py similarity index 100% rename from bot/extensions/poll/commands.py rename to bot/extensions/polls/commands.py diff --git a/bot/extensions/poll/events.py b/bot/extensions/polls/events.py similarity index 100% rename from bot/extensions/poll/events.py rename to bot/extensions/polls/events.py diff --git a/cli.py b/cli.py index 6878c734..7908687f 100644 --- a/cli.py +++ b/cli.py @@ -137,7 +137,7 @@ async def main(ctx): "bot.extensions.tags", "bot.extensions.levelling", "bot.extensions.persistent_roles", - "bot.extensions.poll", + "bot.extensions.polls", "bot.cogs._help", "bot.cogs.clashofcode", "bot.cogs.roles", From a12979f5c39c348ba462a149243e3bc428a169aa Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 16:10:43 +0300 Subject: [PATCH 07/32] Added views.py --- bot/extensions/polls/commands.py | 103 +------------------------------ bot/extensions/polls/views.py | 102 ++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 101 deletions(-) create mode 100644 bot/extensions/polls/views.py diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index 2f11fc4b..b913fbdd 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -3,106 +3,7 @@ from discord.ext import commands from bot import core - - -class PollModal(ui.Modal): - name = ui.TextInput(label="Option name", placeholder="Enter poll option name", max_length=50, required=True) - - def __init__(self, var: discord.Interaction): - self.var = var - super().__init__(title="Poll options") - - @property - def reactions(self): - return { - 0: "1️⃣", - 1: "2️⃣", - 2: "3️⃣", - 3: "4️⃣", - 4: "5️⃣", - 5: "6️⃣", - 6: "7️⃣", - 7: "8️⃣", - 8: "9️⃣", - 9: "🔟", - } - - async def on_submit(self, interaction: discord.Interaction) -> None: - message = await self.var.followup.fetch_message(self.var.message.id) - - # Determine the emoji to use based on the number of options - num = str(message.embeds[0].description).count("\n\n") - message.embeds[0].description += f"\n\n{str(self.reactions[num])} {self.name}" - - await message.edit(embed=message.embeds[0]) - await interaction.response.defer() - - -class Buttons(ui.View): - def __init__(self, *, timeout=180): - super().__init__(timeout=timeout) - - @property - def reactions(self): - return { - 0: "1️⃣", - 1: "2️⃣", - 2: "3️⃣", - 3: "4️⃣", - 4: "5️⃣", - 5: "6️⃣", - 6: "7️⃣", - 7: "8️⃣", - 8: "9️⃣", - 9: "🔟", - } - - @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕") - async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): - # Count the number of options - num = str(interaction.message.embeds[0].description).count("\n\n") - # If there are more than 10 options, return - if num >= 10: - return await interaction.response.send_message( - "You can't make a poll with more than 10 choices", ephemeral=True - ) - - modal = PollModal(var=interaction) - await interaction.response.send_modal(modal) - - @discord.ui.button(label="Remove Choice", style=discord.ButtonStyle.gray, emoji="➖") - async def remove_choice(self, interaction: discord.Interaction, _button: ui.Button): - embed = interaction.message.embeds[0] - - # If there are no options, return - if str(embed.description).count("\n\n") == 0: - return await interaction.response.send_message( - "You can't remove a choice from a poll with no choices", ephemeral=True - ) - - # Remove the last option - embed.description = "\n\n".join(embed.description.split("\n\n")[:-1]) - await interaction.response.edit_message(embed=embed) - - @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.gray, emoji="📝") - async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): - embed = interaction.message.embeds[0] - - # If there are less than 2 options, return - if str(embed.description).count("\n\n") < 2: - return await interaction.response.send_message( - "You can't create a poll with less than 2 choices", ephemeral=True - ) - - message = await interaction.channel.send(embed=embed) - - # Add reactions - for i in range(0, str(embed.description).count("\n\n")): - await message.add_reaction(self.reactions[i]) - - # Delete the original message - await interaction.response.defer() - await interaction.delete_original_response() +from bot.extensions.polls.views import PollButtons class Polls(commands.GroupCog, group_name="poll"): @@ -135,7 +36,7 @@ async def new(self, interaction: core.InteractionType, desc: str): color=discord.colour.Color.gold(), ) embed.set_footer(text=f"Poll by {str(interaction.user.display_name)}") - await interaction.response.send_message(embed=embed, ephemeral=True, view=Buttons()) + await interaction.response.send_message(embed=embed, ephemeral=True, view=PollButtons()) @app_commands.command() async def show(self, interaction: core.InteractionType, message: str, ephemeral: bool = True): diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py new file mode 100644 index 00000000..321084a6 --- /dev/null +++ b/bot/extensions/polls/views.py @@ -0,0 +1,102 @@ +import discord +from discord import ui + + +class PollModal(ui.Modal): + name = ui.TextInput(label="Option name", placeholder="Enter poll option name", max_length=50, required=True) + + def __init__(self, var: discord.Interaction): + self.var = var + super().__init__(title="Poll options") + + @property + def reactions(self): + return { + 0: "1️⃣", + 1: "2️⃣", + 2: "3️⃣", + 3: "4️⃣", + 4: "5️⃣", + 5: "6️⃣", + 6: "7️⃣", + 7: "8️⃣", + 8: "9️⃣", + 9: "🔟", + } + + async def on_submit(self, interaction: discord.Interaction) -> None: + message = await self.var.followup.fetch_message(self.var.message.id) + + # Determine the emoji to use based on the number of options + num = str(message.embeds[0].description).count("\n\n") + message.embeds[0].description += f"\n\n{str(self.reactions[num])} {self.name}" + + await message.edit(embed=message.embeds[0]) + await interaction.response.defer() + + +class PollButtons(ui.View): + def __init__(self, *, timeout=180): + super().__init__(timeout=timeout) + + @property + def reactions(self): + return { + 0: "1️⃣", + 1: "2️⃣", + 2: "3️⃣", + 3: "4️⃣", + 4: "5️⃣", + 5: "6️⃣", + 6: "7️⃣", + 7: "8️⃣", + 8: "9️⃣", + 9: "🔟", + } + + @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕") + async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): + # Count the number of options + num = str(interaction.message.embeds[0].description).count("\n\n") + # If there are more than 10 options, return + if num >= 10: + return await interaction.response.send_message( + "You can't make a poll with more than 10 choices", ephemeral=True + ) + + modal = PollModal(var=interaction) + await interaction.response.send_modal(modal) + + @discord.ui.button(label="Remove Choice", style=discord.ButtonStyle.gray, emoji="➖") + async def remove_choice(self, interaction: discord.Interaction, _button: ui.Button): + embed = interaction.message.embeds[0] + + # If there are no options, return + if str(embed.description).count("\n\n") == 0: + return await interaction.response.send_message( + "You can't remove a choice from a poll with no choices", ephemeral=True + ) + + # Remove the last option + embed.description = "\n\n".join(embed.description.split("\n\n")[:-1]) + await interaction.response.edit_message(embed=embed) + + @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.gray, emoji="📝") + async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): + embed = interaction.message.embeds[0] + + # If there are less than 2 options, return + if str(embed.description).count("\n\n") < 2: + return await interaction.response.send_message( + "You can't create a poll with less than 2 choices", ephemeral=True + ) + + message = await interaction.channel.send(embed=embed) + + # Add reactions + for i in range(0, str(embed.description).count("\n\n")): + await message.add_reaction(self.reactions[i]) + + # Delete the original message + await interaction.response.defer() + await interaction.delete_original_response() \ No newline at end of file From 0062559e2c2682563a35f21e9d854b71d868c6d5 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 16:12:08 +0300 Subject: [PATCH 08/32] linting tools --- bot/extensions/polls/commands.py | 2 +- bot/extensions/polls/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index b913fbdd..f2738d58 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -1,5 +1,5 @@ import discord -from discord import app_commands, ui +from discord import app_commands from discord.ext import commands from bot import core diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 321084a6..96ee2dff 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -99,4 +99,4 @@ async def create_poll(self, interaction: discord.Interaction, _button: ui.Button # Delete the original message await interaction.response.defer() - await interaction.delete_original_response() \ No newline at end of file + await interaction.delete_original_response() From de91f3b77b1d0b03e75e9af3fc46576381b92b4f Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:15:12 +0300 Subject: [PATCH 09/32] Added poll_check to commands.py --- bot/extensions/polls/commands.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index f2738d58..8da633ae 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -24,6 +24,15 @@ def reactions(self): 9: "9️⃣", 10: "🔟", } + + def poll_check(self, message: discord.Message): + try: + embed = message.embeds[0] + except Exception: + return False + if str(embed.footer.text).count("Poll by") == 1: + return message.author == self.bot.user + return False @app_commands.command() @app_commands.checks.cooldown(1, 10) From 31991c24431ef8a24ab60fc9d9be837c547fd19c Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:17:18 +0300 Subject: [PATCH 10/32] slight fix --- bot/extensions/polls/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index 8da633ae..16170edc 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -31,7 +31,7 @@ def poll_check(self, message: discord.Message): except Exception: return False if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.bot.user + return message.author == self.__bot.user return False @app_commands.command() From 62bfacb25673a45be0fa9d8765bc42484769e8de Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:33:44 +0300 Subject: [PATCH 11/32] Maded erros ephemeral, renamed variable and maintained wording consistency --- bot/extensions/polls/commands.py | 10 +++++----- bot/extensions/polls/views.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index 16170edc..7b294fb4 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -36,11 +36,11 @@ def poll_check(self, message: discord.Message): @app_commands.command() @app_commands.checks.cooldown(1, 10) - async def new(self, interaction: core.InteractionType, desc: str): + async def new(self, interaction: core.InteractionType, title: str): """Create a new poll""" embed = discord.Embed( - description=f"**{desc}**\n\n", + description=f"**{title}**\n\n", timestamp=discord.utils.utcnow(), color=discord.colour.Color.gold(), ) @@ -57,12 +57,12 @@ async def show(self, interaction: core.InteractionType, message: str, ephemeral: channel = self.__bot.get_channel(int(channel_id)) message = await channel.fetch_message(int(msg_id)) except Exception: - return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + return await interaction.response.send_message("Please provide the message ID/link for a valid poll", ephemeral=True) except Exception: try: message = await interaction.channel.fetch_message(int(message)) except Exception: - return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + return await interaction.response.send_message("Please provide the message ID/link for a valid poll", ephemeral=True) if self.poll_check(message): poll_embed = message.embeds[0] @@ -103,7 +103,7 @@ async def show(self, interaction: core.InteractionType, message: str, ephemeral: embed.set_footer(text="Poll Result") return await interaction.response.send_message(embed=embed, ephemeral=ephemeral) - return await interaction.response.send_message("Please provide the message ID/link for a valid poll") + return await interaction.response.send_message("Please provide the message ID/link for a valid poll", ephemeral=True) async def setup(bot: commands.Bot): diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 96ee2dff..4bf48476 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -3,7 +3,7 @@ class PollModal(ui.Modal): - name = ui.TextInput(label="Option name", placeholder="Enter poll option name", max_length=50, required=True) + name = ui.TextInput(label="Choice name", placeholder="Enter poll choice", max_length=50, required=True) def __init__(self, var: discord.Interaction): self.var = var From 86fc558934ab5625a178afe0f97f97fe37f6a87b Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:35:40 +0300 Subject: [PATCH 12/32] linting tools --- bot/extensions/polls/commands.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index 7b294fb4..80545d53 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -24,7 +24,7 @@ def reactions(self): 9: "9️⃣", 10: "🔟", } - + def poll_check(self, message: discord.Message): try: embed = message.embeds[0] @@ -57,12 +57,16 @@ async def show(self, interaction: core.InteractionType, message: str, ephemeral: channel = self.__bot.get_channel(int(channel_id)) message = await channel.fetch_message(int(msg_id)) except Exception: - return await interaction.response.send_message("Please provide the message ID/link for a valid poll", ephemeral=True) + return await interaction.response.send_message( + "Please provide the message ID/link for a valid poll", ephemeral=True + ) except Exception: try: message = await interaction.channel.fetch_message(int(message)) except Exception: - return await interaction.response.send_message("Please provide the message ID/link for a valid poll", ephemeral=True) + return await interaction.response.send_message( + "Please provide the message ID/link for a valid poll", ephemeral=True + ) if self.poll_check(message): poll_embed = message.embeds[0] @@ -103,7 +107,9 @@ async def show(self, interaction: core.InteractionType, message: str, ephemeral: embed.set_footer(text="Poll Result") return await interaction.response.send_message(embed=embed, ephemeral=ephemeral) - return await interaction.response.send_message("Please provide the message ID/link for a valid poll", ephemeral=True) + return await interaction.response.send_message( + "Please provide the message ID/link for a valid poll", ephemeral=True + ) async def setup(bot: commands.Bot): From 81d26f284781f642a62140cc82718a69eff6350e Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Fri, 27 Oct 2023 11:12:03 +0300 Subject: [PATCH 13/32] Replaced try and except mess in show poll command with MessageTransformer --- bot/extensions/polls/commands.py | 29 ++++++++--------------------- utils/transformers.py | 8 ++++++-- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index 80545d53..9c5a2fb2 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -4,6 +4,7 @@ from bot import core from bot.extensions.polls.views import PollButtons +from utils.transformers import MessageTransformer class Polls(commands.GroupCog, group_name="poll"): @@ -48,25 +49,13 @@ async def new(self, interaction: core.InteractionType, title: str): await interaction.response.send_message(embed=embed, ephemeral=True, view=PollButtons()) @app_commands.command() - async def show(self, interaction: core.InteractionType, message: str, ephemeral: bool = True): + async def show( + self, + interaction: core.InteractionType, + message: app_commands.Transform[discord.Message, MessageTransformer], + ephemeral: bool = True, + ): """Show a poll result""" - try: - *_, channel_id, msg_id = message.split("/") - - try: - channel = self.__bot.get_channel(int(channel_id)) - message = await channel.fetch_message(int(msg_id)) - except Exception: - return await interaction.response.send_message( - "Please provide the message ID/link for a valid poll", ephemeral=True - ) - except Exception: - try: - message = await interaction.channel.fetch_message(int(message)) - except Exception: - return await interaction.response.send_message( - "Please provide the message ID/link for a valid poll", ephemeral=True - ) if self.poll_check(message): poll_embed = message.embeds[0] @@ -107,9 +96,7 @@ async def show(self, interaction: core.InteractionType, message: str, ephemeral: embed.set_footer(text="Poll Result") return await interaction.response.send_message(embed=embed, ephemeral=ephemeral) - return await interaction.response.send_message( - "Please provide the message ID/link for a valid poll", ephemeral=True - ) + return await interaction.response.send_message("Please provide a valid poll message", ephemeral=True) async def setup(bot: commands.Bot): diff --git a/utils/transformers.py b/utils/transformers.py index b16f5350..d35661b2 100644 --- a/utils/transformers.py +++ b/utils/transformers.py @@ -13,13 +13,17 @@ async def transform(self, interaction: core.InteractionType, value: str, /): try: parts: list[str] = value.split("/") + # check that there are 2 parts + if len(parts) != 2: + return await interaction.channel.fetch_message(int(value)) + message_id = int(parts[-1]) channel_id = int(parts[-2]) channel = interaction.guild.get_channel(channel_id) return await channel.fetch_message(message_id) - except (ValueError, IndexError, AttributeError, discord.HTTPException): - await interaction.response.send_message("Sorry, I couldn't find that message...") + except (ValueError, TypeError, IndexError, AttributeError, discord.HTTPException): + await interaction.response.send_message("Sorry, I couldn't find that message...", ephemeral=True) raise IgnorableException From 5a774c7276f69e7c9596c47b342fa68a0b283390 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Fri, 27 Oct 2023 11:17:10 +0300 Subject: [PATCH 14/32] Did the suggested changes requested by Sylte --- bot/extensions/polls/__init__.py | 4 ++-- bot/extensions/polls/commands.py | 10 +++++----- bot/extensions/polls/events.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bot/extensions/polls/__init__.py b/bot/extensions/polls/__init__.py index 998921e0..24223853 100644 --- a/bot/extensions/polls/__init__.py +++ b/bot/extensions/polls/__init__.py @@ -1,9 +1,9 @@ from bot.core import DiscordBot from .commands import Polls -from .events import PollsEvents +from .events import PollEvents async def setup(bot: DiscordBot) -> None: await bot.add_cog(Polls(bot=bot)) - await bot.add_cog(PollsEvents(bot=bot)) + await bot.add_cog(PollEvents(bot=bot)) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index 9c5a2fb2..d04f96b8 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -9,7 +9,7 @@ class Polls(commands.GroupCog, group_name="poll"): def __init__(self, bot: commands.AutoShardedBot): - self.__bot = bot + self.bot = bot @property def reactions(self): @@ -32,16 +32,16 @@ def poll_check(self, message: discord.Message): except Exception: return False if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.__bot.user + return message.author == self.bot.user return False @app_commands.command() - @app_commands.checks.cooldown(1, 10) - async def new(self, interaction: core.InteractionType, title: str): + @app_commands.describe(question="Your question") + async def new(self, interaction: core.InteractionType, question: str): """Create a new poll""" embed = discord.Embed( - description=f"**{title}**\n\n", + description=f"**{question}**\n\n", timestamp=discord.utils.utcnow(), color=discord.colour.Color.gold(), ) diff --git a/bot/extensions/polls/events.py b/bot/extensions/polls/events.py index 718e1e3b..c4e945be 100644 --- a/bot/extensions/polls/events.py +++ b/bot/extensions/polls/events.py @@ -4,7 +4,7 @@ from bot import core -class PollsEvents(commands.Cog): +class PollEvents(commands.Cog): """Events for polls in discord.""" def __init__(self, bot: core.DiscordBot): From 7374de6ba428271a20596a86d845fd49db804f64 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Fri, 27 Oct 2023 23:01:19 +0300 Subject: [PATCH 15/32] Fixed IgnorableException being sent to errors webhook --- bot/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/core.py b/bot/core.py index 7b5eef2e..f3a14134 100644 --- a/bot/core.py +++ b/bot/core.py @@ -96,7 +96,8 @@ async def process_commands(self, message: discord.Message, /): async def on_app_command_error(self, interaction: "InteractionType", error: app_commands.AppCommandError): """Handle errors in app commands.""" - if isinstance(error, IgnorableException): + + if isinstance(error.__cause__, IgnorableException): return if interaction.command is None: From cb4efd65f59c19b64b1014160948e571c67b49be Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Mon, 30 Oct 2023 20:01:03 +0200 Subject: [PATCH 16/32] Did the suggested changes --- bot/extensions/polls/commands.py | 22 ++++----- bot/extensions/polls/events.py | 9 ++-- bot/extensions/polls/views.py | 76 +++++++++++++++++++++++++------- 3 files changed, 73 insertions(+), 34 deletions(-) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index d04f96b8..c20c2b6b 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -3,12 +3,12 @@ from discord.ext import commands from bot import core -from bot.extensions.polls.views import PollButtons +from bot.extensions.polls.views import CreatePollView from utils.transformers import MessageTransformer class Polls(commands.GroupCog, group_name="poll"): - def __init__(self, bot: commands.AutoShardedBot): + def __init__(self, bot: core.DiscordBot): self.bot = bot @property @@ -27,12 +27,11 @@ def reactions(self): } def poll_check(self, message: discord.Message): - try: + if len(message.embeds) != 0: embed = message.embeds[0] - except Exception: - return False - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.bot.user + if str(embed.footer.text).count("Poll by") == 1: + return message.author == self.bot.user + return False @app_commands.command() @@ -46,7 +45,7 @@ async def new(self, interaction: core.InteractionType, question: str): color=discord.colour.Color.gold(), ) embed.set_footer(text=f"Poll by {str(interaction.user.display_name)}") - await interaction.response.send_message(embed=embed, ephemeral=True, view=PollButtons()) + await interaction.response.send_message(embed=embed, ephemeral=True, view=CreatePollView()) @app_commands.command() async def show( @@ -64,12 +63,7 @@ async def show( [reaction.count - 1 if str(reaction.emoji) in self.reactions.values() else 0 for reaction in reactions] ) - options = list( - map( - lambda o: " ".join(o.split()[1:]), - poll_embed.description.split("1️")[1].split("\n\n"), - ) - ) + options = [field.name for field in poll_embed.fields] desc = poll_embed.description.split("1️")[0] embed = discord.Embed( diff --git a/bot/extensions/polls/events.py b/bot/extensions/polls/events.py index c4e945be..d08532f8 100644 --- a/bot/extensions/polls/events.py +++ b/bot/extensions/polls/events.py @@ -12,12 +12,11 @@ def __init__(self, bot: core.DiscordBot): self.emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "9️⃣", "🔟"] def poll_check(self, message: discord.Message): - try: + if len(message.embeds) != 0: embed = message.embeds[0] - except Exception: - return False - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.bot.user + if str(embed.footer.text).count("Poll by") == 1: + return message.author == self.bot.user + return False @commands.Cog.listener() diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 4bf48476..44a18504 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -3,7 +3,14 @@ class PollModal(ui.Modal): - name = ui.TextInput(label="Choice name", placeholder="Enter poll choice", max_length=50, required=True) + name = ui.TextInput(label="Choice name", placeholder="Enter poll choice", max_length=32, required=True) + description = ui.TextInput( + label="Choice description (optional)", + placeholder="Enter poll choice description", + style=discord.TextStyle.long, + max_length=512, + required=False, + ) def __init__(self, var: discord.Interaction): self.var = var @@ -27,15 +34,21 @@ def reactions(self): async def on_submit(self, interaction: discord.Interaction) -> None: message = await self.var.followup.fetch_message(self.var.message.id) - # Determine the emoji to use based on the number of options - num = str(message.embeds[0].description).count("\n\n") - message.embeds[0].description += f"\n\n{str(self.reactions[num])} {self.name}" + num = len(message.embeds[0].fields) + message.embeds[0].add_field( + name=f"{str(self.reactions[num])} {self.name}", value=self.description, inline=False + ) await message.edit(embed=message.embeds[0]) await interaction.response.defer() -class PollButtons(ui.View): +class CreatePollView(ui.View): + CREATE_CUSTOM_ID = "extensions:polls:create" + ADD_CUSTOM_ID = "extensions:polls:add" + SELECT_CUSTOM_ID = "extensions:polls:select" + DELETE_CUSTOM_ID = "extensions:polls:delete" + def __init__(self, *, timeout=180): super().__init__(timeout=timeout) @@ -54,10 +67,10 @@ def reactions(self): 9: "🔟", } - @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕") + @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕", custom_id=ADD_CUSTOM_ID) async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): # Count the number of options - num = str(interaction.message.embeds[0].description).count("\n\n") + num = len(interaction.message.embeds[0].fields) # If there are more than 10 options, return if num >= 10: return await interaction.response.send_message( @@ -67,26 +80,59 @@ async def add_choice(self, interaction: discord.Interaction, _button: ui.Button) modal = PollModal(var=interaction) await interaction.response.send_modal(modal) - @discord.ui.button(label="Remove Choice", style=discord.ButtonStyle.gray, emoji="➖") + @discord.ui.button(label="Remove Choice", style=discord.ButtonStyle.gray, emoji="➖", custom_id=DELETE_CUSTOM_ID) async def remove_choice(self, interaction: discord.Interaction, _button: ui.Button): embed = interaction.message.embeds[0] # If there are no options, return - if str(embed.description).count("\n\n") == 0: + if len(embed.fields) == 0: return await interaction.response.send_message( "You can't remove a choice from a poll with no choices", ephemeral=True ) - # Remove the last option - embed.description = "\n\n".join(embed.description.split("\n\n")[:-1]) - await interaction.response.edit_message(embed=embed) + # Edit the message to show the select menu + options = [] + for i in range(0, len(embed.fields)): + options.append( + discord.SelectOption(label=embed.fields[i].name[4:], value=str(i + 1), emoji=self.reactions[i]) + ) + + async def callback(interaction: discord.Interaction): + embed = interaction.message.embeds[0] + selected = int(interaction.data["values"][0]) + + # remove the selected option + embed.remove_field(selected - 1) + + # Reset correct emoji to each option + for x in range(0, len(embed.fields)): + embed.set_field_at( + x, + name=f"{str(self.reactions[x])} {embed.fields[x].name[4:]}", + value=embed.fields[x].value, + inline=False, + ) + + # Return old buttons + await interaction.response.defer() + await interaction.edit_original_response(embed=embed, view=self) + + select = discord.ui.Select( + placeholder="Select a choice to remove", options=options, custom_id=self.SELECT_CUSTOM_ID + ) + select.callback = callback + view = discord.ui.View() + view.add_item(select) + + await interaction.response.defer() + await interaction.edit_original_response(embed=embed, view=view) - @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.gray, emoji="📝") + @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.green, emoji="📝", custom_id=CREATE_CUSTOM_ID) async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): embed = interaction.message.embeds[0] # If there are less than 2 options, return - if str(embed.description).count("\n\n") < 2: + if len(embed.fields) < 2: return await interaction.response.send_message( "You can't create a poll with less than 2 choices", ephemeral=True ) @@ -94,7 +140,7 @@ async def create_poll(self, interaction: discord.Interaction, _button: ui.Button message = await interaction.channel.send(embed=embed) # Add reactions - for i in range(0, str(embed.description).count("\n\n")): + for i in range(0, len(embed.fields)): await message.add_reaction(self.reactions[i]) # Delete the original message From 07ce16f77bb910c08c2b9befef41f28019a46035 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Mon, 30 Oct 2023 22:22:17 +0200 Subject: [PATCH 17/32] Did the suggested changes --- bot/extensions/polls/commands.py | 70 ++++++++++++++++---------------- bot/extensions/polls/events.py | 10 ++--- bot/extensions/polls/views.py | 11 +---- 3 files changed, 41 insertions(+), 50 deletions(-) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index c20c2b6b..3c0bbb93 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -27,12 +27,12 @@ def reactions(self): } def poll_check(self, message: discord.Message): - if len(message.embeds) != 0: - embed = message.embeds[0] - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.bot.user + if not message.embeds: + return False - return False + embed = message.embeds[0] + if str(embed.footer.text).count("Poll by") == 1: + return message.author == self.bot.user @app_commands.command() @app_commands.describe(question="Your question") @@ -44,7 +44,7 @@ async def new(self, interaction: core.InteractionType, question: str): timestamp=discord.utils.utcnow(), color=discord.colour.Color.gold(), ) - embed.set_footer(text=f"Poll by {str(interaction.user.display_name)}") + embed.set_footer(text=f"Poll by {interaction.user.display_name}") await interaction.response.send_message(embed=embed, ephemeral=True, view=CreatePollView()) @app_commands.command() @@ -56,41 +56,41 @@ async def show( ): """Show a poll result""" - if self.poll_check(message): - poll_embed = message.embeds[0] - reactions = message.reactions - reactions_total = sum( - [reaction.count - 1 if str(reaction.emoji) in self.reactions.values() else 0 for reaction in reactions] - ) + if not self.poll_check(message): + return await interaction.response.send_message("Please provide a valid poll message", ephemeral=True) - options = [field.name for field in poll_embed.fields] - desc = poll_embed.description.split("1️")[0] + poll_embed = message.embeds[0] + reactions = message.reactions + reactions_total = sum( + [reaction.count - 1 if str(reaction.emoji) in self.reactions.values() else 0 for reaction in reactions] + ) - embed = discord.Embed( - description=desc, - timestamp=poll_embed.timestamp, - color=discord.Color.gold(), - ) + options = [field.name for field in poll_embed.fields] + desc = poll_embed.description.split("1️")[0] - for i, option in enumerate(options): - reaction_count = reactions[i].count - 1 - indicator = "░" * 20 - if reactions_total != 0: - indicator = "█" * int(((reaction_count / reactions_total) * 100) / 5) + "░" * int( - (((reactions_total - reaction_count) / reactions_total) * 100) / 5 - ) - - embed.add_field( - name=option, - value=f"{indicator} {int((reaction_count / (reactions_total or 1)*100))}%" - f" (**{reaction_count} votes**)", - inline=False, + embed = discord.Embed( + description=desc, + timestamp=poll_embed.timestamp, + color=discord.Color.gold(), + ) + + for i, option in enumerate(options): + reaction_count = reactions[i].count - 1 + indicator = "░" * 20 + if reactions_total != 0: + indicator = "█" * int(((reaction_count / reactions_total) * 100) / 5) + "░" * int( + (((reactions_total - reaction_count) / reactions_total) * 100) / 5 ) - embed.set_footer(text="Poll Result") - return await interaction.response.send_message(embed=embed, ephemeral=ephemeral) + embed.add_field( + name=option, + value=f"{indicator} {int((reaction_count / (reactions_total or 1)*100))}%" + f" (**{reaction_count} votes**)", + inline=False, + ) - return await interaction.response.send_message("Please provide a valid poll message", ephemeral=True) + embed.set_footer(text="Poll Result") + return await interaction.response.send_message(embed=embed, ephemeral=ephemeral) async def setup(bot: commands.Bot): diff --git a/bot/extensions/polls/events.py b/bot/extensions/polls/events.py index d08532f8..deb271fe 100644 --- a/bot/extensions/polls/events.py +++ b/bot/extensions/polls/events.py @@ -12,12 +12,12 @@ def __init__(self, bot: core.DiscordBot): self.emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "9️⃣", "🔟"] def poll_check(self, message: discord.Message): - if len(message.embeds) != 0: - embed = message.embeds[0] - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.bot.user + if not message.embeds: + return False - return False + embed = message.embeds[0] + if str(embed.footer.text).count("Poll by") == 1: + return message.author == self.bot.user @commands.Cog.listener() async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 44a18504..c09c3620 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -69,9 +69,8 @@ def reactions(self): @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕", custom_id=ADD_CUSTOM_ID) async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): - # Count the number of options num = len(interaction.message.embeds[0].fields) - # If there are more than 10 options, return + if num >= 10: return await interaction.response.send_message( "You can't make a poll with more than 10 choices", ephemeral=True @@ -84,13 +83,11 @@ async def add_choice(self, interaction: discord.Interaction, _button: ui.Button) async def remove_choice(self, interaction: discord.Interaction, _button: ui.Button): embed = interaction.message.embeds[0] - # If there are no options, return if len(embed.fields) == 0: return await interaction.response.send_message( "You can't remove a choice from a poll with no choices", ephemeral=True ) - # Edit the message to show the select menu options = [] for i in range(0, len(embed.fields)): options.append( @@ -101,10 +98,8 @@ async def callback(interaction: discord.Interaction): embed = interaction.message.embeds[0] selected = int(interaction.data["values"][0]) - # remove the selected option embed.remove_field(selected - 1) - # Reset correct emoji to each option for x in range(0, len(embed.fields)): embed.set_field_at( x, @@ -113,7 +108,6 @@ async def callback(interaction: discord.Interaction): inline=False, ) - # Return old buttons await interaction.response.defer() await interaction.edit_original_response(embed=embed, view=self) @@ -131,7 +125,6 @@ async def callback(interaction: discord.Interaction): async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): embed = interaction.message.embeds[0] - # If there are less than 2 options, return if len(embed.fields) < 2: return await interaction.response.send_message( "You can't create a poll with less than 2 choices", ephemeral=True @@ -139,10 +132,8 @@ async def create_poll(self, interaction: discord.Interaction, _button: ui.Button message = await interaction.channel.send(embed=embed) - # Add reactions for i in range(0, len(embed.fields)): await message.add_reaction(self.reactions[i]) - # Delete the original message await interaction.response.defer() await interaction.delete_original_response() From a38bc52409618eacc7fadf7ed2ebd074494a4ef0 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:56:19 +0200 Subject: [PATCH 18/32] Added utils.py file --- bot/extensions/polls/commands.py | 32 +++-------------------- bot/extensions/polls/events.py | 16 +++--------- bot/extensions/polls/utils.py | 12 +++++++++ bot/extensions/polls/views.py | 44 +++++--------------------------- 4 files changed, 26 insertions(+), 78 deletions(-) create mode 100644 bot/extensions/polls/utils.py diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index 3c0bbb93..5946e3e8 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -3,6 +3,7 @@ from discord.ext import commands from bot import core +from bot.extensions.polls.utils import emojis, poll_check from bot.extensions.polls.views import CreatePollView from utils.transformers import MessageTransformer @@ -11,29 +12,6 @@ class Polls(commands.GroupCog, group_name="poll"): def __init__(self, bot: core.DiscordBot): self.bot = bot - @property - def reactions(self): - return { - 1: "1️⃣", - 2: "2️⃣", - 3: "3️⃣", - 4: "4️⃣", - 5: "5️⃣", - 6: "6️⃣", - 7: "7️⃣", - 8: "8️⃣", - 9: "9️⃣", - 10: "🔟", - } - - def poll_check(self, message: discord.Message): - if not message.embeds: - return False - - embed = message.embeds[0] - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.bot.user - @app_commands.command() @app_commands.describe(question="Your question") async def new(self, interaction: core.InteractionType, question: str): @@ -56,14 +34,12 @@ async def show( ): """Show a poll result""" - if not self.poll_check(message): + if not poll_check(message, self.bot.user): return await interaction.response.send_message("Please provide a valid poll message", ephemeral=True) poll_embed = message.embeds[0] reactions = message.reactions - reactions_total = sum( - [reaction.count - 1 if str(reaction.emoji) in self.reactions.values() else 0 for reaction in reactions] - ) + reactions_total = sum([reaction.count - 1 if str(reaction.emoji) in emojis else 0 for reaction in reactions]) options = [field.name for field in poll_embed.fields] desc = poll_embed.description.split("1️")[0] @@ -93,5 +69,5 @@ async def show( return await interaction.response.send_message(embed=embed, ephemeral=ephemeral) -async def setup(bot: commands.Bot): +async def setup(bot: core.DiscordBot): await bot.add_cog(Polls(bot=bot)) diff --git a/bot/extensions/polls/events.py b/bot/extensions/polls/events.py index deb271fe..220d641b 100644 --- a/bot/extensions/polls/events.py +++ b/bot/extensions/polls/events.py @@ -2,6 +2,7 @@ from discord.ext import commands from bot import core +from bot.extensions.polls.utils import emojis, poll_check class PollEvents(commands.Cog): @@ -9,15 +10,6 @@ class PollEvents(commands.Cog): def __init__(self, bot: core.DiscordBot): self.bot = bot - self.emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "9️⃣", "🔟"] - - def poll_check(self, message: discord.Message): - if not message.embeds: - return False - - embed = message.embeds[0] - if str(embed.footer.text).count("Poll by") == 1: - return message.author == self.bot.user @commands.Cog.listener() async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): @@ -27,14 +19,14 @@ async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): if payload.user_id == self.bot.user.id: return - if not self.poll_check(message): + if not poll_check(message, self.bot.user): return - if str(payload.emoji) not in self.emojis: + if str(payload.emoji) not in emojis: return for reaction in message.reactions: - if str(reaction) not in self.emojis: + if str(reaction) not in emojis: return if str(reaction.emoji) != str(payload.emoji): diff --git a/bot/extensions/polls/utils.py b/bot/extensions/polls/utils.py new file mode 100644 index 00000000..bdb3e4e3 --- /dev/null +++ b/bot/extensions/polls/utils.py @@ -0,0 +1,12 @@ +import discord + +emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "9️⃣", "🔟"] + + +def poll_check(message: discord.Message, bot: discord.ClientUser): + if not message.embeds: + return False + + embed = message.embeds[0] + if str(embed.footer.text).count("Poll by") == 1: + return message.author == bot diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index c09c3620..840ca4f5 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -1,6 +1,8 @@ import discord from discord import ui +from bot.extensions.polls.utils import emojis + class PollModal(ui.Modal): name = ui.TextInput(label="Choice name", placeholder="Enter poll choice", max_length=32, required=True) @@ -16,28 +18,11 @@ def __init__(self, var: discord.Interaction): self.var = var super().__init__(title="Poll options") - @property - def reactions(self): - return { - 0: "1️⃣", - 1: "2️⃣", - 2: "3️⃣", - 3: "4️⃣", - 4: "5️⃣", - 5: "6️⃣", - 6: "7️⃣", - 7: "8️⃣", - 8: "9️⃣", - 9: "🔟", - } - async def on_submit(self, interaction: discord.Interaction) -> None: message = await self.var.followup.fetch_message(self.var.message.id) num = len(message.embeds[0].fields) - message.embeds[0].add_field( - name=f"{str(self.reactions[num])} {self.name}", value=self.description, inline=False - ) + message.embeds[0].add_field(name=f"{str(emojis[num])} {self.name}", value=self.description, inline=False) await message.edit(embed=message.embeds[0]) await interaction.response.defer() @@ -52,21 +37,6 @@ class CreatePollView(ui.View): def __init__(self, *, timeout=180): super().__init__(timeout=timeout) - @property - def reactions(self): - return { - 0: "1️⃣", - 1: "2️⃣", - 2: "3️⃣", - 3: "4️⃣", - 4: "5️⃣", - 5: "6️⃣", - 6: "7️⃣", - 7: "8️⃣", - 8: "9️⃣", - 9: "🔟", - } - @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕", custom_id=ADD_CUSTOM_ID) async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): num = len(interaction.message.embeds[0].fields) @@ -90,9 +60,7 @@ async def remove_choice(self, interaction: discord.Interaction, _button: ui.Butt options = [] for i in range(0, len(embed.fields)): - options.append( - discord.SelectOption(label=embed.fields[i].name[4:], value=str(i + 1), emoji=self.reactions[i]) - ) + options.append(discord.SelectOption(label=embed.fields[i].name[4:], value=str(i + 1), emoji=emojis[i])) async def callback(interaction: discord.Interaction): embed = interaction.message.embeds[0] @@ -103,7 +71,7 @@ async def callback(interaction: discord.Interaction): for x in range(0, len(embed.fields)): embed.set_field_at( x, - name=f"{str(self.reactions[x])} {embed.fields[x].name[4:]}", + name=f"{str(emojis[x])} {embed.fields[x].name[4:]}", value=embed.fields[x].value, inline=False, ) @@ -133,7 +101,7 @@ async def create_poll(self, interaction: discord.Interaction, _button: ui.Button message = await interaction.channel.send(embed=embed) for i in range(0, len(embed.fields)): - await message.add_reaction(self.reactions[i]) + await message.add_reaction(emojis[i]) await interaction.response.defer() await interaction.delete_original_response() From 981e843ff6995db9e06d6710f1cd355ed9818f22 Mon Sep 17 00:00:00 2001 From: Sylte Date: Wed, 1 Nov 2023 21:14:21 +0100 Subject: [PATCH 19/32] Fixes --- bot/extensions/polls/commands.py | 5 ++++- bot/extensions/polls/views.py | 20 ++++++-------------- bot/extensions/tags/events.py | 2 +- bot/extensions/tags/views.py | 3 --- 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/bot/extensions/polls/commands.py b/bot/extensions/polls/commands.py index 5946e3e8..edeca6ad 100644 --- a/bot/extensions/polls/commands.py +++ b/bot/extensions/polls/commands.py @@ -12,6 +12,9 @@ class Polls(commands.GroupCog, group_name="poll"): def __init__(self, bot: core.DiscordBot): self.bot = bot + self._create_poll_view = CreatePollView(timeout=None) + self.bot.add_view(self._create_poll_view) + @app_commands.command() @app_commands.describe(question="Your question") async def new(self, interaction: core.InteractionType, question: str): @@ -23,7 +26,7 @@ async def new(self, interaction: core.InteractionType, question: str): color=discord.colour.Color.gold(), ) embed.set_footer(text=f"Poll by {interaction.user.display_name}") - await interaction.response.send_message(embed=embed, ephemeral=True, view=CreatePollView()) + await interaction.response.send_message(embed=embed, ephemeral=True, view=self._create_poll_view) @app_commands.command() async def show( diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 840ca4f5..3ef0cbb1 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -4,7 +4,7 @@ from bot.extensions.polls.utils import emojis -class PollModal(ui.Modal): +class PollModal(ui.Modal, title="Poll Options"): name = ui.TextInput(label="Choice name", placeholder="Enter poll choice", max_length=32, required=True) description = ui.TextInput( label="Choice description (optional)", @@ -14,18 +14,13 @@ class PollModal(ui.Modal): required=False, ) - def __init__(self, var: discord.Interaction): - self.var = var - super().__init__(title="Poll options") - async def on_submit(self, interaction: discord.Interaction) -> None: - message = await self.var.followup.fetch_message(self.var.message.id) + embed = interaction.message.embeds[0] + field_count = len(embed.fields) - num = len(message.embeds[0].fields) - message.embeds[0].add_field(name=f"{str(emojis[num])} {self.name}", value=self.description, inline=False) + embed.add_field(name=f"{str(emojis[field_count])} {self.name}", value=self.description, inline=False) - await message.edit(embed=message.embeds[0]) - await interaction.response.defer() + await interaction.response.edit_message(embed=embed) class CreatePollView(ui.View): @@ -34,9 +29,6 @@ class CreatePollView(ui.View): SELECT_CUSTOM_ID = "extensions:polls:select" DELETE_CUSTOM_ID = "extensions:polls:delete" - def __init__(self, *, timeout=180): - super().__init__(timeout=timeout) - @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕", custom_id=ADD_CUSTOM_ID) async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): num = len(interaction.message.embeds[0].fields) @@ -46,7 +38,7 @@ async def add_choice(self, interaction: discord.Interaction, _button: ui.Button) "You can't make a poll with more than 10 choices", ephemeral=True ) - modal = PollModal(var=interaction) + modal = PollModal() await interaction.response.send_modal(modal) @discord.ui.button(label="Remove Choice", style=discord.ButtonStyle.gray, emoji="➖", custom_id=DELETE_CUSTOM_ID) diff --git a/bot/extensions/tags/events.py b/bot/extensions/tags/events.py index 23059aba..23bfd048 100644 --- a/bot/extensions/tags/events.py +++ b/bot/extensions/tags/events.py @@ -13,7 +13,7 @@ class TagEvents(commands.Cog): def __init__(self, bot: core.DiscordBot): self.bot = bot - self._log_tag_creation_view = LogTagCreationView() + self._log_tag_creation_view = LogTagCreationView(timeout=None) self.bot.add_view(self._log_tag_creation_view) @property diff --git a/bot/extensions/tags/views.py b/bot/extensions/tags/views.py index 67c824d0..fc307d47 100644 --- a/bot/extensions/tags/views.py +++ b/bot/extensions/tags/views.py @@ -33,9 +33,6 @@ class LogTagCreationView(ui.View): DELETE_CUSTOM_ID = "extensions:tags:delete" FEATURE_CUSTOM_ID = "extensions:tags:feature" - def __init__(self, timeout: float = None): - super().__init__(timeout=timeout) - @staticmethod async def wait_for_confirmation(interaction: core.InteractionType, tag: Tag, reason: str): """If the tag name or content has changed, wait for confirmation that they really want to delete.""" From bfd14f3d73c5aea33026a9c87dee829633044193 Mon Sep 17 00:00:00 2001 From: Sylte Date: Thu, 2 Nov 2023 00:16:43 +0100 Subject: [PATCH 20/32] Progress on new delete menu and updated button states --- bot/core.py | 2 +- bot/extensions/polls/utils.py | 2 +- bot/extensions/polls/views.py | 122 +++++++++++++++++++--------------- cli.py | 20 +++--- 4 files changed, 80 insertions(+), 66 deletions(-) diff --git a/bot/core.py b/bot/core.py index f3a14134..618ddd9b 100644 --- a/bot/core.py +++ b/bot/core.py @@ -57,7 +57,7 @@ async def when_online(self): await self.wait_until_ready() await self.load_extensions() - await self.sync_commands() + # await self.sync_commands() async def load_extensions(self): for ext in self.initial_extensions: diff --git a/bot/extensions/polls/utils.py b/bot/extensions/polls/utils.py index bdb3e4e3..e103145c 100644 --- a/bot/extensions/polls/utils.py +++ b/bot/extensions/polls/utils.py @@ -1,6 +1,6 @@ import discord -emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "9️⃣", "🔟"] +emojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"] def poll_check(message: discord.Message, bot: discord.ClientUser): diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 3ef0cbb1..e2e91454 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -1,10 +1,16 @@ +import typing as t + import discord from discord import ui +from bot import core from bot.extensions.polls.utils import emojis +if t.TYPE_CHECKING: + from discord.embeds import _EmbedFieldProxy + -class PollModal(ui.Modal, title="Poll Options"): +class PollModal(ui.Modal, title="Add Choice"): name = ui.TextInput(label="Choice name", placeholder="Enter poll choice", max_length=32, required=True) description = ui.TextInput( label="Choice description (optional)", @@ -20,69 +26,77 @@ async def on_submit(self, interaction: discord.Interaction) -> None: embed.add_field(name=f"{str(emojis[field_count])} {self.name}", value=self.description, inline=False) - await interaction.response.edit_message(embed=embed) + view = discord.ui.View.from_message(interaction.message, timeout=None) + + add_choice_btn = discord.utils.get(view.children, custom_id=CreatePollView.ADD_CUSTOM_ID) + create_poll_btn = discord.utils.get(view.children, custom_id=CreatePollView.CREATE_CUSTOM_ID) + delete_select = discord.utils.find(lambda child: isinstance(child, discord.ui.Select), view.children) + + if field_count + 1 == 10: + add_choice_btn.disabled = True + else: + add_choice_btn.disabled = False + + if len(view.children) == 2: + create_poll_btn.disabled = False + view.add_item(DeletePollOptions(embed.fields)) + else: + view.remove_item(delete_select) + view.add_item(DeletePollOptions(embed.fields)) + + await interaction.response.edit_message(embed=embed, view=view) + print() + + +class DeletePollOptions(discord.ui.Select): + def __init__(self, fields: list["_EmbedFieldProxy"]): + super().__init__( + row=2, + max_values=len(fields), + placeholder="➖ Select a choice to remove", + custom_id=CreatePollView.DELETE_CUSTOM_ID, + options=[ + discord.SelectOption(emoji=emojis[i], label=field.name[5:], value=str(i + 1)) + for i, field in enumerate(fields) + ], + ) + async def callback(self, interaction: core.InteractionType): + embed = interaction.message.embeds[0] -class CreatePollView(ui.View): - CREATE_CUSTOM_ID = "extensions:polls:create" - ADD_CUSTOM_ID = "extensions:polls:add" - SELECT_CUSTOM_ID = "extensions:polls:select" - DELETE_CUSTOM_ID = "extensions:polls:delete" + for value in self.values: + embed.remove_field(int(value) - 1) - @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.gray, emoji="➕", custom_id=ADD_CUSTOM_ID) - async def add_choice(self, interaction: discord.Interaction, _button: ui.Button): - num = len(interaction.message.embeds[0].fields) + for i, field in enumerate(embed.fields): + embed.set_field_at(i, name=f"{emojis[i]} {field.name[5:]}", value=field.value, inline=False) - if num >= 10: - return await interaction.response.send_message( - "You can't make a poll with more than 10 choices", ephemeral=True - ) + self.view.remove_item(self) + self.view.add_item(DeletePollOptions(embed.fields)) - modal = PollModal() - await interaction.response.send_modal(modal) + await interaction.response.edit_message(embed=embed, view=self.view) + + +class CreatePollView(ui.View): + ADD_CUSTOM_ID = "extensions:polls:add" + DELETE_CUSTOM_ID = "extensions:polls:delete" + CREATE_CUSTOM_ID = "extensions:polls:create" - @discord.ui.button(label="Remove Choice", style=discord.ButtonStyle.gray, emoji="➖", custom_id=DELETE_CUSTOM_ID) - async def remove_choice(self, interaction: discord.Interaction, _button: ui.Button): + @discord.ui.button(label="Add Choice", style=discord.ButtonStyle.blurple, emoji="➕", custom_id=ADD_CUSTOM_ID) + async def add_choice(self, interaction: core.InteractionType, _button: ui.Button): embed = interaction.message.embeds[0] + field_count = len(embed.fields) - if len(embed.fields) == 0: + if field_count >= 10: return await interaction.response.send_message( - "You can't remove a choice from a poll with no choices", ephemeral=True + "You can't make a poll with more than 10 choices", ephemeral=True ) - options = [] - for i in range(0, len(embed.fields)): - options.append(discord.SelectOption(label=embed.fields[i].name[4:], value=str(i + 1), emoji=emojis[i])) - - async def callback(interaction: discord.Interaction): - embed = interaction.message.embeds[0] - selected = int(interaction.data["values"][0]) + await interaction.response.send_modal(PollModal()) - embed.remove_field(selected - 1) - - for x in range(0, len(embed.fields)): - embed.set_field_at( - x, - name=f"{str(emojis[x])} {embed.fields[x].name[4:]}", - value=embed.fields[x].value, - inline=False, - ) - - await interaction.response.defer() - await interaction.edit_original_response(embed=embed, view=self) - - select = discord.ui.Select( - placeholder="Select a choice to remove", options=options, custom_id=self.SELECT_CUSTOM_ID - ) - select.callback = callback - view = discord.ui.View() - view.add_item(select) - - await interaction.response.defer() - await interaction.edit_original_response(embed=embed, view=view) - - @discord.ui.button(label="Create Poll", style=discord.ButtonStyle.green, emoji="📝", custom_id=CREATE_CUSTOM_ID) - async def create_poll(self, interaction: discord.Interaction, _button: ui.Button): + @discord.ui.button( + label="Create Poll", style=discord.ButtonStyle.green, emoji="📝", custom_id=CREATE_CUSTOM_ID, disabled=True + ) + async def create_poll(self, interaction: core.InteractionType, _button: ui.Button): embed = interaction.message.embeds[0] if len(embed.fields) < 2: @@ -90,10 +104,10 @@ async def create_poll(self, interaction: discord.Interaction, _button: ui.Button "You can't create a poll with less than 2 choices", ephemeral=True ) - message = await interaction.channel.send(embed=embed) + await interaction.response.send_message(embed=embed) + message = await interaction.original_response() for i in range(0, len(embed.fields)): await message.add_reaction(emojis[i]) - await interaction.response.defer() await interaction.delete_original_response() diff --git a/cli.py b/cli.py index 7908687f..34c6c97d 100644 --- a/cli.py +++ b/cli.py @@ -130,17 +130,17 @@ async def main(ctx): prefixes = ("t.",) extensions = ( "jishaku", - "bot.extensions.challenges", - "bot.extensions.readthedocs", - "bot.extensions.suggestions", - "bot.extensions.github", - "bot.extensions.tags", - "bot.extensions.levelling", - "bot.extensions.persistent_roles", + # "bot.extensions.challenges", + # "bot.extensions.readthedocs", + # "bot.extensions.suggestions", + # "bot.extensions.github", + # "bot.extensions.tags", + # "bot.extensions.levelling", + # "bot.extensions.persistent_roles", "bot.extensions.polls", - "bot.cogs._help", - "bot.cogs.clashofcode", - "bot.cogs.roles", + # "bot.cogs._help", + # "bot.cogs.clashofcode", + # "bot.cogs.roles", ) intents = discord.Intents.all() From 948c2b5dd6ac228beadd9e853806b30f3955f479 Mon Sep 17 00:00:00 2001 From: Sylte Date: Thu, 2 Nov 2023 00:19:37 +0100 Subject: [PATCH 21/32] Update MessageTransformer --- utils/transformers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/utils/transformers.py b/utils/transformers.py index d35661b2..6558a717 100644 --- a/utils/transformers.py +++ b/utils/transformers.py @@ -22,9 +22,12 @@ async def transform(self, interaction: core.InteractionType, value: str, /): channel = interaction.guild.get_channel(channel_id) return await channel.fetch_message(message_id) - except (ValueError, TypeError, IndexError, AttributeError, discord.HTTPException): + except (ValueError, TypeError, IndexError, AttributeError): + await interaction.response.send_message("Please provide a valid message URL.", ephemeral=True) + except discord.HTTPException: await interaction.response.send_message("Sorry, I couldn't find that message...", ephemeral=True) - raise IgnorableException + + raise IgnorableException class CommandTransformer(app_commands.Transformer): From a70c7dff21a876dea4a5632a0bdcf14ff197daf4 Mon Sep 17 00:00:00 2001 From: HETHAT Date: Thu, 2 Nov 2023 12:05:12 +0100 Subject: [PATCH 22/32] Fix --- bot/extensions/polls/views.py | 37 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index e2e91454..66434629 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -25,27 +25,21 @@ async def on_submit(self, interaction: discord.Interaction) -> None: field_count = len(embed.fields) embed.add_field(name=f"{str(emojis[field_count])} {self.name}", value=self.description, inline=False) - - view = discord.ui.View.from_message(interaction.message, timeout=None) + field_count += 1 + view = CreatePollView() add_choice_btn = discord.utils.get(view.children, custom_id=CreatePollView.ADD_CUSTOM_ID) create_poll_btn = discord.utils.get(view.children, custom_id=CreatePollView.CREATE_CUSTOM_ID) delete_select = discord.utils.find(lambda child: isinstance(child, discord.ui.Select), view.children) - if field_count + 1 == 10: - add_choice_btn.disabled = True - else: - add_choice_btn.disabled = False + add_choice_btn.disabled = field_count > 9 + create_poll_btn.disabled = field_count < 2 - if len(view.children) == 2: - create_poll_btn.disabled = False - view.add_item(DeletePollOptions(embed.fields)) - else: - view.remove_item(delete_select) + view.remove_item(delete_select) + if field_count >= 1: view.add_item(DeletePollOptions(embed.fields)) await interaction.response.edit_message(embed=embed, view=view) - print() class DeletePollOptions(discord.ui.Select): @@ -56,7 +50,7 @@ def __init__(self, fields: list["_EmbedFieldProxy"]): placeholder="➖ Select a choice to remove", custom_id=CreatePollView.DELETE_CUSTOM_ID, options=[ - discord.SelectOption(emoji=emojis[i], label=field.name[5:], value=str(i + 1)) + discord.SelectOption(emoji=emojis[i], label=field.name.split(maxsplit=1)[1], value=str(i)) for i, field in enumerate(fields) ], ) @@ -64,14 +58,21 @@ def __init__(self, fields: list["_EmbedFieldProxy"]): async def callback(self, interaction: core.InteractionType): embed = interaction.message.embeds[0] - for value in self.values: - embed.remove_field(int(value) - 1) + for value in sorted(self.values, reverse=True): # to avoid conflict + embed.remove_field(int(value)) for i, field in enumerate(embed.fields): - embed.set_field_at(i, name=f"{emojis[i]} {field.name[5:]}", value=field.value, inline=False) + embed.set_field_at( + i, name=f"{emojis[i]} {field.name.split(maxsplit=1)[1]}", value=field.value, inline=False + ) self.view.remove_item(self) - self.view.add_item(DeletePollOptions(embed.fields)) + if len(embed.fields) >= 1: + self.view.add_item(DeletePollOptions(embed.fields)) + + # We removed a choice so there gotta be some space for more + add_choice_btn = discord.utils.get(self.view.children, custom_id=CreatePollView.ADD_CUSTOM_ID) + add_choice_btn.disabled = False await interaction.response.edit_message(embed=embed, view=self.view) @@ -109,5 +110,3 @@ async def create_poll(self, interaction: core.InteractionType, _button: ui.Butto for i in range(0, len(embed.fields)): await message.add_reaction(emojis[i]) - - await interaction.delete_original_response() From d10bb93203cb40539df2973cd12be42481c3ef29 Mon Sep 17 00:00:00 2001 From: Sylte Date: Thu, 2 Nov 2023 13:36:00 +0100 Subject: [PATCH 23/32] Update tag.py --- bot/models/tag.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/models/tag.py b/bot/models/tag.py index 85c5158e..8a168f55 100644 --- a/bot/models/tag.py +++ b/bot/models/tag.py @@ -19,8 +19,8 @@ class Tag(Model): @classmethod async def create(cls, guild_id: int, author_id: int, name: str, content: str) -> "Tag": query = """ - INSERT INTO tags (guild_id, author_id, name, content) - VALUES ($1, $2, $3, $4) + INSERT INTO tags (guild_id, author_id, name, content, uses) + VALUES ($1, $2, $3, $4, 0) RETURNING *; """ return await cls.fetchrow(query, guild_id, author_id, name, content) From e06b2fa854f8aa4388370bf892f83c8b79a49d9d Mon Sep 17 00:00:00 2001 From: Sylte Date: Thu, 2 Nov 2023 13:41:58 +0100 Subject: [PATCH 24/32] Uncomment extensions and sync commands on startup --- bot/core.py | 2 +- cli.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bot/core.py b/bot/core.py index 618ddd9b..f3a14134 100644 --- a/bot/core.py +++ b/bot/core.py @@ -57,7 +57,7 @@ async def when_online(self): await self.wait_until_ready() await self.load_extensions() - # await self.sync_commands() + await self.sync_commands() async def load_extensions(self): for ext in self.initial_extensions: diff --git a/cli.py b/cli.py index 34c6c97d..7908687f 100644 --- a/cli.py +++ b/cli.py @@ -130,17 +130,17 @@ async def main(ctx): prefixes = ("t.",) extensions = ( "jishaku", - # "bot.extensions.challenges", - # "bot.extensions.readthedocs", - # "bot.extensions.suggestions", - # "bot.extensions.github", - # "bot.extensions.tags", - # "bot.extensions.levelling", - # "bot.extensions.persistent_roles", + "bot.extensions.challenges", + "bot.extensions.readthedocs", + "bot.extensions.suggestions", + "bot.extensions.github", + "bot.extensions.tags", + "bot.extensions.levelling", + "bot.extensions.persistent_roles", "bot.extensions.polls", - # "bot.cogs._help", - # "bot.cogs.clashofcode", - # "bot.cogs.roles", + "bot.cogs._help", + "bot.cogs.clashofcode", + "bot.cogs.roles", ) intents = discord.Intents.all() From e001d43f817e36c30726b5cb96d36e12e61a2e06 Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:08:42 +0200 Subject: [PATCH 25/32] bug fixes --- bot/extensions/polls/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 66434629..96e6386b 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -70,9 +70,13 @@ async def callback(self, interaction: core.InteractionType): if len(embed.fields) >= 1: self.view.add_item(DeletePollOptions(embed.fields)) - # We removed a choice so there gotta be some space for more + field_count = len(embed.fields) + + # We removed a choice so reset the disabled state of the buttons add_choice_btn = discord.utils.get(self.view.children, custom_id=CreatePollView.ADD_CUSTOM_ID) + create_poll_btn = discord.utils.get(self.view.children, custom_id=CreatePollView.CREATE_CUSTOM_ID) add_choice_btn.disabled = False + create_poll_btn.disabled = field_count < 2 await interaction.response.edit_message(embed=embed, view=self.view) @@ -105,7 +109,7 @@ async def create_poll(self, interaction: core.InteractionType, _button: ui.Butto "You can't create a poll with less than 2 choices", ephemeral=True ) - await interaction.response.send_message(embed=embed) + await interaction.channel.send(embed=embed) message = await interaction.original_response() for i in range(0, len(embed.fields)): From d1f15c1e61d2376e366def1b4b308f38f44ffc7c Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:27:55 +0200 Subject: [PATCH 26/32] defer the response and remove the ephemeral after sending poll --- bot/extensions/polls/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 96e6386b..77249d83 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -109,8 +109,10 @@ async def create_poll(self, interaction: core.InteractionType, _button: ui.Butto "You can't create a poll with less than 2 choices", ephemeral=True ) - await interaction.channel.send(embed=embed) - message = await interaction.original_response() + await interaction.response.defer() + message = await interaction.channel.send(embed=embed) for i in range(0, len(embed.fields)): await message.add_reaction(emojis[i]) + + await interaction.delete_original_response() From a1d99f66b7d969ab2c85e9b8ac5557ab31688e7b Mon Sep 17 00:00:00 2001 From: FirePlank <44502537+FirePlank@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:47:24 +0200 Subject: [PATCH 27/32] delete response before sending poll --- bot/extensions/polls/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/extensions/polls/views.py b/bot/extensions/polls/views.py index 77249d83..52a6b6db 100644 --- a/bot/extensions/polls/views.py +++ b/bot/extensions/polls/views.py @@ -110,9 +110,8 @@ async def create_poll(self, interaction: core.InteractionType, _button: ui.Butto ) await interaction.response.defer() + await interaction.delete_original_response() message = await interaction.channel.send(embed=embed) for i in range(0, len(embed.fields)): await message.add_reaction(emojis[i]) - - await interaction.delete_original_response() From 2a1ec49da6cb344ac4be5cebc65712ab4035a80c Mon Sep 17 00:00:00 2001 From: Guilherme Candeias <126275722+FelpyOfGulet@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:07:26 +0100 Subject: [PATCH 28/32] Update events.py to check if the user is a bot --- bot/extensions/challenges/events.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bot/extensions/challenges/events.py b/bot/extensions/challenges/events.py index b88431ae..c964d3da 100644 --- a/bot/extensions/challenges/events.py +++ b/bot/extensions/challenges/events.py @@ -23,6 +23,7 @@ def submitted_role(self) -> discord.Role | None: @property def participant_role(self) -> discord.Role | None: + if return self.bot.guild.get_role(settings.challenges.participant_role_id) @commands.Cog.listener() @@ -32,6 +33,9 @@ async def on_message(self, message: discord.Message): if message.channel.id != settings.challenges.channel_id: return + if message.author == self.bot.user: + return + await message.add_reaction(self.challenge_reaction) @commands.Cog.listener() @@ -42,6 +46,10 @@ async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): if payload.emoji != self.challenge_reaction: return + member = self.bot.guild.get_member(payload.user_id) + if member is None or member.bot: + return + if self.submitted_role in payload.member.roles: return From e10dbc5b19d4ae4d831f660fdb83e95ae8a1f00c Mon Sep 17 00:00:00 2001 From: Guilherme Candeias <126275722+FelpyOfGulet@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:17:19 +0100 Subject: [PATCH 29/32] Update events.py to remove the lone "if" statement --- bot/extensions/challenges/events.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/extensions/challenges/events.py b/bot/extensions/challenges/events.py index c964d3da..cf7ddd90 100644 --- a/bot/extensions/challenges/events.py +++ b/bot/extensions/challenges/events.py @@ -23,7 +23,6 @@ def submitted_role(self) -> discord.Role | None: @property def participant_role(self) -> discord.Role | None: - if return self.bot.guild.get_role(settings.challenges.participant_role_id) @commands.Cog.listener() From b2113b4ee52908ce1a6c19f85128e5adcd61217b Mon Sep 17 00:00:00 2001 From: Guilherme Candeias <126275722+FelpyOfGulet@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:22:56 +0100 Subject: [PATCH 30/32] Removed unwanted code block, edited function to check if it's our bot --- bot/extensions/challenges/events.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/bot/extensions/challenges/events.py b/bot/extensions/challenges/events.py index cf7ddd90..9d41084b 100644 --- a/bot/extensions/challenges/events.py +++ b/bot/extensions/challenges/events.py @@ -32,9 +32,6 @@ async def on_message(self, message: discord.Message): if message.channel.id != settings.challenges.channel_id: return - if message.author == self.bot.user: - return - await message.add_reaction(self.challenge_reaction) @commands.Cog.listener() @@ -45,9 +42,8 @@ async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): if payload.emoji != self.challenge_reaction: return - member = self.bot.guild.get_member(payload.user_id) - if member is None or member.bot: - return + if payload.user_id == self.bot.user.id: + return if self.submitted_role in payload.member.roles: return From 9338cfa54470d6ccac94bc6e43bb1eff22fd6ce4 Mon Sep 17 00:00:00 2001 From: Guilherme Candeias <126275722+FelpyOfGulet@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:24:21 +0100 Subject: [PATCH 31/32] Added indentation --- bot/extensions/challenges/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/extensions/challenges/events.py b/bot/extensions/challenges/events.py index 9d41084b..16c209a5 100644 --- a/bot/extensions/challenges/events.py +++ b/bot/extensions/challenges/events.py @@ -43,7 +43,7 @@ async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): return if payload.user_id == self.bot.user.id: - return + return if self.submitted_role in payload.member.roles: return From 1ab905b6f94b7c47cb4f970f5deb35cbb3468c45 Mon Sep 17 00:00:00 2001 From: Sylte Date: Mon, 6 Nov 2023 18:09:18 +0100 Subject: [PATCH 32/32] Fix xp range --- bot/extensions/levelling/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/extensions/levelling/commands.py b/bot/extensions/levelling/commands.py index 60d0e0be..09d80751 100644 --- a/bot/extensions/levelling/commands.py +++ b/bot/extensions/levelling/commands.py @@ -102,7 +102,7 @@ async def on_message(self, message): """ # TODO: Allow each guild to set custom xp range and boost. - xp = random.randint(5, 25) * self.xp_boost + xp = random.randint(15, 25) * self.xp_boost after = await LevellingUser.fetchrow(query, message.guild.id, message.author.id, xp) if after is None: