Parcourir la source

CogSetting accessors now ordinary functions not methods

pull/13/head
Rocketsoup il y a 2 mois
Parent
révision
bf727e6eaf
1 fichiers modifiés avec 47 ajouts et 77 suppressions
  1. 47
    77
      rocketbot/cogsetting.py

+ 47
- 77
rocketbot/cogsetting.py Voir le fichier

@@ -14,15 +14,6 @@ from rocketbot.storage import Storage
14 14
 from rocketbot.utils import bot_log, TimeDeltaTransformer, MOD_PERMISSIONS, dump_stacktrace, str_from_timedelta
15 15
 
16 16
 
17
-# def _fix_command(command: Command) -> None:
18
-# 	"""
19
-# 	HACK: Fixes bug in discord.py 2.3.2 where it's requiring the user to
20
-# 	supply the context argument. This removes that argument from the list.
21
-# 	"""
22
-# 	params = command.params
23
-# 	del params['context']
24
-# 	command.params = params
25
-
26 17
 class CogSetting:
27 18
 	"""
28 19
 	Describes a configuration setting for a guild that can be edited by the
@@ -50,8 +41,7 @@ class CogSetting:
50 41
 		datatype: Optional[Type]
51 42
 		    Datatype of the setting. E.g. int, float, str
52 43
 		default_value: Any
53
-			Value to use in the absence of a specified value for the guild and
54
-			no value in config.py.
44
+			Value to use if a guild has not yet configured one.
55 45
 		brief: Optional[str]
56 46
 			Description of the setting, starting with lower case.
57 47
 			Will be inserted into phrases like "Sets <brief>" and
@@ -97,7 +87,7 @@ class CogSetting:
97 87
 			allowed_values = '`' + ('`, `'.join(self.enum_values)) + '`'
98 88
 			raise ValueError(f'`{self.name}` must be one of {allowed_values}')
99 89
 
100
-	def set_up(self, cog: 'BaseCog', bot: Bot) -> None:
90
+	def set_up(self, cog: 'BaseCog') -> None:
101 91
 		"""
102 92
 		Sets up getter and setter commands for this setting. This should
103 93
 		usually only be called by BaseCog.
@@ -110,16 +100,20 @@ class CogSetting:
110 100
 			self.__set_group.add_command(self.__make_setter_command(cog))
111 101
 
112 102
 	def to_stored_value(self, native_value: Any) -> Any:
103
+		"""Converts a configuration value to a JSON-compatible datatype."""
113 104
 		if self.datatype is timedelta:
114 105
 			return native_value.total_seconds()
115 106
 		return native_value
116 107
 
117 108
 	def to_native_value(self, stored_value: Any) -> Any:
109
+		"""Converts the stored JSON-compatible datatype to its actual value."""
118 110
 		if self.datatype is timedelta and isinstance(stored_value, (int, float)):
119 111
 			return timedelta(seconds=stored_value)
120 112
 		return stored_value
121 113
 
122
-	def native_value_to_str(self, native_value: Any) -> str:
114
+	@staticmethod
115
+	def native_value_to_str(native_value: Any) -> str:
116
+		"""Formats a native configuration value to a user-presentable string."""
123 117
 		if native_value is None:
124 118
 			return '<no value>'
125 119
 		if isinstance(native_value, timedelta):
@@ -133,29 +127,21 @@ class CogSetting:
133 127
 		setting_name = setting.name
134 128
 		if cog.config_prefix is not None:
135 129
 			setting_name = f'{cog.config_prefix}_{setting_name}'
136
-		datatype = self.datatype
137
-		async def getter(cog0, interaction: Interaction) -> None:
138
-			print(f"invoking getter for {setting_name}")
139
-			key = f'{cog0.__class__.__name__}.{setting.name}'
130
+		async def getter(interaction: Interaction) -> None:
131
+			key = f'{cog.__class__.__name__}.{setting.name}'
140 132
 			value = setting.to_native_value(Storage.get_config_value(interaction.guild, key))
141 133
 			if value is None:
142
-				value = setting.to_native_value(cog0.get_cog_default(setting.name))
134
+				value = setting.to_native_value(cog.get_cog_default(setting.name))
143 135
 				await interaction.response.send_message(
144
-					f'{CONFIG["info_emoji"]} `{setting_name}` is using default of `{setting.native_value_to_str(value)}`',
136
+					f'{CONFIG["info_emoji"]} `{setting_name}` is using default of `{CogSetting.native_value_to_str(value)}`',
145 137
 					ephemeral=True
146 138
 				)
147 139
 			else:
148 140
 				await interaction.response.send_message(
149
-					f'{CONFIG["info_emoji"]} `{setting_name}` is set to `{setting.native_value_to_str(value)}`',
141
+					f'{CONFIG["info_emoji"]} `{setting_name}` is set to `{CogSetting.native_value_to_str(value)}`',
150 142
 					ephemeral=True
151 143
 				)
152 144
 
153
-		# We have to do some surgery to make the getter function a proper method on the cog
154
-		# that discord.py will recognize and wire up correctly. Same for other accessors below.
155
-		setattr(cog.__class__, f'_cmd_get_{setting.name}', getter)  # add method to cog class
156
-		getter.__qualname__ = f'{self.__class__.__name__}._cmd_get_{setting.name}'  # discord.py checks this to know if it's a method vs function
157
-		getter.__self__ = cog  # discord.py uses this as the self argument
158
-
159 145
 		bot_log(None, cog.__class__, f"Creating /get {setting_name}")
160 146
 		command = Command(
161 147
 			name=setting_name,
@@ -171,13 +157,11 @@ class CogSetting:
171 157
 		return command
172 158
 
173 159
 	def __make_setter_command(self, cog: 'BaseCog') -> Command:
174
-		from rocketbot.cogs.basecog import BaseCog
175 160
 		setting: CogSetting = self
176 161
 		setting_name = setting.name
177 162
 		if cog.config_prefix is not None:
178 163
 			setting_name = f'{cog.config_prefix}_{setting_name}'
179
-		async def setter_general(cog0: BaseCog, interaction: Interaction, new_value) -> None:
180
-			print(f"invoking setter for {setting_name} with value {new_value}")
164
+		async def setter_general(interaction: Interaction, new_value) -> None:
181 165
 			try:
182 166
 				setting.validate_value(new_value)
183 167
 			except ValueError as ve:
@@ -186,14 +170,14 @@ class CogSetting:
186 170
 					ephemeral=True
187 171
 				)
188 172
 				return
189
-			key = f'{cog0.__class__.__name__}.{setting.name}'
173
+			key = f'{cog.__class__.__name__}.{setting.name}'
190 174
 			Storage.set_config_value(interaction.guild, key, setting.to_stored_value(new_value))
191 175
 			await interaction.response.send_message(
192 176
 				f'{CONFIG["success_emoji"]} `{setting_name}` is now set to `{setting.to_native_value(new_value)}`',
193 177
 				ephemeral=True
194 178
 			)
195
-			await cog0.on_setting_updated(interaction.guild, setting)
196
-			cog0.log(interaction.guild, f'{interaction.user.name} set {key} to {new_value}')
179
+			await cog.on_setting_updated(interaction.guild, setting)
180
+			cog.log(interaction.guild, f'{interaction.user.name} set {key} to {new_value}')
197 181
 
198 182
 		setter: CommandCallback = setter_general
199 183
 		if self.datatype == int:
@@ -202,33 +186,33 @@ class CogSetting:
202 186
 				r_max = self.max_value
203 187
 				@rename(new_value=self.name)
204 188
 				@describe(new_value=self.brief)
205
-				async def setter_range(cog0, interaction: Interaction, new_value: Range[int, r_min, r_max]) -> None:
206
-					await setter_general(cog0, interaction, new_value)
189
+				async def setter_range(interaction: Interaction, new_value: Range[int, r_min, r_max]) -> None:
190
+					await setter_general(interaction, new_value)
207 191
 				setter = setter_range
208 192
 			else:
209 193
 				@rename(new_value=self.name)
210 194
 				@describe(new_value=self.brief)
211
-				async def setter_int(cog0, interaction: Interaction, new_value: int) -> None:
212
-					await setter_general(cog0, interaction, new_value)
195
+				async def setter_int(interaction: Interaction, new_value: int) -> None:
196
+					await setter_general(interaction, new_value)
213 197
 				setter = setter_int
214 198
 		elif self.datatype == float:
215 199
 			@rename(new_value=self.name)
216 200
 			@describe(new_value=self.brief)
217
-			async def setter_float(cog0, interaction: Interaction, new_value: float) -> None:
218
-				await setter_general(cog0, interaction, new_value)
201
+			async def setter_float(interaction: Interaction, new_value: float) -> None:
202
+				await setter_general(interaction, new_value)
219 203
 			setter = setter_float
220 204
 		elif self.datatype == timedelta:
221 205
 			@rename(new_value=self.name)
222 206
 			@describe(new_value=f'{self.brief} (e.g. 30s, 5m, 1h30s, or 7d)')
223
-			async def setter_timedelta(cog0, interaction: Interaction, new_value: Transform[timedelta, TimeDeltaTransformer]) -> None:
224
-				await setter_general(cog0, interaction, new_value)
207
+			async def setter_timedelta(interaction: Interaction, new_value: Transform[timedelta, TimeDeltaTransformer]) -> None:
208
+				await setter_general(interaction, new_value)
225 209
 			setter = setter_timedelta
226 210
 		elif getattr(self.datatype, '__origin__', None) == Literal:
227 211
 			dt = self.datatype
228 212
 			@rename(new_value=self.name)
229 213
 			@describe(new_value=self.brief)
230
-			async def setter_enum(cog0, interaction: Interaction, new_value: dt) -> None:
231
-				await setter_general(cog0, interaction, new_value)
214
+			async def setter_enum(interaction: Interaction, new_value: dt) -> None:
215
+				await setter_general(interaction, new_value)
232 216
 			setter = setter_enum
233 217
 		elif self.datatype == str:
234 218
 			if self.enum_values is not None:
@@ -236,20 +220,18 @@ class CogSetting:
236 220
 			else:
237 221
 				@rename(new_value=self.name)
238 222
 				@describe(new_value=self.brief)
239
-				async def setter_str(cog0, interaction: Interaction, new_value: str) -> None:
240
-					await setter_general(cog0, interaction, new_value)
223
+				async def setter_str(interaction: Interaction, new_value: str) -> None:
224
+					await setter_general(interaction, new_value)
241 225
 				setter = setter_str
242
-		elif setting.datatype == bool:
226
+		elif self.datatype == bool:
243 227
 			@rename(new_value=self.name)
244 228
 			@describe(new_value=self.brief)
245
-			async def setter_bool(cog0, interaction: Interaction, new_value: bool) -> None:
246
-				await setter_general(cog0, interaction, new_value)
229
+			async def setter_bool(interaction: Interaction, new_value: bool) -> None:
230
+				await setter_general(interaction, new_value)
247 231
 			setter = setter_bool
248
-		elif setting.datatype is not None:
232
+		elif self.datatype is not None:
249 233
 			raise ValueError(f'Invalid type {self.datatype}')
250
-		setattr(cog.__class__, f'_cmd_set_{setting.name}', setter)
251
-		setter.__qualname__ = f'{cog.__class__.__name__}._cmd_set_{setting.name}'
252
-		setter.__self__ = cog
234
+
253 235
 		bot_log(None, cog.__class__, f"Creating /set {setting_name} {self.datatype}")
254 236
 		command = Command(
255 237
 			name=setting_name,
@@ -265,23 +247,18 @@ class CogSetting:
265 247
 		return command
266 248
 
267 249
 	def __make_enable_command(self, cog: 'BaseCog') -> Command:
268
-		from rocketbot.cogs.basecog import BaseCog
269 250
 		setting: CogSetting = self
270
-		async def enabler(cog0: BaseCog, interaction: Interaction) -> None:
271
-			print(f"invoking enable for {cog0.config_prefix}")
272
-			key = f'{cog0.__class__.__name__}.{setting.name}'
251
+		async def enabler(interaction: Interaction) -> None:
252
+			key = f'{cog.__class__.__name__}.{setting.name}'
273 253
 			Storage.set_config_value(interaction.guild, key, True)
274 254
 			await interaction.response.send_message(
275 255
 				f'{CONFIG["success_emoji"]} {setting.brief.capitalize()} enabled.',
276 256
 				ephemeral=True
277 257
 			)
278
-			await cog0.on_setting_updated(interaction.guild, setting)
279
-			cog0.log(interaction.guild, f'{interaction.user.name} enabled {cog0.__class__.__name__}')
280
-		setattr(cog.__class__, f'_cmd_enable', enabler)
281
-		enabler.__qualname__ = f'{cog.__class__.__name__}._cmd_enable'
282
-		enabler.__self__ = cog
283
-		bot_log(None, cog.__class__, f"Creating /enable {cog.config_prefix}")
258
+			await cog.on_setting_updated(interaction.guild, setting)
259
+			cog.log(interaction.guild, f'{interaction.user.name} enabled {cog.__class__.__name__}')
284 260
 
261
+		bot_log(None, cog.__class__, f"Creating /enable {cog.config_prefix}")
285 262
 		command = Command(
286 263
 			name=cog.config_prefix,
287 264
 			description=f'Enables {cog.qualified_name} functionality.',
@@ -296,23 +273,18 @@ class CogSetting:
296 273
 		return command
297 274
 
298 275
 	def __make_disable_command(self, cog: 'BaseCog') -> Command:
299
-		from rocketbot.cogs.basecog import BaseCog
300 276
 		setting: CogSetting = self
301
-		async def disabler(cog0: BaseCog, interaction: Interaction) -> None:
302
-			print(f"invoking disable for {cog0.config_prefix}")
303
-			key = f'{cog0.__class__.__name__}.{setting.name}'
277
+		async def disabler(interaction: Interaction) -> None:
278
+			key = f'{cog.__class__.__name__}.{setting.name}'
304 279
 			Storage.set_config_value(interaction.guild, key, False)
305 280
 			await interaction.response.send_message(
306 281
 				f'{CONFIG["success_emoji"]} {setting.brief.capitalize()} disabled.',
307 282
 				ephemeral=True
308 283
 			)
309
-			await cog0.on_setting_updated(interaction.guild, setting)
310
-			cog0.log(interaction.guild, f'{interaction.user.name} disabled {cog0.__class__.__name__}')
311
-		setattr(cog.__class__, f'_cmd_disable', disabler)
312
-		disabler.__qualname__ = f'{cog.__class__.__name__}._cmd_disable'
313
-		disabler.__self__ = cog
314
-		bot_log(None, cog.__class__, f"Creating /disable {cog.config_prefix}")
284
+			await cog.on_setting_updated(interaction.guild, setting)
285
+			cog.log(interaction.guild, f'{interaction.user.name} disabled {cog.__class__.__name__}')
315 286
 
287
+		bot_log(None, cog.__class__, f"Creating /disable {cog.config_prefix}")
316 288
 		command = Command(
317 289
 			name=cog.config_prefix,
318 290
 			description=f'Disables {cog.config_prefix} functionality',
@@ -326,7 +298,6 @@ class CogSetting:
326 298
 		)
327 299
 		return command
328 300
 
329
-	__has_set_up_base_commands: bool = False
330 301
 	__set_group: Group
331 302
 	__get_group: Group
332 303
 	__enable_group: Group
@@ -344,14 +315,13 @@ class CogSetting:
344 315
 			return
345 316
 		bot_log(None, cog.__class__, f"Setting up slash commands for {cog.__class__.__name__}")
346 317
 		for setting in settings:
347
-			setting.set_up(cog, bot)
318
+			setting.set_up(cog)
348 319
 		bot_log(None, cog.__class__, f"Done setting up slash commands for {cog.__class__.__name__}")
349 320
 
350 321
 	@classmethod
351 322
 	def __set_up_base_commands(cls, bot: Bot) -> None:
352
-		if cls.__has_set_up_base_commands:
323
+		if getattr(cls, f'_CogSetting__set_group', None) is not None:
353 324
 			return
354
-		cls.__has_set_up_base_commands = True
355 325
 		cls.__set_group = Group(
356 326
 			name='set',
357 327
 			description='Sets a configuration value for this guild.',
@@ -424,9 +394,9 @@ class CogSetting:
424 394
 								text += ('enabled' if deflt else 'disabled') + ' _(default)_'
425 395
 						else:
426 396
 							if value is not None:
427
-								text += f'\n- `{bcog.config_prefix}_{setting.name}` = **{setting.native_value_to_str(value)}**'
397
+								text += f'\n- `{bcog.config_prefix}_{setting.name}` = **{CogSetting.native_value_to_str(value)}**'
428 398
 							else:
429
-								text += f'\n- `{bcog.config_prefix}_{setting.name}` = {setting.native_value_to_str(deflt)} _(using default)_'
399
+								text += f'\n- `{bcog.config_prefix}_{setting.name}` = {CogSetting.native_value_to_str(deflt)} _(using default)_'
430 400
 				await interaction.response.send_message(
431 401
 					text,
432 402
 					ephemeral=True,

Chargement…
Annuler
Enregistrer