Very intense application command fetching

Bbomi2/5/2023
I'm using the new sapphire, v4.0.1 , with discord js v14.7.1

Suddenly, the the bot does an incredible amount of fetching of application commands, resulting in rate limit.

{"timeToReset":1100,"limit":50,"method":"GET","hash":"Global(GET:/applications/:id/commands)","url":"https://discord.com/api/v10/applications/ID/commands?with_localizations=true","route":"/applications/:id/commands","majorParameter":"global","global":true}

I have no clue why as I do not use any application commands. Neither any clue to what triggers it.

It could be a discord.js problem of course but since I dont have any code at all relating to application commands, I think it's more likely sapphire?
AOAnswer Overflow2/5/2023
Bbomi2/5/2023
Is there a way to disable application command registries?
00hachi2/5/2023
@Favna this sounds interesting
Bbomi2/5/2023
Do you know some more logging I could add around this, I am not familiar with the ApplicationCommandRegistries
FFavna2/5/2023
there is no way to disable it but if there are no commands to register then it simply does nothing
FFavna2/5/2023
there is basically an if (!something.length) return in there
FFavna2/5/2023
like it's one of the first lines called
Bbomi2/5/2023
And it only adds the command to the array/map/etc to be processed if you call something like this?
registerApplicationCommands(registry) {
    registry.registerChatInputCommand((builder) =>
      builder.setName('ping').setDescription('Ping bot to see if it is alive')
    );
  }
FFavna2/5/2023
correct
Bbomi2/5/2023
It has happened... Hmm, about 5 times now
FFavna2/5/2023
@Vladdy tbh
Bbomi2/5/2023
What if there is some old slash command registered on discord could that affect it?
VVladdy2/5/2023
I'm gonna need a bit more than this, as we only fetch global commands once on ready, and then whenever a command or a store is reloaded
Bbomi2/5/2023
Oh oh oh
VVladdy2/5/2023
Also hitting the ratelimit isn't a big deal unless it slows down some other code, djs handles them for you already
Bbomi2/5/2023
Yes I have been reloading
VVladdy2/5/2023
How often have you been reloading
Bbomi2/5/2023
Unfortunately in this case there was 10k calls resulting in global rate limit
VVladdy2/5/2023
Uh yeah so that's not what the 10k limit is
VVladdy2/5/2023
10k limit is for 403 or 401, aka missing access or missing permissions
VVladdy2/5/2023
10k 429s in 10 minutes sounds kinda impossible
FFavna2/5/2023
could it be that we don't properly check if it should register at all when calling command#reload tho :Thonkang: ?
Bbomi2/5/2023
Actuall yeah sorry. I havent counted them. But I am looking at the lines in the rate limit log. There was 10k lines of rate limit logging
Bbomi2/5/2023
let cmdsrelo = botclient.stores.get("commands")

for (let [key, value] of cmdsrelo) {
  value.reload()
}

This is what I do hehe
VVladdy2/5/2023
Oh god
VVladdy2/5/2023
Please just use commands.reloadAll()
VVladdy2/5/2023
If you want to reload all commands
Bbomi2/5/2023
let cmdsrelo = await botclient.stores.get("commands")
cmdsrelo.reloadAll()
like this?
VVladdy2/5/2023
Ye
VVladdy2/5/2023
I don't remember fully if that's the method name
Bbomi2/5/2023
Im scared
VVladdy2/5/2023
@Favna maybe you can help as I'm currently away from laptop
Bbomi2/5/2023
There is CommandStore.loadAll() ?
Bbomi2/5/2023
public override async loadAll() {
        await super.loadAll();

        // If we don't have an application, that means this was called on login...
        if (!this.container.client.application) return;

        // super.loadAll() currently deletes all application command registries while unloading old pieces,
        // re-register application commands to ensure allGuildIdsToFetchCommandsFor has new guild ids for getNeededRegistryParameters
        for (const command of this.values()) {
            if (command.registerApplicationCommands) {
                try {
                    await command.registerApplicationCommands(command.applicationCommandRegistry);
                } catch (error) {
                    emitRegistryError(error, command);
                }
            }
        }

        const { applicationCommands, globalCommands, guildCommands } = await getNeededRegistryParameters(allGuildIdsToFetchCommandsFor);

        for (const command of this.values()) {
            // eslint-disable-next-line @typescript-eslint/dot-notation
            await command.applicationCommandRegistry['runAPICalls'](applicationCommands, globalCommands, guildCommands);

            // Reinitialize the aliases
            for (const nameOrId of command.applicationCommandRegistry.chatInputCommands) {
                this.aliases.set(nameOrId, command);
            }

            for (const nameOrId of command.applicationCommandRegistry.contextMenuCommands) {
                this.aliases.set(nameOrId, command);
            }
        }
    }
VVladdy2/5/2023
Yes
VVladdy2/5/2023
Which i haven't updated to support bulk overwrite fuck me
Bbomi2/5/2023
Well I don't know that much about this code and how it works but it seems like it fetches the application commands from discord upon reload
Bbomi2/5/2023
I dont really dare trying it but I think the problem is replicable
Bbomi2/5/2023
async reload() {
    // This looks like it runs even when its not a slash/application command?

    const { applicationCommands, globalCommands, guildCommands } = await getNeededParameters_js.getNeededRegistryParameters(updatedRegistry.guildIdsToFetch);
    await updatedRegistry["runAPICalls"](applicationCommands, globalCommands, guildCommands);


    for (const nameOrId of updatedRegistry.chatInputCommands) {
      store.aliases.set(nameOrId, updatedPiece);
    }
    for (const nameOrId of updatedRegistry.contextMenuCommands) {
      store.aliases.set(nameOrId, updatedPiece);
    }
  }
FFavna2/5/2023
ill see what I can find after I finish 2 more cyberpunk missions and have dinner
FFavna2/5/2023
been meaning to look into reload because of bulk overwrite anyway
Bbomi2/5/2023
there is 10k servers about 120 commands, 16-32 shards
FFavna2/5/2023
thought you said you dont use application commands?
Bbomi2/5/2023
Yes, sorry. 0 Application commands, 120 message.content commands
FFavna2/5/2023
yeah those dont care outside of the using loadAll vs reload
Bbomi2/5/2023
I think that a command type can be determined Like so command.supportsChatInputCommands()
Bbomi2/5/2023
So I am tempted to try this modified version of reload()

async reload() {
    const store = this.store;


    // if it's a chatInputCommand, ContextMenuCommand, or AutocompleteCommand, then it's a command that uses application commands. Thus it needs to be reloaded.
    if (this.supportsChatInputCommands() || this.supportsContextMenuCommands() || this.supportsAutocompleteInteractions()) {

      const registry = this.applicationCommandRegistry;
      for (const nameOrId of registry.chatInputCommands) {
        const aliasedPiece = store.aliases.get(nameOrId);
        if (aliasedPiece === this) {
          store.aliases.delete(nameOrId);
        }
      }
      for (const nameOrId of registry.contextMenuCommands) {
        const aliasedPiece = store.aliases.get(nameOrId);
        if (aliasedPiece === this) {
          store.aliases.delete(nameOrId);
        }
      }
      registry.chatInputCommands.clear();
      registry.contextMenuCommands.clear();
      registry.guildIdsToFetch.clear();
      registry["apiCalls"].length = 0;
    }
    await super.reload();
    const updatedPiece = store.get(this.name);
    if (!updatedPiece)
      return;

    if (this.supportsChatInputCommands() || this.supportsContextMenuCommands() || this.supportsAutocompleteInteractions()) {
      const updatedRegistry = updatedPiece.applicationCommandRegistry;
      if (updatedPiece.registerApplicationCommands) {
        try {
          await updatedPiece.registerApplicationCommands(updatedRegistry);
        } catch (err) {
          emitRegistryError_js.emitRegistryError(err, updatedPiece);
          return;
        }
      }
      const { applicationCommands, globalCommands, guildCommands } = await getNeededParameters_js.getNeededRegistryParameters(updatedRegistry.guildIdsToFetch);
      await updatedRegistry["runAPICalls"](applicationCommands, globalCommands, guildCommands);
      for (const nameOrId of updatedRegistry.chatInputCommands) {
        store.aliases.set(nameOrId, updatedPiece);
      }
      for (const nameOrId of updatedRegistry.contextMenuCommands) {
        store.aliases.set(nameOrId, updatedPiece);
      }
    }
Bbomi2/5/2023
This modifiaction was seemingly successful.
VVladdy2/5/2023
hmmmmmmmmmmmmmmmmm
Bbomi2/5/2023
Its probably not the way you would want it, but I think it would be nice if reload had this check
Solution
FFavna2/5/2023
@bomi can you test the changes made in the PR release if they achieve the same end result?

npm install @sapphire/framework@pr-598


https://github.com/sapphiredev/framework/pull/598

Edit: Resolved with this PR, will be released in v4.1.0
Bbomi2/6/2023
I can
Bbomi2/6/2023
But it would be nice if there was someway I could log any times the bot tries to fetch application command endpoints to make testing safer but idk how to
Bbomi2/6/2023
I'll just... Take the chance..
Bbomi2/6/2023
Hmm trying it on one shard only I did not see any problem
Bbomi2/6/2023
It seems to be successful on all shards.
Thanks!!
FFavna2/6/2023
good then we can merge it
AOAnswer Overflow2/6/2023