From d0305fcd409a070c80fe8649e709d30c55e3b4c9 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Fri, 19 Apr 2024 16:25:29 -0400 Subject: [PATCH 01/14] Hopefully fix a bug with warns --- techsupport_bot/commands/protect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techsupport_bot/commands/protect.py b/techsupport_bot/commands/protect.py index f94aa0b7e..b06c0c081 100644 --- a/techsupport_bot/commands/protect.py +++ b/techsupport_bot/commands/protect.py @@ -415,7 +415,7 @@ async def handle_warn(self, ctx, user: discord.Member, reason: str, bypass=False # Attempt DM for manually initiated, non-banning warns if ctx.command == self.bot.get_command("warn"): # Cancel warns in channels invisible to user - if user not in ctx.channel.members: + if not ctx.channel.permissions_for(user).view_channel: await auxiliary.send_deny_embed( message=f"{user} cannot see this warning.", channel=ctx.channel ) From 6ba414fefb49665837c0ca222ec5e2d2474dcd61 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Fri, 19 Apr 2024 18:00:23 -0400 Subject: [PATCH 02/14] Update News to a slash command to fix categories --- techsupport_bot/commands/news.py | 66 ++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index f009bac49..7acacbb3b 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -7,9 +7,10 @@ from typing import TYPE_CHECKING import aiocron +import discord from botlogging import LogContext, LogLevel -from core import auxiliary, cogs, extensionconfig -from discord.ext import commands +from core import cogs, extensionconfig +from discord import app_commands if TYPE_CHECKING: import bot @@ -109,6 +110,17 @@ async def get_headlines(self, country_code, category=None): async def get_random_headline(self, country_code, category=None): """Method to get a random headline for the news command.""" articles = await self.get_headlines(country_code, category) + # Filter out articles with URLs containing "https://removed.com" + articles = [ + article + for article in articles + if not article.get("url", "").startswith("https://removed.com") + ] + # Check if there are any articles left after filtering + if not articles: + return None + + # Choose a random article from the filtered list return random.choice(articles) async def execute(self, config, guild): @@ -140,30 +152,29 @@ async def wait(self, config, _): """Method to define the wait time for the news api pull.""" await aiocron.crontab(config.extensions.news.cron_config.value).next() - @commands.group( - brief="Executes a news command", - description="Executes a news command", - ) - async def news(self, ctx): - """Method to set up the news command.""" + # @commands.group( + # brief="Executes a news command", + # description="Executes a news command", + # ) + # async def news(self, ctx): + # """Method to set up the news command.""" - # Executed if there are no/invalid args supplied - await auxiliary.extension_help(self, ctx, self.__module__[9:]) + # # Executed if there are no/invalid args supplied + # await auxiliary.extension_help(self, ctx, self.__module__[9:]) - @news.command( - name="random", - brief="Gets a random news article", + @app_commands.command( + name="news", description="Gets a random news headline", - usage="[category] (optional)", + extras={"module": "news"}, ) - async def random(self, ctx, category=None): + async def news_command(self, interaction: discord.Interaction, category: str = ""): """Method to define the random to get a news.""" if category is None or category.lower() not in self.valid_category: category = random.choice(list(Category)).value else: category.lower() - config = self.bot.guild_configs[str(ctx.guild.id)] + config = self.bot.guild_configs[str(interaction.guild.id)] url = None while not url: @@ -175,4 +186,25 @@ async def random(self, ctx, category=None): if url.endswith("/"): url = url[:-1] - await ctx.send(content=url) + await interaction.response.send_message(content=url) + + @news_command.autocomplete("category") + async def news_autocompletion( + self, interaction: discord.Interaction, current: str + ) -> list: + """This command creates a list of categories for autocomplete the news command. + + Args: + interaction (discord.Interaction): The interaction that started the command + current (str): The current input from the user. + + Returns: + The list of autocomplete for the news command. + """ + news_category = [] + for category in Category: + if current.lower() in category.value.lower(): + news_category.append( + app_commands.Choice(name=category.value, value=category.value) + ) + return news_category From 92788f61c8e3e7053b5f3a2c85c19f9848ce39ac Mon Sep 17 00:00:00 2001 From: TheKrol Date: Fri, 19 Apr 2024 18:01:48 -0400 Subject: [PATCH 03/14] Removing just plain news command comment --- techsupport_bot/commands/news.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index 7acacbb3b..3ad78a3de 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -152,16 +152,6 @@ async def wait(self, config, _): """Method to define the wait time for the news api pull.""" await aiocron.crontab(config.extensions.news.cron_config.value).next() - # @commands.group( - # brief="Executes a news command", - # description="Executes a news command", - # ) - # async def news(self, ctx): - # """Method to set up the news command.""" - - # # Executed if there are no/invalid args supplied - # await auxiliary.extension_help(self, ctx, self.__module__[9:]) - @app_commands.command( name="news", description="Gets a random news headline", From 857452c026df895699685c9d67823ea2f472277d Mon Sep 17 00:00:00 2001 From: TheKrol Date: Fri, 19 Apr 2024 19:01:36 -0400 Subject: [PATCH 04/14] Update filtered articles --- techsupport_bot/commands/news.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index 3ad78a3de..77c4ca2d0 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -5,9 +5,11 @@ import enum import random from typing import TYPE_CHECKING +from urllib.parse import urlparse import aiocron import discord +import munch from botlogging import LogContext, LogLevel from core import cogs, extensionconfig from discord import app_commands @@ -107,21 +109,23 @@ async def get_headlines(self, country_code, category=None): return articles - async def get_random_headline(self, country_code, category=None): + async def get_random_headline(self, country_code, category=None) -> munch.Munch: """Method to get a random headline for the news command.""" articles = await self.get_headlines(country_code, category) - # Filter out articles with URLs containing "https://removed.com" - articles = [ - article - for article in articles - if not article.get("url", "").startswith("https://removed.com") - ] + + # Filter out articles with URLs containing "removed.com" + filtered_articles = [] + for article in articles: + host = urlparse(article.get("url", "")).hostname + if host and not host.endswith(".removed.com"): + filtered_articles.append(article) + # Check if there are any articles left after filtering if not articles: return None # Choose a random article from the filtered list - return random.choice(articles) + return random.choice(filtered_articles) async def execute(self, config, guild): """Method to execute the news command.""" From 408fe653a28d4691e2601ab75ce863ea188c9f04 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Fri, 19 Apr 2024 19:33:21 -0400 Subject: [PATCH 05/14] update news filter --- techsupport_bot/commands/news.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index 77c4ca2d0..c68363315 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -116,12 +116,12 @@ async def get_random_headline(self, country_code, category=None) -> munch.Munch: # Filter out articles with URLs containing "removed.com" filtered_articles = [] for article in articles: - host = urlparse(article.get("url", "")).hostname - if host and not host.endswith(".removed.com"): + url = article.get("url", "") + if url != "https://removed.com": filtered_articles.append(article) # Check if there are any articles left after filtering - if not articles: + if not filtered_articles: return None # Choose a random article from the filtered list @@ -141,6 +141,9 @@ async def execute(self, config, guild): ) url = article.get("url") + if article is None: + return + log_channel = config.get("logging_channel") await self.bot.logger.send_log( message=f"Sending news headline to #{channel.name}", @@ -177,6 +180,9 @@ async def news_command(self, interaction: discord.Interaction, category: str = " ) url = article.get("url") + if article is None: + return + if url.endswith("/"): url = url[:-1] From 1e4aabbe94c4b5d4fb575460625bc788a2dc4447 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Fri, 19 Apr 2024 19:35:03 -0400 Subject: [PATCH 06/14] update for pylint --- techsupport_bot/commands/news.py | 1 - 1 file changed, 1 deletion(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index c68363315..7e1ae596e 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -5,7 +5,6 @@ import enum import random from typing import TYPE_CHECKING -from urllib.parse import urlparse import aiocron import discord From 60050a2614d38dbbe415a7088c66c24261c1caff Mon Sep 17 00:00:00 2001 From: TheKrol Date: Tue, 30 Apr 2024 10:26:48 -0400 Subject: [PATCH 07/14] Reverting protect change --- techsupport_bot/commands/protect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techsupport_bot/commands/protect.py b/techsupport_bot/commands/protect.py index b06c0c081..f94aa0b7e 100644 --- a/techsupport_bot/commands/protect.py +++ b/techsupport_bot/commands/protect.py @@ -415,7 +415,7 @@ async def handle_warn(self, ctx, user: discord.Member, reason: str, bypass=False # Attempt DM for manually initiated, non-banning warns if ctx.command == self.bot.get_command("warn"): # Cancel warns in channels invisible to user - if not ctx.channel.permissions_for(user).view_channel: + if user not in ctx.channel.members: await auxiliary.send_deny_embed( message=f"{user} cannot see this warning.", channel=ctx.channel ) From a21597dfcfad6b5af4744ecd84b3771d2944c26b Mon Sep 17 00:00:00 2001 From: TheKrol Date: Wed, 21 Aug 2024 19:58:26 -0400 Subject: [PATCH 08/14] Make it so it stops logging every autocomplete --- techsupport_bot/bot.py | 2 ++ techsupport_bot/commands/news.py | 37 ++++++++++++++++---------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/techsupport_bot/bot.py b/techsupport_bot/bot.py index b4413f4b5..40c342123 100644 --- a/techsupport_bot/bot.py +++ b/techsupport_bot/bot.py @@ -963,6 +963,8 @@ async def slash_command_log(self: Self, interaction: discord.Interaction) -> Non Args: interaction (discord.Interaction): The interaction the slash command generated """ + if interaction.type != discord.InteractionType.application_command: + return embed = discord.Embed() embed.add_field(name="User", value=interaction.user) embed.add_field( diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index df19cf163..984cb52a0 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -204,23 +204,8 @@ async def wait(self: Self, config: munch.Munch, _: discord.Guild) -> None: """ await aiocron.crontab(config.extensions.news.cron_config.value).next() - @commands.group( - brief="Executes a news command", - description="Executes a news command", - ) - async def news(self: Self, ctx: commands.Context) -> None: - """The bare .news command. This does nothing but generate the help message - - Args: - ctx (commands.Context): The context in which the command was run in - """ - - # Executed if there are no/invalid args supplied - await auxiliary.extension_help(self, ctx, self.__module__[9:]) - - @news.command( - name="random", - brief="Gets a random news article", + @app_commands.command( + name="news", description="Gets a random news headline", extras={"module": "news"}, ) @@ -228,10 +213,12 @@ async def news_command(self, interaction: discord.Interaction, category: str = " """Discord command entry point for getting a news article Args: - ctx (commands.Context): The context in which the command was run + interaction (discord.Interaction): The interaction in which the command was run category (str, optional): The category to get news headlines from. Defaults to None. """ + # Debug statement + print("Executing news command") if category is None or category.lower() not in self.valid_category: category = random.choice(list(Category)).value else: @@ -254,6 +241,18 @@ async def news_command(self, interaction: discord.Interaction, category: str = " await interaction.response.send_message(content=url) + # Log the command execution + log_channel = config.get("logging_channel") + if log_channel: + await self.bot.logger.send_log( + message=f"News command executed: Sent a news headline to {interaction.channel.name}", + level=LogLevel.INFO, + context=LogContext( + guild=interaction.guild, channel=interaction.channel + ), + channel=log_channel, + ) + @news_command.autocomplete("category") async def news_autocompletion( self, interaction: discord.Interaction, current: str @@ -267,6 +266,8 @@ async def news_autocompletion( Returns: The list of autocomplete for the news command. """ + # Debug statement + print("Autocomplete interaction") news_category = [] for category in Category: if current.lower() in category.value.lower(): From 0415030685cf7d13e60049d79b09a0ffeac11ccb Mon Sep 17 00:00:00 2001 From: TheKrol Date: Wed, 21 Aug 2024 20:04:19 -0400 Subject: [PATCH 09/14] Formatting update --- techsupport_bot/commands/news.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index 984cb52a0..3a3cc572e 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -245,7 +245,8 @@ async def news_command(self, interaction: discord.Interaction, category: str = " log_channel = config.get("logging_channel") if log_channel: await self.bot.logger.send_log( - message=f"News command executed: Sent a news headline to {interaction.channel.name}", + message = f"""News command executed: + Sent a news headline to {interaction.channel.name}""" level=LogLevel.INFO, context=LogContext( guild=interaction.guild, channel=interaction.channel From 6b6c96fb05482929c6a014eb34c06d976e65a619 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Wed, 21 Aug 2024 20:09:45 -0400 Subject: [PATCH 10/14] More formatting changes --- techsupport_bot/commands/news.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index 3a3cc572e..cdcc10886 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -245,8 +245,10 @@ async def news_command(self, interaction: discord.Interaction, category: str = " log_channel = config.get("logging_channel") if log_channel: await self.bot.logger.send_log( - message = f"""News command executed: - Sent a news headline to {interaction.channel.name}""" + message=( + f"News command executed: " + f"Sent a news headline to {interaction.channel.name}" + ), level=LogLevel.INFO, context=LogContext( guild=interaction.guild, channel=interaction.channel From 4822075f36a9d4be4b66958b346a6af99b9725f3 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Wed, 21 Aug 2024 20:19:52 -0400 Subject: [PATCH 11/14] Flake8 formatting update --- techsupport_bot/commands/news.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index cdcc10886..145684521 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -209,7 +209,9 @@ async def wait(self: Self, config: munch.Munch, _: discord.Guild) -> None: description="Gets a random news headline", extras={"module": "news"}, ) - async def news_command(self, interaction: discord.Interaction, category: str = ""): + async def news_command( + self: Self, interaction: discord.Interaction, category: str = "" + ) -> None: """Discord command entry point for getting a news article Args: @@ -258,7 +260,7 @@ async def news_command(self, interaction: discord.Interaction, category: str = " @news_command.autocomplete("category") async def news_autocompletion( - self, interaction: discord.Interaction, current: str + self: Self, interaction: discord.Interaction, current: str ) -> list: """This command creates a list of categories for autocomplete the news command. @@ -267,7 +269,7 @@ async def news_autocompletion( current (str): The current input from the user. Returns: - The list of autocomplete for the news command. + ['list']: The list of autocomplete for the news command. """ # Debug statement print("Autocomplete interaction") From 182de0b20c296c101deab118a4db712b2fe5b100 Mon Sep 17 00:00:00 2001 From: TheKrol Date: Wed, 21 Aug 2024 20:21:16 -0400 Subject: [PATCH 12/14] Small fix for flake8 --- techsupport_bot/commands/news.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index 145684521..f1030d829 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -269,7 +269,7 @@ async def news_autocompletion( current (str): The current input from the user. Returns: - ['list']: The list of autocomplete for the news command. + list: The list of autocomplete for the news command. """ # Debug statement print("Autocomplete interaction") From 1183cf9f77cbb32946b079bae5b5aa40165f279d Mon Sep 17 00:00:00 2001 From: ajax146 <31014239+ajax146@users.noreply.github.com> Date: Sun, 4 May 2025 12:21:05 -0400 Subject: [PATCH 13/14] Hacky rate limit error fix --- techsupport_bot/commands/news.py | 20 +++++++++++++++----- techsupport_bot/core/custom_errors.py | 15 +++++++++++++++ techsupport_bot/core/http.py | 3 +++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/techsupport_bot/commands/news.py b/techsupport_bot/commands/news.py index f1030d829..957d12629 100644 --- a/techsupport_bot/commands/news.py +++ b/techsupport_bot/commands/news.py @@ -108,13 +108,17 @@ async def preconfig(self: Self) -> None: self.valid_category.append(item.value) async def get_headlines( - self: Self, country_code: str, category: str = None + self: Self, + country_code: str, + category: str = None, + is_interaction: bool = False, ) -> list[munch.Munch]: """Calls the API to get the list of headlines based on the category and country Args: country_code (str): The country code to get headlines from category (str, optional): The category of headlines to get. Defaults to None. + is_interaction (bool): If the headline is being called from an interaction Returns: list[munch.Munch]: The list of article objects from the API @@ -126,7 +130,9 @@ async def get_headlines( if category: url = f"{url}&category={category}" - response = await self.bot.http_functions.http_call("get", url) + response = await self.bot.http_functions.http_call( + "get", url, use_app_error=is_interaction + ) articles = response.get("articles") if not articles: @@ -134,19 +140,23 @@ async def get_headlines( return articles async def get_random_headline( - self: Self, country_code: str, category: str = None + self: Self, + country_code: str, + category: str = None, + is_interaction: bool = False, ) -> munch.Munch: """Gets a single article object from the news API Args: country_code (str): The country code of the headliens to get category (str, optional): The category of headlines to get. Defaults to None. + is_interaction (bool): If the headline is being called from an interaction Returns: munch.Munch: The raw API object representing a news headline """ - articles = await self.get_headlines(country_code, category) + articles = await self.get_headlines(country_code, category, is_interaction) # Filter out articles with URLs containing "removed.com" filtered_articles = [] @@ -231,7 +241,7 @@ async def news_command( url = None while not url: article = await self.get_random_headline( - config.extensions.news.country.value, category + config.extensions.news.country.value, category, True ) url = article.get("url") diff --git a/techsupport_bot/core/custom_errors.py b/techsupport_bot/core/custom_errors.py index f90f7f009..a1ff53e28 100644 --- a/techsupport_bot/core/custom_errors.py +++ b/techsupport_bot/core/custom_errors.py @@ -67,6 +67,17 @@ def __init__(self: Self, wait: int) -> None: self.wait = wait +class HTTPRateLimitAppCommand(app_commands.CommandInvokeError): + """An API call is on rate limit + + Args: + wait (int): The amount of seconds left until the rate limit expires + """ + + def __init__(self: Self, wait: int) -> None: + self.wait = wait + + class ErrorResponse: """Object for generating a custom error message from an exception. @@ -252,6 +263,10 @@ def get_message(self: Self, exception: Exception = None) -> str: "That API is on cooldown. Try again in %.2f seconds", {"key": "wait"}, ), + HTTPRateLimitAppCommand: ErrorResponse( + "That API is on cooldown. Try again in %.2f seconds", + {"key": "wait"}, + ), # -Custom errors- FactoidNotFoundError: ErrorResponse( "I couldn't find the factoid `%s`", {"key": "argument"} diff --git a/techsupport_bot/core/http.py b/techsupport_bot/core/http.py index 33bc89ba6..48a55b21f 100644 --- a/techsupport_bot/core/http.py +++ b/techsupport_bot/core/http.py @@ -106,6 +106,7 @@ async def http_call( """ # Get the URL not the endpoint being called + use_app_error = kwargs.pop("use_app_error", False) ignore_rate_limit = False root_url = urlparse(url).netloc @@ -138,6 +139,8 @@ async def http_call( now - self.url_rate_limit_history[root_url][0] ) time_to_wait = max(time_to_wait, 0) + if use_app_error: + raise custom_errors.HTTPRateLimitAppCommand(time_to_wait) raise custom_errors.HTTPRateLimit(time_to_wait) # Add an entry for this call with the timestamp the call was placed From 7dc47a1dcc20a192e1298d5c581114b0047cc6cc Mon Sep 17 00:00:00 2001 From: ajax146 <31014239+ajax146@users.noreply.github.com> Date: Sun, 4 May 2025 12:22:05 -0400 Subject: [PATCH 14/14] Update docstring --- techsupport_bot/core/http.py | 1 + 1 file changed, 1 insertion(+) diff --git a/techsupport_bot/core/http.py b/techsupport_bot/core/http.py index 48a55b21f..ffe747174 100644 --- a/techsupport_bot/core/http.py +++ b/techsupport_bot/core/http.py @@ -100,6 +100,7 @@ async def http_call( Raises: HTTPRateLimit: Raised if the API is currently on cooldown + HTTPRateLimitAppCommand: Raised if the API is currently on cooldown Returns: munch.Munch: The munch object containing the response from the API