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") diff --git a/alexBot/cogs/errors.py b/alexBot/cogs/errors.py index a63f700..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,7 +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): + 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 @@ -50,47 +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}" - 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.' - - elif isinstance(error, commands.NoPrivateMessage): - msg = f'{ctx.command} can not be used in Private Messages.' - - elif 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.' + 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 - 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): + if isinstance(error, commands.CommandInvokeError) and isinstance(error.original, discord.Forbidden): 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.' - ) + 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: 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): 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: diff --git a/alexBot/cogs/mudae.py b/alexBot/cogs/mudae.py index 61cd85e..1f4e05e 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)) diff --git a/alexBot/cogs/reminders.py b/alexBot/cogs/reminders.py index 6982fe1..22fce47 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 await 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 = await self._handle_no_target(reminder, target) + if not owner: return + message = reminder.message if message.startswith("["): # random messages; @@ -133,64 +106,125 @@ 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( - "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!") + await self._handle_clearing(reminder, allowedMentions, target, message) - else: - msg = await target.send(message, allowed_mentions=allowedMentions) - if reminder.auto_react: - await msg.add_reaction("<:greentick:1255344157761867816>") + if self._handle_reminder(self, reminder): + return - 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 - 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() # 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!") + 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: + # 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") + should_exit = True + else: + edited.next_remind = reminder.next_remind + session.add(edited) + await session.commit() + should_exit = True + 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()) + + 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", 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])}" 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 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)])) diff --git a/alexBot/cogs/voiceCommands.py b/alexBot/cogs/voiceCommands.py index 4845538..5ca46c2 100644 --- a/alexBot/cogs/voiceCommands.py +++ b/alexBot/cogs/voiceCommands.py @@ -244,30 +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 = 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 + 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")] @@ -300,33 +277,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 = await 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 +313,36 @@ 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 + + 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): 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) 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) 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: 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""" 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)) 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)