Skip to content

Commit 8e41745

Browse files
TheKrolajax146dkay0670
authored
Move news random to be a slash command with autofill (#969)
* Hopefully fix a bug with warns * Update News to a slash command to fix categories * Removing just plain news command comment * Update filtered articles * update news filter * update for pylint * Reverting protect change * Make it so it stops logging every autocomplete * Formatting update * More formatting changes * Flake8 formatting update * Small fix for flake8 * Hacky rate limit error fix * Update docstring --------- Co-authored-by: ajax146 <31014239+ajax146@users.noreply.github.com> Co-authored-by: dkay <dkay@nortnet.org>
1 parent 99f5d14 commit 8e41745

4 files changed

Lines changed: 109 additions & 30 deletions

File tree

techsupport_bot/bot.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,8 @@ async def slash_command_log(self: Self, interaction: discord.Interaction) -> Non
978978
Args:
979979
interaction (discord.Interaction): The interaction the slash command generated
980980
"""
981+
if interaction.type != discord.InteractionType.application_command:
982+
return
981983
embed = discord.Embed()
982984
embed.add_field(name="User", value=interaction.user)
983985
embed.add_field(

techsupport_bot/commands/news.py

Lines changed: 88 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
import discord
1111
import munch
1212
from botlogging import LogContext, LogLevel
13-
from core import auxiliary, cogs, extensionconfig
14-
from discord.ext import commands
13+
from core import cogs, extensionconfig
14+
from discord import app_commands
1515

1616
if TYPE_CHECKING:
1717
import bot
@@ -108,13 +108,17 @@ async def preconfig(self: Self) -> None:
108108
self.valid_category.append(item.value)
109109

110110
async def get_headlines(
111-
self: Self, country_code: str, category: str = None
111+
self: Self,
112+
country_code: str,
113+
category: str = None,
114+
is_interaction: bool = False,
112115
) -> list[munch.Munch]:
113116
"""Calls the API to get the list of headlines based on the category and country
114117
115118
Args:
116119
country_code (str): The country code to get headlines from
117120
category (str, optional): The category of headlines to get. Defaults to None.
121+
is_interaction (bool): If the headline is being called from an interaction
118122
119123
Returns:
120124
list[munch.Munch]: The list of article objects from the API
@@ -126,27 +130,47 @@ async def get_headlines(
126130
if category:
127131
url = f"{url}&category={category}"
128132

129-
response = await self.bot.http_functions.http_call("get", url)
133+
response = await self.bot.http_functions.http_call(
134+
"get", url, use_app_error=is_interaction
135+
)
130136

131137
articles = response.get("articles")
132138
if not articles:
133139
return None
134140
return articles
135141

136142
async def get_random_headline(
137-
self: Self, country_code: str, category: str = None
143+
self: Self,
144+
country_code: str,
145+
category: str = None,
146+
is_interaction: bool = False,
138147
) -> munch.Munch:
139148
"""Gets a single article object from the news API
140149
141150
Args:
142151
country_code (str): The country code of the headliens to get
143152
category (str, optional): The category of headlines to get. Defaults to None.
153+
is_interaction (bool): If the headline is being called from an interaction
144154
145155
Returns:
146156
munch.Munch: The raw API object representing a news headline
147157
"""
148-
articles = await self.get_headlines(country_code, category)
149-
return random.choice(articles)
158+
159+
articles = await self.get_headlines(country_code, category, is_interaction)
160+
161+
# Filter out articles with URLs containing "removed.com"
162+
filtered_articles = []
163+
for article in articles:
164+
url = article.get("url", "")
165+
if url != "https://removed.com":
166+
filtered_articles.append(article)
167+
168+
# Check if there are any articles left after filtering
169+
if not filtered_articles:
170+
return None
171+
172+
# Choose a random article from the filtered list
173+
return random.choice(filtered_articles)
150174

151175
async def execute(self: Self, config: munch.Munch, guild: discord.Guild) -> None:
152176
"""Loop entry point for the news command
@@ -168,6 +192,9 @@ async def execute(self: Self, config: munch.Munch, guild: discord.Guild) -> None
168192
)
169193
url = article.get("url")
170194

195+
if article is None:
196+
return
197+
171198
log_channel = config.get("logging_channel")
172199
await self.bot.logger.send_log(
173200
message=f"Sending news headline to #{channel.name}",
@@ -187,48 +214,79 @@ async def wait(self: Self, config: munch.Munch, _: discord.Guild) -> None:
187214
"""
188215
await aiocron.crontab(config.extensions.news.cron_config.value).next()
189216

190-
@commands.group(
191-
brief="Executes a news command",
192-
description="Executes a news command",
193-
)
194-
async def news(self: Self, ctx: commands.Context) -> None:
195-
"""The bare .news command. This does nothing but generate the help message
196-
197-
Args:
198-
ctx (commands.Context): The context in which the command was run in
199-
"""
200-
201-
# Executed if there are no/invalid args supplied
202-
await auxiliary.extension_help(self, ctx, self.__module__[9:])
203-
204-
@news.command(
205-
name="random",
206-
brief="Gets a random news article",
217+
@app_commands.command(
218+
name="news",
207219
description="Gets a random news headline",
208-
usage="[category] (optional)",
220+
extras={"module": "news"},
209221
)
210-
async def random(self: Self, ctx: commands.Context, category: str = None) -> None:
222+
async def news_command(
223+
self: Self, interaction: discord.Interaction, category: str = ""
224+
) -> None:
211225
"""Discord command entry point for getting a news article
212226
213227
Args:
214-
ctx (commands.Context): The context in which the command was run
228+
interaction (discord.Interaction): The interaction in which the command was run
215229
category (str, optional): The category to get news headlines from. Defaults to None.
216230
"""
231+
232+
# Debug statement
233+
print("Executing news command")
217234
if category is None or category.lower() not in self.valid_category:
218235
category = random.choice(list(Category)).value
219236
else:
220237
category.lower()
221238

222-
config = self.bot.guild_configs[str(ctx.guild.id)]
239+
config = self.bot.guild_configs[str(interaction.guild.id)]
223240

224241
url = None
225242
while not url:
226243
article = await self.get_random_headline(
227-
config.extensions.news.country.value, category
244+
config.extensions.news.country.value, category, True
228245
)
229246
url = article.get("url")
230247

248+
if article is None:
249+
return
250+
231251
if url.endswith("/"):
232252
url = url[:-1]
233253

234-
await ctx.send(content=url)
254+
await interaction.response.send_message(content=url)
255+
256+
# Log the command execution
257+
log_channel = config.get("logging_channel")
258+
if log_channel:
259+
await self.bot.logger.send_log(
260+
message=(
261+
f"News command executed: "
262+
f"Sent a news headline to {interaction.channel.name}"
263+
),
264+
level=LogLevel.INFO,
265+
context=LogContext(
266+
guild=interaction.guild, channel=interaction.channel
267+
),
268+
channel=log_channel,
269+
)
270+
271+
@news_command.autocomplete("category")
272+
async def news_autocompletion(
273+
self: Self, interaction: discord.Interaction, current: str
274+
) -> list:
275+
"""This command creates a list of categories for autocomplete the news command.
276+
277+
Args:
278+
interaction (discord.Interaction): The interaction that started the command
279+
current (str): The current input from the user.
280+
281+
Returns:
282+
list: The list of autocomplete for the news command.
283+
"""
284+
# Debug statement
285+
print("Autocomplete interaction")
286+
news_category = []
287+
for category in Category:
288+
if current.lower() in category.value.lower():
289+
news_category.append(
290+
app_commands.Choice(name=category.value, value=category.value)
291+
)
292+
return news_category

techsupport_bot/core/custom_errors.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ def __init__(self: Self, wait: int) -> None:
6767
self.wait = wait
6868

6969

70+
class HTTPRateLimitAppCommand(app_commands.CommandInvokeError):
71+
"""An API call is on rate limit
72+
73+
Args:
74+
wait (int): The amount of seconds left until the rate limit expires
75+
"""
76+
77+
def __init__(self: Self, wait: int) -> None:
78+
self.wait = wait
79+
80+
7081
class ErrorResponse:
7182
"""Object for generating a custom error message from an exception.
7283
@@ -252,6 +263,10 @@ def get_message(self: Self, exception: Exception = None) -> str:
252263
"That API is on cooldown. Try again in %.2f seconds",
253264
{"key": "wait"},
254265
),
266+
HTTPRateLimitAppCommand: ErrorResponse(
267+
"That API is on cooldown. Try again in %.2f seconds",
268+
{"key": "wait"},
269+
),
255270
# -Custom errors-
256271
FactoidNotFoundError: ErrorResponse(
257272
"I couldn't find the factoid `%s`", {"key": "argument"}

techsupport_bot/core/http.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,14 @@ async def http_call(
100100
101101
Raises:
102102
HTTPRateLimit: Raised if the API is currently on cooldown
103+
HTTPRateLimitAppCommand: Raised if the API is currently on cooldown
103104
104105
Returns:
105106
munch.Munch: The munch object containing the response from the API
106107
"""
107108

108109
# Get the URL not the endpoint being called
110+
use_app_error = kwargs.pop("use_app_error", False)
109111
ignore_rate_limit = False
110112
root_url = urlparse(url).netloc
111113

@@ -138,6 +140,8 @@ async def http_call(
138140
now - self.url_rate_limit_history[root_url][0]
139141
)
140142
time_to_wait = max(time_to_wait, 0)
143+
if use_app_error:
144+
raise custom_errors.HTTPRateLimitAppCommand(time_to_wait)
141145
raise custom_errors.HTTPRateLimit(time_to_wait)
142146

143147
# Add an entry for this call with the timestamp the call was placed

0 commit comments

Comments
 (0)