|
|
@@ -4,7 +4,6 @@ from typing import Union, Optional
|
|
4
|
4
|
|
|
5
|
5
|
from discord import Interaction, Permissions, AppCommandType
|
|
6
|
6
|
from discord.app_commands import Group, Command, autocomplete, guild_only, command, Choice
|
|
7
|
|
-from discord.ext.commands import cog
|
|
8
|
7
|
|
|
9
|
8
|
from config import CONFIG
|
|
10
|
9
|
from rocketbot.bot import Rocketbot
|
|
|
@@ -13,99 +12,29 @@ from rocketbot.utils import MOD_PERMISSIONS, dump_stacktrace
|
|
13
|
12
|
|
|
14
|
13
|
HelpTopic = Union[Command, Group, BaseCog]
|
|
15
|
14
|
|
|
16
|
|
-def choice_from_obj(obj: HelpTopic, include_full_command: bool = False) -> Choice:
|
|
17
|
|
- if isinstance(obj, BaseCog):
|
|
18
|
|
- return Choice(name=f'⚙ {obj.qualified_name}', value=f'cog:{obj.qualified_name}')
|
|
19
|
|
- if isinstance(obj, Group):
|
|
20
|
|
- return Choice(name=f'/{obj.name}', value=f'cmd:{obj.name}')
|
|
21
|
|
- if isinstance(obj, Command):
|
|
22
|
|
- if obj.parent:
|
|
|
15
|
+def choice_from_topic(topic: HelpTopic, include_full_command: bool = False) -> Choice:
|
|
|
16
|
+ if isinstance(topic, BaseCog):
|
|
|
17
|
+ return Choice(name=f'⚙ {topic.qualified_name}', value=f'cog:{topic.qualified_name}')
|
|
|
18
|
+ if isinstance(topic, Group):
|
|
|
19
|
+ return Choice(name=f'/{topic.name}', value=f'cmd:{topic.name}')
|
|
|
20
|
+ if isinstance(topic, Command):
|
|
|
21
|
+ if topic.parent:
|
|
23
|
22
|
if include_full_command:
|
|
24
|
|
- return Choice(name=f'/{obj.parent.name} {obj.name}', value=f'subcmd:{obj.parent.name}.{obj.name}')
|
|
25
|
|
- return Choice(name=f'{obj.name}', value=f'subcmd:{obj.name}')
|
|
26
|
|
- return Choice(name=f'/{obj.name}', value=f'cmd:{obj.name}')
|
|
|
23
|
+ return Choice(name=f'/{topic.parent.name} {topic.name}', value=f'subcmd:{topic.parent.name}.{topic.name}')
|
|
|
24
|
+ return Choice(name=f'{topic.name}', value=f'subcmd:{topic.name}')
|
|
|
25
|
+ return Choice(name=f'/{topic.name}', value=f'cmd:{topic.name}')
|
|
27
|
26
|
return Choice(name='', value='')
|
|
28
|
27
|
|
|
29
|
|
-async def command_autocomplete(interaction: Interaction, current: str) -> list[Choice[str]]:
|
|
30
|
|
- """Autocomplete handler for top-level command names."""
|
|
31
|
|
- choices: list[Choice] = []
|
|
32
|
|
- try:
|
|
33
|
|
- if current.startswith('/'):
|
|
34
|
|
- current = current[1:]
|
|
35
|
|
- current = current.lower().strip()
|
|
36
|
|
- user_permissions = interaction.permissions
|
|
37
|
|
- cmds = HelpCog.shared.get_command_list(user_permissions)
|
|
38
|
|
- return [
|
|
39
|
|
- Choice(name=f'/{cmdname} command', value=f'cmd:{cmdname}')
|
|
40
|
|
- for cmdname in sorted(cmds.keys())
|
|
41
|
|
- if len(current) == 0 or current in cmdname
|
|
42
|
|
- ][:25]
|
|
43
|
|
- except BaseException as e:
|
|
44
|
|
- dump_stacktrace(e)
|
|
45
|
|
- return choices
|
|
46
|
|
-
|
|
47
|
|
-async def subcommand_autocomplete(interaction: Interaction, current: str) -> list[Choice[str]]:
|
|
48
|
|
- """Autocomplete handler for subcommand names. Command taken from previous command token."""
|
|
49
|
|
- try:
|
|
50
|
|
- current = current.lower().strip()
|
|
51
|
|
- cmd_name = interaction.namespace.get('topic', None)
|
|
52
|
|
- cmd = HelpCog.shared.object_for_help_symbol(cmd_name)
|
|
53
|
|
- if isinstance(cmd, Command):
|
|
54
|
|
- # No subcommands
|
|
55
|
|
- return []
|
|
56
|
|
- user_permissions = interaction.permissions
|
|
57
|
|
- if cmd is None or not isinstance(cmd, Group):
|
|
58
|
|
- return []
|
|
59
|
|
- grp = cmd
|
|
60
|
|
- subcmds = HelpCog.shared.get_subcommand_list(grp, user_permissions)
|
|
61
|
|
- if subcmds is None:
|
|
62
|
|
- return []
|
|
63
|
|
- return [
|
|
64
|
|
- choice_from_obj(subcmd)
|
|
65
|
|
- for subcmd_name, subcmd in sorted(subcmds.items())
|
|
66
|
|
- if len(current) == 0 or current in subcmd_name
|
|
67
|
|
- ][:25]
|
|
68
|
|
- except BaseException as e:
|
|
69
|
|
- dump_stacktrace(e)
|
|
70
|
|
- return []
|
|
71
|
|
-
|
|
72
|
|
-async def cog_autocomplete(interaction: Interaction, current: str) -> list[Choice[str]]:
|
|
73
|
|
- """Autocomplete handler for cog names."""
|
|
74
|
|
- try:
|
|
75
|
|
- current = current.lower().strip()
|
|
76
|
|
- return [
|
|
77
|
|
- choice_from_obj(cog)
|
|
78
|
|
- for cog in sorted(HelpCog.shared.bot.cogs.values(), key=lambda c: c.qualified_name)
|
|
79
|
|
- if isinstance(cog, BaseCog) and
|
|
80
|
|
- can_use_cog(cog, interaction.permissions) and
|
|
81
|
|
- (len(cog.get_commands()) > 0 or len(cog.settings) > 0) and \
|
|
82
|
|
- (len(current) == 0 or current in cog.qualified_name.lower())
|
|
83
|
|
- ]
|
|
84
|
|
- except BaseException as e:
|
|
85
|
|
- dump_stacktrace(e)
|
|
86
|
|
- return []
|
|
87
|
|
-
|
|
88
|
|
-async def topic_autocomplete(interaction: Interaction, current: str) -> list[Choice[str]]:
|
|
89
|
|
- """Autocomplete handler that combines slash commands and cog names."""
|
|
90
|
|
- command_choices = await command_autocomplete(interaction, current)
|
|
91
|
|
- cog_choices = await cog_autocomplete(interaction, current)
|
|
92
|
|
- return (command_choices + cog_choices)[:25]
|
|
93
|
|
-
|
|
94
|
|
-async def subtopic_autocomplete(interaction: Interaction, current: str) -> list[Choice[str]]:
|
|
95
|
|
- """Autocomplete handler for subtopic names. Currently just handles subcommands."""
|
|
96
|
|
- subcommand_choices = await subcommand_autocomplete(interaction, current)
|
|
97
|
|
- return subcommand_choices[:25]
|
|
98
|
|
-
|
|
99
|
28
|
async def search_autocomplete(interaction: Interaction, current: str) -> list[Choice[str]]:
|
|
100
|
29
|
try:
|
|
101
|
30
|
if len(current) == 0:
|
|
102
|
31
|
return [
|
|
103
|
|
- choice_from_obj(obj, include_full_command=True)
|
|
104
|
|
- for obj in HelpCog.shared.all_accessible_objects(interaction.permissions)
|
|
|
32
|
+ choice_from_topic(topic, include_full_command=True)
|
|
|
33
|
+ for topic in HelpCog.shared.all_accessible_topics(interaction.permissions)
|
|
105
|
34
|
]
|
|
106
|
35
|
return [
|
|
107
|
|
- choice_from_obj(obj, include_full_command=True)
|
|
108
|
|
- for obj in HelpCog.shared.objects_for_keywords(current, interaction.permissions)
|
|
|
36
|
+ choice_from_topic(topic, include_full_command=True)
|
|
|
37
|
+ for topic in HelpCog.shared.topics_for_keywords(current, interaction.permissions)
|
|
109
|
38
|
]
|
|
110
|
39
|
except BaseException as e:
|
|
111
|
40
|
dump_stacktrace(e)
|
|
|
@@ -118,22 +47,22 @@ class HelpCog(BaseCog, name='Help'):
|
|
118
|
47
|
super().__init__(
|
|
119
|
48
|
bot,
|
|
120
|
49
|
config_prefix='help',
|
|
121
|
|
- short_description='Provides help on using commands and modules.'
|
|
|
50
|
+ short_description='Provides help on using this bot.'
|
|
122
|
51
|
)
|
|
123
|
52
|
HelpCog.shared = self
|
|
124
|
53
|
|
|
125
|
54
|
def __create_help_index(self) -> None:
|
|
126
|
55
|
"""
|
|
127
|
|
- Populates self.obj_index and self.keyword_index. Bails if already
|
|
|
56
|
+ Populates self.topic_index and self.keyword_index. Bails if already
|
|
128
|
57
|
populated. Intended to be run on demand so all cogs and commands have
|
|
129
|
58
|
had time to get set up and synced.
|
|
130
|
59
|
"""
|
|
131
|
|
- if getattr(self, 'obj_index', None) is not None:
|
|
|
60
|
+ if getattr(self, 'topic_index', None) is not None:
|
|
132
|
61
|
return
|
|
133
|
|
- self.obj_index: dict[str, HelpTopic] = {}
|
|
|
62
|
+ self.topic_index: dict[str, HelpTopic] = {}
|
|
134
|
63
|
self.keyword_index: dict[str, set[HelpTopic]] = {}
|
|
135
|
64
|
|
|
136
|
|
- def add_text_to_index(obj, text: str):
|
|
|
65
|
+ def add_text_to_index(topic: HelpTopic, text: str):
|
|
137
|
66
|
words = [
|
|
138
|
67
|
word
|
|
139
|
68
|
for word in re.split(r"[^a-zA-Z']+", text.lower())
|
|
|
@@ -141,20 +70,20 @@ class HelpCog(BaseCog, name='Help'):
|
|
141
|
70
|
]
|
|
142
|
71
|
for word in words:
|
|
143
|
72
|
matches = self.keyword_index.get(word, set())
|
|
144
|
|
- matches.add(obj)
|
|
|
73
|
+ matches.add(topic)
|
|
145
|
74
|
self.keyword_index[word] = matches
|
|
146
|
75
|
|
|
147
|
76
|
cmds = self.all_commands()
|
|
148
|
77
|
for cmd in cmds:
|
|
149
|
|
- self.obj_index[f'cmd:{cmd.name}'] = cmd
|
|
150
|
|
- self.obj_index[f'/{cmd.name}'] = cmd
|
|
|
78
|
+ self.topic_index[f'cmd:{cmd.name}'] = cmd
|
|
|
79
|
+ self.topic_index[f'/{cmd.name}'] = cmd
|
|
151
|
80
|
add_text_to_index(cmd, cmd.name)
|
|
152
|
81
|
if cmd.description:
|
|
153
|
82
|
add_text_to_index(cmd, cmd.description)
|
|
154
|
83
|
if isinstance(cmd, Group):
|
|
155
|
84
|
for subcmd in cmd.commands:
|
|
156
|
|
- self.obj_index[f'subcmd:{cmd.name}.{subcmd.name}'] = subcmd
|
|
157
|
|
- self.obj_index[f'/{cmd.name} {subcmd.name}'] = subcmd
|
|
|
85
|
+ self.topic_index[f'subcmd:{cmd.name}.{subcmd.name}'] = subcmd
|
|
|
86
|
+ self.topic_index[f'/{cmd.name} {subcmd.name}'] = subcmd
|
|
158
|
87
|
add_text_to_index(subcmd, cmd.name)
|
|
159
|
88
|
add_text_to_index(subcmd, subcmd.name)
|
|
160
|
89
|
if subcmd.description:
|
|
|
@@ -163,14 +92,14 @@ class HelpCog(BaseCog, name='Help'):
|
|
163
|
92
|
if not isinstance(cog, BaseCog):
|
|
164
|
93
|
continue
|
|
165
|
94
|
key = f'cog:{cog_qname}'
|
|
166
|
|
- self.obj_index[key] = cog
|
|
|
95
|
+ self.topic_index[key] = cog
|
|
167
|
96
|
add_text_to_index(cog, cog.qualified_name)
|
|
168
|
97
|
if cog.description:
|
|
169
|
98
|
add_text_to_index(cog, cog.description)
|
|
170
|
99
|
|
|
171
|
|
- def object_for_help_symbol(self, symbol: str) -> Optional[HelpTopic]:
|
|
|
100
|
+ def topic_for_help_symbol(self, symbol: str) -> Optional[HelpTopic]:
|
|
172
|
101
|
self.__create_help_index()
|
|
173
|
|
- return self.obj_index.get(symbol, None)
|
|
|
102
|
+ return self.topic_index.get(symbol, None)
|
|
174
|
103
|
|
|
175
|
104
|
def all_commands(self) -> list[Union[Command, Group]]:
|
|
176
|
105
|
# PyCharm not interpreting conditional return type correctly.
|
|
|
@@ -202,20 +131,20 @@ class HelpCog(BaseCog, name='Help'):
|
|
202
|
131
|
if can_use_cog(cog, permissions)
|
|
203
|
132
|
]
|
|
204
|
133
|
|
|
205
|
|
- def all_accessible_objects(self, permissions: Optional[Permissions], *,
|
|
206
|
|
- include_cogs: bool = True,
|
|
207
|
|
- include_commands: bool = True,
|
|
208
|
|
- include_subcommands: bool = True) -> list[HelpTopic]:
|
|
209
|
|
- objs = []
|
|
|
134
|
+ def all_accessible_topics(self, permissions: Optional[Permissions], *,
|
|
|
135
|
+ include_cogs: bool = True,
|
|
|
136
|
+ include_commands: bool = True,
|
|
|
137
|
+ include_subcommands: bool = True) -> list[HelpTopic]:
|
|
|
138
|
+ topics = []
|
|
210
|
139
|
if include_cogs:
|
|
211
|
|
- objs += self.all_accessible_cogs(permissions)
|
|
|
140
|
+ topics += self.all_accessible_cogs(permissions)
|
|
212
|
141
|
if include_commands:
|
|
213
|
|
- objs += self.all_accessible_commands(permissions)
|
|
|
142
|
+ topics += self.all_accessible_commands(permissions)
|
|
214
|
143
|
if include_subcommands:
|
|
215
|
|
- objs += self.all_accessible_subcommands(permissions)
|
|
216
|
|
- return objs
|
|
|
144
|
+ topics += self.all_accessible_subcommands(permissions)
|
|
|
145
|
+ return topics
|
|
217
|
146
|
|
|
218
|
|
- def objects_for_keywords(self, search: str, permissions: Optional[Permissions]) -> list[HelpTopic]:
|
|
|
147
|
+ def topics_for_keywords(self, search: str, permissions: Optional[Permissions]) -> list[HelpTopic]:
|
|
219
|
148
|
self.__create_help_index()
|
|
220
|
149
|
|
|
221
|
150
|
# Break into words (or word fragments)
|
|
|
@@ -229,53 +158,55 @@ class HelpCog(BaseCog, name='Help'):
|
|
229
|
158
|
# to known indexed keywords, then collecting those associated results. Should
|
|
230
|
159
|
# just keep corpuses of searchable, normalized text for each topic and do a
|
|
231
|
160
|
# direct `in` test.
|
|
232
|
|
- matching_objects_set = None
|
|
|
161
|
+ matching_topics_set = None
|
|
233
|
162
|
for word in words:
|
|
234
|
163
|
word_matches = set()
|
|
235
|
164
|
for k in self.keyword_index.keys():
|
|
236
|
165
|
if word in k:
|
|
237
|
|
- objs = self.keyword_index.get(k, None)
|
|
238
|
|
- if objs is not None:
|
|
239
|
|
- word_matches.update(objs)
|
|
240
|
|
- if matching_objects_set is None:
|
|
241
|
|
- matching_objects_set = word_matches
|
|
|
166
|
+ topics = self.keyword_index.get(k, None)
|
|
|
167
|
+ if topics is not None:
|
|
|
168
|
+ word_matches.update(topics)
|
|
|
169
|
+ if matching_topics_set is None:
|
|
|
170
|
+ matching_topics_set = word_matches
|
|
242
|
171
|
else:
|
|
243
|
|
- matching_objects_set = matching_objects_set & word_matches
|
|
|
172
|
+ matching_topics_set = matching_topics_set & word_matches
|
|
244
|
173
|
|
|
245
|
174
|
# Filter by accessibility
|
|
246
|
|
- accessible_objects = [
|
|
247
|
|
- obj
|
|
248
|
|
- for obj in matching_objects_set or {}
|
|
249
|
|
- if ((isinstance(obj, Command) or isinstance(obj, Group)) and can_use_command(obj, permissions)) or \
|
|
250
|
|
- (isinstance(obj, BaseCog) and can_use_cog(obj, permissions))
|
|
|
175
|
+ accessible_topics = [
|
|
|
176
|
+ topic
|
|
|
177
|
+ for topic in matching_topics_set or {}
|
|
|
178
|
+ if ((isinstance(topic, Command) or isinstance(topic, Group)) and can_use_command(topic, permissions)) or \
|
|
|
179
|
+ (isinstance(topic, BaseCog) and can_use_cog(topic, permissions))
|
|
251
|
180
|
]
|
|
252
|
181
|
|
|
253
|
182
|
# Sort and return
|
|
254
|
|
- return sorted(accessible_objects, key=lambda obj: (
|
|
255
|
|
- isinstance(obj, Command),
|
|
256
|
|
- isinstance(obj, BaseCog),
|
|
257
|
|
- obj.qualified_name if isinstance(obj, BaseCog) else obj.name
|
|
|
183
|
+ return sorted(accessible_topics, key=lambda topic: (
|
|
|
184
|
+ isinstance(topic, Command),
|
|
|
185
|
+ isinstance(topic, BaseCog),
|
|
|
186
|
+ topic.qualified_name if isinstance(topic, BaseCog) else topic.name
|
|
258
|
187
|
))
|
|
259
|
188
|
|
|
260
|
|
- @command(name='help')
|
|
|
189
|
+ @command(
|
|
|
190
|
+ name='help',
|
|
|
191
|
+ description='Shows help for using commands and module configuration.',
|
|
|
192
|
+ extras={
|
|
|
193
|
+ 'long_description': '`/help` will show a list of top-level topics.\n'
|
|
|
194
|
+ '\n'
|
|
|
195
|
+ "`/help /<command_name>` will show help about a specific command or list a command's subcommands.\n"
|
|
|
196
|
+ '\n'
|
|
|
197
|
+ '`/help /<command_name> <subcommand_name>` will show help about a specific subcommand.\n'
|
|
|
198
|
+ '\n'
|
|
|
199
|
+ '`/help <module_name>` will show help about configuring a module.\n'
|
|
|
200
|
+ '\n'
|
|
|
201
|
+ '`/help <keywords>` will do a text search for topics.',
|
|
|
202
|
+ }
|
|
|
203
|
+ )
|
|
261
|
204
|
@guild_only()
|
|
262
|
205
|
@autocomplete(search=search_autocomplete)
|
|
263
|
206
|
async def help_command(self, interaction: Interaction, search: Optional[str]) -> None:
|
|
264
|
207
|
"""
|
|
265
|
208
|
Shows help for using commands and subcommands and configuring modules.
|
|
266
|
209
|
|
|
267
|
|
- `/help` will show a list of top-level topics.
|
|
268
|
|
-
|
|
269
|
|
- `/help /<command_name>` will show help about a specific command or
|
|
270
|
|
- list a command's subcommands.
|
|
271
|
|
-
|
|
272
|
|
- `/help /<command_name> <subcommand_name>` will show help about a
|
|
273
|
|
- specific subcommand.
|
|
274
|
|
-
|
|
275
|
|
- `/help <module_name>` will show help about configuring a module.
|
|
276
|
|
-
|
|
277
|
|
- `/help <keywords>` will do a text search for topics.
|
|
278
|
|
-
|
|
279
|
210
|
Parameters
|
|
280
|
211
|
----------
|
|
281
|
212
|
interaction: Interaction
|
|
|
@@ -285,27 +216,24 @@ class HelpCog(BaseCog, name='Help'):
|
|
285
|
216
|
if search is None:
|
|
286
|
217
|
await self.__send_general_help(interaction)
|
|
287
|
218
|
return
|
|
288
|
|
- obj = self.object_for_help_symbol(search)
|
|
289
|
|
- if obj:
|
|
290
|
|
- await self.__send_object_help(interaction, obj)
|
|
|
219
|
+ topic = self.topic_for_help_symbol(search)
|
|
|
220
|
+ if topic:
|
|
|
221
|
+ await self.__send_topic_help(interaction, topic)
|
|
291
|
222
|
return
|
|
292
|
|
- matches = self.objects_for_keywords(search, interaction.permissions)
|
|
|
223
|
+ matches = self.topics_for_keywords(search, interaction.permissions)
|
|
293
|
224
|
await self.__send_keyword_help(interaction, matches)
|
|
294
|
225
|
|
|
295
|
|
- async def __send_object_help(self, interaction: Interaction, obj: HelpTopic) -> None:
|
|
296
|
|
- if isinstance(obj, Command):
|
|
297
|
|
- if obj.parent:
|
|
298
|
|
- await self.__send_subcommand_help(interaction, obj.parent, obj)
|
|
299
|
|
- else:
|
|
300
|
|
- await self.__send_command_help(interaction, obj)
|
|
|
226
|
+ async def __send_topic_help(self, interaction: Interaction, topic: HelpTopic) -> None:
|
|
|
227
|
+ if isinstance(topic, Command):
|
|
|
228
|
+ await self.__send_command_help(interaction, topic)
|
|
301
|
229
|
return
|
|
302
|
|
- if isinstance(obj, Group):
|
|
303
|
|
- await self.__send_command_help(interaction, obj)
|
|
|
230
|
+ if isinstance(topic, Group):
|
|
|
231
|
+ await self.__send_command_help(interaction, topic)
|
|
304
|
232
|
return
|
|
305
|
|
- if isinstance(obj, BaseCog):
|
|
306
|
|
- await self.__send_cog_help(interaction, obj)
|
|
|
233
|
+ if isinstance(topic, BaseCog):
|
|
|
234
|
+ await self.__send_cog_help(interaction, topic)
|
|
307
|
235
|
return
|
|
308
|
|
- self.log(interaction.guild, f'No help for object {obj}')
|
|
|
236
|
+ self.log(interaction.guild, f'No help for topic object {topic}')
|
|
309
|
237
|
await interaction.response.send_message(
|
|
310
|
238
|
f'{CONFIG["failure_emoji"]} Failed to get help info.',
|
|
311
|
239
|
ephemeral=True,
|
|
|
@@ -361,15 +289,15 @@ class HelpCog(BaseCog, name='Help'):
|
|
361
|
289
|
ephemeral=True,
|
|
362
|
290
|
)
|
|
363
|
291
|
|
|
364
|
|
- async def __send_keyword_help(self, interaction: Interaction, matching_objects: Optional[list[HelpTopic]]) -> None:
|
|
|
292
|
+ async def __send_keyword_help(self, interaction: Interaction, matching_topics: Optional[list[HelpTopic]]) -> None:
|
|
365
|
293
|
matching_commands = [
|
|
366
|
294
|
cmd
|
|
367
|
|
- for cmd in matching_objects or []
|
|
|
295
|
+ for cmd in matching_topics or []
|
|
368
|
296
|
if isinstance(cmd, Command) or isinstance(cmd, Group)
|
|
369
|
297
|
]
|
|
370
|
298
|
matching_cogs = [
|
|
371
|
299
|
cog
|
|
372
|
|
- for cog in matching_objects or []
|
|
|
300
|
+ for cog in matching_topics or []
|
|
373
|
301
|
if isinstance(cog, BaseCog)
|
|
374
|
302
|
]
|
|
375
|
303
|
if len(matching_commands) + len(matching_cogs) == 0:
|
|
|
@@ -379,9 +307,9 @@ class HelpCog(BaseCog, name='Help'):
|
|
379
|
307
|
delete_after=10,
|
|
380
|
308
|
)
|
|
381
|
309
|
return
|
|
382
|
|
- if len(matching_objects) == 1:
|
|
383
|
|
- obj = matching_objects[0]
|
|
384
|
|
- await self.__send_object_help(interaction, obj)
|
|
|
310
|
+ if len(matching_topics) == 1:
|
|
|
311
|
+ topic = matching_topics[0]
|
|
|
312
|
+ await self.__send_topic_help(interaction, topic)
|
|
385
|
313
|
return
|
|
386
|
314
|
|
|
387
|
315
|
text = '## :information_source: Matching Help Topics'
|
|
|
@@ -393,7 +321,7 @@ class HelpCog(BaseCog, name='Help'):
|
|
393
|
321
|
else:
|
|
394
|
322
|
text += f'\n- `/{cmd.name}`'
|
|
395
|
323
|
if len(matching_cogs) > 0:
|
|
396
|
|
- text += '\n### Cogs'
|
|
|
324
|
+ text += '\n### Modules'
|
|
397
|
325
|
for cog in matching_cogs:
|
|
398
|
326
|
text += f'\n- {cog.qualified_name}'
|
|
399
|
327
|
await interaction.response.send_message(
|
|
|
@@ -405,37 +333,53 @@ class HelpCog(BaseCog, name='Help'):
|
|
405
|
333
|
text = ''
|
|
406
|
334
|
if addendum is not None:
|
|
407
|
335
|
text += addendum + '\n\n'
|
|
408
|
|
- text += f'## :information_source: Command Help\n`/{command_or_group.name}`\n\n{command_or_group.description}'
|
|
|
336
|
+ if command_or_group.parent:
|
|
|
337
|
+ text += f'## :information_source: Subcommand Help'
|
|
|
338
|
+ text += f'\n`/{command_or_group.parent.name} {command_or_group.name}`'
|
|
|
339
|
+ else:
|
|
|
340
|
+ text += f'## :information_source: Command Help'
|
|
|
341
|
+ if isinstance(command_or_group, Group):
|
|
|
342
|
+ text += f'\n`/{command_or_group.name} subcommand_name`'
|
|
|
343
|
+ else:
|
|
|
344
|
+ text += f'\n`/{command_or_group.name}`'
|
|
|
345
|
+ if isinstance(command_or_group, Command):
|
|
|
346
|
+ optional_nesting = 0
|
|
|
347
|
+ for param in command_or_group.parameters:
|
|
|
348
|
+ text += ' '
|
|
|
349
|
+ if not param.required:
|
|
|
350
|
+ text += '['
|
|
|
351
|
+ optional_nesting += 1
|
|
|
352
|
+ text += f'_{param.name}_'
|
|
|
353
|
+ if optional_nesting > 0:
|
|
|
354
|
+ text += ']' * optional_nesting
|
|
|
355
|
+ text += f'\n\n{command_or_group.description}'
|
|
|
356
|
+ if command_or_group.extras.get('long_description'):
|
|
|
357
|
+ text += f'\n\n{command_or_group.extras["long_description"]}'
|
|
409
|
358
|
if isinstance(command_or_group, Group):
|
|
410
|
359
|
subcmds: dict[str, Command] = self.get_subcommand_list(command_or_group, permissions=interaction.permissions)
|
|
411
|
360
|
if len(subcmds) > 0:
|
|
412
|
361
|
text += '\n### Subcommands:'
|
|
413
|
362
|
for subcmd_name, subcmd in sorted(subcmds.items()):
|
|
414
|
363
|
text += f'\n- `{subcmd_name}`: {subcmd.description}'
|
|
|
364
|
+ text += f'\n-# To use a subcommand, type it after the command. e.g. `/{command_or_group.name} subcommand_name`'
|
|
|
365
|
+ text += f'\n-# Get help on a subcommand by typing `/help /{command_or_group.name} subcommand_name`'
|
|
415
|
366
|
else:
|
|
416
|
367
|
params = command_or_group.parameters
|
|
417
|
368
|
if len(params) > 0:
|
|
418
|
369
|
text += '\n### Parameters:'
|
|
419
|
370
|
for param in params:
|
|
420
|
371
|
text += f'\n- `{param.name}`: {param.description}'
|
|
421
|
|
- await interaction.response.send_message(text, ephemeral=True)
|
|
422
|
|
-
|
|
423
|
|
- async def __send_subcommand_help(self, interaction: Interaction, group: Group, subcommand: Command) -> None:
|
|
424
|
|
- text = f'## :information_source: Subcommand Help'
|
|
425
|
|
- text += f'\n`/{group.name} {subcommand.name}`'
|
|
426
|
|
- text += f'\n\n{subcommand.description}'
|
|
427
|
|
- params = subcommand.parameters
|
|
428
|
|
- if len(params) > 0:
|
|
429
|
|
- text += '\n### Parameters:'
|
|
430
|
|
- for param in params:
|
|
431
|
|
- text += f'\n- `{param.name}`: {param.description}'
|
|
|
372
|
+ if not param.required:
|
|
|
373
|
+ text += ' (optional)'
|
|
432
|
374
|
await interaction.response.send_message(text, ephemeral=True)
|
|
433
|
375
|
|
|
434
|
376
|
async def __send_cog_help(self, interaction: Interaction, cog: BaseCog) -> None:
|
|
435
|
377
|
text = f'## :information_source: Module Help'
|
|
436
|
378
|
text += f'\n**{cog.qualified_name}** module'
|
|
437
|
|
- if cog.description is not None:
|
|
438
|
|
- text += f'\n\n{cog.description}'
|
|
|
379
|
+ if cog.short_description is not None:
|
|
|
380
|
+ text += f'\n\n{cog.short_description}'
|
|
|
381
|
+ if cog.long_description is not None:
|
|
|
382
|
+ text += f'\n\n{cog.long_description}'
|
|
439
|
383
|
|
|
440
|
384
|
cmds = [
|
|
441
|
385
|
cmd
|