11import asyncio
22import os
3- import re
43from collections .abc import Awaitable , Mapping
54from pathlib import Path
65from typing import Any , get_type_hints
@@ -103,17 +102,21 @@ def _add_command_field(embed: discord.Embed, command: commands.Command[Any, Any,
103102 async def _create_select_options (
104103 self ,
105104 command_categories : dict [str , dict [str , str ]],
106- cog_groups : list [str ],
107105 menu : ViewMenu ,
108106 ) -> dict [discord .SelectOption , list [Page ]]:
107+ """
108+ Creates select options for the help menu by iterating over the keys in the
109+ cached command categories. This approach ensures that even categories without
110+ a corresponding folder (e.g. "extra") are included.
111+ """
109112 select_options : dict [discord .SelectOption , list [Page ]] = {}
110-
111113 prefix : str = await self ._get_prefix ()
112114
115+ # Iterate over the keys in command_categories
113116 tasks : list [Awaitable [tuple [str , Page ]]] = [
114117 self ._create_page (cog_group , command_categories , menu , prefix )
115- for cog_group in cog_groups
116- if cog_group in command_categories and any (command_categories [cog_group ].values ())
118+ for cog_group in command_categories
119+ if any (command_categories [cog_group ].values ())
117120 ]
118121
119122 select_options_data : list [tuple [str , Page ]] = await asyncio .gather (* tasks )
@@ -132,6 +135,9 @@ async def _create_select_options(
132135 emoji = category_emoji_map .get (cog_group , "❓" )
133136 select_options [discord .SelectOption (label = cog_group .capitalize (), emoji = emoji )] = [page ]
134137
138+ logger .info (f"Select options: { select_options } " )
139+ logger .info (f"Cached categories: { self ._category_cache } " )
140+
135141 return select_options
136142
137143 async def _create_page (
@@ -148,7 +154,6 @@ async def _create_page(
148154
149155 sorted_commands : list [tuple [str , str ]] = sorted (command_categories [cog_group ].items ())
150156 description : str = "\n " .join (f"**`{ prefix } { cmd } `** | { command_list } " for cmd , command_list in sorted_commands )
151-
152157 embed .description = description
153158 page : Page = Page (embed = embed )
154159 menu .add_page (embed )
@@ -169,8 +174,8 @@ async def _add_cog_pages(
169174 ) -> None :
170175 """Adds pages for each cog category to the help menu."""
171176 command_categories = await self ._get_command_categories (mapping )
172- cog_groups = self . _get_cog_groups ()
173- select_options = await self ._create_select_options (command_categories , cog_groups , menu )
177+ # Instead of using filesystem folders, iterate over cached categories.
178+ select_options = await self ._create_select_options (command_categories , menu )
174179 self ._add_navigation_and_selection (menu , select_options )
175180
176181 async def _get_command_categories (
@@ -185,6 +190,7 @@ async def _get_command_categories(
185190
186191 for cog , mapping_commands in mapping .items ():
187192 if cog and len (mapping_commands ) > 0 :
193+ # Attempt to extract the group using the cog's module name.
188194 cog_group = self ._extract_cog_group (cog ) or "extra"
189195 command_categories .setdefault (cog_group , {})
190196 for command in mapping_commands :
@@ -204,9 +210,15 @@ def _get_cog_groups() -> list[str]:
204210
205211 @staticmethod
206212 def _extract_cog_group (cog : commands .Cog ) -> str | None :
207- """Extracts the cog group from a cog's string representation."""
208- if match := re .search (r"<cogs\.([^\.]+)\..*>" , str (cog )):
209- return match [1 ]
213+ """
214+ Extracts the cog group using the cog's module attribute.
215+ For example, if a cog's module is 'tux.cogs.admin.some_cog', this returns 'admin'.
216+ """
217+ module = getattr (cog , "__module__" , "" )
218+ parts = module .split ("." )
219+ # Assuming the structure is: tux.cogs.<group>...
220+ if len (parts ) >= 3 and parts [1 ].lower () == "cogs" :
221+ return parts [2 ].lower ()
210222 return None
211223
212224 # Sending Help Messages
0 commit comments