There is currently no way to register multiple implementations of the same command. Users who want to vary behaviour based on the number of arguments or their types must branch manually inside a single handler.
The idea is to introduce an @bot.overload() decorator that registers an alternate signature for an existing command. Rather than a separate structure, _commands itself would become two-level: Dict[str, Dict[Signature + TypeHints, Command]]. At dispatch time the bot looks up by name, then walks the inner dict to find the entry whose signature (parameter count and declared types) best matches the parsed arguments.
Proposed API
@bot.command()
async def greet(ctx, name: str):
await ctx.reply(f"Hello {name}!")
@bot.overload()
async def greet(ctx):
await ctx.reply("Hello World!")
@bot.command("roman")
async def roman_converter(ctx, number: str):
result = roman_to_decimal(number)
await ctx.reply(f"Roman -> Decimal: {result}!")
@bot.overload()
async def roman_converter(ctx, number: int):
result = decimal_to_roman(number)
await ctx.reply(f"Decimal -> Roman: {result}!")
!roman VI → Roman -> Decimal: 6!
!roman 6 → Decimal -> Roman: VI!
Proposed Solution
_commands currently maps str and Command, so the type changes to Dict[str, Dict[tuple[type, ...], Command]]. The inner key is a tuple of parameter types derived from what Command already computes in its callback setter, tuple(type_hints.get(p.name, str) for p in params), with ctx already excluded. register_command raises AlreadyRegisteredError on duplicate names today, so both it and any callers that do a plain _commands[name] lookup need to be updated.
A few things worth particular attention during implementation: unannotated parameters default to str (matching existing _convert_type behaviour), meaning (ctx, name) and (ctx, name: str) would produce the same key and collide, registration should raise an error in that case. *args (VAR_POSITIONAL) doesn't map cleanly to a fixed-length tuple and may need a sentinel value or be excluded from overload dispatch entirely in a first version. Specific types (int, float, custom resolvers) must be tried before str regardless of registration order.
⚠️ There is still lots of unknown to be discussed. Anyone working on this issue should propose a complete solution before implementing it.
There is currently no way to register multiple implementations of the same command. Users who want to vary behaviour based on the number of arguments or their types must branch manually inside a single handler.
The idea is to introduce an
@bot.overload()decorator that registers an alternate signature for an existing command. Rather than a separate structure,_commandsitself would become two-level:Dict[str, Dict[Signature + TypeHints, Command]]. At dispatch time the bot looks up by name, then walks the inner dict to find the entry whose signature (parameter count and declared types) best matches the parsed arguments.Proposed API
Proposed Solution
_commandscurrently mapsstr and Command, so the type changes toDict[str, Dict[tuple[type, ...], Command]]. The inner key is a tuple of parameter types derived from whatCommandalready computes in itscallbacksetter,tuple(type_hints.get(p.name, str) for p in params), withctxalready excluded.register_commandraisesAlreadyRegisteredErroron duplicate names today, so both it and any callers that do a plain_commands[name]lookup need to be updated.A few things worth particular attention during implementation: unannotated parameters default to
str(matching existing_convert_typebehaviour), meaning(ctx, name)and(ctx, name: str)would produce the same key and collide, registration should raise an error in that case.*args(VAR_POSITIONAL) doesn't map cleanly to a fixed-length tuple and may need a sentinel value or be excluded from overload dispatch entirely in a first version. Specific types (int,float, custom resolvers) must be tried beforestrregardless of registration order.