Просмотр исходного кода

CogSetting accessors now ordinary functions not methods

pull/13/head
Rocketsoup 2 месяцев назад
Родитель
Сommit
bf727e6eaf
1 измененных файлов: 47 добавлений и 77 удалений
  1. 47
    77
      rocketbot/cogsetting.py

+ 47
- 77
rocketbot/cogsetting.py Просмотреть файл

14
 from rocketbot.utils import bot_log, TimeDeltaTransformer, MOD_PERMISSIONS, dump_stacktrace, str_from_timedelta
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
 class CogSetting:
17
 class CogSetting:
27
 	"""
18
 	"""
28
 	Describes a configuration setting for a guild that can be edited by the
19
 	Describes a configuration setting for a guild that can be edited by the
50
 		datatype: Optional[Type]
41
 		datatype: Optional[Type]
51
 		    Datatype of the setting. E.g. int, float, str
42
 		    Datatype of the setting. E.g. int, float, str
52
 		default_value: Any
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
 		brief: Optional[str]
45
 		brief: Optional[str]
56
 			Description of the setting, starting with lower case.
46
 			Description of the setting, starting with lower case.
57
 			Will be inserted into phrases like "Sets <brief>" and
47
 			Will be inserted into phrases like "Sets <brief>" and
97
 			allowed_values = '`' + ('`, `'.join(self.enum_values)) + '`'
87
 			allowed_values = '`' + ('`, `'.join(self.enum_values)) + '`'
98
 			raise ValueError(f'`{self.name}` must be one of {allowed_values}')
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
 		Sets up getter and setter commands for this setting. This should
92
 		Sets up getter and setter commands for this setting. This should
103
 		usually only be called by BaseCog.
93
 		usually only be called by BaseCog.
110
 			self.__set_group.add_command(self.__make_setter_command(cog))
100
 			self.__set_group.add_command(self.__make_setter_command(cog))
111
 
101
 
112
 	def to_stored_value(self, native_value: Any) -> Any:
102
 	def to_stored_value(self, native_value: Any) -> Any:
103
+		"""Converts a configuration value to a JSON-compatible datatype."""
113
 		if self.datatype is timedelta:
104
 		if self.datatype is timedelta:
114
 			return native_value.total_seconds()
105
 			return native_value.total_seconds()
115
 		return native_value
106
 		return native_value
116
 
107
 
117
 	def to_native_value(self, stored_value: Any) -> Any:
108
 	def to_native_value(self, stored_value: Any) -> Any:
109
+		"""Converts the stored JSON-compatible datatype to its actual value."""
118
 		if self.datatype is timedelta and isinstance(stored_value, (int, float)):
110
 		if self.datatype is timedelta and isinstance(stored_value, (int, float)):
119
 			return timedelta(seconds=stored_value)
111
 			return timedelta(seconds=stored_value)
120
 		return stored_value
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
 		if native_value is None:
117
 		if native_value is None:
124
 			return '<no value>'
118
 			return '<no value>'
125
 		if isinstance(native_value, timedelta):
119
 		if isinstance(native_value, timedelta):
133
 		setting_name = setting.name
127
 		setting_name = setting.name
134
 		if cog.config_prefix is not None:
128
 		if cog.config_prefix is not None:
135
 			setting_name = f'{cog.config_prefix}_{setting_name}'
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
 			value = setting.to_native_value(Storage.get_config_value(interaction.guild, key))
132
 			value = setting.to_native_value(Storage.get_config_value(interaction.guild, key))
141
 			if value is None:
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
 				await interaction.response.send_message(
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
 					ephemeral=True
137
 					ephemeral=True
146
 				)
138
 				)
147
 			else:
139
 			else:
148
 				await interaction.response.send_message(
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
 					ephemeral=True
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
 		bot_log(None, cog.__class__, f"Creating /get {setting_name}")
145
 		bot_log(None, cog.__class__, f"Creating /get {setting_name}")
160
 		command = Command(
146
 		command = Command(
161
 			name=setting_name,
147
 			name=setting_name,
171
 		return command
157
 		return command
172
 
158
 
173
 	def __make_setter_command(self, cog: 'BaseCog') -> Command:
159
 	def __make_setter_command(self, cog: 'BaseCog') -> Command:
174
-		from rocketbot.cogs.basecog import BaseCog
175
 		setting: CogSetting = self
160
 		setting: CogSetting = self
176
 		setting_name = setting.name
161
 		setting_name = setting.name
177
 		if cog.config_prefix is not None:
162
 		if cog.config_prefix is not None:
178
 			setting_name = f'{cog.config_prefix}_{setting_name}'
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
 			try:
165
 			try:
182
 				setting.validate_value(new_value)
166
 				setting.validate_value(new_value)
183
 			except ValueError as ve:
167
 			except ValueError as ve:
186
 					ephemeral=True
170
 					ephemeral=True
187
 				)
171
 				)
188
 				return
172
 				return
189
-			key = f'{cog0.__class__.__name__}.{setting.name}'
173
+			key = f'{cog.__class__.__name__}.{setting.name}'
190
 			Storage.set_config_value(interaction.guild, key, setting.to_stored_value(new_value))
174
 			Storage.set_config_value(interaction.guild, key, setting.to_stored_value(new_value))
191
 			await interaction.response.send_message(
175
 			await interaction.response.send_message(
192
 				f'{CONFIG["success_emoji"]} `{setting_name}` is now set to `{setting.to_native_value(new_value)}`',
176
 				f'{CONFIG["success_emoji"]} `{setting_name}` is now set to `{setting.to_native_value(new_value)}`',
193
 				ephemeral=True
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
 		setter: CommandCallback = setter_general
182
 		setter: CommandCallback = setter_general
199
 		if self.datatype == int:
183
 		if self.datatype == int:
202
 				r_max = self.max_value
186
 				r_max = self.max_value
203
 				@rename(new_value=self.name)
187
 				@rename(new_value=self.name)
204
 				@describe(new_value=self.brief)
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
 				setter = setter_range
191
 				setter = setter_range
208
 			else:
192
 			else:
209
 				@rename(new_value=self.name)
193
 				@rename(new_value=self.name)
210
 				@describe(new_value=self.brief)
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
 				setter = setter_int
197
 				setter = setter_int
214
 		elif self.datatype == float:
198
 		elif self.datatype == float:
215
 			@rename(new_value=self.name)
199
 			@rename(new_value=self.name)
216
 			@describe(new_value=self.brief)
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
 			setter = setter_float
203
 			setter = setter_float
220
 		elif self.datatype == timedelta:
204
 		elif self.datatype == timedelta:
221
 			@rename(new_value=self.name)
205
 			@rename(new_value=self.name)
222
 			@describe(new_value=f'{self.brief} (e.g. 30s, 5m, 1h30s, or 7d)')
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
 			setter = setter_timedelta
209
 			setter = setter_timedelta
226
 		elif getattr(self.datatype, '__origin__', None) == Literal:
210
 		elif getattr(self.datatype, '__origin__', None) == Literal:
227
 			dt = self.datatype
211
 			dt = self.datatype
228
 			@rename(new_value=self.name)
212
 			@rename(new_value=self.name)
229
 			@describe(new_value=self.brief)
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
 			setter = setter_enum
216
 			setter = setter_enum
233
 		elif self.datatype == str:
217
 		elif self.datatype == str:
234
 			if self.enum_values is not None:
218
 			if self.enum_values is not None:
236
 			else:
220
 			else:
237
 				@rename(new_value=self.name)
221
 				@rename(new_value=self.name)
238
 				@describe(new_value=self.brief)
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
 				setter = setter_str
225
 				setter = setter_str
242
-		elif setting.datatype == bool:
226
+		elif self.datatype == bool:
243
 			@rename(new_value=self.name)
227
 			@rename(new_value=self.name)
244
 			@describe(new_value=self.brief)
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
 			setter = setter_bool
231
 			setter = setter_bool
248
-		elif setting.datatype is not None:
232
+		elif self.datatype is not None:
249
 			raise ValueError(f'Invalid type {self.datatype}')
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
 		bot_log(None, cog.__class__, f"Creating /set {setting_name} {self.datatype}")
235
 		bot_log(None, cog.__class__, f"Creating /set {setting_name} {self.datatype}")
254
 		command = Command(
236
 		command = Command(
255
 			name=setting_name,
237
 			name=setting_name,
265
 		return command
247
 		return command
266
 
248
 
267
 	def __make_enable_command(self, cog: 'BaseCog') -> Command:
249
 	def __make_enable_command(self, cog: 'BaseCog') -> Command:
268
-		from rocketbot.cogs.basecog import BaseCog
269
 		setting: CogSetting = self
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
 			Storage.set_config_value(interaction.guild, key, True)
253
 			Storage.set_config_value(interaction.guild, key, True)
274
 			await interaction.response.send_message(
254
 			await interaction.response.send_message(
275
 				f'{CONFIG["success_emoji"]} {setting.brief.capitalize()} enabled.',
255
 				f'{CONFIG["success_emoji"]} {setting.brief.capitalize()} enabled.',
276
 				ephemeral=True
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
 		command = Command(
262
 		command = Command(
286
 			name=cog.config_prefix,
263
 			name=cog.config_prefix,
287
 			description=f'Enables {cog.qualified_name} functionality.',
264
 			description=f'Enables {cog.qualified_name} functionality.',
296
 		return command
273
 		return command
297
 
274
 
298
 	def __make_disable_command(self, cog: 'BaseCog') -> Command:
275
 	def __make_disable_command(self, cog: 'BaseCog') -> Command:
299
-		from rocketbot.cogs.basecog import BaseCog
300
 		setting: CogSetting = self
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
 			Storage.set_config_value(interaction.guild, key, False)
279
 			Storage.set_config_value(interaction.guild, key, False)
305
 			await interaction.response.send_message(
280
 			await interaction.response.send_message(
306
 				f'{CONFIG["success_emoji"]} {setting.brief.capitalize()} disabled.',
281
 				f'{CONFIG["success_emoji"]} {setting.brief.capitalize()} disabled.',
307
 				ephemeral=True
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
 		command = Command(
288
 		command = Command(
317
 			name=cog.config_prefix,
289
 			name=cog.config_prefix,
318
 			description=f'Disables {cog.config_prefix} functionality',
290
 			description=f'Disables {cog.config_prefix} functionality',
326
 		)
298
 		)
327
 		return command
299
 		return command
328
 
300
 
329
-	__has_set_up_base_commands: bool = False
330
 	__set_group: Group
301
 	__set_group: Group
331
 	__get_group: Group
302
 	__get_group: Group
332
 	__enable_group: Group
303
 	__enable_group: Group
344
 			return
315
 			return
345
 		bot_log(None, cog.__class__, f"Setting up slash commands for {cog.__class__.__name__}")
316
 		bot_log(None, cog.__class__, f"Setting up slash commands for {cog.__class__.__name__}")
346
 		for setting in settings:
317
 		for setting in settings:
347
-			setting.set_up(cog, bot)
318
+			setting.set_up(cog)
348
 		bot_log(None, cog.__class__, f"Done setting up slash commands for {cog.__class__.__name__}")
319
 		bot_log(None, cog.__class__, f"Done setting up slash commands for {cog.__class__.__name__}")
349
 
320
 
350
 	@classmethod
321
 	@classmethod
351
 	def __set_up_base_commands(cls, bot: Bot) -> None:
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
 			return
324
 			return
354
-		cls.__has_set_up_base_commands = True
355
 		cls.__set_group = Group(
325
 		cls.__set_group = Group(
356
 			name='set',
326
 			name='set',
357
 			description='Sets a configuration value for this guild.',
327
 			description='Sets a configuration value for this guild.',
424
 								text += ('enabled' if deflt else 'disabled') + ' _(default)_'
394
 								text += ('enabled' if deflt else 'disabled') + ' _(default)_'
425
 						else:
395
 						else:
426
 							if value is not None:
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
 							else:
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
 				await interaction.response.send_message(
400
 				await interaction.response.send_message(
431
 					text,
401
 					text,
432
 					ephemeral=True,
402
 					ephemeral=True,

Загрузка…
Отмена
Сохранить