Quellcode durchsuchen

Very WIP for converting config settings to slash commands

pull/13/head
Rocketsoup vor 2 Monaten
Ursprung
Commit
2fc71547d5

+ 1
- 1
rocketbot/cogs/autokickcog.py Datei anzeigen

@@ -53,7 +53,7 @@ class AutoKickCog(BaseCog, name='Auto Kick'):
53 53
 	STATE_KEY_RECENT_KICKS = "AutoKickCog.recent_joins"
54 54
 
55 55
 	def __init__(self, bot):
56
-		super().__init__(bot)
56
+		super().__init__(bot, 'autokick')
57 57
 		self.add_setting(AutoKickCog.SETTING_ENABLED)
58 58
 		self.add_setting(AutoKickCog.SETTING_BAN_COUNT)
59 59
 		self.add_setting(AutoKickCog.SETTING_OFFLINE_ONLY)

+ 14
- 3
rocketbot/cogs/basecog.py Datei anzeigen

@@ -28,10 +28,21 @@ class BaseCog(commands.Cog):
28 28
 	Superclass for all Rocketbot cogs. Provides lots of conveniences for
29 29
 	common tasks.
30 30
 	"""
31
-	def __init__(self, bot):
31
+	def __init__(self, bot: Rocketbot, config_prefix: Optional[str]):
32
+		"""
33
+		Parameters
34
+		----------
35
+		bot: Rocketbot
36
+		config_prefix: str
37
+		    Prefix to show on variables in /set and /get commands to namespace
38
+		    configuration variables. E.g. if config_prefix is "foo", a config
39
+		    variable named "bar" in that cog will show as "foo.bar". If None,
40
+		    config variable acts as a top-level variable with no prefix.
41
+		"""
32 42
 		self.bot: Rocketbot = bot
33
-		self.are_settings_setup = False
34
-		self.settings = []
43
+		self.are_settings_setup: bool = False
44
+		self.settings: list[CogSetting] = []
45
+		self.config_prefix: Optional[str] = config_prefix
35 46
 
36 47
 	# Config
37 48
 

+ 5
- 0
rocketbot/cogs/configcog.py Datei anzeigen

@@ -3,6 +3,7 @@ Cog handling general configuration for a guild.
3 3
 """
4 4
 from discord import Guild, TextChannel
5 5
 from discord.ext import commands
6
+from discord.ext.commands import Bot
6 7
 
7 8
 from config import CONFIG
8 9
 from rocketbot.storage import ConfigKey, Storage
@@ -12,6 +13,10 @@ class ConfigCog(BaseCog, name='Configuration'):
12 13
 	"""
13 14
 	Cog for handling general bot configuration.
14 15
 	"""
16
+
17
+	def __init__(self, bot: Bot) -> None:
18
+		super().__init__(bot, 'config')
19
+
15 20
 	@commands.group(
16 21
 		brief='Manages general bot configuration'
17 22
 	)

+ 1
- 1
rocketbot/cogs/crosspostcog.py Datei anzeigen

@@ -95,7 +95,7 @@ class CrossPostCog(BaseCog, name='Crosspost Detection'):
95 95
 	STATE_KEY_SPAM_CONTEXT = "CrossPostCog.spam_context"
96 96
 
97 97
 	def __init__(self, bot):
98
-		super().__init__(bot)
98
+		super().__init__(bot, 'crosspost')
99 99
 		self.add_setting(CrossPostCog.SETTING_ENABLED)
100 100
 		self.add_setting(CrossPostCog.SETTING_WARN_COUNT)
101 101
 		self.add_setting(CrossPostCog.SETTING_DUPE_WARN_COUNT)

+ 3
- 2
rocketbot/cogs/generalcog.py Datei anzeigen

@@ -10,6 +10,7 @@ from discord.errors import DiscordException
10 10
 from discord.ext import commands
11 11
 
12 12
 from config import CONFIG
13
+from rocketbot.bot import Rocketbot
13 14
 from rocketbot.cogs.basecog import BaseCog, BotMessage
14 15
 from rocketbot.utils import timedelta_from_str, describe_timedelta, dump_stacktrace
15 16
 from rocketbot.storage import ConfigKey, Storage
@@ -19,8 +20,8 @@ class GeneralCog(BaseCog, name='General'):
19 20
 	Cog for handling high-level bot functionality and commands. Should be the
20 21
 	first cog added to the bot.
21 22
 	"""
22
-	def __init__(self, bot: commands.Bot):
23
-		super().__init__(bot)
23
+	def __init__(self, bot: Rocketbot):
24
+		super().__init__(bot, None)
24 25
 		self.is_connected = False
25 26
 		self.is_ready = False
26 27
 		self.is_first_ready = True

+ 3
- 2
rocketbot/cogs/joinagecog.py Datei anzeigen

@@ -5,6 +5,7 @@ from discord import Guild, Member
5 5
 from discord.ext import commands
6 6
 
7 7
 from config import CONFIG
8
+from rocketbot.bot import Rocketbot
8 9
 from rocketbot.cogs.basecog import BaseCog, BotMessage, BotMessageReaction, CogSetting
9 10
 from rocketbot.collections import AgeBoundList
10 11
 from rocketbot.storage import Storage
@@ -36,8 +37,8 @@ class JoinAgeCog(BaseCog, name='Join Age'):
36 37
 
37 38
 	STATE_KEY_RECENT_JOINS = "JoinAgeCog.recent_joins"
38 39
 
39
-	def __init__(self, bot):
40
-		super().__init__(bot)
40
+	def __init__(self, bot: Rocketbot):
41
+		super().__init__(bot, 'joinage')
41 42
 		self.add_setting(JoinAgeCog.SETTING_ENABLED)
42 43
 		self.add_setting(JoinAgeCog.SETTING_JOIN_TIME)
43 44
 

+ 1
- 1
rocketbot/cogs/joinraidcog.py Datei anzeigen

@@ -51,7 +51,7 @@ class JoinRaidCog(BaseCog, name='Join Raids'):
51 51
 	STATE_KEY_LAST_RAID = "JoinRaidCog.last_raid"
52 52
 
53 53
 	def __init__(self, bot):
54
-		super().__init__(bot)
54
+		super().__init__(bot, 'joinraid')
55 55
 		self.add_setting(JoinRaidCog.SETTING_ENABLED)
56 56
 		self.add_setting(JoinRaidCog.SETTING_JOIN_COUNT)
57 57
 		self.add_setting(JoinRaidCog.SETTING_JOIN_TIME)

+ 1
- 1
rocketbot/cogs/logcog.py Datei anzeigen

@@ -44,7 +44,7 @@ class LoggingCog(BaseCog, name='Logging'):
44 44
 	STATE_EVENT_BUFFER = 'LoggingCog.eventBuffer'
45 45
 
46 46
 	def __init__(self, bot):
47
-		super().__init__(bot)
47
+		super().__init__(bot, 'logging')
48 48
 		self.add_setting(LoggingCog.SETTING_ENABLED)
49 49
 		self.flush_buffers.start()
50 50
 		self.buffered_guilds: set[Guild] = set()

+ 4
- 0
rocketbot/cogs/patterncog.py Datei anzeigen

@@ -9,6 +9,7 @@ from discord import Guild, Member, Message, utils as discordutils
9 9
 from discord.ext import commands
10 10
 
11 11
 from config import CONFIG
12
+from rocketbot.bot import Rocketbot
12 13
 from rocketbot.cogs.basecog import BaseCog, BotMessage, BotMessageReaction
13 14
 from rocketbot.cogsetting import CogSetting
14 15
 from rocketbot.pattern import PatternCompiler, PatternDeprecationError, \
@@ -35,6 +36,9 @@ class PatternCog(BaseCog, name='Pattern Matching'):
35 36
 
36 37
 	SETTING_PATTERNS = CogSetting('patterns', None)
37 38
 
39
+	def __init__(self, bot: Rocketbot):
40
+		super().__init__(bot, 'patterns')
41
+
38 42
 	def __get_patterns(self, guild: Guild) -> dict[str, PatternStatement]:
39 43
 		"""
40 44
 		Returns a name -> PatternStatement lookup for the guild.

+ 1
- 1
rocketbot/cogs/urlspamcog.py Datei anzeigen

@@ -52,7 +52,7 @@ class URLSpamCog(BaseCog, name='URL Spam'):
52 52
 				'chatwarn', 'chatwarndelete', 'delete', 'kick', 'ban'})
53 53
 
54 54
 	def __init__(self, bot):
55
-		super().__init__(bot)
55
+		super().__init__(bot, 'urlspam')
56 56
 		self.add_setting(URLSpamCog.SETTING_ENABLED)
57 57
 		self.add_setting(URLSpamCog.SETTING_ACTION)
58 58
 		self.add_setting(URLSpamCog.SETTING_JOIN_AGE)

+ 1
- 1
rocketbot/cogs/usernamecog.py Datei anzeigen

@@ -51,7 +51,7 @@ class UsernamePatternCog(BaseCog, name='Username Pattern'):
51 51
 	SETTING_PATTERNS = CogSetting('patterns', None)
52 52
 
53 53
 	def __init__(self, bot):
54
-		super().__init__(bot)
54
+		super().__init__(bot, 'username')
55 55
 		self.add_setting(UsernamePatternCog.SETTING_ENABLED)
56 56
 
57 57
 	def __get_patterns(self, guild: Guild) -> list[str]:

+ 167
- 129
rocketbot/cogsetting.py Datei anzeigen

@@ -1,23 +1,25 @@
1 1
 """
2 2
 A guild configuration setting available for editing via bot commands.
3 3
 """
4
-from typing import Any, Callable, Coroutine, Optional, Type
4
+from __future__ import annotations
5
+from typing import Any, Optional, Type
5 6
 
6
-from discord.ext import commands
7
-from discord.ext.commands import Bot, Command, Context, Group, Cog
7
+from discord import Interaction, Permissions
8
+from discord.app_commands.commands import Command, Group
9
+from discord.ext.commands import Bot
8 10
 
9 11
 from config import CONFIG
12
+from rocketbot.cogs.basecog import BaseCog
10 13
 from rocketbot.storage import Storage
11
-from rocketbot.utils import first_command_group
12 14
 
13
-def _fix_command(command: Command) -> None:
14
-	"""
15
-	HACK: Fixes bug in discord.py 2.3.2 where it's requiring the user to
16
-	supply the context argument. This removes that argument from the list.
17
-	"""
18
-	params = command.params
19
-	del params['context']
20
-	command.params = params
15
+# def _fix_command(command: Command) -> None:
16
+# 	"""
17
+# 	HACK: Fixes bug in discord.py 2.3.2 where it's requiring the user to
18
+# 	supply the context argument. This removes that argument from the list.
19
+# 	"""
20
+# 	params = command.params
21
+# 	del params['context']
22
+# 	command.params = params
21 23
 
22 24
 class CogSetting:
23 25
 	"""
@@ -36,21 +38,28 @@ class CogSetting:
36 38
 			max_value: Optional[Any] = None,
37 39
 			enum_values: Optional[set[Any]] = None):
38 40
 		"""
39
-		Params:
40
-		- name         Setting identifier. Must follow variable naming
41
-		               conventions.
42
-		- datatype     Datatype of the setting. E.g. int, float, str
43
-		- brief        Description of the setting, starting with lower case.
44
-		               Will be inserted into phrases like "Sets <brief>" and
45
-					   "Gets <brief".
46
-		- description  Long-form description. Min, max, and enum values will be
47
-		               appended to the end, so does not need to include these.
48
-		- usage        Description of the value argument in a set command, e.g.
49
-		               "<maxcount:int>"
50
-		- min_value    Smallest allowable value. Must be of the same datatype as
51
-		               the value. None for no minimum.
52
-		- max_value    Largest allowable value. None for no maximum.
53
-		- enum_values  Set of allowed values. None if unconstrained.
41
+		Parameters
42
+		----------
43
+		name: str
44
+			Setting identifier. Must follow variable naming conventions.
45
+		datatype: Optional[Type]
46
+		    Datatype of the setting. E.g. int, float, str
47
+		brief: Optional[str]
48
+			Description of the setting, starting with lower case.
49
+			Will be inserted into phrases like "Sets <brief>" and
50
+			"Gets <brief".
51
+		description: Optional[str]
52
+		  	Long-form description. Min, max, and enum values will be
53
+		    appended to the end, so does not need to include these.
54
+		usage: Optional[str]
55
+		    Description of the value argument in a set command, e.g. "<maxcount:int>"
56
+		min_value: Optional[Any]
57
+		    Smallest allowable value. Must be of the same datatype as
58
+		    the value. None for no minimum.
59
+		max_value: Optional[Any]
60
+		    Largest allowable value. None for no maximum.
61
+		enum_values: Optional[set[Any]]
62
+		  	Set of allowed values. None if unconstrained.
54 63
 		"""
55 64
 		self.name: str = name
56 65
 		self.datatype: Type = datatype
@@ -72,7 +81,7 @@ class CogSetting:
72 81
 		if self.usage is None:
73 82
 			self.usage = f'<{self.name}>'
74 83
 
75
-	def validate_value(self, new_value) -> None:
84
+	def validate_value(self, new_value: Any) -> None:
76 85
 		"""
77 86
 		Checks if a value is legal for this setting. Raises a ValueError if not.
78 87
 		"""
@@ -84,161 +93,190 @@ class CogSetting:
84 93
 			allowed_values = '`' + ('`, `'.join(self.enum_values)) + '`'
85 94
 			raise ValueError(f'`{self.name}` must be one of {allowed_values}')
86 95
 
87
-	def set_up(self, cog: Cog, bot: Bot, group: Group) -> None:
96
+	def set_up(self, cog: BaseCog, bot: Bot) -> None:
88 97
 		"""
89 98
 		Sets up getter and setter commands for this setting. This should
90 99
 		usually only be called by BaseCog.
91 100
 		"""
92 101
 		if self.name in ('enabled', 'is_enabled'):
93
-			(group or bot).add_command(self.__make_enable_command(cog))
94
-			(group or bot).add_command(self.__make_disable_command(cog))
102
+			bot.tree.add_command(self.__make_enable_command(cog))
103
+			bot.tree.add_command(self.__make_disable_command(cog))
95 104
 		else:
96
-			(group or bot).add_command(self.__make_getter_command(cog))
97
-			(group or bot).add_command(self.__make_setter_command(cog))
105
+			bot.tree.add_command(self.__make_getter_command(cog))
106
+			bot.tree.add_command(self.__make_setter_command(cog))
98 107
 
99
-	def __make_getter_command(self, cog: Cog) -> Command:
108
+	def __make_getter_command(self, cog: BaseCog) -> Command:
100 109
 		setting: CogSetting = self
101
-		async def getter(cog0: Cog, context: Context) -> None:
102
-			setting_name = setting.name
103
-			if isinstance(context.command.parent, Group):
104
-				setting_name = f'{context.command.parent.name}.{setting_name}'
110
+		setting_name = setting.name
111
+		if cog.config_prefix is not None:
112
+			setting_name = f'{cog.config_prefix}.{setting_name}'
113
+		async def getter(cog0: BaseCog, interaction: Interaction) -> None:
105 114
 			key = f'{cog0.__class__.__name__}.{setting.name}'
106
-			value = Storage.get_config_value(context.guild, key)
115
+			value = Storage.get_config_value(interaction.guild, key)
107 116
 			if value is None:
108 117
 				value = cog0.get_cog_default(setting.name)
109
-				await context.message.reply(
118
+				await interaction.response.send_message(
110 119
 					f'{CONFIG["info_emoji"]} `{setting_name}` is using default of `{value}`',
111
-					mention_author=False)
120
+					ephemeral=True
121
+				)
112 122
 			else:
113
-				await context.message.reply(
123
+				await interaction.response.send_message(
114 124
 					f'{CONFIG["info_emoji"]} `{setting_name}` is set to `{value}`',
115
-					mention_author=False)
125
+					ephemeral=True
126
+				)
116 127
 		command = Command(
117
-			getter,
118
-			name=f'get{setting.name}',
119
-			brief=f'Shows {setting.brief}',
128
+			name=setting_name,
120 129
 			description=setting.description,
121
-			checks=[
122
-				commands.has_permissions(ban_members=True),
123
-				commands.guild_only(),
124
-			])
125
-		command.cog = cog
126
-		_fix_command(command)
130
+			callback=getter,
131
+			parent=CogSetting.__get_group
132
+		)
127 133
 		return command
128 134
 
129
-	def __make_setter_command(self, cog: Cog) -> Command:
135
+	def __make_setter_command(self, cog: BaseCog) -> Command:
130 136
 		setting: CogSetting = self
131
-		async def setter_common(cog0: Cog, context: Context, new_value) -> None:
137
+		setting_name = setting.name
138
+		if cog.config_prefix is not None:
139
+			setting_name = f'{cog.config_prefix}.{setting_name}'
140
+		async def setter(cog0: BaseCog, interaction: Interaction, new_value) -> None:
132 141
 			try:
133 142
 				setting.validate_value(new_value)
134 143
 			except ValueError as ve:
135
-				await context.message.reply(
144
+				await interaction.response.send_message(
136 145
 					f'{CONFIG["failure_emoji"]} {ve}',
137
-					mention_author=False)
146
+					ephemeral=True
147
+				)
138 148
 				return
139
-			setting_name = setting.name
140
-			if isinstance(context.command.parent, Group):
141
-				setting_name = f'{context.command.parent.name}.{setting_name}'
142 149
 			key = f'{cog0.__class__.__name__}.{setting.name}'
143
-			Storage.set_config_value(context.guild, key, new_value)
144
-			await context.message.reply(
150
+			Storage.set_config_value(interaction.guild, key, new_value)
151
+			await interaction.response.send_message(
145 152
 				f'{CONFIG["success_emoji"]} `{setting_name}` is now set to `{new_value}`',
146
-				mention_author=False)
147
-			await cog0.on_setting_updated(context.guild, setting)
148
-			cog0.log(context.guild, f'{context.author.name} set {key} to {new_value}')
149
-
150
-		async def setter_int(cog1, context, new_value: int):
151
-			await setter_common(cog1, context, new_value)
152
-		async def setter_float(cog2, context, new_value: float):
153
-			await setter_common(cog2, context, new_value)
154
-		async def setter_str(cog3, context, new_value: str):
155
-			await setter_common(cog3, context, new_value)
156
-		async def setter_bool(cog4, context, new_value: bool):
157
-			await setter_common(cog4, context, new_value)
158
-
159
-		setter: Callable[[Cog, Context, Any], Coroutine]
160
-		if setting.datatype == int:
161
-			setter = setter_int
162
-		elif setting.datatype == float:
163
-			setter = setter_float
153
+				ephemeral=True
154
+			)
155
+			await cog0.on_setting_updated(interaction.guild, setting)
156
+			cog0.log(interaction.guild, f'{interaction.message.author.name} set {key} to {new_value}')
157
+
158
+		type_str: str = 'any'
159
+		if self.datatype == int:
160
+			if self.min_value is not None or self.max_value is not None:
161
+				type_str = f'discord.app_commands.Range[int, {self.min_value}, {self.max_value}]'
162
+			else:
163
+				type_str = 'int'
164 164
 		elif setting.datatype == str:
165
-			setter = setter_str
165
+			if self.enum_values is not None:
166
+				value_list = '"' + '", "'.join(self.enum_values) + '"'
167
+				type_str = f'typing.Literal[{value_list}]'
168
+			else:
169
+				type_str = 'str'
166 170
 		elif setting.datatype == bool:
167
-			setter = setter_bool
168
-		else:
169
-			raise ValueError(f'Datatype {setting.datatype} unsupported')
171
+			type_str = f'typing.Literal["false", "true"]'
170 172
 
173
+		setter.__doc__ = f"""Sets {self.description}.
174
+
175
+Parameters
176
+----------
177
+cog: discord.ext.commands.Cog
178
+interaction: discord.Interaction
179
+new_value: {type_str}
180
+"""
171 181
 		command = Command(
172
-			setter,
173
-			name=f'set{setting.name}',
174
-			brief=f'Sets {setting.brief}',
182
+			name=f'{setting.name}',
175 183
 			description=setting.description,
176
-			usage=setting.usage,
177
-			checks=[
178
-				commands.has_permissions(ban_members=True),
179
-				commands.guild_only(),
180
-			])
184
+			callback=setter,
185
+			parent=CogSetting.__set_group,
186
+		)
181 187
 		# HACK: Passing `cog` in init gets ignored and set to `None` so set after.
182 188
 		# This ensures the callback is passed the cog as `self` argument.
183
-		command.cog = cog
184
-		_fix_command(command)
189
+		# command.cog = cog
190
+		# _fix_command(command)
185 191
 		return command
186 192
 
187
-	def __make_enable_command(self, cog: Cog) -> Command:
193
+	def __make_enable_command(self, cog: BaseCog) -> Command:
188 194
 		setting: CogSetting = self
189
-		async def enabler(cog0: Cog, context: Context) -> None:
195
+		async def enabler(cog0: BaseCog, interaction: Interaction) -> None:
190 196
 			key = f'{cog0.__class__.__name__}.{setting.name}'
191
-			Storage.set_config_value(context.guild, key, True)
192
-			await context.message.reply(
197
+			Storage.set_config_value(interaction.guild, key, True)
198
+			await interaction.response.send_message(
193 199
 				f'{CONFIG["success_emoji"]} {setting.brief.capitalize()} enabled.',
194
-				mention_author=False)
195
-			await cog0.on_setting_updated(context.guild, setting)
196
-			cog0.log(context.guild, f'{context.author.name} enabled {cog0.__class__.__name__}')
200
+				ephemeral=True
201
+			)
202
+			await cog0.on_setting_updated(interaction.guild, setting)
203
+			cog0.log(interaction.guild, f'{interaction.message.author.name} enabled {cog0.__class__.__name__}')
197 204
 
198 205
 		command = Command(
199
-			enabler,
200
-			name='enable',
201
-			brief=f'Enables {setting.brief}',
206
+			name=cog.config_prefix,
202 207
 			description=setting.description,
203
-			checks=[
204
-				commands.has_permissions(ban_members=True),
205
-				commands.guild_only(),
206
-			])
207
-		command.cog = cog
208
-		_fix_command(command)
208
+			callback=enabler,
209
+			parent=CogSetting.__enable_group,
210
+		)
211
+		# command.cog = cog
212
+		# _fix_command(command)
209 213
 		return command
210 214
 
211
-	def __make_disable_command(self, cog: Cog) -> Command:
215
+	def __make_disable_command(self, cog: BaseCog) -> Command:
212 216
 		setting: CogSetting = self
213
-		async def disabler(cog0: Cog, context: Context) -> None:
217
+		async def disabler(cog0: BaseCog, interaction: Interaction) -> None:
214 218
 			key = f'{cog0.__class__.__name__}.{setting.name}'
215
-			Storage.set_config_value(context.guild, key, False)
216
-			await context.message.reply(
219
+			Storage.set_config_value(interaction.guild, key, False)
220
+			await interaction.response.send_message(
217 221
 				f'{CONFIG["success_emoji"]} {setting.brief.capitalize()} disabled.',
218
-				mention_author=False)
219
-			await cog0.on_setting_updated(context.guild, setting)
220
-			cog0.log(context.guild, f'{context.author.name} disabled {cog0.__class__.__name__}')
222
+				ephemeral=True
223
+			)
224
+			await cog0.on_setting_updated(interaction.guild, setting)
225
+			cog0.log(interaction.guild, f'{interaction.message.author.name} disabled {cog0.__class__.__name__}')
221 226
 
222 227
 		command = Command(
223
-			disabler,
224
-			name='disable',
225
-			brief=f'Disables {setting.brief}',
228
+			name=cog.config_prefix,
226 229
 			description=setting.description,
227
-			checks=[
228
-				commands.has_permissions(ban_members=True),
229
-				commands.guild_only(),
230
-			])
231
-		command.cog = cog
232
-		_fix_command(command)
230
+			callback=disabler,
231
+			parent=CogSetting.__disable_group,
232
+		)
233
+		# command.cog = cog
234
+		# _fix_command(command)
233 235
 		return command
234 236
 
237
+	__has_set_up_base_commands: bool = False
238
+	__set_group: Group
239
+	__get_group: Group
240
+	__enable_group: Group
241
+	__disable_group: Group
242
+
235 243
 	@classmethod
236
-	def set_up_all(cls, cog: Cog, bot: Bot, settings: list) -> None:
244
+	def set_up_all(cls, cog: BaseCog, bot: Bot, settings: list['CogSetting']) -> None:
237 245
 		"""
238 246
 		Sets up editing commands for a list of CogSettings and adds them to a
239 247
 		cog. If the cog has a command Group, commands will be added to it.
240
-		Otherwise they will be added at the top level.
248
+		Otherwise, they will be added at the top level.
241 249
 		"""
242
-		group: Group = first_command_group(cog)
250
+		cls.__set_up_base_commands(bot)
243 251
 		for setting in settings:
244
-			setting.set_up(cog, bot, group)
252
+			setting.set_up(cog, bot)
253
+
254
+	@classmethod
255
+	def __set_up_base_commands(cls, bot: Bot) -> None:
256
+		if cls.__has_set_up_base_commands:
257
+			return
258
+		cls.__has_set_up_base_commands = True
259
+		cls.__set_group = Group(
260
+			name='set',
261
+			description='Sets a bot configuration value for this guild',
262
+			default_permissions=Permissions.manage_messages
263
+		)
264
+		cls.__get_group = Group(
265
+			name='get',
266
+			description='Shows a configured bot value for this guild',
267
+			default_permissions=Permissions.manage_messages
268
+		)
269
+		cls.__enable_group = Group(
270
+			name='enable',
271
+			description='Enables a set of bot functionality for this guild',
272
+			default_permissions=Permissions.manage_messages
273
+		)
274
+		cls.__disable_group = Group(
275
+			name='disable',
276
+			description='Disables a set of bot functionality for this guild',
277
+			default_permissions=Permissions.manage_messages
278
+		)
279
+		bot.tree.add_command(cls.__set_group)
280
+		bot.tree.add_command(cls.__get_group)
281
+		bot.tree.add_command(cls.__enable_group)
282
+		bot.tree.add_command(cls.__disable_group)

+ 12
- 3
rocketbot/utils.py Datei anzeigen

@@ -7,8 +7,9 @@ import traceback
7 7
 from datetime import datetime, timedelta
8 8
 from typing import Any, Optional, Type, Union
9 9
 
10
+import discord
10 11
 from discord import Guild
11
-from discord.ext.commands import Cog, Group
12
+from discord.ext.commands import Cog
12 13
 
13 14
 def dump_stacktrace(e: Exception) -> None:
14 15
 	print(e, file=sys.stderr)
@@ -84,11 +85,19 @@ def describe_timedelta(td: timedelta, max_components: int = 2) -> str:
84 85
 		components = components[0:max_components]
85 86
 	return ' '.join(components)
86 87
 
87
-def first_command_group(cog: Cog) -> Optional[Group]:
88
+def _old_first_command_group(cog: Cog) -> Optional[discord.ext.commands.Group]:
88 89
 	"""Returns the first command Group found in a cog."""
89 90
 	for member_name in dir(cog):
90 91
 		member = getattr(cog, member_name)
91
-		if isinstance(member, Group):
92
+		if isinstance(member, discord.ext.commands.Group):
93
+			return member
94
+	return None
95
+
96
+def first_command_group(cog: Cog) -> Optional[discord.app_commands.Group]:
97
+	"""Returns the first slash command Group found in a cog."""
98
+	for member_name in dir(cog):
99
+		member = getattr(cog, member_name)
100
+		if isinstance(member, discord.app_commands.Group):
92 101
 			return member
93 102
 	return None
94 103
 

Laden…
Abbrechen
Speichern