@@ -139,7 +139,10 @@ async def __register_name_autocomplete(self) -> None:
139
139
name = f"autocomplete_{ _command } _{ self .__name_autocomplete [key ]['name' ]} " ,
140
140
)
141
141
142
- async def __compare_sync (self , data : dict , pool : List [dict ]) -> Tuple [bool , dict ]:
142
+ @staticmethod
143
+ async def __compare_sync (
144
+ data : dict , pool : List [dict ]
145
+ ) -> Tuple [bool , dict ]: # sourcery no-metrics
143
146
"""
144
147
Compares an application command during the synchronization process.
145
148
@@ -150,10 +153,14 @@ async def __compare_sync(self, data: dict, pool: List[dict]) -> Tuple[bool, dict
150
153
:return: Whether the command has changed or not.
151
154
:rtype: bool
152
155
"""
156
+
157
+ # sourcery skip: none-compare
153
158
attrs : List [str ] = [
154
159
name
155
160
for name in ApplicationCommand .__slots__
156
- if not name .startswith ("_" ) and not name .endswith ("id" ) and name != "version"
161
+ if not name .startswith ("_" )
162
+ and not name .endswith ("id" )
163
+ and name not in {"version" , "default_permission" }
157
164
]
158
165
159
166
option_attrs : List [str ] = [name for name in Option .__slots__ if not name .startswith ("_" )]
@@ -252,6 +259,14 @@ async def __compare_sync(self, data: dict, pool: List[dict]) -> Tuple[bool, dict
252
259
return clean , _command
253
260
else :
254
261
continue
262
+ elif option_attr == "required" :
263
+ if (
264
+ option .get (option_attr ) == None # noqa: E711
265
+ and _option .get (option_attr )
266
+ == False # noqa: E712
267
+ ):
268
+ # API not including if False
269
+ continue
255
270
elif option .get (option_attr ) != _option .get (
256
271
option_attr
257
272
):
@@ -271,6 +286,14 @@ async def __compare_sync(self, data: dict, pool: List[dict]) -> Tuple[bool, dict
271
286
# This is an API/Version difference.
272
287
continue
273
288
289
+ elif (
290
+ attr == "dm_permission"
291
+ and data .get (attr ) == True # noqa: E712
292
+ and command .get (attr ) == None # noqa: E711
293
+ ):
294
+ # idk, it encountered me and synced unintentionally
295
+ continue
296
+
274
297
# elif data.get(attr, None) and command.get(attr) == data.get(attr):
275
298
elif command .get (attr , None ) == data .get (attr , None ):
276
299
# hasattr checks `dict.attr` not `dict[attr]`
@@ -708,22 +731,27 @@ def __check_options(_option: Option, _names: list, _sub_command: Option = MISSIN
708
731
def __check_coro ():
709
732
__indent = 4
710
733
log .debug (f"{ ' ' * __indent } Checking coroutine: '{ coro .__name__ } '" )
711
- if not len (coro .__code__ .co_varnames ):
734
+ _ismethod = hasattr (coro , "__func__" )
735
+ if not len (coro .__code__ .co_varnames ) ^ (
736
+ _ismethod and len (coro .__code__ .co_varnames ) == 1
737
+ ):
712
738
raise InteractionException (
713
739
11 , message = "Your command needs at least one argument to return context."
714
740
)
715
741
elif "kwargs" in coro .__code__ .co_varnames :
716
742
return
717
- elif _sub_cmds_present and len (coro .__code__ .co_varnames ) < 2 :
743
+ elif _sub_cmds_present and len (coro .__code__ .co_varnames ) < ( 3 if _ismethod else 2 ) :
718
744
raise InteractionException (
719
745
11 , message = "Your command needs one argument for the sub_command."
720
746
)
721
- elif _sub_groups_present and len (coro .__code__ .co_varnames ) < 3 :
747
+ elif _sub_groups_present and len (coro .__code__ .co_varnames ) < ( 4 if _ismethod else 3 ) :
722
748
raise InteractionException (
723
749
11 ,
724
750
message = "Your command needs one argument for the sub_command and one for the sub_command_group." ,
725
751
)
726
- add : int = 1 + abs (_sub_cmds_present ) + abs (_sub_groups_present )
752
+ add : int = (
753
+ 1 + abs (_sub_cmds_present ) + abs (_sub_groups_present ) + 1 if _ismethod else + 0
754
+ )
727
755
728
756
if len (coro .__code__ .co_varnames ) - add < len (set (_options_names )):
729
757
log .debug (
@@ -896,7 +924,11 @@ def decorator(coro: Coroutine) -> Callable[..., Any]:
896
924
coro = coro ,
897
925
)
898
926
899
- coro ._command_data = commands
927
+ if hasattr (coro , "__func__" ):
928
+ coro .__func__ ._command_data = commands
929
+ else :
930
+ coro ._command_data = commands
931
+
900
932
self .__command_coroutines .append (coro )
901
933
902
934
if scope is not MISSING :
@@ -1268,19 +1300,28 @@ def remove(self, name: str, package: Optional[str] = None) -> None:
1268
1300
log .error (f"Extension { name } has not been loaded before. Skipping." )
1269
1301
return
1270
1302
1271
- try :
1272
- extension .teardown () # made for Extension, usable by others
1273
- except AttributeError :
1274
- pass
1275
-
1276
1303
if isinstance (extension , ModuleType ): # loaded as a module
1277
1304
for ext_name , ext in getmembers (
1278
1305
extension , lambda x : isinstance (x , type ) and issubclass (x , Extension )
1279
1306
):
1280
- self .remove (ext_name )
1307
+
1308
+ if ext_name != "Extension" :
1309
+ _extension = self ._extensions .get (ext_name )
1310
+ try :
1311
+ self ._loop .create_task (
1312
+ _extension .teardown ()
1313
+ ) # made for Extension, usable by others
1314
+ except AttributeError :
1315
+ pass
1281
1316
1282
1317
del sys .modules [_name ]
1283
1318
1319
+ else :
1320
+ try :
1321
+ self ._loop .create_task (extension .teardown ()) # made for Extension, usable by others
1322
+ except AttributeError :
1323
+ pass
1324
+
1284
1325
del self ._extensions [_name ]
1285
1326
1286
1327
log .debug (f"Removed extension { name } ." )
@@ -1291,6 +1332,9 @@ def reload(
1291
1332
r"""
1292
1333
"Reloads" an extension off of current client from an import resolve.
1293
1334
1335
+ .. warning::
1336
+ This will remove and re-add application commands, counting towards your daily application command creation limit.
1337
+
1294
1338
:param name: The name of the extension.
1295
1339
:type name: str
1296
1340
:param package?: The package of the extension.
@@ -1307,8 +1351,7 @@ def reload(
1307
1351
1308
1352
if extension is None :
1309
1353
log .warning (f"Extension { name } could not be reloaded because it was never loaded." )
1310
- self .load (name , package )
1311
- return
1354
+ return self .load (name , package )
1312
1355
1313
1356
self .remove (name , package )
1314
1357
return self .load (name , package , * args , ** kwargs )
@@ -1429,7 +1472,6 @@ def __new__(cls, client: Client, *args, **kwargs) -> "Extension":
1429
1472
# This gets every coroutine in a way that we can easily change them
1430
1473
# cls
1431
1474
for name , func in getmembers (self , predicate = iscoroutinefunction ):
1432
-
1433
1475
# TODO we can make these all share the same list, might make it easier to load/unload
1434
1476
if hasattr (func , "__listener_name__" ): # set by extension_listener
1435
1477
func = client .event (
@@ -1497,32 +1539,24 @@ def __new__(cls, client: Client, *args, **kwargs) -> "Extension":
1497
1539
1498
1540
client ._extensions [cls .__name__ ] = self
1499
1541
1542
+ if client ._websocket .ready .is_set () and client ._automate_sync :
1543
+ client ._loop .create_task (client ._Client__sync ())
1544
+
1500
1545
return self
1501
1546
1502
- def teardown (self ):
1547
+ async def teardown (self ):
1503
1548
for event , funcs in self ._listeners .items ():
1504
1549
for func in funcs :
1505
1550
self .client ._websocket ._dispatch .events [event ].remove (func )
1506
1551
1507
1552
for cmd , funcs in self ._commands .items ():
1508
1553
for func in funcs :
1554
+ _index = self .client ._Client__command_coroutines .index (func )
1555
+ self .client ._Client__command_coroutines .pop (_index )
1509
1556
self .client ._websocket ._dispatch .events [cmd ].remove (func )
1510
1557
1511
- clean_cmd_names = [cmd [7 :] for cmd in self ._commands .keys ()]
1512
- cmds = filter (
1513
- lambda cmd_data : cmd_data ["name" ] in clean_cmd_names ,
1514
- self .client ._http .cache .interactions .view ,
1515
- )
1516
-
1517
1558
if self .client ._automate_sync :
1518
- [
1519
- self .client ._loop .create_task (
1520
- self .client ._http .delete_application_command (
1521
- cmd ["application_id" ], cmd ["id" ], cmd ["guild_id" ]
1522
- )
1523
- )
1524
- for cmd in cmds
1525
- ]
1559
+ await self .client ._Client__sync ()
1526
1560
1527
1561
1528
1562
@wraps (command )
0 commit comments