|
39 | 39 |
|
40 | 40 | if TYPE_CHECKING:
|
41 | 41 | from cogs.tickets import Tickets
|
| 42 | + |
42 | 43 | from rodhaj import Rodhaj
|
43 | 44 |
|
44 | 45 |
|
@@ -232,6 +233,44 @@ async def get_config(self) -> Optional[GuildWebhook]:
|
232 | 233 | return GuildWebhook(bot=self.bot, **dict(rows))
|
233 | 234 |
|
234 | 235 |
|
| 236 | +class ConfigHelpEntry(msgspec.Struct, frozen=True): |
| 237 | + key: str |
| 238 | + default: str |
| 239 | + description: str |
| 240 | + examples: list[str] |
| 241 | + notes: list[str] |
| 242 | + |
| 243 | + |
| 244 | +class ConfigEntryEmbed(Embed): |
| 245 | + def __init__(self, entry: ConfigHelpEntry, **kwargs): |
| 246 | + super().__init__(**kwargs) |
| 247 | + self.title = entry.key |
| 248 | + self.description = entry.description |
| 249 | + self.add_field(name="Default", value=entry.default, inline=False) |
| 250 | + self.add_field(name="Example(s)", value="\n".join(entry.examples), inline=False) |
| 251 | + self.add_field( |
| 252 | + name="Notes", |
| 253 | + value="\n".join(f"- {note}" for note in entry.notes) or None, |
| 254 | + inline=False, |
| 255 | + ) |
| 256 | + |
| 257 | + |
| 258 | +class ConfigHelpPageSource(menus.ListPageSource): |
| 259 | + async def format_page(self, menu: ConfigHelpPages, entry: ConfigHelpEntry): |
| 260 | + embed = ConfigEntryEmbed(entry=entry) |
| 261 | + |
| 262 | + maximum = self.get_max_pages() |
| 263 | + if maximum > 1: |
| 264 | + embed.set_footer(text=f"Page {menu.current_page + 1}/{maximum}") |
| 265 | + return embed |
| 266 | + |
| 267 | + |
| 268 | +class ConfigHelpPages(RoboPages): |
| 269 | + def __init__(self, entries: list[ConfigHelpEntry], *, ctx: GuildContext): |
| 270 | + super().__init__(ConfigHelpPageSource(entries, per_page=1), ctx=ctx) |
| 271 | + self.embed = discord.Embed() |
| 272 | + |
| 273 | + |
235 | 274 | class ConfigPageSource(menus.AsyncIteratorPageSource):
|
236 | 275 | def __init__(self, entries: dict[str, Any], active: Optional[bool] = None):
|
237 | 276 | super().__init__(self.config_iterator(entries), per_page=20)
|
@@ -300,15 +339,16 @@ def disambiguate(self, argument: str, keys: list[str]) -> str:
|
300 | 339 | return f"Key not found. Did you mean...\n{close_keys}"
|
301 | 340 |
|
302 | 341 | async def convert(self, ctx: GuildContext, argument: str) -> str:
|
| 342 | + lowered = argument.lower() |
303 | 343 | cog: Optional[Config] = ctx.bot.get_cog("Config") # type: ignore
|
304 | 344 |
|
305 | 345 | if not cog:
|
306 | 346 | raise RuntimeError("Unable to get Config cog")
|
307 | 347 |
|
308 |
| - if argument not in cog.config_keys: |
309 |
| - raise commands.BadArgument(self.disambiguate(argument, cog.config_keys)) |
| 348 | + if lowered not in cog.config_keys: |
| 349 | + raise commands.BadArgument(self.disambiguate(lowered, cog.config_keys)) |
310 | 350 |
|
311 |
| - return argument |
| 351 | + return lowered |
312 | 352 |
|
313 | 353 |
|
314 | 354 | class ConfigValueConverter(commands.Converter):
|
@@ -750,6 +790,34 @@ async def config_options(
|
750 | 790 | pages = ConfigPages(guild_settings.to_dict(), ctx=ctx, active=flags.active)
|
751 | 791 | await pages.start()
|
752 | 792 |
|
| 793 | + @is_manager() |
| 794 | + @commands.guild_only() |
| 795 | + @config.group(name="help", aliases=["info"]) |
| 796 | + async def config_help( |
| 797 | + self, ctx: GuildContext, option: Annotated[str, ConfigKeyConverter] |
| 798 | + ) -> None: |
| 799 | + """Shows help information for different configuration options""" |
| 800 | + # Because we are using the converter, all options are guaranteed to be correct |
| 801 | + embed = ConfigEntryEmbed( |
| 802 | + ConfigHelpEntry(key=option, **self.options_help[option]) |
| 803 | + ) |
| 804 | + await ctx.send(embed=embed) |
| 805 | + |
| 806 | + @is_manager() |
| 807 | + @commands.guild_only() |
| 808 | + @config_help.command(name="all") |
| 809 | + async def config_help_all(self, ctx: GuildContext): |
| 810 | + """Shows all possible help information for all configurations""" |
| 811 | + # We need to separate this since we are using the key converter. If it is an invalid option, it passes back None, |
| 812 | + # thus causing it to show all entries. This isn't that useful when you just made one mistake. |
| 813 | + # Modmail handles this differently by internally looking for the key, and giving an whole embed list of possible options |
| 814 | + converted = [ |
| 815 | + ConfigHelpEntry(key=key, **item) |
| 816 | + for key, item in self.options_help.all().items() |
| 817 | + ] |
| 818 | + pages = ConfigHelpPages(entries=converted, ctx=ctx) |
| 819 | + await pages.start() |
| 820 | + |
753 | 821 | @is_manager()
|
754 | 822 | @commands.guild_only()
|
755 | 823 | @config.command(name="set-age")
|
@@ -853,6 +921,12 @@ async def on_config_toggle_error(
|
853 | 921 | ):
|
854 | 922 | await self._handle_error(error, ctx=ctx)
|
855 | 923 |
|
| 924 | + @config_help.error |
| 925 | + async def on_config_help_error( |
| 926 | + self, ctx: GuildContext, error: commands.CommandError |
| 927 | + ): |
| 928 | + await self._handle_error(error, ctx=ctx) |
| 929 | + |
856 | 930 | @is_manager()
|
857 | 931 | @commands.guild_only()
|
858 | 932 | @config.group(name="prefix", fallback="info")
|
|
0 commit comments