How do I handle rate-limiting?

Hi, all! Sorry if this is a silly question.

When I kick and re-invite my Discord bot to my server for diagnostic purposes, it needs to reinstall all of its commands. Unfortunately, I have implemented enough commands so far that the bot is rate-limited during the installation attempt, which prevents commands after a certain point from installing entirely. I want to program my bot to respond intelligently to rate-limiting and wait until a given interval has passed to continue submitting requests.

This seems straightforward enough, and I thought so too - but every attempt I have made to integrate this sort of mitigation into my codebase has met with failure. I’m at my wits’ end.

Here are some code samples:

In app.js (main executed file):

app.listen(PORT, () => {
  console.log("Listening on port", PORT);
  HasGuildCommands(process.env.APP_ID, process.env.GUILD_ID, [
    COMMAND_A,
    COMMAND_B,
    COMMAND_C,
    COMMAND_D,
    COMMAND_E,
    COMMAND_F,
    COMMAND_G,
  ]);
});

HasGuildCommands, HasGuildCommand, and InstallGuildCommand in commands.js:

export async function HasGuildCommands(appId, guildId, commands) {
  if (guildId === "" || appId === "") return;
  commands.forEach((c) => HasGuildCommand(appId, guildId, c));
}

async function HasGuildCommand(appId, guildId, command) {
  let update = false;
  const endpoint = `applications/${appId}/guilds/${guildId}/commands`;
  try {
    const res = await DiscordRequest(endpoint, { method: "GET" });
    const data = await res.json();

    if (data) {
      const installedNames = data.map((c) => c["name"]);
      if (!installedNames.includes(command["name"])) {
        console.log(`Installing "${command["name"]}"`);
        InstallGuildCommand(appId, guildId, command);
        console.log(`Installed  "${command["name"]}"`);
      } else {
        console.log(`"${command["name"]}" command already installed.`);
        if (update) {
          console.log(`"Forcibly reinstalling "${command["name"]}"...`);
          InstallGuildCommand(appId, guildId, command);
        }
      }
    }
  } catch (err) {
    console.error("Command has-check error: ", err);
  }
}

export async function InstallGuildCommand(appId, guildId, command) {
  const endpoint = `applications/${appId}/guilds/${guildId}/commands`;
  try {
    await DiscordRequest(endpoint, { method: "POST", body: command });
  } catch (err) {
    console.error("Installation error: ", err);
    console.error("Command: ", command);
  }
}

DiscordRequest in utils.js:

export async function DiscordRequest(endpoint, options) {
  const url = "https://discord.com/api/v10/" + endpoint;
  if (options.body) options.body = JSON.stringify(options.body);
  const res = await fetch(url, {
    headers: {
      Authorization: `Bot ${process.env.DISCORD_TOKEN}`,
      "Content-Type": "application/json, charset=UTF-8",
      "User-Agent":
        "user agent name",
    },
    ...options,
  });

  if (!res.ok) {
    const data = await res.json();
    console.log(res.status);
    throw new Error(JSON.stringify(data));
  }
  return res;
}

I suspect any code I’d wish to implement for my problems would have to go in DiscordRequest. What should I do next?

1 Like