11import typing
22from .context import Context
3- from .exception import CheckFailed
4- from .utils import read_function , is_coro
3+ from .exception import CheckFailed , InvalidArgument
4+ from .utils import read_function , is_coro , smart_split
55
66
77class Command :
@@ -14,12 +14,20 @@ def __init__(self,
1414 self .name = name
1515 self .checks = checks or []
1616 self .aliases = aliases or []
17+ self .subcommands = {}
1718
1819 self .args_data = read_function (self .func )
1920 if hasattr (func , "_checks" ):
2021 self .checks .extend (func ._checks )
2122 self .addon = None
2223
24+ def subcommand (self , * args , ** kwargs ):
25+ def wrap (coro ):
26+ cmd = command (* args , ** kwargs )(coro )
27+ self .subcommands [cmd .name ] = cmd
28+ return cmd
29+ return wrap
30+
2331 def register_addon (self , addon ):
2432 self .addon = addon
2533
@@ -30,8 +38,33 @@ async def evaluate_checks(self, ctx: Context):
3038 async def invoke (self , ctx : Context , * args , ** kwargs ):
3139 if not await self .evaluate_checks (ctx ):
3240 raise CheckFailed
33- init_args = (ctx ,) if self .addon is None else (self .addon , ctx )
34- return await self .func (* init_args , * args , ** kwargs )
41+ tgt = self .func
42+ args = [* args ]
43+ subcommand_invoking = False
44+ subcommand_name = None
45+ subcommand = None
46+ if self .subcommands :
47+ if args and args [0 ] in self .subcommands :
48+ subcommand = self .subcommands [args [0 ]]
49+ tgt = subcommand .invoke
50+ del args [0 ]
51+ subcommand_invoking = True
52+ elif kwargs and [* kwargs .values ()][0 ] in self .subcommands :
53+ subcommand = kwargs .pop ([* kwargs .keys ()][0 ])
54+ tgt = subcommand .invoke
55+ subcommand_invoking = True
56+ elif kwargs or args :
57+ raise InvalidArgument ("unknown subcommand or invalid argument passed." )
58+ elif (args or kwargs ) and not self .args_data :
59+ raise InvalidArgument ("invalid argument data." )
60+ if subcommand_invoking :
61+ ctx .subcommand_name = subcommand_name
62+ msg = ctx .content
63+ ipt = msg .split (maxsplit = 1 )
64+ ipt = ipt [1 ].split (maxsplit = 1 ) if len (ipt ) > 1 else []
65+ args , kwargs = smart_split (ipt [1 ] if len (ipt ) > 1 else "" , subcommand .args_data , subcommand = bool (subcommand .subcommands ))
66+ init_args = (ctx ,) if self .addon is None or subcommand_invoking else (self .addon , ctx )
67+ return await tgt (* init_args , * args , ** kwargs )
3568
3669
3770def command (name : typing .Optional [str ] = None , * , aliases : typing .Optional [typing .List [str ]] = None ):
0 commit comments