Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: physical suggestions queue #77

Merged
merged 13 commits into from
Mar 3, 2024
51 changes: 46 additions & 5 deletions suggestions/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@
QueueImbalance,
BlocklistedUser,
PartialResponse,
MissingQueueLogsChannel,
MissingPermissionsToAccessQueueChannel,
InvalidFileType,
)
from suggestions.http_error_parser import try_parse_http_error
from suggestions.interaction_handler import InteractionHandler
from suggestions.objects import Error, GuildConfig, UserConfig
from suggestions.stats import Stats, StatsEnum
from suggestions.database import SuggestionsMongoManager
Expand Down Expand Up @@ -240,11 +243,16 @@ async def process_commands(self, message: disnake.Message):
await self.invoke(ctx)

async def _push_slash_error_stats(
self, interaction: disnake.ApplicationCommandInteraction
self,
interaction: disnake.ApplicationCommandInteraction | disnake.MessageInteraction,
):
stat_type: Optional[StatsEnum] = StatsEnum.from_command_name(
name = (
interaction.application_command.qualified_name
if isinstance(interaction, disnake.ApplicationCommandInteraction)
else interaction.data["custom_id"].split(":")[0] # Button name
)

stat_type: Optional[StatsEnum] = StatsEnum.from_command_name(name)
if not stat_type:
return

Expand Down Expand Up @@ -380,6 +388,30 @@ async def on_slash_command_error(
ephemeral=True,
)

elif isinstance(exception, MissingQueueLogsChannel):
return await interaction.send(
embed=self.error_embed(
"Missing Queue Logs Channel",
"This command requires a queue log channel to use.\n"
"Please contact an administrator and ask them to set one up "
"using the following command.\n`/config queue_channel`",
error_code=ErrorCode.MISSING_QUEUE_LOG_CHANNEL,
error=error,
),
ephemeral=True,
)

elif isinstance(exception, MissingPermissionsToAccessQueueChannel):
return await interaction.send(
embed=self.error_embed(
title="Missing permissions within queue logs channel",
description="The bot does not have the required permissions in your queue channel. "
"Please contact an administrator and ask them to fix this.",
error=error,
error_code=ErrorCode.MISSING_PERMISSIONS_IN_QUEUE_CHANNEL,
)
)

elif isinstance(exception, commands.MissingPermissions):
perms = ",".join(i for i in exception.missing_permissions)
return await interaction.send(
Expand Down Expand Up @@ -543,7 +575,12 @@ async def on_slash_command_error(
)
return

if interaction.deferred_without_send:
ih: InteractionHandler = await InteractionHandler.fetch_handler(
interaction.id, self
)
if interaction.deferred_without_send or (
ih is not None and not ih.has_sent_something
):
gid = interaction.guild_id if interaction.guild_id else None
# Fix "Bot is thinking" hanging on edge cases...
await interaction.send(
Expand Down Expand Up @@ -803,7 +840,7 @@ def get_locale(self, key: str, locale: Locale) -> str:
return values[str(locale)]
except KeyError:
# Default to known translations if not set
return values["en-GB"]
return values.get("en-GB", values["en-US"])

@staticmethod
def inject_locale_values(
Expand Down Expand Up @@ -843,11 +880,15 @@ def inject_locale_values(
def get_localized_string(
self,
key: str,
interaction: disnake.Interaction,
interaction: disnake.Interaction | InteractionHandler,
*,
extras: Optional[dict] = None,
guild_config: Optional[GuildConfig] = None,
):
if isinstance(interaction, InteractionHandler):
# Support this so easier going forward
interaction = interaction.interaction

content = self.get_locale(key, interaction.locale)
return self.inject_locale_values(
content, interaction=interaction, guild_config=guild_config, extras=extras
Expand Down
4 changes: 3 additions & 1 deletion suggestions/codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ class ErrorCode(IntEnum):
QUEUE_IMBALANCE = 20
MISSING_QUEUE_CHANNEL = 21
BLOCKLISTED_USER = 22
INVALID_FILE_TYPE = 23
MISSING_QUEUE_LOG_CHANNEL = 23
MISSING_PERMISSIONS_IN_QUEUE_CHANNEL = 24
INVALID_FILE_TYPE = 25

@classmethod
def from_value(cls, value: int) -> ErrorCode:
Expand Down
185 changes: 184 additions & 1 deletion suggestions/cogs/guild_config_cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from suggestions import Stats
from suggestions.cooldown_bucket import InteractionBucket
from suggestions.exceptions import InvalidGuildConfigOption
from suggestions.interaction_handler import InteractionHandler
from suggestions.objects import GuildConfig
from suggestions.stats import StatsEnum

Expand Down Expand Up @@ -97,6 +98,83 @@ async def logs(
self.stats.type.GUILD_CONFIG_LOG_CHANNEL,
)

@config.sub_command()
async def queue_channel(
self,
interaction: disnake.GuildCommandInteraction,
channel: disnake.TextChannel,
):
"""Set your guilds physical suggestions queue channel."""
ih: InteractionHandler = await InteractionHandler.new_handler(interaction)
guild_config: GuildConfig = await GuildConfig.from_id(
interaction.guild_id, self.state
)
try:
message = await channel.send("This is a test message and can be ignored.")
await message.delete()
except disnake.Forbidden:
return await ih.send(
f"I do not have permissions to delete messages in {channel.mention}. "
f"Please give me permissions and run this command again.",
)

guild_config.queued_channel_id = channel.id
self.state.refresh_guild_config(guild_config)
await self.state.guild_config_db.upsert(guild_config, guild_config)
await ih.send(
self.bot.get_localized_string(
"CONFIG_QUEUE_CHANNEL_INNER_MESSAGE",
ih,
extras={"CHANNEL": channel.mention},
)
)
log.debug(
"User %s changed physical queue channel to %s in guild %s",
interaction.author.id,
channel.id,
interaction.guild_id,
)
await self.stats.log_stats(
interaction.author.id,
interaction.guild_id,
self.stats.type.GUILD_CONFIG_QUEUE_CHANNEL,
)

@config.sub_command()
async def queue_log_channel(
self,
interaction: disnake.GuildCommandInteraction,
channel: disnake.TextChannel = None,
):
"""Set your guilds suggestion queue log channel for rejected suggestions."""
ih: InteractionHandler = await InteractionHandler.new_handler(interaction)
guild_config: GuildConfig = await GuildConfig.from_id(
interaction.guild_id, self.state
)
guild_config.queued_log_channel_id = channel.id if channel else None
self.state.refresh_guild_config(guild_config)
await self.state.guild_config_db.upsert(guild_config, guild_config)
key = (
"CONFIG_QUEUE_CHANNEL_INNER_MESSAGE_REMOVED"
if channel is None
else "CONFIG_QUEUE_LOG_CHANNEL_INNER_MESSAGE"
)
msg = self.bot.get_locale(key, interaction.locale)
if channel is not None:
msg = msg.format(channel.mention)
await ih.send(msg)
log.debug(
"User %s changed rejected queue log channel to %s in guild %s",
interaction.author.id,
channel.id if channel is not None else None,
interaction.guild_id,
)
await self.stats.log_stats(
interaction.author.id,
interaction.guild_id,
self.stats.type.GUILD_CONFIG_REJECTED_QUEUE_CHANNEL,
)

@config.sub_command()
async def get(
self,
Expand All @@ -114,6 +192,9 @@ async def get(
"Suggestions queue",
"Images in suggestions",
"Anonymous resolutions",
"Using channel queue",
"Queue channel",
"Queue rejection channel",
],
default=None,
),
Expand Down Expand Up @@ -147,6 +228,33 @@ async def get(
)
embed.description += log_channel

elif config == "Queue channel":
log_channel = (
self.bot.get_locale(
"CONFIG_GET_INNER_PARTIAL_QUEUE_LOG_CHANNEL_SET", interaction.locale
).format(guild_config.queued_channel_id)
if guild_config.queued_channel_id
else self.bot.get_locale(
"CONFIG_GET_INNER_PARTIAL_QUEUE_LOG_CHANNEL_NOT_SET",
interaction.locale,
)
)
embed.description += log_channel

elif config == "Queue rejection channel":
log_channel = (
self.bot.get_locale(
"CONFIG_GET_INNER_PARTIAL_QUEUE_REJECTION_LOG_CHANNEL_SET",
interaction.locale,
).format(guild_config.queued_log_channel_id)
if guild_config.queued_log_channel_id
else self.bot.get_locale(
"CONFIG_GET_INNER_PARTIAL_QUEUE_REJECTION_LOG_CHANNEL_NOT_SET",
interaction.locale,
)
)
embed.description += log_channel

elif config == "Suggestions channel":
suggestions_channel = (
self.bot.get_locale(
Expand Down Expand Up @@ -265,6 +373,17 @@ async def get(

embed.description += text

elif config == "Using channel queue":
locale_string = (
"CONFIG_GET_INNER_USES_PHYSICAL_QUEUE_NOT_SET"
if guild_config.virtual_suggestion_queue
else "CONFIG_GET_INNER_USES_PHYSICAL_QUEUE_SET"
)

text = self.bot.get_localized_string(locale_string, interaction)

embed.description += text

else:
raise InvalidGuildConfigOption

Expand Down Expand Up @@ -300,6 +419,22 @@ async def send_full_config(self, interaction: disnake.GuildCommandInteraction):
interaction.locale,
)
)
queue_channel = (
f"<#{guild_config.queued_channel_id}>"
if guild_config.queued_channel_id
else self.bot.get_locale(
"CONFIG_GET_INNER_PARTIAL_QUEUE_LOG_CHANNEL_NOT_SET",
interaction.locale,
)
)
queue_rejection_channel = (
f"<#{guild_config.queued_log_channel_id}>"
if guild_config.queued_log_channel_id
else self.bot.get_locale(
"CONFIG_GET_INNER_PARTIAL_QUEUE_REJECTION_LOG_CHANNEL_NOT_SET",
interaction.locale,
)
)
dm_responses = (
self.bot.get_locale(
"CONFIG_GET_INNER_PARTIAL_DM_RESPONSES_NOT_SET", interaction.locale
Expand Down Expand Up @@ -345,6 +480,16 @@ async def send_full_config(self, interaction: disnake.GuildCommandInteraction):
"CONFIG_GET_INNER_ANONYMOUS_SUGGESTIONS_MESSAGE", interaction.locale
).format(anon_text)

physical_queue = (
self.bot.get_locale(
"CONFIG_GET_INNER_USES_PHYSICAL_QUEUE_NOT_SET", interaction.locale
)
if guild_config.virtual_suggestion_queue
else self.bot.get_locale(
"CONFIG_GET_INNER_USES_PHYSICAL_QUEUE_SET", interaction.locale
)
)

image_text = (
self.bot.get_locale(
"CONFIG_GET_INNER_IMAGES_IN_SUGGESTIONS_SET", interaction.locale
Expand Down Expand Up @@ -394,7 +539,9 @@ async def send_full_config(self, interaction: disnake.GuildCommandInteraction):
f"Log channel: {log_channel}\nDm responses: I {dm_responses} DM users on actions such as suggest\n"
f"Suggestion threads: {threads}\nKeep Logs: {keep_logs}\nAnonymous suggestions: {anon}\n"
f"Automatic thread archiving: {auto_archive_threads}\nSuggestions queue: {suggestions_queue}\n"
f"Images in suggestions: {images}\nAnonymous resolutions: {anonymous_resolutions}",
f"Channel queue: {physical_queue}\nImages in suggestions: {images}\n"
f"Anonymous resolutions: {anonymous_resolutions}\n"
f"Queue channel: {queue_channel}\nQueue rejection channel: {queue_rejection_channel}",
color=self.bot.colors.embed_color,
timestamp=self.bot.state.now,
).set_author(name=guild.name, icon_url=icon_url)
Expand Down Expand Up @@ -642,6 +789,42 @@ async def anonymous_resolutions_disable(
self.stats.type.GUILD_ANONYMOUS_RESOLUTIONS_DISABLE,
)

@config.sub_command_group()
async def use_channel_queue(self, interaction: disnake.GuildCommandInteraction):
pass

@use_channel_queue.sub_command(name="enable")
async def use_physical_queue_enable(
self, interaction: disnake.GuildCommandInteraction
):
"""Set the queue to use a channel for queuing suggestions."""
await self.modify_guild_config(
interaction,
"virtual_suggestion_queue",
False,
self.bot.get_localized_string(
"CONFIG_USE_PHYSICAL_QUEUE_ENABLE_INNER_MESSAGE", interaction
),
"Enabled physical queue on suggestions for guild %s",
self.stats.type.GUILD_PHYSICAL_QUEUE_ENABLE,
)

@use_channel_queue.sub_command(name="disable")
async def use_physical_queue_disable(
self, interaction: disnake.GuildCommandInteraction
):
"""Use a virtual queue for suggestions in this guild."""
await self.modify_guild_config(
interaction,
"virtual_suggestion_queue",
True,
self.bot.get_localized_string(
"CONFIG_USE_PHYSICAL_QUEUE_DISABLE_INNER_MESSAGE", interaction
),
"Disabled physical queue on suggestions for guild %s",
self.stats.type.GUILD_PHYSICAL_QUEUE_DISABLE,
)

@config.sub_command_group()
async def images_in_suggestions(self, interaction: disnake.GuildCommandInteraction):
pass
Expand Down
Loading
Loading