Repository: Cog-Creators/Red-DiscordBot

Issue:
Add some way to recover when someone forgets what they set their server prefix to
### What component of Red (cog, command, API) would you like to see improvements on?

Core

### Describe the enhancement you're suggesting.

Currently, it's possible to "lock yourself out" in a server if you set a server prefix and forget it. Global prefixes won't work, in best case the bot owner has enabled `--mentionable` and you can use mention prefix in the server but if the bot owner did not do that then you don't really have any good way of finding out what the prefix is, setting it to something else or resetting it. To alleviate this problem, at least one of those things needs to be possible.

Potential options are:
- adding `[guild]` parameter to `[p]set showsettings` command which could be used in DMs to get server's settings (including server prefix) without knowing the server prefix
- adding a new command for reseting server prefix from DMs (e.g. `[p]set serverprefix reset <guild>`)
- adding (or changing existing) command for setting server prefix from DMs

### Anything else?

_No response_


Discussion:
Huh, I always thought mentionable commands were on by default. Maybe make a recovery command that's always mentionable similar to how licenseinfo will be under #5865?
We've decided to go with:
- Modify `[p]set showsettings` to accept an **optional** `[guild]` parameter.
- Modify `[p]set serverprefix` to have a **`typing.Optional`** `[guild]` parameter _before_ the prefix(es).
Hi. I plan on working on this. 

Resolving patch:
diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py
--- a/redbot/core/core_commands.py
+++ b/redbot/core/core_commands.py
@@ -3351,13 +3351,16 @@ async def _set_ownernotifications_listdestinations(self, ctx: commands.Context):
     # -- End Set Ownernotifications Commands -- ###
 
     @_set.command(name="showsettings")
-    async def _set_showsettings(self, ctx: commands.Context):
+    async def _set_showsettings(self, ctx: commands.Context, server: discord.Guild = None):
         """
-        Show the current settings for [botname].
+        Show the current settings for [botname]. Accepts optional guild parameter if its prefix must be recovered.
         """
-        if ctx.guild:
-            guild_data = await ctx.bot._config.guild(ctx.guild).all()
-            guild = ctx.guild
+        if server is None:
+            server = ctx.guild
+
+        if server:
+            guild_data = await ctx.bot._config.guild(server).all()
+            guild = server
             admin_role_ids = guild_data["admin_role"]
             admin_role_names = [r.name for r in guild.roles if r.id in admin_role_ids]
             admin_roles_str = (
@@ -3367,9 +3370,9 @@ async def _set_showsettings(self, ctx: commands.Context):
             mod_role_names = [r.name for r in guild.roles if r.id in mod_role_ids]
             mod_roles_str = humanize_list(mod_role_names) if mod_role_names else _("Not Set.")
 
-            guild_locale = await i18n.get_locale_from_guild(self.bot, ctx.guild)
+            guild_locale = await i18n.get_locale_from_guild(self.bot, server)
             guild_regional_format = (
-                await i18n.get_regional_format_from_guild(self.bot, ctx.guild) or guild_locale
+                await i18n.get_regional_format_from_guild(self.bot, server) or guild_locale
             )
 
             guild_settings = _(
@@ -3386,7 +3389,7 @@ async def _set_showsettings(self, ctx: commands.Context):
         else:
             guild_settings = ""
 
-        prefixes = await ctx.bot._prefix_cache.get_prefixes(ctx.guild)
+        prefixes = await ctx.bot._prefix_cache.get_prefixes(server)
         global_data = await ctx.bot._config.all()
         locale = global_data["locale"]
         regional_format = global_data["regional_format"] or locale
@@ -3599,8 +3602,9 @@ async def _set_prefix(self, ctx: commands.Context, *prefixes: str):
 
     @_set.command(name="serverprefix", aliases=["serverprefixes"])
     @checks.admin_or_permissions(manage_guild=True)
-    @commands.guild_only()
-    async def _set_serverprefix(self, ctx: commands.Context, *prefixes: str):
+    async def _set_serverprefix(
+        self, ctx: commands.Context, server: Optional[discord.Guild], *prefixes: str
+    ):
         """
         Sets [botname]'s server prefix(es).
 
@@ -3613,12 +3617,16 @@ async def _set_serverprefix(self, ctx: commands.Context, *prefixes: str):
             - `[p]set serverprefix "! "` - Quotes are needed to use spaces in prefixes.
             - `[p]set serverprefix "@[botname] "` - This uses a mention as the prefix.
             - `[p]set serverprefix ! ? .` - Sets multiple prefixes.
+            - `[p]set serverprefix "Red - Discord Bot" ? - Sets the prefix for a specific server. Quotes are needed to use spaces in the server name.
 
         **Arguments:**
             - `[prefixes...]` - The prefixes the bot will respond to on this server. Leave blank to clear server prefixes.
         """
+        if server is None:
+            server = ctx.guild
+
         if not prefixes:
-            await ctx.bot.set_prefixes(guild=ctx.guild, prefixes=[])
+            await ctx.bot.set_prefixes(guild=server, prefixes=[])
             await ctx.send(_("Server prefixes have been reset."))
             return
         if any(prefix.startswith("/") for prefix in prefixes):
@@ -3630,7 +3638,7 @@ async def _set_serverprefix(self, ctx: commands.Context, *prefixes: str):
             await ctx.send(_("You cannot have a prefix longer than 25 characters."))
             return
         prefixes = sorted(prefixes, reverse=True)
-        await ctx.bot.set_prefixes(guild=ctx.guild, prefixes=prefixes)
+        await ctx.bot.set_prefixes(guild=server, prefixes=prefixes)
         if len(prefixes) == 1:
             await ctx.send(_("Server prefix set."))
         else:


Test patch: