Skip to content

Commit b18324e

Browse files
authored
Merge pull request #634 from PenguinDevs/global-slash-commands
Global commands will be sent to Discord to register only if it differs from the existing global commands from Discord's servers
2 parents 87ad51f + 9183f8b commit b18324e

File tree

1 file changed

+74
-24
lines changed

1 file changed

+74
-24
lines changed

discord/bot.py

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -214,51 +214,101 @@ async def sync_commands(self) -> None:
214214

215215
async def register_commands(self) -> None:
216216
"""|coro|
217-
218217
Registers all commands that have been added through :meth:`.add_application_command`.
219218
This method cleans up all commands over the API and should sync them with the internal cache of commands.
220-
219+
This will only be rolled out to Discord if :meth:`.http.get_global_commands` has certain keys that differ from :data:`.pending_application_commands`
221220
By default, this coroutine is called inside the :func:`.on_connect`
222221
event. If you choose to override the :func:`.on_connect` event, then
223222
you should invoke this coroutine as well.
224-
225223
.. versionadded:: 2.0
226224
"""
227-
commands = []
225+
commands_to_bulk = []
226+
227+
needs_bulk = False
228228

229229
# Global Command Permissions
230230
global_permissions: List = []
231231

232232
registered_commands = await self.http.get_global_commands(self.user.id)
233+
# '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.'
234+
# We can therefore safely use the name of the command in our global slash commands as a unique identifier
235+
registered_commands_dict = {cmd["name"]:cmd for cmd in registered_commands}
236+
global_pending_application_commands_dict = {}
237+
233238
for command in [
234239
cmd for cmd in self.pending_application_commands if cmd.guild_ids is None
235240
]:
236241
as_dict = command.to_dict()
237-
if len(registered_commands) > 0:
238-
matches = [
239-
x
240-
for x in registered_commands
241-
if x["name"] == command.name and x["type"] == command.type
242-
]
243-
# TODO: rewrite this, it seems inefficient
244-
if matches:
245-
as_dict["id"] = matches[0]["id"]
246-
commands.append(as_dict)
247-
248-
cmds = await self.http.bulk_upsert_global_commands(self.user.id, commands)
249-
250-
for i in cmds:
242+
243+
global_pending_application_commands_dict[command.name] = as_dict
244+
if command.name in registered_commands_dict:
245+
match = registered_commands_dict[command.name]
246+
else:
247+
match = None
248+
# TODO: There is probably a far more efficient way of doing this
249+
# We want to check if the registered global command on Discord servers matches the given global commands
250+
if match:
251+
as_dict["id"] = match["id"]
252+
253+
keys_to_check = {"default_permission": True, "name": True, "description": True, "options": ["type", "name", "description", "autocomplete", "choices"]}
254+
for key, more_keys in {
255+
key:more_keys
256+
for key, more_keys in keys_to_check.items()
257+
if key in as_dict.keys()
258+
if key in match.keys()
259+
}.items():
260+
if key == "options":
261+
for i, option_dict in enumerate(as_dict[key]):
262+
if command.name == "recent":
263+
print(option_dict, "|||||", match[key][i])
264+
for key2 in more_keys:
265+
pendingVal = None
266+
if key2 in option_dict.keys():
267+
pendingVal = option_dict[key2]
268+
if pendingVal == False or pendingVal == []: # Registered commands are not available if choices is an empty array or if autocomplete is false
269+
pendingVal = None
270+
matchVal = None
271+
if key2 in match[key][i].keys():
272+
matchVal = match[key][i][key2]
273+
if matchVal == False or matchVal == []: # Registered commands are not available if choices is an empty array or if autocomplete is false
274+
matchVal = None
275+
276+
if pendingVal != matchVal:
277+
# When a property in the options of a pending global command is changed
278+
needs_bulk = True
279+
else:
280+
if as_dict[key] != match[key]:
281+
# When a property in a pending global command is changed
282+
needs_bulk = True
283+
else:
284+
# When a name of a pending global command is not registered in Discord
285+
needs_bulk = True
286+
287+
commands_to_bulk.append(as_dict)
288+
289+
for name, command in registered_commands_dict.items():
290+
if not name in global_pending_application_commands_dict.keys():
291+
# When a registered global command is not available in the pending global commands
292+
needs_bulk = True
293+
294+
if needs_bulk:
295+
commands = await self.http.bulk_upsert_global_commands(self.user.id, commands_to_bulk)
296+
else:
297+
commands = registered_commands
298+
299+
for i in commands:
251300
cmd = get(
252301
self.pending_application_commands,
253302
name=i["name"],
254303
guild_ids=None,
255304
type=i["type"],
256305
)
257-
cmd.id = i["id"]
258-
self._application_commands[cmd.id] = cmd
306+
if cmd:
307+
cmd.id = i["id"]
308+
self._application_commands[cmd.id] = cmd
259309

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

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

276326
for guild_id, guild_data in update_guild_commands.items():
277327
try:
278-
cmds = await self.http.bulk_upsert_guild_commands(
328+
commands = await self.http.bulk_upsert_guild_commands(
279329
self.user.id, guild_id, update_guild_commands[guild_id]
280330
)
281331

@@ -287,7 +337,7 @@ async def register_commands(self) -> None:
287337
print(f"Failed to add command to guild {guild_id}", file=sys.stderr)
288338
raise
289339
else:
290-
for i in cmds:
340+
for i in commands:
291341
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)
292342
cmd.id = i["id"]
293343
self._application_commands[cmd.id] = cmd

0 commit comments

Comments
 (0)