Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make get_desynced_commands work with slash command groups #986

Merged
96 changes: 51 additions & 45 deletions discord/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
Union,
)

import discord
Ratery marked this conversation as resolved.
Show resolved Hide resolved

from .client import Client
from .cog import CogMixin
from .commands import (
Expand Down Expand Up @@ -239,6 +241,45 @@ async def get_desynced_commands(
"""
# We can suggest the user to upsert, edit, delete, or bulk upsert the commands

def _check_command(cmd: ApplicationCommand, match: Dict) -> bool:
if isinstance(cmd, SlashCommandGroup):
if len(cmd.subcommands) != len(match.get("options", [])):
return True
for i, subcommand in enumerate(cmd.subcommands):
match_ = next(
(
data
for data in match["options"]
if data["name"] == subcommand.name
),
None,
)
if match_ is not None and _check_command(subcommand, match_):
return True
else:
as_dict = cmd.to_dict()
for check in to_check:
if isinstance(to_check[check], list):
for opt in to_check[check]:
cmd_vals = (
[val.get(opt, MISSING) for val in as_dict[check]]
if check in as_dict
else []
)
for i, val in enumerate(cmd_vals):
# We need to do some falsy conversion here
# The API considers False (autocomplete) and [] (choices) to be falsy values
if val in (False, []):
cmd_vals[i] = MISSING
if cmd_vals != [
val.get(opt, MISSING) for val in match.get(check, [])
]:
return True # We have a difference
else:
if check in match and getattr(cmd, check) != match[check]:
return True # We have a difference
return False

return_value = []
cmds = self.pending_application_commands.copy()

Expand Down Expand Up @@ -268,47 +309,14 @@ async def get_desynced_commands(
if match is None:
# We don't have this command registered
return_value.append({"command": cmd, "action": "upsert"})
continue

as_dict = cmd.to_dict()

for check, value in to_check.items():
if type(to_check[check]) == list:
# We need to do some falsy conversion here
# The API considers False (autocomplete) and [] (choices) to be falsy values
falsy_vals = (False, [])
for opt in value:

cmd_vals = (
[val.get(opt, MISSING) for val in as_dict[check]]
if check in as_dict
else []
)
for i, val in enumerate(cmd_vals):
if val in falsy_vals:
cmd_vals[i] = MISSING
if match.get(check, MISSING) is not MISSING and cmd_vals != [
val.get(opt, MISSING) for val in match[check]
]:
# We have a difference
return_value.append(
{
"command": cmd,
"action": "edit",
"id": int(registered_commands_dict[cmd.name]["id"]),
}
)
break
elif getattr(cmd, check) != match[check]:
# We have a difference
return_value.append(
{
"command": cmd,
"action": "edit",
"id": int(registered_commands_dict[cmd.name]["id"]),
}
)
break
elif _check_command(cmd, match):
return_value.append(
{
"command": cmd,
"action": "edit",
"id": int(registered_commands_dict[cmd.name]["id"]),
}
)

# Now let's see if there are any commands on discord that we need to delete
for cmd, value_ in registered_commands_dict.items():
Expand All @@ -323,8 +331,6 @@ async def get_desynced_commands(
}
)

continue

return return_value

async def register_command(
Expand Down Expand Up @@ -471,7 +477,7 @@ def register(method: str, *args, **kwargs):
data = [cmd["command"].to_dict() for cmd in filtered_deleted]
registered = await register("bulk", data)
else:
if len(pending_actions) == 0:
if not pending_actions:
registered = []
for cmd in pending_actions:
if cmd["action"] == "delete":
Expand Down Expand Up @@ -1209,7 +1215,7 @@ async def can_run(
) -> bool:
data = self._check_once if call_once else self._checks

if len(data) == 0:
if not data:
return True

# type-checker doesn't distinguish between functions and methods
Expand Down