Pārlūkot izejas kodu

More help command work

pull/13/head
Rocketsoup 2 mēnešus atpakaļ
vecāks
revīzija
971fe0bdd2

+ 2
- 0
config.sample.py Parādīt failu

@@ -46,6 +46,8 @@ CONFIG = {
46 46
 	'info_emoji': 'ℹ️',
47 47
 	# Logging something that happened on the server (e.g. a user joined the server)
48 48
 	'log_emoji': '📋',
49
+	# A task is in progress
50
+	'wait_emoji': '⏳',
49 51
 
50 52
     # -----------------------------------------------------------------------
51 53
     # Default values for cog-specific settings that a guild has not overridden

+ 15
- 15
rocketbot/cogs/autokickcog.py Parādīt failu

@@ -37,19 +37,19 @@ class AutoKickCog(BaseCog, name='Auto Kick'):
37 37
 		brief='autokick',
38 38
 		description='Whether this cog is enabled for a guild.')
39 39
 	SETTING_BAN_COUNT = CogSetting('bancount', int,
40
-			brief='number of repeat kicks before a ban',
41
-			description='The number of times a user can join and be kicked ' + \
42
-					'before the next rejoin will result in a ban. A value of 0 ' + \
40
+		brief='number of repeat kicks before a ban',
41
+		description='The number of times a user can join and be kicked '
42
+					'before the next rejoin will result in a ban. A value of 0 '
43 43
 					'disables this feature (only kick, never ban).',
44
-			usage='<count:int>',
45
-			min_value=0)
44
+		usage='<count:int>',
45
+		min_value=0)
46 46
 	SETTING_OFFLINE_ONLY = CogSetting('offlineonly', bool,
47
-			brief='whether to only kick users whose status is offline',
48
-			description='Compromised accounts may have a status of offline. ' + \
49
-					'If this setting is enabled, the user\'s status will be ' + \
50
-					'checked a few seconds after joining. If it is offline ' + \
47
+		brief='whether to only kick users whose status is offline',
48
+		description='Compromised accounts may have a status of offline. '
49
+					'If this setting is enabled, the user\'s status will be '
50
+					'checked a few seconds after joining. If it is offline '
51 51
 					'they will be kicked.',
52
-			usage='<true|false>')
52
+		usage='<true|false>')
53 53
 
54 54
 	STATE_KEY_RECENT_KICKS = "AutoKickCog.recent_joins"
55 55
 
@@ -129,11 +129,11 @@ class AutoKickCog(BaseCog, name='Auto Kick'):
129 129
 		else:
130 130
 			await member.kick(reason='Rocketbot: Autokick enabled.')
131 131
 			msg = BotMessage(guild,
132
-							text=f'Autokicked {member.mention} ({member.id}) ' + \
133
-								f'({AutoKickCog.ordinal(context.kick_count)} time). ' + \
134
-								disable_help + ' ' + ban_help,
135
-							type=BotMessage.TYPE_INFO,
136
-							context=None)
132
+				text=f'Autokicked {member.mention} ({member.id}) ' + \
133
+					f'({AutoKickCog.ordinal(context.kick_count)} time). ' + \
134
+					disable_help + ' ' + ban_help,
135
+				type=BotMessage.TYPE_INFO,
136
+				context=None)
137 137
 			await self.post_message(msg)
138 138
 			self.log(guild, f'Autokicked {member.name} ' + \
139 139
 				f'({AutoKickCog.ordinal(context.kick_count)} time)')

+ 9
- 6
rocketbot/cogs/basecog.py Parādīt failu

@@ -123,7 +123,7 @@ class BaseCog(Cog):
123 123
 
124 124
 	@classmethod
125 125
 	def get_guild_setting(cls,
126
-			guild: Guild,
126
+			guild: Optional[Guild],
127 127
 			setting: CogSetting,
128 128
 			use_cog_default_if_not_set: bool = True):
129 129
 		"""
@@ -132,11 +132,14 @@ class BaseCog(Cog):
132 132
 		unless the optional `use_cog_default_if_not_set` is `False`, then
133 133
 		`None` will be returned.
134 134
 		"""
135
-		key = f'{cls.__name__}.{setting.name}'
136
-		value = Storage.get_config_value(guild, key)
137
-		if value is None and use_cog_default_if_not_set:
138
-			value = cls.get_cog_default(setting.name)
139
-		return value
135
+		if guild:
136
+			key = f'{cls.__name__}.{setting.name}'
137
+			value = Storage.get_config_value(guild, key)
138
+			if value is not None:
139
+				return value
140
+		if use_cog_default_if_not_set:
141
+			return cls.get_cog_default(setting.name)
142
+		return None
140 143
 
141 144
 	@classmethod
142 145
 	def set_guild_setting(cls,

+ 35
- 18
rocketbot/cogs/generalcog.py Parādīt failu

@@ -64,17 +64,15 @@ class GeneralCog(BaseCog, name='General'):
64 64
 			self.log(None, f'Session resumed after {disconnect_duration.total_seconds()} seconds')
65 65
 
66 66
 	@command(
67
-		description='Posts a test warning',
67
+		description='Posts a test warning.',
68 68
 		extras={
69
-			'long_description': 'Tests whether a warning channel is configured for this ' + \
70
-				'guild by posting a test warning. If a mod mention is ' + \
71
-				'configured, that user/role will be tagged in the test warning.',
69
+			'long_description': 'Simulates a warning. The configured warning channel '
70
+								'and mod mention will be used.',
72 71
 		},
73 72
 	)
74 73
 	@guild_only()
75 74
 	@default_permissions(ban_members=True)
76 75
 	async def test_warn(self, interaction: Interaction):
77
-		"""Command handler"""
78 76
 		if Storage.get_config_value(interaction.guild, ConfigKey.WARNING_CHANNEL_ID) is None:
79 77
 			await interaction.response.send_message(
80 78
 				f'{CONFIG["warning_emoji"]} No warning channel set!',
@@ -92,10 +90,10 @@ class GeneralCog(BaseCog, name='General'):
92 90
 			)
93 91
 
94 92
 	@command(
95
-		description='Greets the user',
93
+		description='Greets the user.',
96 94
 		extras={
97
-			'long_description': 'Replies to the command message. Useful to ensure the ' + \
98
-				'bot is working properly.',
95
+			'long_description': 'Replies to the command message. Useful to ensure the '
96
+								'bot is responsive. Message is only visible to the user.',
99 97
 		},
100 98
 	)
101 99
 	async def hello(self, interaction: Interaction):
@@ -106,10 +104,10 @@ class GeneralCog(BaseCog, name='General'):
106 104
 		)
107 105
 
108 106
 	@command(
109
-		description='Shuts down the bot',
107
+		description='Shuts down the bot.',
110 108
 		extras={
111
-			'long_description': 'Causes the bot script to terminate. Only usable by a ' + \
112
-				'user with server admin permissions.',
109
+			'long_description': 'Terminates the bot script. Only usable by a '
110
+								'server administrator.',
113 111
 		},
114 112
 	)
115 113
 	@guild_only()
@@ -120,7 +118,7 @@ class GeneralCog(BaseCog, name='General'):
120 118
 		await self.bot.close()
121 119
 
122 120
 	@command(
123
-		description='Mass deletes messages',
121
+		description='Mass deletes messages.',
124 122
 		extras={
125 123
 			'long_description': 'Deletes recent messages by the given user. The user ' +
126 124
 				'can be either an @ mention or a numeric user ID. The age is ' +
@@ -132,9 +130,22 @@ class GeneralCog(BaseCog, name='General'):
132 130
 	@guild_only()
133 131
 	@default_permissions(manage_messages=True)
134 132
 	async def delete_messages(self, interaction: Interaction, user: User, age: Transform[timedelta, TimeDeltaTransformer]) -> None:
135
-		"""Command handler"""
133
+		"""
134
+		Mass deletes messages.
135
+
136
+		Parameters
137
+		----------
138
+		interaction: :class:`Interaction`
139
+		user: :class:`User`
140
+			user to delete messages from
141
+		age: :class:`timedelta`
142
+			maximum age of messages to delete
143
+		"""
136 144
 		member_id = user.id
137 145
 		cutoff: datetime = datetime.now(timezone.utc) - age
146
+
147
+		resp = await interaction.response.defer(ephemeral=True, thinking=True)
148
+
138 149
 		def predicate(message: Message) -> bool:
139 150
 			return str(message.author.id) == member_id and message.created_at >= cutoff
140 151
 		deleted_messages = []
@@ -144,8 +155,14 @@ class GeneralCog(BaseCog, name='General'):
144 155
 			except DiscordException:
145 156
 				# XXX: Sloppily glossing over access errors instead of checking access
146 157
 				pass
147
-		await interaction.response.send_message(
148
-			f'{CONFIG["success_emoji"]} Deleted {len(deleted_messages)} ' + \
149
-			f'messages by {user.mention}> from the past {describe_timedelta(age)}.',
150
-			ephemeral=True,
151
-		)
158
+
159
+		if len(deleted_messages) > 0:
160
+			await resp.resource.edit(
161
+				content=f'{CONFIG["success_emoji"]} Deleted {len(deleted_messages)} '
162
+						f'messages by {user.mention} from the past {describe_timedelta(age)}.',
163
+			)
164
+		else:
165
+			await resp.resource.edit(
166
+				content=f'{CONFIG["success_emoji"]} No messages found for {user.mention} '
167
+						'from the past {describe_timedelta(age)}.',
168
+			)

+ 31
- 12
rocketbot/cogs/helpcog.py Parādīt failu

@@ -45,7 +45,7 @@ async def subcommand_autocomplete(interaction: Interaction, current: str) -> lis
45 45
 			print(f'Subcommands for {cmd_name} was None')
46 46
 			return []
47 47
 		return [
48
-			Choice(name=f'{subcmd_name} subcommand', value=f'subcmd:{cmd_name}.{subcmd_name}')
48
+			Choice(name=f'{subcmd_name} subcommand', value=f'subcmd:{cmd.name}.{subcmd_name}')
49 49
 			for subcmd_name in sorted(subcmds.keys())
50 50
 			if len(current) == 0 or current in subcmd_name
51 51
 		][:25]
@@ -137,8 +137,6 @@ class HelpCog(BaseCog, name='Help'):
137 137
 			add_text_to_index(cog, cog.qualified_name)
138 138
 			if cog.description:
139 139
 				add_text_to_index(cog, cog.description)
140
-		print(self.obj_index.keys())
141
-		print(self.keyword_index.keys())
142 140
 
143 141
 	def object_for_help_symbol(self, symbol: str) -> Optional[Union[Command, Group, BaseCog]]:
144 142
 		self.__create_help_index()
@@ -167,9 +165,9 @@ class HelpCog(BaseCog, name='Help'):
167 165
 		----------
168 166
 		interaction: Interaction
169 167
 		topic: Optional[str]
170
-			Optional topic to get specific help for. Getting help on a command can optionally start with a leading slash.
168
+			optional command, module, or keywords to get specific help for
171 169
 		subtopic: Optional[str]
172
-			Optional subtopic to get specific help for.
170
+			optional subcommand to get specific help for
173 171
 		"""
174 172
 		print(f'help_command(interaction, {topic}, {subtopic})')
175 173
 
@@ -316,6 +314,8 @@ class HelpCog(BaseCog, name='Help'):
316 314
 		)
317 315
 
318 316
 	async def __send_command_help(self, interaction: Interaction, command_or_group: Union[Command, Group], addendum: Optional[str] = None) -> None:
317
+		if isinstance(command_or_group, Command):
318
+			print(f"Doc:\n{command_or_group.callback.__doc__}")
319 319
 		text = ''
320 320
 		if addendum is not None:
321 321
 			text += addendum + '\n\n'
@@ -323,30 +323,48 @@ class HelpCog(BaseCog, name='Help'):
323 323
 		if isinstance(command_or_group, Group):
324 324
 			subcmds: dict[str, Command] = self.get_subcommand_list(command_or_group, permissions=interaction.permissions)
325 325
 			if len(subcmds) > 0:
326
-				text += '\n\n### Subcommands:'
326
+				text += '\n### Subcommands:'
327 327
 				for subcmd_name, subcmd in sorted(subcmds.items()):
328 328
 					text += f'\n- `{subcmd_name}`: {subcmd.description}'
329 329
 		else:
330 330
 			params = command_or_group.parameters
331 331
 			if len(params) > 0:
332
-				text += '\n\n### Parameters:'
332
+				text += '\n### Parameters:'
333 333
 				for param in params:
334 334
 					text += f'\n- `{param.name}`: {param.description}'
335 335
 		await interaction.response.send_message(text, ephemeral=True)
336 336
 
337 337
 	async def __send_subcommand_help(self, interaction: Interaction, group: Group, subcommand: Command) -> None:
338
-		text = f'## :information_source: Subcommand Help\n`/{group.name} {subcommand.name}`\n\n{subcommand.description}\n\n{subcommand.description}'
338
+		text = f'## :information_source: Subcommand Help'
339
+		text += f'\n`/{group.name} {subcommand.name}`'
340
+		text += f'\n\n{subcommand.description}'
339 341
 		params = subcommand.parameters
340 342
 		if len(params) > 0:
341
-			text += '\n\n### Parameters:'
343
+			text += '\n### Parameters:'
342 344
 			for param in params:
343 345
 				text += f'\n- `{param.name}`: {param.description}'
344 346
 		await interaction.response.send_message(text, ephemeral=True)
345 347
 
346 348
 	async def __send_cog_help(self, interaction: Interaction, cog: BaseCog) -> None:
347
-		text = f'## :information_source: Module Help\n{cog.qualified_name}'
349
+		text = f'## :information_source: Module Help'
350
+		text += f'\n**{cog.qualified_name}** module'
348 351
 		if cog.description is not None:
349
-			text += f'\n{cog.description}'
352
+			text += f'\n\n{cog.description}'
353
+
354
+		cmds = [
355
+			cmd
356
+			for cmd in sorted(cog.get_app_commands(), key=lambda c: c.name)
357
+			if can_use_command(cmd, interaction.permissions)
358
+		]
359
+		if len(cmds) > 0:
360
+			text += '\n### Commands:'
361
+			for cmd in cmds:
362
+				text += f'\n- `/{cmd.name}` - {cmd.description}'
363
+				if isinstance(cmd, Group):
364
+					subcmds = [ subcmd for subcmd in cmd.commands if can_use_command(subcmd, interaction.permissions) ]
365
+					if len(subcmds) > 0:
366
+						text += f' ({len(subcmds)} subcommands)'
367
+
350 368
 		settings = cog.settings
351 369
 		if len(settings) > 0:
352 370
 			text += '\n### Configuration'
@@ -356,7 +374,8 @@ class HelpCog(BaseCog, name='Help'):
356 374
 			for setting in sorted(settings, key=lambda s: s.name):
357 375
 				if setting.name == 'enabled':
358 376
 					continue
359
-				text += f'\n- `/get` or `/set {cog.config_prefix}_{setting.name}` - {setting.description}'
377
+				text += f'\n- `/get` or `/set {cog.config_prefix}_{setting.name}` - {setting.brief}'
378
+		print(text)
360 379
 		await interaction.response.send_message(
361 380
 			text,
362 381
 			ephemeral=True,

+ 6
- 6
rocketbot/cogs/urlspamcog.py Parādīt failu

@@ -38,17 +38,17 @@ class URLSpamCog(BaseCog, name='URL Spam'):
38 38
 			enum_values={'nothing', 'modwarn', 'delete', 'kick', 'ban'})
39 39
 	SETTING_JOIN_AGE = CogSetting('joinage', timedelta,
40 40
 			brief='seconds since member joined',
41
-			description='The minimum seconds since the user joined the ' + \
42
-				'server before they can post URLs. URLs posted by users ' + \
43
-				'who joined too recently will be flagged. Keep in mind ' + \
44
-				'many servers have a minimum 10 minute cooldown before ' + \
45
-				'new members can say anything. Setting to 0 effectively ' + \
41
+			description='The minimum seconds since the user joined the '
42
+				'server before they can post URLs. URLs posted by users '
43
+				'who joined too recently will be flagged. Keep in mind '
44
+				'many servers have a minimum 10 minute cooldown before '
45
+				'new members can say anything. Setting to 0 effectively '
46 46
 				'disables URL spam detection.',
47 47
 			usage='<seconds:int>',
48 48
 			min_value=timedelta(seconds=0))
49 49
 	SETTING_DECEPTIVE_ACTION = CogSetting('deceptiveaction', Literal['nothing', 'modwarn', 'modwarndelete', 'chatwarn', 'chatwarndelete', 'delete', 'kick', 'ban'],
50 50
 			brief='action to take on deceptive link markdown',
51
-			description='The action to take on chat messages with links ' + \
51
+			description='The action to take on chat messages with links '
52 52
 				'where the text looks like a different URL than the actual link.',
53 53
 			enum_values={'nothing', 'modwarn', 'modwarndelete',
54 54
 				'chatwarn', 'chatwarndelete', 'delete', 'kick', 'ban'})

+ 27
- 11
rocketbot/cogs/usernamecog.py Parādīt failu

@@ -88,38 +88,54 @@ class UsernamePatternCog(BaseCog, name='Username Pattern'):
88 88
 	)
89 89
 
90 90
 	@username.command(
91
-		description='Adds a username pattern',
91
+		description='Adds a username pattern to match against new members.',
92 92
 		extras={
93 93
 			'long_description': 'Adds a username pattern.',
94 94
 			'usage': '<pattern>',
95 95
 		},
96 96
 	)
97 97
 	async def add(self, interaction: Interaction, pattern: str) -> None:
98
-		"""Command handler"""
98
+		"""
99
+		Adds a username pattern to match against new members.
100
+
101
+		Parameters
102
+		----------
103
+		interaction : Interaction
104
+		pattern : str
105
+			a substring to look for in usernames
106
+		"""
99 107
 		norm_pattern = pattern.lower()
100 108
 		patterns: list[str] = self.__get_patterns(interaction.guild)
101 109
 		if norm_pattern in patterns:
102 110
 			await interaction.response.send_message(
103
-				f'Pattern `{norm_pattern}` already added.',
111
+				f'{CONFIG["warning_emoji"]} Pattern `{norm_pattern}` already added.',
104 112
 				ephemeral=True
105 113
 			)
106 114
 			return
107 115
 		patterns.append(norm_pattern)
108 116
 		self.__save_patterns(interaction.guild, patterns)
109 117
 		await interaction.response.send_message(
110
-			f'Pattern `{norm_pattern}` added.',
118
+			f'{CONFIG["success_emoji"]} Pattern `{norm_pattern}` added.',
111 119
 			ephemeral=True
112 120
 		)
113 121
 
114 122
 	@username.command(
115
-		description='Removes a username pattern',
123
+		description='Removes a username pattern.',
116 124
 		extras={
117 125
 			'long_description': 'Removes an existing username pattern',
118 126
 			'usage': '<pattern>',
119 127
 		},
120 128
 	)
121 129
 	async def remove(self, interaction: Interaction, pattern: str) -> None:
122
-		"""Command handler"""
130
+		"""
131
+		Removes a username pattern.
132
+
133
+		Parameters
134
+		----------
135
+		interaction : Interaction
136
+		pattern : str
137
+		    the existing username pattern to remove
138
+		"""
123 139
 		norm_pattern = pattern.lower()
124 140
 		guild: Guild = interaction.guild
125 141
 		patterns: list[str] = self.__get_patterns(guild)
@@ -127,18 +143,18 @@ class UsernamePatternCog(BaseCog, name='Username Pattern'):
127 143
 		patterns = list(filter(lambda p: p != norm_pattern, patterns))
128 144
 		if len(patterns) == len_before:
129 145
 			await interaction.response.send_message(
130
-				f'Pattern `{norm_pattern}` not found.',
146
+				f'{CONFIG["warning_emoji"]} Pattern `{norm_pattern}` not found.',
131 147
 				ephemeral=True,
132 148
 			)
133 149
 			return
134 150
 		self.__save_patterns(guild, patterns)
135 151
 		await interaction.response.send_message(
136
-			f'Pattern `{norm_pattern}` removed.',
152
+			f'{CONFIG["success_emoji"]} Pattern `{norm_pattern}` removed.',
137 153
 			ephemeral=True,
138 154
 		)
139 155
 
140 156
 	@username.command(
141
-		description='Lists username patterns'
157
+		description='Lists existing username patterns.'
142 158
 	)
143 159
 	async def list(self, interaction: Interaction) -> None:
144 160
 		"""Command handler"""
@@ -146,11 +162,11 @@ class UsernamePatternCog(BaseCog, name='Username Pattern'):
146 162
 		patterns: list[str] = self.__get_patterns(guild)
147 163
 		if len(patterns) == 0:
148 164
 			await interaction.response.send_message(
149
-				'No patterns defined',
165
+				f'{CONFIG["success_emoji"]} No patterns defined.',
150 166
 				ephemeral=True,
151 167
 			)
152 168
 		else:
153
-			msg = 'Patterns:\n\n> `' + '`\n> `'.join(patterns) + '`'
169
+			msg = f'{CONFIG["info_emoji"]} Patterns:\n\n> `' + '`\n> `'.join(patterns) + '`'
154 170
 			await interaction.response.send_message(
155 171
 				msg,
156 172
 				ephemeral=True,

Notiek ielāde…
Atcelt
Saglabāt