From 0a6d54251d775b5111117de430683e2b6e7c3cb3 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Sun, 1 Dec 2024 20:13:55 +0200 Subject: [PATCH 01/21] alexBot\cogs\configs.py simplifiable-if-expression The if is not needed. Using "!=" instead of "==" further simplifies the expression. --- alexBot/cogs/configs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alexBot/cogs/configs.py b/alexBot/cogs/configs.py index c7c8aa2..71bcf34 100644 --- a/alexBot/cogs/configs.py +++ b/alexBot/cogs/configs.py @@ -132,7 +132,7 @@ async def setConfig( session.add(uc) await session.commit() await interaction.response.send_message( - f"Set {key} to {val}", ephemeral=False if config_type == 'guild' else True + f"Set {key} to {val}", ephemeral=config_type != 'guild' ) @configGuildCommandGroup.command(name="show", description="shows the current config") From d5e5fcde997086dbdfa03c38f064318641357ab4 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Mon, 2 Dec 2024 16:22:20 +0200 Subject: [PATCH 02/21] alexBot\disabled\bots.py too-many-branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Method on_member_update of class Bots had 13 branches while Pylint recommends having at most 12. I fixed that by extracting the method _get_messagable. --- alexBot/disabled/bots.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/alexBot/disabled/bots.py b/alexBot/disabled/bots.py index 22afdd4..fc3de42 100644 --- a/alexBot/disabled/bots.py +++ b/alexBot/disabled/bots.py @@ -49,9 +49,7 @@ async def on_member_update(self, before: discord.Member, after: discord.Member): return key = (before.id, (before.guild.id >> 22) % config['shards']) - messagable = self.bot.get_user(config['messagable_id']) - if messagable is None: - messagable = self.bot.get_channel(config['messagable_id']) + messagable = self._get_messagable(config) status = after.status @@ -80,6 +78,12 @@ async def on_member_update(self, before: discord.Member, after: discord.Member): except discord.HTTPException: pass + def _get_messagable(self, config): + messagable = self.bot.get_user(config['messagable_id']) + if messagable is None: + messagable = self.bot.get_channel(config['messagable_id']) + return messagable + @staticmethod async def send(messagable, message, wait=30): """sends a message to a messagable after 30 seconds unless cancled""" From cf150d67b2e2c2d7533affd8adba9b3affb2a560 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Mon, 2 Dec 2024 18:58:57 +0200 Subject: [PATCH 03/21] alexBot\cogs\reminders.py too-many-branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The method remind of the class Reminders had 27 branches while Pyline recommends having at most 12. I extracted methods, making the code more structured and readable and solving the alert. --- alexBot/cogs/reminders.py | 188 +++++++++++++++++++++++--------------- 1 file changed, 112 insertions(+), 76 deletions(-) diff --git a/alexBot/cogs/reminders.py b/alexBot/cogs/reminders.py index 6982fe1..aea830e 100644 --- a/alexBot/cogs/reminders.py +++ b/alexBot/cogs/reminders.py @@ -65,46 +65,19 @@ async def reminder_loop(self): async def remind(self, reminder: Reminder): await self.bot.wait_until_ready() try: - # wait for the reminder time - now = datetime.datetime.now(datetime.UTC) - if now > reminder.next_remind: - log.warning(f"reminder {reminder} is overdue") - if reminder.frequency: - # ok, we should skip the missed recurres, and just do the next one - while now > reminder.next_remind: - reminder.next_remind = reminder.next_remind + reminder.frequency - - async with db.async_session() as session: - async with session.begin(): - edited = await session.scalar(select(Reminder).where(Reminder.id == reminder.id)) - if not edited: - log.error(f"reminder {reminder} not found in database") - return - edited.next_remind = reminder.next_remind - session.add(edited) - await session.commit() - return - else: - # modify the remidner message in memory to show it's overdue - reminder.message = f"**OVERDUE** {reminder.message}" - - else: - await asyncio.sleep((reminder.next_remind - now).total_seconds()) + if self._wait_for_reminder(reminder): + return allowedMentions = discord.AllowedMentions.none() log.debug(f"reminding {reminder}") try: target = await self.bot.fetch_channel(reminder.target) except discord.NotFound: target = None - if not target: - log.error(f"Could not find target {reminder.target} for reminder {reminder}") - # try to message the owner about it: - owner = self.bot.get_user(reminder.owner) - if not owner: - log.error(f"Could not find owner {reminder.owner} for reminder {reminder}") - return - await owner.send(f"Could not find target channel {reminder.target} for reminder {reminder.message}") + + owner = self._handle_no_target(reminder, target) + if not owner: return + message = reminder.message if message.startswith("["): # random messages; @@ -133,63 +106,126 @@ async def remind(self, reminder: Reminder): if owner and target.permissions_for(owner).mention_everyone: allowedMentions.everyone = True - if reminder.require_clearing: - v = ClearReminderView() - dis_message = await target.send(message, view=v, allowed_mentions=allowedMentions) - if reminder.auto_react: - await dis_message.add_reaction("<:greentick:1255344157761867816>") - while v.waiting and v.times < 8: # 8 * 5 minutes = 40 minutes - msg = None - try: - msg = await self.bot.wait_for( + await self._handle_clearing(reminder, allowedMentions, target, message) + + if self._handle_reminder(self, reminder): + return + + # remove task from tasks dict + finally: + del self.tasks[reminder.id] + return + + async def _handle_no_target(self, reminder, target): + owner = None + if not target: + log.error(f"Could not find target {reminder.target} for reminder {reminder}") + # try to message the owner about it: + owner = self.bot.get_user(reminder.owner) + if not owner: + log.error(f"Could not find owner {reminder.owner} for reminder {reminder}") + return None + await owner.send(f"Could not find target channel {reminder.target} for reminder {reminder.message}") + return owner + + return owner + + + async def _handle_clearing(self, reminder, allowedMentions, target, message): + if reminder.require_clearing: + await self._reminder_clearing(reminder, allowedMentions, target, message) + + else: + msg = await target.send(message, allowed_mentions=allowedMentions) + if reminder.auto_react: + await msg.add_reaction("<:greentick:1255344157761867816>") + + async def _handle_reminder(self, reminder): + + should_exit = False + if reminder.frequency: + # reschedule the reminder for later + async with db.async_session() as session: + async with session.begin(): + edited = await session.scalar(select(Reminder).where(Reminder.id == reminder.id)) + if not edited: + log.error(f"reminder {reminder} not found in database") + return True + edited.next_remind = ( + edited.next_remind + reminder.frequency + ) # prevent drift by adding the frequency + session.add(edited) + await session.commit() + else: + # delete the reminder + async with db.async_session() as session: + async with session.begin(): + delete = await session.scalar(select(Reminder).where(Reminder.id == reminder.id)) + await session.delete(delete) + await session.commit() + + return should_exit + + async def _reminder_clearing(self, reminder, allowedMentions, target, message): + v = ClearReminderView() + dis_message = await target.send(message, view=v, allowed_mentions=allowedMentions) + if reminder.auto_react: + await dis_message.add_reaction("<:greentick:1255344157761867816>") + while v.waiting and v.times < 8: # 8 * 5 minutes = 40 minutes + msg = None + try: + msg = await self.bot.wait_for( "message", check=lambda m: m.channel.id == target.id and m.content.lower().startswith('ack'), timeout=300, ) - v.waiting = False - v.children[0].disabled = True - await msg.reply("reminder cleared") - await dis_message.edit(view=v) - except asyncio.TimeoutError: - pass - if v.waiting: - v.times += 1 - await dis_message.reply("reminder!") + v.waiting = False + v.children[0].disabled = True + await msg.reply("reminder cleared") + await dis_message.edit(view=v) + except asyncio.TimeoutError: + pass + if v.waiting: + v.times += 1 + await dis_message.reply("reminder!") - else: - msg = await target.send(message, allowed_mentions=allowedMentions) - if reminder.auto_react: - await msg.add_reaction("<:greentick:1255344157761867816>") + remindersGroup = app_commands.Group( + name="reminders", + description="menu for working with reminders", + ) + + async def _wait_for_reminder(self, reminder): + should_exit = False + + # wait for the reminder time + now = datetime.datetime.now(datetime.UTC) + if now > reminder.next_remind: + log.warning(f"reminder {reminder} is overdue") if reminder.frequency: - # reschedule the reminder for later + # ok, we should skip the missed recurres, and just do the next one + while now > reminder.next_remind: + reminder.next_remind = reminder.next_remind + reminder.frequency + async with db.async_session() as session: async with session.begin(): edited = await session.scalar(select(Reminder).where(Reminder.id == reminder.id)) if not edited: log.error(f"reminder {reminder} not found in database") - return - edited.next_remind = ( - edited.next_remind + reminder.frequency - ) # prevent drift by adding the frequency - session.add(edited) - await session.commit() + should_exit = True + else: + edited.next_remind = reminder.next_remind + session.add(edited) + await session.commit() + should_exit = True else: - # delete the reminder - async with db.async_session() as session: - async with session.begin(): - delete = await session.scalar(select(Reminder).where(Reminder.id == reminder.id)) - await session.delete(delete) - await session.commit() - # remove task from tasks dict - finally: - del self.tasks[reminder.id] - return + # modify the remidner message in memory to show it's overdue + reminder.message = f"**OVERDUE** {reminder.message}" + else: + await asyncio.sleep((reminder.next_remind - now).total_seconds()) + + return should_exit - remindersGroup = app_commands.Group( - name="reminders", - description="menu for working with reminders", - ) @remindersGroup.command(name="add", description="add a new reminder") @app_commands.describe( From 800f6b548ab43986cebd7bb5353f266c4c8bb0b6 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Mon, 2 Dec 2024 18:59:38 +0200 Subject: [PATCH 04/21] alexBot\cogs\fun.py unnecessary-pass Removed unnecessary pass --- alexBot/cogs/fun.py | 1 - 1 file changed, 1 deletion(-) diff --git a/alexBot/cogs/fun.py b/alexBot/cogs/fun.py index 8cbf402..590cfd6 100644 --- a/alexBot/cogs/fun.py +++ b/alexBot/cogs/fun.py @@ -208,7 +208,6 @@ async def on_message(self, message: discord.Message): ) await message.add_reaction(uploaded) await uploaded.delete(reason="removed from temp addition") - pass return else: From e4ea110e40cf0c27335fe426a74e1a68857a4698 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Tue, 3 Dec 2024 19:26:10 +0200 Subject: [PATCH 05/21] bot.py broad-exception-caught MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Catching Exception might hide unexpected exceptions (e.g., due to new code that will be added). The method cogSetup of the class Bot catches exception (line 81).The try section is                await self.load_extension(f"alexBot.cogs.{cog}")                log.info(f'loaded {cog}') The exceptions are of the discord parent class in load_extension.https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.Bot.load_extensionI narrowed the Exception to the list of specific possible exceptions. --- bot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 919b0e2..2b6f5c4 100755 --- a/bot.py +++ b/bot.py @@ -80,7 +80,10 @@ async def cogSetup(self): try: await self.load_extension(f"alexBot.cogs.{cog}") log.info(f'loaded {cog}') - except Exception as e: + except (commands.ExtensionNotFound + , commands.ExtensionAlreadyLoaded + , commands.NoEntryPointError + , commands.ExtensionFailed) as e: log.error(f'Could not load extension {cog} due to {e.__class__.__name__}: {e}') log.exception(e) From cf385227fe0a7a162ab1a03271aa82eb6e3fa84b Mon Sep 17 00:00:00 2001 From: evidencebp Date: Tue, 3 Dec 2024 19:32:32 +0200 Subject: [PATCH 06/21] alexBot\cogs\voicePrivVC.py line-too-long Made two readable lines shorter --- alexBot/cogs/voicePrivVC.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/alexBot/cogs/voicePrivVC.py b/alexBot/cogs/voicePrivVC.py index 054d56b..f46e8e8 100644 --- a/alexBot/cogs/voicePrivVC.py +++ b/alexBot/cogs/voicePrivVC.py @@ -23,7 +23,10 @@ def __init__(self, bot: "Bot"): self.beingShaken: Dict[int, bool] = {} @Cog.listener() - async def on_voice_state_update(self, member: discord.Member, before: VoiceState, after: VoiceState): + async def on_voice_state_update(self + , member: discord.Member + , before: VoiceState + , after: VoiceState): """ only for actions in nerdiowo hide events that do with ther admin category in any way @@ -37,7 +40,8 @@ async def on_voice_state_update(self, member: discord.Member, before: VoiceState if gd.privateOnePersonVCs: if after.channel and after.channel.user_limit == 1 and len(after.channel.members) == 1: # give the user channel override for manage menbers - await after.channel.set_permissions(member, overwrite=discord.PermissionOverwrite(move_members=True)) + await after.channel.set_permissions(member + , overwrite=discord.PermissionOverwrite(move_members=True)) if before.channel and before.channel.user_limit == 1: # remove the user channel override for manage menbers await before.channel.set_permissions(member, overwrite=None) From 0954514582745e01623772c982e422bd59efa1f4 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Tue, 3 Dec 2024 20:47:50 +0200 Subject: [PATCH 07/21] alexBot\cogs\utils.py broad-exception-caught Catching Exception might hide unexpected exceptions (e.g., due to new code that will be added). The method roll of the class Utils catches exception (line 44). The try section seems to protect discord's send_message See here its exceptions https://discordpy.readthedocs.io/en/stable/interactions/api.html#discord.InteractionResponse.send_message I narrowed the Exception to the list of specific possible exceptions. --- alexBot/cogs/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/alexBot/cogs/utils.py b/alexBot/cogs/utils.py index 0def43a..87957ba 100644 --- a/alexBot/cogs/utils.py +++ b/alexBot/cogs/utils.py @@ -41,7 +41,10 @@ async def roll(self, interaction: discord.Interaction, dice: str): return await interaction.response.send_message( "You can't roll more than 100 dice at once!", ephemeral=True ) - except Exception: + except (TypeError + , ValueError + , discord.HTTPException + , discord.InteractionResponded): return await interaction.response.send_message("Format has to be in `WdX YdZ`...!", ephemeral=True) roll_results.append(Roll(f"{rolls}d{limit}", [random.randint(1, limit) for r in range(rolls)])) From 417ea6de6853e1a6a29fde89d8d68a37032261c8 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Tue, 3 Dec 2024 20:58:03 +0200 Subject: [PATCH 08/21] alexBot\cogs\flight.py too-many-branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Method mater of class Flight had 18 branches while Pylint recommends having at most 12. I extracted the methods _compute_magdec and _handle_wind to make the code more structured and solve that. --- alexBot/cogs/flight.py | 59 +++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/alexBot/cogs/flight.py b/alexBot/cogs/flight.py index 6631b05..fd88e42 100644 --- a/alexBot/cogs/flight.py +++ b/alexBot/cogs/flight.py @@ -53,17 +53,7 @@ async def metar(self, interaction: discord.Interaction, station: str): f"please only use this data for planning purposes." ) - magdec = None - if metar.data.wind_direction.value is not None: - declination = self.wmm.calc_mag_field( - location.latitude, location.longitude, location.elevation_ft - ).declination - - magdec = declination + int(metar.data.wind_direction.value) # add the magdec to the direction of the wind - if magdec > 360: # if the declaration ends up being more than 360, subtract the extra. - magdec = magdec - 360 - elif magdec < 0: # same as above, but for less than 0 condition. - magdec = magdec + 360 + magdec = self._compute_magdec(location, metar) embed.title = f"{location.name}, {location.country} ({location.icao})" @@ -74,6 +64,26 @@ async def metar(self, interaction: discord.Interaction, station: str): if translations.clouds != "": embed.add_field(name="Clouds", value=translations.clouds, inline=False) + self._handle_wind(metar, embed, magdec, translations) + + if translations.altimeter != "": + embed.add_field(name="Altimeter", value=translations.altimeter, inline=False) + + if translations.temperature != "": + embed.add_field(name="Temperature", value=translations.temperature, inline=False) + + embed.add_field(name="Flight Rule", value=metar.data.flight_rules, inline=False) + + if translations.visibility != "": + embed.add_field(name="Visibility", value=translations.visibility, inline=False) + + embed.timestamp = metar.data.time.dt + if metar.data.flight_rules in ["LIFR", "IFR"]: + await interaction.followup.send('you might want to reconsider flying.', embed=embed) + else: + await interaction.followup.send(embed=embed) + + def _handle_wind(self, metar, embed, magdec, translations): if translations.wind is not None: if magdec is not None: if metar.data.wind_gust is not None: @@ -96,22 +106,19 @@ async def metar(self, interaction: discord.Interaction, station: str): else: embed.add_field(name="Wind", value=translations.wind, inline=False) - if translations.altimeter != "": - embed.add_field(name="Altimeter", value=translations.altimeter, inline=False) - - if translations.temperature != "": - embed.add_field(name="Temperature", value=translations.temperature, inline=False) - - embed.add_field(name="Flight Rule", value=metar.data.flight_rules, inline=False) - - if translations.visibility != "": - embed.add_field(name="Visibility", value=translations.visibility, inline=False) + def _compute_magdec(self, location, metar): + magdec = None + if metar.data.wind_direction.value is not None: + declination = self.wmm.calc_mag_field( + location.latitude, location.longitude, location.elevation_ft + ).declination - embed.timestamp = metar.data.time.dt - if metar.data.flight_rules in ["LIFR", "IFR"]: - await interaction.followup.send('you might want to reconsider flying.', embed=embed) - else: - await interaction.followup.send(embed=embed) + magdec = declination + int(metar.data.wind_direction.value) # add the magdec to the direction of the wind + if magdec > 360: # if the declaration ends up being more than 360, subtract the extra. + magdec = magdec - 360 + elif magdec < 0: # same as above, but for less than 0 condition. + magdec = magdec + 360 + return magdec @app_commands.command(name="taf") async def taf(self, interaction: discord.Interaction, station: str): From b87edcb3a5e0577099fae51d3effc8e98df5307f Mon Sep 17 00:00:00 2001 From: evidencebp Date: Tue, 3 Dec 2024 21:43:16 +0200 Subject: [PATCH 09/21] alexBot\cogs\mudae.py too-many-branches Method extract_series of class Mudae had 14 branches while Pylint recommends having at most 12. I extracted the method _scan_pages to make the code more structured and solve that. --- alexBot/cogs/mudae.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/alexBot/cogs/mudae.py b/alexBot/cogs/mudae.py index 61cd85e..db5433b 100644 --- a/alexBot/cogs/mudae.py +++ b/alexBot/cogs/mudae.py @@ -237,19 +237,7 @@ async def extract_series(self, interaction: discord.Interaction, message: discor # ^ captures first page await interaction.response.send_message("tab through your liked series, i'll save them!", ephemeral=True) - while current_page < total_pages: - - # get the next page - payload = await self.bot.wait_for( - "raw_message_edit", - check=lambda payload: payload.message_id == message.id, - timeout=60, - ) - serieses.extend(SERIES_REGEX.findall(payload.data['embeds'][0]['description'])) # type: ignore ; checked above if embed exists - # captures pages 2 thru n - current_page += 1 - if current_page == total_pages: - break + await self._scan_pages(message, serieses, current_page, total_pages) else: serieses.extend(SERIES_REGEX.findall(message.embeds[0].description)) # type: ignore # captures single page @@ -275,6 +263,20 @@ async def extract_series(self, interaction: discord.Interaction, message: discor else: await interaction.response.send_message(reply_text, ephemeral=True) + async def _scan_pages(self, message, serieses, current_page, total_pages): + while current_page < total_pages: + # get the next page + payload = await self.bot.wait_for( + "raw_message_edit", + check=lambda payload: payload.message_id == message.id, + timeout=60, + ) + serieses.extend(SERIES_REGEX.findall(payload.data['embeds'][0]['description'])) # type: ignore ; checked above if embed exists + # captures pages 2 thru n + current_page += 1 + if current_page == total_pages: + break + async def setup(bot: "Bot"): await bot.add_cog(Mudae(bot)) From 6599139c7912841ec69257a9e572665d31ac439c Mon Sep 17 00:00:00 2001 From: evidencebp Date: Tue, 3 Dec 2024 21:55:41 +0200 Subject: [PATCH 10/21] alexBot\disabled\feedreader.py broad-exception-caught MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Catching Exception might hide unexpected exceptions (e.g., due to new code that will be added). The method nerdiowoFeed of the class FeedReader catches exception (line 103). The try section seems to protect discord's feedparser.parse See here its exceptions https://stackoverflow.com/questions/46641848/feedparser-returns-a-saxparseexception I narrowed the Exception to the specific SAXParseException . --- alexBot/disabled/feedreader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alexBot/disabled/feedreader.py b/alexBot/disabled/feedreader.py index 452f788..4fc7b1e 100644 --- a/alexBot/disabled/feedreader.py +++ b/alexBot/disabled/feedreader.py @@ -100,7 +100,7 @@ async def nerdiowoFeed(self, interaction: discord.Interaction, feedurl: str, tag text = await get_text(session, feedurl) try: feed = feedparser.parse(text) - except Exception as e: + except SAXParseException as e: await interaction.response.send_message("Invalid feed!", ephemeral=True) return feeds.append(FeedConfig(tag if tag is not None else None, feedurl)) From 0bd1537971968692ccbfae936ea50668aeab8029 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Wed, 4 Dec 2024 16:18:27 +0200 Subject: [PATCH 11/21] alexBot\cogs\errors.py too-many-branches Method on_command_error of class CommandErrorHandler had 19 branches while Pylint recommends having at most 12. I aggregated error to message mapping and extracted methods to make the code more structured and solve that. --- alexBot/cogs/errors.py | 82 ++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/alexBot/cogs/errors.py b/alexBot/cogs/errors.py index a63f700..8d81b84 100644 --- a/alexBot/cogs/errors.py +++ b/alexBot/cogs/errors.py @@ -42,6 +42,16 @@ async def on_app_command_error(self, interaction: Interaction, error: AppCommand @Cog.listener() async def on_command_error(self, ctx: commands.Context, error: commands.CommandError): + + error_messages = {str(commands.DisabledCommand): f'{ctx.command} has been disabled.' + , str(commands.NotOwner): f'{ctx.command} is a owner only command.' + , str(commands.NoPrivateMessage): f'{ctx.command} can not be used in Private Messages.' + , str(commands.CheckFailure): 'A Check failed for this command.' + , str(commands.MissingRequiredArgument): + f'Parameter {error.param} is required but missing, See {ctx.prefix}help {ctx.command} for help!' + , str(commands.MissingPermissions): 'You do not have permission to run that command.' + } + """The event triggered when an error is raised while invoking a command.""" if isinstance(error, commands.CommandNotFound): return @@ -50,47 +60,22 @@ async def on_command_error(self, ctx: commands.Context, error: commands.CommandE if isinstance(error, asyncio.TimeoutError): msg = f"timed out. you can start again with {ctx.prefix}{ctx.command}" - if isinstance(error, commands.MaxConcurrencyReached): - if ctx.author.id == 335928292542513162 and random.random() < 0.2: - msg = "DAWN PLS" - else: - msg = f"{ctx.command} is currently being ran. please wait for it to finish." - - if isinstance(error, commands.CommandOnCooldown): - if ctx.author.id == 335928292542513162 and random.random() < 0.2: - msg = "DAWN PLS" - else: - msg = f"{ctx.command} is being used too often, try again later" - - if isinstance(error, commands.DisabledCommand): - msg = f'{ctx.command} has been disabled.' - - elif isinstance(error, commands.NotOwner): - msg = f'{ctx.command} is a owner only command.' + msg = self._handle_load_errors(ctx, error, msg) - elif isinstance(error, commands.NoPrivateMessage): - msg = f'{ctx.command} can not be used in Private Messages.' + if (isinstance(error, commands.DisabledCommand) + or isinstance(error, commands.NotOwner) + or isinstance(error, commands.NoPrivateMessage) + or isinstance(error, commands.CheckFailure) + or isinstance(error, commands.MissingRequiredArgument) + or isinstance(error, commands.MissingPermissions)): + msg = error_messages[str(error)] - elif isinstance(error, commands.BadArgument): + if isinstance(error, commands.BadArgument): ctx.command.reset_cooldown(ctx) msg = f'Bad argument: {error} See {ctx.prefix}help {ctx.command} for help!' log.warning(f"bad argument on {ctx.command}: {error}") - elif isinstance(error, commands.CheckFailure): - msg = 'A Check failed for this command.' - - elif isinstance(error, commands.MissingRequiredArgument): - msg = f'Parameter {error.param} is required but missing, See {ctx.prefix}help {ctx.command} for help!' - elif isinstance(error, commands.MissingPermissions): - msg = 'You do not have permission to run that command.' - elif isinstance(error, commands.CommandInvokeError): - error = error.original - - if isinstance(error, discord.Forbidden): - msg = ( - 'A permission error occurred while executing this command, ' - 'Make sure I have the required permissions and try again.' - ) + error, msg = self._hadle_invoke_error(error) # post the error into the chat if no short error message could be generated if not msg: @@ -113,6 +98,33 @@ async def on_command_error(self, ctx: commands.Context, error: commands.CommandE except discord.HTTPException: await ctx.send('error message too long') + def _handle_load_errors(self, ctx, error, msg): + if isinstance(error, commands.MaxConcurrencyReached): + if ctx.author.id == 335928292542513162 and random.random() < 0.2: + msg = "DAWN PLS" + else: + msg = f"{ctx.command} is currently being ran. please wait for it to finish." + + if isinstance(error, commands.CommandOnCooldown): + if ctx.author.id == 335928292542513162 and random.random() < 0.2: + msg = "DAWN PLS" + else: + msg = f"{ctx.command} is being used too often, try again later" + + return msg + + def _hadle_invoke_error(self, error): + if isinstance(error, commands.CommandInvokeError): + error = error.original + + if isinstance(error, discord.Forbidden): + msg = ( + 'A permission error occurred while executing this command, ' + 'Make sure I have the required permissions and try again.' + ) + + return error,msg + async def setup(bot: commands.Bot): await bot.add_cog(CommandErrorHandler(bot)) From ebf50570c299241310030751a1ce38641e97e61c Mon Sep 17 00:00:00 2001 From: evidencebp Date: Wed, 4 Dec 2024 16:56:46 +0200 Subject: [PATCH 12/21] alexBot\cogs\voiceStats.py too-many-branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Method on_voice_state_update of class VoiceStats  had 14 branches while Pylint recommends having at most 12. Some of the ifs assign the result into a variable. I used a direct assignment of the Boolean if expression instead. --- alexBot/cogs/voiceStats.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/alexBot/cogs/voiceStats.py b/alexBot/cogs/voiceStats.py index a13a88b..5ec6848 100644 --- a/alexBot/cogs/voiceStats.py +++ b/alexBot/cogs/voiceStats.py @@ -52,19 +52,13 @@ async def on_voice_state_update( await session.commit() # ?? are we getting an event for someone leaving? - if before.channel: - LEAVING = True - else: - LEAVING = False + LEAVING = bool(before.channel) + # ?? were they the last person? - if len([m for m in channel.members if not m.bot]) == 0: - LAST = True - else: - LAST = False - if not LEAVING and len([m for m in after.channel.members if not m.bot]) == 1: # type: ignore # after.channel is gareted if not leaving - FIRST = True - else: - FIRST = False + LAST = len([m for m in channel.members if not m.bot]) == 0 + + FIRST = not LEAVING and len([m for m in after.channel.members if not m.bot]) == 1 # type: ignore # after.channel is gareted if not leaving + if LEAVING and LAST and gc.collectVoiceData: # definitly ending of a call await self.ending_a_call(channel, session) From ae62642038179bdac1c0955d5fff07bdc42d1d5e Mon Sep 17 00:00:00 2001 From: evidencebp Date: Wed, 4 Dec 2024 16:57:31 +0200 Subject: [PATCH 13/21] alexBot\cogs\smartHome.py too-many-statements Method on_voice_state_update of class PhoneMonitor had 66 statements while Pylint recommends having at most 50. I extracted methods to make the code more structured and solve that. --- alexBot/cogs/smartHome.py | 52 ++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/alexBot/cogs/smartHome.py b/alexBot/cogs/smartHome.py index d5dfb99..c11f0da 100644 --- a/alexBot/cogs/smartHome.py +++ b/alexBot/cogs/smartHome.py @@ -253,29 +253,9 @@ async def on_voice_state_update( message = f"{member.display_name} left {before.channel.name}" memberList = before.channel.members - if before.channel and after.channel and (before.channel != after.channel): - if user in [user.id for user in before.channel.members]: - # person left chat to another channel in server - log.debug(f"{member.display_name} moved from {before.channel.name} to {after.channel.name}") - message = f"{member.display_name} was moved to {after.channel.name}" - memberList = before.channel.members - if user in [user.id for user in after.channel.members]: - # person joined chat from another channel in server - if SELF_MOVED: - log.debug(f"Self moved from {before.channel.name} to {after.channel.name}") - message = f"you were moved to {after.channel.name}" - else: - log.debug(f"{member.display_name} moved from {before.channel.name} to {after.channel.name}") - message = f"{member.display_name} joined {after.channel.name}" + message, memberList = self._handle_different_channels(member, before, after, user, SELF_MOVED, message, memberList) - memberList = after.channel.members - - if before.channel and not after.channel and user in [user.id for user in before.channel.members]: - # person left chat - log.debug(f"{member.display_name} left {before.channel.name}") - message = f"{member.display_name} left {before.channel.name}" - memberList = before.channel.members - pass + message, memberList = self._handle_leave_chat(member, before, after, user, message, memberList) if not before.channel and after.channel and user in [user.id for user in after.channel.members]: # person joined chat log.debug(f"{member.display_name} joined {after.channel.name}") @@ -288,6 +268,34 @@ async def on_voice_state_update( after.channel = oldAfter + def _handle_different_channels(self, member, before, after, user, SELF_MOVED, message, memberList): + if before.channel and after.channel and (before.channel != after.channel): + if user in [user.id for user in before.channel.members]: + # person left chat to another channel in server + log.debug(f"{member.display_name} moved from {before.channel.name} to {after.channel.name}") + message = f"{member.display_name} was moved to {after.channel.name}" + memberList = before.channel.members + if user in [user.id for user in after.channel.members]: + # person joined chat from another channel in server + if SELF_MOVED: + log.debug(f"Self moved from {before.channel.name} to {after.channel.name}") + message = f"you were moved to {after.channel.name}" + else: + log.debug(f"{member.display_name} moved from {before.channel.name} to {after.channel.name}") + message = f"{member.display_name} joined {after.channel.name}" + + memberList = after.channel.members + return message, memberList + + def _handle_leave_chat(self, member, before, after, user, message, memberList): + if before.channel and not after.channel and user in [user.id for user in before.channel.members]: + # person left chat + log.debug(f"{member.display_name} left {before.channel.name}") + message = f"{member.display_name} left {before.channel.name}" + memberList = before.channel.members + + return message, memberList + async def send_notification(self, user_id: int, title: str, members: List[discord.Member]): log.debug(f"title: {title}") content = f"Current members in your channel are:\n{NEWLINE.join([f'{m.display_name} {render_voiceState(m)}' for m in members])}" From 56c361b07a298bfa345f1848456389ad766c161c Mon Sep 17 00:00:00 2001 From: evidencebp Date: Wed, 4 Dec 2024 17:15:58 +0200 Subject: [PATCH 14/21] alexBot\cogs\voiceTTS.py too-many-branches Method vc_tts of class VoiceTTS had 15 branches while Pylint recommends having at most 12. I extracted the validation into a method to make the code more structured and solve that. --- alexBot/cogs/voiceTTS.py | 49 ++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/alexBot/cogs/voiceTTS.py b/alexBot/cogs/voiceTTS.py index a8b7f25..d61ae97 100644 --- a/alexBot/cogs/voiceTTS.py +++ b/alexBot/cogs/voiceTTS.py @@ -187,27 +187,9 @@ async def model_autocomplete( @app_commands.autocomplete(model=model_autocomplete) async def vc_tts(self, interaction: discord.Interaction, model: str): - if interaction.guild is None: - await interaction.response.send_message("This command can only be used in a guild", ephemeral=True) - return - - if interaction.user.voice is None: - await interaction.response.send_message("You are not in a voice channel", ephemeral=True) + if not self._vc_tts_validation(interaction): return - - if interaction.guild_id in self.runningTTS: - if interaction.user.id in self.runningTTS[interaction.guild_id].users and model == "QUIT": - del self.runningTTS[interaction.guild_id].users[interaction.user.id] - await interaction.response.send_message("ended your voice tts session.", ephemeral=True) - if len(self.runningTTS[interaction.guild_id].users) == 0: - await self.runningTTS[interaction.guild_id].voiceClient.disconnect() - return - if interaction.user.voice.channel.id != self.runningTTS[interaction.guild_id].voiceClient.channel.id: - await interaction.response.send_message( - "You are not in the same voice channel as the existing session. can not start.", ephemeral=True - ) - return - + if model == "SAVED": # we pull from database, and use that async with async_session() as session: @@ -264,6 +246,33 @@ def after(self, error: Optional[Exception]): if error: log.exception(error) + async def _vc_tts_validation(self, interaction): + + valid = True + + if interaction.guild is None: + await interaction.response.send_message("This command can only be used in a guild", ephemeral=True) + valid = False + + elif interaction.user.voice is None: + await interaction.response.send_message("You are not in a voice channel", ephemeral=True) + valid = False + + elif interaction.guild_id in self.runningTTS: + if interaction.user.id in self.runningTTS[interaction.guild_id].users and model == "QUIT": + del self.runningTTS[interaction.guild_id].users[interaction.user.id] + await interaction.response.send_message("ended your voice tts session.", ephemeral=True) + if len(self.runningTTS[interaction.guild_id].users) == 0: + await self.runningTTS[interaction.guild_id].voiceClient.disconnect() + valid = False + elif interaction.user.voice.channel.id != self.runningTTS[interaction.guild_id].voiceClient.channel.id: + await interaction.response.send_message( + "You are not in the same voice channel as the existing session. can not start.", ephemeral=True + ) + valid = False + + return valid + async def setup(bot): try: From d68e0a974bba4bffc6213b86ff08f16aac3dc983 Mon Sep 17 00:00:00 2001 From: evidencebp Date: Wed, 4 Dec 2024 18:09:10 +0200 Subject: [PATCH 15/21] alexBot\cogs\voiceCommands.py too-many-branches Method vcShake of class VoiceCommands had 20 branches while Pylint recommends having at most 12. I extracted _get_channel to make the code more structured and solve that. Method vcShake of class target_autocomplete had 14 branches while Pylint recommends having at most 12. I extracted target_autocomplete_get_channel to make the code more structured and solve that. Please note that the methods _get_channel and target_autocomplete_get_channel are rather close and it might be good to merge them. Also note that in the prior code of target_autocomplete in case of not finding a channel in if channel is None: await interaction.response.send_message("No suitable channel to shake into found", ephemeral=True) return None is return On the other hand in if channel is None or interaction.user.voice.channel == channel: return [app_commands.Choice(name="err: no suitable shake channel found", value="0")] There is a more detailed value. To be more consistent I return the detailed value. --- alexBot/cogs/voiceCommands.py | 97 ++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/alexBot/cogs/voiceCommands.py b/alexBot/cogs/voiceCommands.py index 4845538..9f4b7cf 100644 --- a/alexBot/cogs/voiceCommands.py +++ b/alexBot/cogs/voiceCommands.py @@ -244,6 +244,24 @@ async def vc_move(self, interaction: discord.Interaction, channel: discord.Voice async def target_autocomplete(self, interaction: discord.Interaction, guess: str) -> List[app_commands.Choice]: if interaction.user.voice is None: return [app_commands.Choice(name="err: not in a voice channel", value="0")] + channel: discord.VoiceChannel | discord.StageChannel | None = self.target_autocomplete_get_channel(interaction) + if channel is None or interaction.user.voice.channel == channel: + return [app_commands.Choice(name="err: no suitable shake channel found", value="0")] + + valid_targets = [ + m + for m in interaction.user.voice.channel.members + if not m.bot and not m.id == interaction.user.id and not m.voice.self_stream + ] + if len(valid_targets) == 0: + return [app_commands.Choice(name="err: no valid targets", value="0")] + return [ + app_commands.Choice(name=m.display_name, value=str(m.id)) + for m in valid_targets + if guess in m.display_name.lower() or guess in m.name.lower() + ] + + async def target_autocomplete_get_channel(self, interaction): channel: discord.VoiceChannel | discord.StageChannel | None = interaction.guild.afk_channel if channel is None: if interaction.user.voice.channel.category is not None: @@ -267,22 +285,9 @@ async def target_autocomplete(self, interaction: discord.Interaction, guess: str break if channel is None: await interaction.response.send_message("No suitable channel to shake into found", ephemeral=True) - return - if channel is None or interaction.user.voice.channel == channel: - return [app_commands.Choice(name="err: no suitable shake channel found", value="0")] - - valid_targets = [ - m - for m in interaction.user.voice.channel.members - if not m.bot and not m.id == interaction.user.id and not m.voice.self_stream - ] - if len(valid_targets) == 0: - return [app_commands.Choice(name="err: no valid targets", value="0")] - return [ - app_commands.Choice(name=m.display_name, value=str(m.id)) - for m in valid_targets - if guess in m.display_name.lower() or guess in m.name.lower() - ] + return None + + return channel @app_commands.guild_only() @app_commands.autocomplete(target=target_autocomplete) @@ -300,33 +305,8 @@ async def vcShake(self, interaction: discord.Interaction, target: str): if interaction.user.voice is None: await interaction.response.send_message("you are not in a voice channel", ephemeral=True) return - channel: discord.VoiceChannel | discord.StageChannel | None = interaction.guild.afk_channel + channel: discord.VoiceChannel | discord.StageChannel | None = self._get_channel(interaction) if channel is None: - if interaction.user.voice.channel.category is not None: - for chan in interaction.user.voice.channel.category.channels: - if ( - (isinstance(chan, discord.VoiceChannel) or isinstance(chan, discord.StageChannel)) - and len(chan.members) == 0 - and chan.permissions_for(interaction.user).view_channel - ): - channel = chan - break - if channel is None: - for chan in interaction.guild.voice_channels: - if len(chan.members) == 0 and chan.permissions_for(interaction.user).view_channel: - channel = chan - break - if channel is None: - for chan in interaction.guild.stage_channels: - if len(chan.members) == 0 and chan.permissions_for(interaction.user).view_channel: - channel = chan - break - if channel is None: - await interaction.response.send_message("No suitable channel to shake into found", ephemeral=True) - return - - if interaction.user.voice.channel == channel: - await interaction.response.send_message("you are in the shaking channel, somehow", ephemeral=True) return valid_targets = [ @@ -361,6 +341,39 @@ async def vcShake(self, interaction: discord.Interaction, target: str): if voiceLog: del voiceLog.beingShaken[user.id] + + async def _get_channel(self, interaction): + channel: discord.VoiceChannel | discord.StageChannel | None = interaction.guild.afk_channel + if channel is None: + if interaction.user.voice.channel.category is not None: + for chan in interaction.user.voice.channel.category.channels: + if ( + (isinstance(chan, discord.VoiceChannel) or isinstance(chan, discord.StageChannel)) + and len(chan.members) == 0 + and chan.permissions_for(interaction.user).view_channel + ): + channel = chan + break + if channel is None: + for chan in interaction.guild.voice_channels: + if len(chan.members) == 0 and chan.permissions_for(interaction.user).view_channel: + channel = chan + break + if channel is None: + for chan in interaction.guild.stage_channels: + if len(chan.members) == 0 and chan.permissions_for(interaction.user).view_channel: + channel = chan + break + if channel is None: + await interaction.response.send_message("No suitable channel to shake into found", ephemeral=True) + return None + + if interaction.user.voice.channel == channel: + await interaction.response.send_message("you are in the shaking channel, somehow", ephemeral=True) + return None + + return channel + @app_commands.checks.bot_has_permissions(mute_members=True, deafen_members=True) @app_commands.checks.has_permissions(mute_members=True, deafen_members=True) async def sleep(self, interaction: discord.Interaction): From 328fe619b06487e34f2d850dc363f28fec2615ff Mon Sep 17 00:00:00 2001 From: evidencebp Date: Wed, 4 Dec 2024 18:48:46 +0200 Subject: [PATCH 16/21] alexBot\cogs\sugery.py broad-exception-caught Catching Exception might hide unexpected exceptions (e.g., due to new code that will be added). Method sugery_update of class Sugery catches exception (line 157) The try section is mainly member.edit Exception was changed to discord.ClientException, discord.Forbidde, and discord.HTTPException For details see https://discordpy.readthedocs.io/en/latest/api.html#discord.Member.edit --- alexBot/cogs/sugery.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/alexBot/cogs/sugery.py b/alexBot/cogs/sugery.py index 2a41854..f5466b7 100644 --- a/alexBot/cogs/sugery.py +++ b/alexBot/cogs/sugery.py @@ -154,7 +154,9 @@ async def sugery_update(self): nick=f"{name} ({ZAPSTR if charging else BATTERYSTR}{BATTERYINDICATORS[math.ceil(battery * 0.08)]})", reason="user's bloodsuger group or direction changed", ) - except Exception as e: + except (discord.ClientException + , discord.Forbidden + , discord.HTTPException) as e: log.error(f"cannot update {member}; {e.args[0]}") continue From c4a91afb7eaa996071795cda9d3a966fccb9f207 Mon Sep 17 00:00:00 2001 From: Alexander Terry Date: Wed, 4 Dec 2024 16:13:18 -0900 Subject: [PATCH 17/21] further refactor error handleing --- alexBot/cogs/errors.py | 84 +++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 55 deletions(-) diff --git a/alexBot/cogs/errors.py b/alexBot/cogs/errors.py index 8d81b84..58e1249 100644 --- a/alexBot/cogs/errors.py +++ b/alexBot/cogs/errors.py @@ -19,9 +19,6 @@ def cog_load(self): self._old_tree_error = tree.on_error tree.on_error = self.on_app_command_error - # -> Option 1 --- - # detaching the handler when the cog is unloaded - # this is optional for option 1 def cog_unload(self): tree = self.bot.tree tree.on_error = self._old_tree_error @@ -41,17 +38,30 @@ async def on_app_command_error(self, interaction: Interaction, error: AppCommand ) @Cog.listener() - async def on_command_error(self, ctx: commands.Context, error: commands.CommandError): - - error_messages = {str(commands.DisabledCommand): f'{ctx.command} has been disabled.' - , str(commands.NotOwner): f'{ctx.command} is a owner only command.' - , str(commands.NoPrivateMessage): f'{ctx.command} can not be used in Private Messages.' - , str(commands.CheckFailure): 'A Check failed for this command.' - , str(commands.MissingRequiredArgument): - f'Parameter {error.param} is required but missing, See {ctx.prefix}help {ctx.command} for help!' - , str(commands.MissingPermissions): 'You do not have permission to run that command.' - } - + async def on_command_error(self, ctx: commands.Context, error: Exception): + + error_messages = { + commands.DisabledCommand: lambda: (f'{ctx.command} has been disabled.', None), + commands.NotOwner: lambda: (f'{ctx.command} is a owner only command.', None), + commands.NoPrivateMessage: lambda: (f'{ctx.command} can not be used in Private Messages.', None), + commands.CheckFailure: lambda: ('A Check failed for this command.', None), + commands.MissingRequiredArgument: lambda: ( + f'Parameter {error.param} is required but missing, See {ctx.prefix}help {ctx.command} for help!', + None, + ), + commands.MissingPermissions: lambda: ('You do not have permission to run that command.', None), + commands.CommandOnCooldown: lambda: (f"{ctx.command} is being used too often, try again later", None), + commands.MaxConcurrencyReached: lambda: ( + f"{ctx.command} is currently being ran. please wait for it to finish.", + None, + ), + asyncio.TimeoutError: lambda: (f"timed out. you can start again with {ctx.prefix}{ctx.command}", None), + commands.BadArgument: lambda: ( + f'Bad argument: {error} See {ctx.prefix}help {ctx.command} for help!', + ctx.command.reset_cooldown(ctx), + ), + } + """The event triggered when an error is raised while invoking a command.""" if isinstance(error, commands.CommandNotFound): return @@ -60,22 +70,13 @@ async def on_command_error(self, ctx: commands.Context, error: commands.CommandE if isinstance(error, asyncio.TimeoutError): msg = f"timed out. you can start again with {ctx.prefix}{ctx.command}" - msg = self._handle_load_errors(ctx, error, msg) - - if (isinstance(error, commands.DisabledCommand) - or isinstance(error, commands.NotOwner) - or isinstance(error, commands.NoPrivateMessage) - or isinstance(error, commands.CheckFailure) - or isinstance(error, commands.MissingRequiredArgument) - or isinstance(error, commands.MissingPermissions)): - msg = error_messages[str(error)] + if any(isinstance(error, e) for e in error_messages): + msg = error_messages[type(error)]()[0] # type: ignore # no fail because we just checked for existing keys - if isinstance(error, commands.BadArgument): - ctx.command.reset_cooldown(ctx) - msg = f'Bad argument: {error} See {ctx.prefix}help {ctx.command} for help!' - log.warning(f"bad argument on {ctx.command}: {error}") + if isinstance(error, commands.CommandInvokeError) and isinstance(error.original, discord.Forbidden): + error = error.original - error, msg = self._hadle_invoke_error(error) + msg = 'A permission error occurred while executing this command, Make sure I have the required permissions and try again.' # post the error into the chat if no short error message could be generated if not msg: @@ -98,33 +99,6 @@ async def on_command_error(self, ctx: commands.Context, error: commands.CommandE except discord.HTTPException: await ctx.send('error message too long') - def _handle_load_errors(self, ctx, error, msg): - if isinstance(error, commands.MaxConcurrencyReached): - if ctx.author.id == 335928292542513162 and random.random() < 0.2: - msg = "DAWN PLS" - else: - msg = f"{ctx.command} is currently being ran. please wait for it to finish." - - if isinstance(error, commands.CommandOnCooldown): - if ctx.author.id == 335928292542513162 and random.random() < 0.2: - msg = "DAWN PLS" - else: - msg = f"{ctx.command} is being used too often, try again later" - - return msg - - def _hadle_invoke_error(self, error): - if isinstance(error, commands.CommandInvokeError): - error = error.original - - if isinstance(error, discord.Forbidden): - msg = ( - 'A permission error occurred while executing this command, ' - 'Make sure I have the required permissions and try again.' - ) - - return error,msg - async def setup(bot: commands.Bot): await bot.add_cog(CommandErrorHandler(bot)) From 94d024a583b08569ea73bb7dca30a81730f90bd2 Mon Sep 17 00:00:00 2001 From: Alexander Terry Date: Wed, 4 Dec 2024 16:15:44 -0900 Subject: [PATCH 18/21] fix overindent --- alexBot/cogs/mudae.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/alexBot/cogs/mudae.py b/alexBot/cogs/mudae.py index db5433b..1f4e05e 100644 --- a/alexBot/cogs/mudae.py +++ b/alexBot/cogs/mudae.py @@ -265,14 +265,14 @@ async def extract_series(self, interaction: discord.Interaction, message: discor async def _scan_pages(self, message, serieses, current_page, total_pages): while current_page < total_pages: - # get the next page + # get the next page payload = await self.bot.wait_for( - "raw_message_edit", - check=lambda payload: payload.message_id == message.id, - timeout=60, - ) + "raw_message_edit", + check=lambda payload: payload.message_id == message.id, + timeout=60, + ) serieses.extend(SERIES_REGEX.findall(payload.data['embeds'][0]['description'])) # type: ignore ; checked above if embed exists - # captures pages 2 thru n + # captures pages 2 thru n current_page += 1 if current_page == total_pages: break From e4512b4711a516d92e7bd58eb1fcac4e70b8ee05 Mon Sep 17 00:00:00 2001 From: Alexander Terry Date: Wed, 4 Dec 2024 16:19:31 -0900 Subject: [PATCH 19/21] await forgotten awaitable --- alexBot/cogs/reminders.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/alexBot/cogs/reminders.py b/alexBot/cogs/reminders.py index aea830e..22fce47 100644 --- a/alexBot/cogs/reminders.py +++ b/alexBot/cogs/reminders.py @@ -65,7 +65,7 @@ async def reminder_loop(self): async def remind(self, reminder: Reminder): await self.bot.wait_until_ready() try: - if self._wait_for_reminder(reminder): + if await self._wait_for_reminder(reminder): return allowedMentions = discord.AllowedMentions.none() log.debug(f"reminding {reminder}") @@ -74,10 +74,10 @@ async def remind(self, reminder: Reminder): except discord.NotFound: target = None - owner = self._handle_no_target(reminder, target) + owner = await self._handle_no_target(reminder, target) if not owner: return - + message = reminder.message if message.startswith("["): # random messages; @@ -110,7 +110,7 @@ async def remind(self, reminder: Reminder): if self._handle_reminder(self, reminder): return - + # remove task from tasks dict finally: del self.tasks[reminder.id] @@ -127,9 +127,8 @@ async def _handle_no_target(self, reminder, target): return None await owner.send(f"Could not find target channel {reminder.target} for reminder {reminder.message}") return owner - + return owner - async def _handle_clearing(self, reminder, allowedMentions, target, message): if reminder.require_clearing: @@ -139,7 +138,7 @@ async def _handle_clearing(self, reminder, allowedMentions, target, message): msg = await target.send(message, allowed_mentions=allowedMentions) if reminder.auto_react: await msg.add_reaction("<:greentick:1255344157761867816>") - + async def _handle_reminder(self, reminder): should_exit = False @@ -175,10 +174,10 @@ async def _reminder_clearing(self, reminder, allowedMentions, target, message): msg = None try: msg = await self.bot.wait_for( - "message", - check=lambda m: m.channel.id == target.id and m.content.lower().startswith('ack'), - timeout=300, - ) + "message", + check=lambda m: m.channel.id == target.id and m.content.lower().startswith('ack'), + timeout=300, + ) v.waiting = False v.children[0].disabled = True await msg.reply("reminder cleared") @@ -197,7 +196,7 @@ async def _reminder_clearing(self, reminder, allowedMentions, target, message): async def _wait_for_reminder(self, reminder): should_exit = False - + # wait for the reminder time now = datetime.datetime.now(datetime.UTC) if now > reminder.next_remind: @@ -226,7 +225,6 @@ async def _wait_for_reminder(self, reminder): return should_exit - @remindersGroup.command(name="add", description="add a new reminder") @app_commands.describe( message="the message for the reminder. if the message starts with a [, it will be treated as a list of messages to choose from, using ; to seperate them", From fa11a0c7d086513993201b5216077217ed9f0ab3 Mon Sep 17 00:00:00 2001 From: Alexander Terry Date: Wed, 4 Dec 2024 16:33:25 -0900 Subject: [PATCH 20/21] missing await --- alexBot/cogs/voiceCommands.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/alexBot/cogs/voiceCommands.py b/alexBot/cogs/voiceCommands.py index 9f4b7cf..c446bb9 100644 --- a/alexBot/cogs/voiceCommands.py +++ b/alexBot/cogs/voiceCommands.py @@ -244,7 +244,9 @@ async def vc_move(self, interaction: discord.Interaction, channel: discord.Voice async def target_autocomplete(self, interaction: discord.Interaction, guess: str) -> List[app_commands.Choice]: if interaction.user.voice is None: return [app_commands.Choice(name="err: not in a voice channel", value="0")] - channel: discord.VoiceChannel | discord.StageChannel | None = self.target_autocomplete_get_channel(interaction) + channel: discord.VoiceChannel | discord.StageChannel | None = await self.target_autocomplete_get_channel( + interaction + ) if channel is None or interaction.user.voice.channel == channel: return [app_commands.Choice(name="err: no suitable shake channel found", value="0")] @@ -286,7 +288,7 @@ async def target_autocomplete_get_channel(self, interaction): if channel is None: await interaction.response.send_message("No suitable channel to shake into found", ephemeral=True) return None - + return channel @app_commands.guild_only() @@ -305,7 +307,7 @@ async def vcShake(self, interaction: discord.Interaction, target: str): if interaction.user.voice is None: await interaction.response.send_message("you are not in a voice channel", ephemeral=True) return - channel: discord.VoiceChannel | discord.StageChannel | None = self._get_channel(interaction) + channel: discord.VoiceChannel | discord.StageChannel | None = await self._get_channel(interaction) if channel is None: return @@ -341,7 +343,6 @@ async def vcShake(self, interaction: discord.Interaction, target: str): if voiceLog: del voiceLog.beingShaken[user.id] - async def _get_channel(self, interaction): channel: discord.VoiceChannel | discord.StageChannel | None = interaction.guild.afk_channel if channel is None: @@ -371,7 +372,7 @@ async def _get_channel(self, interaction): if interaction.user.voice.channel == channel: await interaction.response.send_message("you are in the shaking channel, somehow", ephemeral=True) return None - + return channel @app_commands.checks.bot_has_permissions(mute_members=True, deafen_members=True) From 744ea1c8ef829119caff2dab4af08c879a5dcd48 Mon Sep 17 00:00:00 2001 From: Alexander Terry Date: Wed, 4 Dec 2024 16:40:08 -0900 Subject: [PATCH 21/21] refactor to single _get_channel --- alexBot/cogs/voiceCommands.py | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/alexBot/cogs/voiceCommands.py b/alexBot/cogs/voiceCommands.py index c446bb9..5ca46c2 100644 --- a/alexBot/cogs/voiceCommands.py +++ b/alexBot/cogs/voiceCommands.py @@ -244,9 +244,7 @@ async def vc_move(self, interaction: discord.Interaction, channel: discord.Voice async def target_autocomplete(self, interaction: discord.Interaction, guess: str) -> List[app_commands.Choice]: if interaction.user.voice is None: return [app_commands.Choice(name="err: not in a voice channel", value="0")] - channel: discord.VoiceChannel | discord.StageChannel | None = await self.target_autocomplete_get_channel( - interaction - ) + channel: discord.VoiceChannel | discord.StageChannel | None = await self._get_channel(interaction) if channel is None or interaction.user.voice.channel == channel: return [app_commands.Choice(name="err: no suitable shake channel found", value="0")] @@ -263,34 +261,6 @@ async def target_autocomplete(self, interaction: discord.Interaction, guess: str if guess in m.display_name.lower() or guess in m.name.lower() ] - async def target_autocomplete_get_channel(self, interaction): - channel: discord.VoiceChannel | discord.StageChannel | None = interaction.guild.afk_channel - if channel is None: - if interaction.user.voice.channel.category is not None: - for chan in interaction.user.voice.channel.category.channels: - if ( - (isinstance(chan, discord.VoiceChannel) or isinstance(chan, discord.StageChannel)) - and len(chan.members) == 0 - and chan.permissions_for(interaction.user).view_channel - ): - channel = chan - break - if channel is None: - for chan in interaction.guild.voice_channels: - if len(chan.members) == 0 and chan.permissions_for(interaction.user).view_channel: - channel = chan - break - if channel is None: - for chan in interaction.guild.stage_channels: - if len(chan.members) == 0 and chan.permissions_for(interaction.user).view_channel: - channel = chan - break - if channel is None: - await interaction.response.send_message("No suitable channel to shake into found", ephemeral=True) - return None - - return channel - @app_commands.guild_only() @app_commands.autocomplete(target=target_autocomplete) async def vcShake(self, interaction: discord.Interaction, target: str): @@ -369,8 +339,6 @@ async def _get_channel(self, interaction): await interaction.response.send_message("No suitable channel to shake into found", ephemeral=True) return None - if interaction.user.voice.channel == channel: - await interaction.response.send_message("you are in the shaking channel, somehow", ephemeral=True) return None return channel