Skip to content
Merged
98 changes: 74 additions & 24 deletions discord/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,51 +214,101 @@ async def sync_commands(self) -> None:

async def register_commands(self) -> None:
"""|coro|

Registers all commands that have been added through :meth:`.add_application_command`.
This method cleans up all commands over the API and should sync them with the internal cache of commands.

This will only be rolled out to Discord if :meth:`.http.get_global_commands` has certain keys that differ from :data:`.pending_application_commands`
By default, this coroutine is called inside the :func:`.on_connect`
event. If you choose to override the :func:`.on_connect` event, then
you should invoke this coroutine as well.

.. versionadded:: 2.0
"""
commands = []
commands_to_bulk = []

needs_bulk = False

# Global Command Permissions
global_permissions: List = []

registered_commands = await self.http.get_global_commands(self.user.id)
# 'Your app cannot have two global commands with the same name. Your app cannot have two guild commands within the same name on the same guild.'
# We can therefore safely use the name of the command in our global slash commands as a unique identifier
registered_commands_dict = {cmd["name"]:cmd for cmd in registered_commands}
global_pending_application_commands_dict = {}

for command in [
cmd for cmd in self.pending_application_commands if cmd.guild_ids is None
]:
as_dict = command.to_dict()
if len(registered_commands) > 0:
matches = [
x
for x in registered_commands
if x["name"] == command.name and x["type"] == command.type
]
# TODO: rewrite this, it seems inefficient
if matches:
as_dict["id"] = matches[0]["id"]
commands.append(as_dict)

cmds = await self.http.bulk_upsert_global_commands(self.user.id, commands)

for i in cmds:

global_pending_application_commands_dict[command.name] = as_dict
if command.name in registered_commands_dict:
match = registered_commands_dict[command.name]
else:
match = None
# TODO: There is probably a far more efficient way of doing this
# We want to check if the registered global command on Discord servers matches the given global commands
if match:
as_dict["id"] = match["id"]

keys_to_check = {"default_permission": True, "name": True, "description": True, "options": ["type", "name", "description", "autocomplete", "choices"]}
for key, more_keys in {
key:more_keys
for key, more_keys in keys_to_check.items()
if key in as_dict.keys()
if key in match.keys()
}.items():
if key == "options":
for i, option_dict in enumerate(as_dict[key]):
if command.name == "recent":
print(option_dict, "|||||", match[key][i])
for key2 in more_keys:
pendingVal = None
if key2 in option_dict.keys():
pendingVal = option_dict[key2]
if pendingVal == False or pendingVal == []: # Registered commands are not available if choices is an empty array or if autocomplete is false
pendingVal = None
matchVal = None
if key2 in match[key][i].keys():
matchVal = match[key][i][key2]
if matchVal == False or matchVal == []: # Registered commands are not available if choices is an empty array or if autocomplete is false
matchVal = None

if pendingVal != matchVal:
# When a property in the options of a pending global command is changed
needs_bulk = True
else:
if as_dict[key] != match[key]:
# When a property in a pending global command is changed
needs_bulk = True
else:
# When a name of a pending global command is not registered in Discord
needs_bulk = True

commands_to_bulk.append(as_dict)

for name, command in registered_commands_dict.items():
if not name in global_pending_application_commands_dict.keys():
# When a registered global command is not available in the pending global commands
needs_bulk = True

if needs_bulk:
commands = await self.http.bulk_upsert_global_commands(self.user.id, commands_to_bulk)
else:
commands = registered_commands

for i in commands:
cmd = get(
self.pending_application_commands,
name=i["name"],
guild_ids=None,
type=i["type"],
)
cmd.id = i["id"]
self._application_commands[cmd.id] = cmd
if cmd:
cmd.id = i["id"]
self._application_commands[cmd.id] = cmd

# Permissions (Roles will be converted to IDs just before Upsert for Global Commands)
global_permissions.append({"id": i["id"], "permissions": cmd.permissions})
# Permissions (Roles will be converted to IDs just before Upsert for Global Commands)
global_permissions.append({"id": i["id"], "permissions": cmd.permissions})

update_guild_commands = {}
async for guild in self.fetch_guilds(limit=None):
Expand All @@ -275,7 +325,7 @@ async def register_commands(self) -> None:

for guild_id, guild_data in update_guild_commands.items():
try:
cmds = await self.http.bulk_upsert_guild_commands(
commands = await self.http.bulk_upsert_guild_commands(
self.user.id, guild_id, update_guild_commands[guild_id]
)

Expand All @@ -287,7 +337,7 @@ async def register_commands(self) -> None:
print(f"Failed to add command to guild {guild_id}", file=sys.stderr)
raise
else:
for i in cmds:
for i in commands:
cmd = find(lambda cmd: cmd.name == i["name"] and cmd.type == i["type"] and int(i["guild_id"]) in cmd.guild_ids, self.pending_application_commands)
cmd.id = i["id"]
self._application_commands[cmd.id] = cmd
Expand Down