diff --git a/.github/labeler.yml b/.github/labeler.yml
index 9424ed106..c2b67c66a 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -14,6 +14,9 @@ packages:builders:
packages:collection:
- packages/collection/*
- packages/collection/**/*
+packages/core:
+ - packages:core/*
+ - packages:core/**/*
packages:discord.js:
- packages/discord.js/*
- packages/discord.js/**/*
diff --git a/.github/labels.yml b/.github/labels.yml
index 8bc0a7d3b..7396a3511 100644
--- a/.github/labels.yml
+++ b/.github/labels.yml
@@ -56,6 +56,8 @@
color: fbca04
- name: packages:collection
color: fbca04
+- name: packages:core
+ color: fbca04
- name: packages:discord.js
color: fbca04
- name: packages:docgen
diff --git a/packages/core/.cliff-jumperrc.json b/packages/core/.cliff-jumperrc.json
new file mode 100644
index 000000000..2d962203a
--- /dev/null
+++ b/packages/core/.cliff-jumperrc.json
@@ -0,0 +1,5 @@
+{
+ "name": "core",
+ "org": "discordjs",
+ "packagePath": "packages/core"
+}
diff --git a/packages/core/.eslintrc.json b/packages/core/.eslintrc.json
new file mode 100644
index 000000000..216ef39df
--- /dev/null
+++ b/packages/core/.eslintrc.json
@@ -0,0 +1,6 @@
+{
+ "extends": "../../.eslintrc.json",
+ "rules": {
+ "jsdoc/check-param-names": "off"
+ }
+}
diff --git a/packages/core/.gitignore b/packages/core/.gitignore
new file mode 100644
index 000000000..86b93e929
--- /dev/null
+++ b/packages/core/.gitignore
@@ -0,0 +1,27 @@
+# Packages
+node_modules/
+
+# Log files
+logs/
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Env
+.env
+
+# Dist
+dist/
+typings/
+docs/**/*
+!docs/index.json
+!docs/README.md
+
+# Miscellaneous
+.tmp/
+coverage/
+tsconfig.tsbuildinfo
diff --git a/packages/core/.lintstagedrc.js b/packages/core/.lintstagedrc.js
new file mode 100644
index 000000000..dc17706a5
--- /dev/null
+++ b/packages/core/.lintstagedrc.js
@@ -0,0 +1 @@
+module.exports = require('../../.lintstagedrc.json');
diff --git a/packages/core/.prettierignore b/packages/core/.prettierignore
new file mode 100644
index 000000000..553e0ea6c
--- /dev/null
+++ b/packages/core/.prettierignore
@@ -0,0 +1,8 @@
+# Autogenerated
+CHANGELOG.md
+.turbo
+dist/
+docs/**/*
+!docs/index.yml
+!docs/README.md
+coverage/
diff --git a/packages/core/.prettierrc.js b/packages/core/.prettierrc.js
new file mode 100644
index 000000000..f004026c7
--- /dev/null
+++ b/packages/core/.prettierrc.js
@@ -0,0 +1 @@
+module.exports = require('../../.prettierrc.json');
diff --git a/packages/core/LICENSE b/packages/core/LICENSE
new file mode 100644
index 000000000..fd65c1f0b
--- /dev/null
+++ b/packages/core/LICENSE
@@ -0,0 +1,191 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ Copyright 2022 Noel Buechler
+ Copyright 2022 Suneet Tipirneni
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/packages/core/README.md b/packages/core/README.md
new file mode 100644
index 000000000..46540e2fd
--- /dev/null
+++ b/packages/core/README.md
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## About
+
+`@discordjs/core` is a thinly abstracted wrapper around the "core" components of the Discord API: REST, and gateway.
+
+## Installation
+
+**Node.js 16.9.0 or newer is required.**
+
+```sh-session
+npm install @discordjs/core
+yarn add @discordjs/core
+pnpm add @discordjs/core
+```
+
+## Example usage
+
+```ts
+import { REST } from '@discordjs/rest';
+import { WebSocketManager } from '@discordjs/ws';
+import { GatewayIntentBits, InteractionType, MessageFlags, createClient } from '@discordjs/core';
+
+// Create REST and WebSocket managers directly
+const rest = new REST({ version: '10' }).setToken(token);
+const ws = new WebSocketManager({
+ token,
+ intents: GatewayIntentBits.GuildMessages | GatewayIntentBits.MessageContent,
+ rest,
+});
+
+// Create a client to emit relevant events.
+const client = createClient({ rest, ws });
+
+// Listen for interactions
+// Each event contains an `api` prop along with the event data that allows you to interface with the Discord REST API
+client.on('interactionCreate', async ({ interaction, api }) => {
+ if (!(interaction.type === InteractionType.ApplicationCommand) || interaction.data.name !== 'ping') {
+ return;
+ }
+
+ await api.interactions.reply(interaction.id, interaction.token, { content: 'Pong!', flags: MessageFlags.Ephemeral });
+});
+
+// Listen for the ready event
+client.on('ready', () => console.log('Ready!'));
+
+// Start the WebSocket connection.
+ws.connect();
+```
+
+## Independent REST API Usage
+
+```ts
+// Create REST instance
+const rest = new REST({ version: '10' }).setToken(token);
+
+// Pass into API
+const api = new API(rest);
+
+// Fetch a guild using the API wrapper
+const guild = await api.guilds.get('1234567891011');
+```
+
+## Links
+
+- [Website][website] ([source][website-source])
+- [Documentation][documentation]
+- [Guide][guide] ([source][guide-source])
+ See also the [Update Guide][guide-update], including updated and removed items in the library.
+- [discord.js Discord server][discord]
+- [Discord API Discord server][discord-api]
+- [GitHub][source]
+- [npm][npm]
+- [Related libraries][related-libs]
+
+## Contributing
+
+Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
+[documentation][documentation].
+See [the contribution guide][contributing] if you'd like to submit a PR.
+
+## Help
+
+If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
+nudge in the right direction, please don't hesitate to join our official [discord.js Server][discord].
+
+[website]: https://discord.js.org/
+[website-source]: https://github.com/discordjs/discord.js/tree/main/apps/website
+[documentation]: https://discord.js.org/
+[guide]: https://discordjs.guide/
+[guide-source]: https://github.com/discordjs/guide
+[guide-update]: https://discordjs.guide/additional-info/changes-in-v14.html
+[discord]: https://discord.gg/djs
+[discord-api]: https://discord.gg/discord-api
+[source]: https://github.com/discordjs/discord.js/tree/main/packages/core
+[npm]: https://www.npmjs.com/package/@discordjs/core
+[related-libs]: https://discord.com/developers/docs/topics/community-resources#libraries
+[contributing]: https://github.com/discordjs/discord.js/blob/main/.github/CONTRIBUTING.md
diff --git a/packages/core/api-extractor.json b/packages/core/api-extractor.json
new file mode 100644
index 000000000..bc73f2cc0
--- /dev/null
+++ b/packages/core/api-extractor.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../api-extractor.json"
+}
diff --git a/packages/core/cliff.toml b/packages/core/cliff.toml
new file mode 100644
index 000000000..66d9194ef
--- /dev/null
+++ b/packages/core/cliff.toml
@@ -0,0 +1,63 @@
+[changelog]
+header = """
+# Changelog
+
+All notable changes to this project will be documented in this file.\n
+"""
+body = """
+{% if version %}\
+ # [{{ version | trim_start_matches(pat="v") }}]\
+ {% if previous %}\
+ {% if previous.version %}\
+ (https://github.com/discordjs/discord.js/compare/{{ previous.version }}...{{ version }})\
+ {% else %}\
+ (https://github.com/discordjs/discord.js/tree/{{ version }})\
+ {% endif %}\
+ {% endif %} \
+ - ({{ timestamp | date(format="%Y-%m-%d") }})
+{% else %}\
+ # [unreleased]
+{% endif %}\
+{% for group, commits in commits | group_by(attribute="group") %}
+ ## {{ group | upper_first }}
+ {% for commit in commits %}
+ - {% if commit.scope %}\
+ **{{commit.scope}}:** \
+ {% endif %}\
+ {{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}](https://github.com/discordjs/discord.js/commit/{{ commit.id }}))\
+ {% if commit.breaking %}\
+ {% for breakingChange in commit.footers %}\
+ \n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
+ {% endfor %}\
+ {% endif %}\
+ {% endfor %}
+{% endfor %}\n
+"""
+trim = true
+footer = ""
+
+[git]
+conventional_commits = true
+filter_unconventional = true
+commit_parsers = [
+ { message = "^feat", group = "Features"},
+ { message = "^fix", group = "Bug Fixes"},
+ { message = "^docs", group = "Documentation"},
+ { message = "^perf", group = "Performance"},
+ { message = "^refactor", group = "Refactor"},
+ { message = "^typings", group = "Typings"},
+ { message = "^types", group = "Typings"},
+ { message = ".*deprecated", body = ".*deprecated", group = "Deprecation"},
+ { message = "^revert", skip = true},
+ { message = "^style", group = "Styling"},
+ { message = "^test", group = "Testing"},
+ { message = "^chore", skip = true},
+ { message = "^ci", skip = true},
+ { message = "^build", skip = true},
+ { body = ".*security", group = "Security"},
+]
+filter_commits = true
+tag_pattern = "@discordjs/core@[0-9]*"
+ignore_tags = ""
+date_order = true
+sort_commits = "newest"
diff --git a/packages/core/package.json b/packages/core/package.json
new file mode 100644
index 000000000..38f3fc0cf
--- /dev/null
+++ b/packages/core/package.json
@@ -0,0 +1,73 @@
+{
+ "name": "@discordjs/core",
+ "version": "0.1.0",
+ "description": "A thinly abstracted wrapper around the rest API, and gateway.",
+ "scripts": {
+ "test": "vitest run",
+ "build": "tsup",
+ "lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.ts --format=pretty",
+ "format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.ts --fix --format=pretty",
+ "docs": "api-extractor run --local",
+ "prepack": "yarn build && yarn lint",
+ "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/core/*'",
+ "release": "cliff-jumper"
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "typings": "./dist/index.d.ts",
+ "exports": {
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.js",
+ "types": "./dist/index.d.ts"
+ },
+ "directories": {
+ "lib": "src",
+ "test": "__tests__"
+ },
+ "files": [
+ "dist"
+ ],
+ "contributors": [
+ "Crawl ",
+ "SpaceEEC ",
+ "Vlad Frangu ",
+ "Aura Román ",
+ "Suneet Tipirneni "
+ ],
+ "license": "Apache-2.0",
+ "keywords": [],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/discordjs/discord.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/discordjs/discord.js/issues"
+ },
+ "homepage": "https://discord.js.org",
+ "dependencies": {
+ "@discordjs/rest": "workspace:^",
+ "@discordjs/ws": "workspace:^",
+ "@vladfrangu/async_event_emitter": "^2.1.2",
+ "discord-api-types": "^0.37.20"
+ },
+ "devDependencies": {
+ "@favware/cliff-jumper": "^1.9.0",
+ "@microsoft/api-extractor": "^7.33.6",
+ "@types/node": "16.18.3",
+ "@vitest/coverage-c8": "^0.25.3",
+ "cross-env": "^7.0.3",
+ "eslint": "^8.28.0",
+ "eslint-config-neon": "^0.1.40",
+ "eslint-formatter-pretty": "^4.1.0",
+ "prettier": "^2.8.0",
+ "tsup": "^6.5.0",
+ "typescript": "^4.9.3",
+ "vitest": "^0.25.3"
+ },
+ "engines": {
+ "node": ">=16.9.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/core/src/api/applicationCommands.ts b/packages/core/src/api/applicationCommands.ts
new file mode 100644
index 000000000..1f66c1a45
--- /dev/null
+++ b/packages/core/src/api/applicationCommands.ts
@@ -0,0 +1,240 @@
+import { makeURLSearchParams, type REST } from '@discordjs/rest';
+import {
+ Routes,
+ type RESTGetAPIApplicationCommandPermissionsResult,
+ type RESTGetAPIApplicationCommandResult,
+ type RESTGetAPIApplicationCommandsResult,
+ type RESTGetAPIGuildApplicationCommandsPermissionsResult,
+ type RESTPatchAPIApplicationCommandJSONBody,
+ type RESTPatchAPIApplicationCommandResult,
+ type RESTPostAPIApplicationCommandsJSONBody,
+ type RESTPostAPIApplicationCommandsResult,
+ type RESTPutAPIApplicationCommandPermissionsJSONBody,
+ type RESTPutAPIApplicationCommandPermissionsResult,
+ type RESTPutAPIApplicationCommandsJSONBody,
+ type RESTGetAPIApplicationCommandsQuery,
+ type RESTPutAPIApplicationCommandsResult,
+ type RESTGetAPIApplicationGuildCommandsQuery,
+ type Snowflake,
+} from 'discord-api-types/v10';
+
+export class ApplicationCommandsAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Fetches all global commands for a application
+ *
+ * @param applicationId - The application id to fetch commands for
+ * @param options - The options to use when fetching commands
+ */
+ public async getGlobalCommands(applicationId: Snowflake, options: RESTGetAPIApplicationCommandsQuery = {}) {
+ return this.rest.get(Routes.applicationCommands(applicationId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Creates a new global command
+ *
+ * @param applicationId - The application id to create the command for
+ * @param data - The data to use when creating the command
+ */
+ public async createGlobalCommand(applicationId: Snowflake, data: RESTPostAPIApplicationCommandsJSONBody) {
+ return this.rest.post(Routes.applicationCommands(applicationId), {
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches a global command
+ *
+ * @param applicationId - The application id to fetch the command from
+ * @param commandId - The command id to fetch
+ */
+ public async getGlobalCommand(applicationId: Snowflake, commandId: Snowflake) {
+ return this.rest.get(
+ Routes.applicationCommand(applicationId, commandId),
+ ) as Promise;
+ }
+
+ /**
+ * Edits a global command
+ *
+ * @param applicationId - The application id of the command
+ * @param commandId - The id of the command to edit
+ * @param data - The data to use when editing the command
+ */
+ public async editGlobalCommand(
+ applicationId: Snowflake,
+ commandId: Snowflake,
+ data: RESTPatchAPIApplicationCommandJSONBody,
+ ) {
+ return this.rest.patch(Routes.applicationCommand(applicationId, commandId), {
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes a global command
+ *
+ * @param applicationId - The application id of the command
+ * @param commandId - The id of the command to delete
+ */
+ public async deleteGlobalCommand(applicationId: Snowflake, commandId: Snowflake) {
+ await this.rest.delete(Routes.applicationCommand(applicationId, commandId));
+ }
+
+ /**
+ * Overwrites global commands
+ *
+ * @param applicationId - The application id to overwrite commands for
+ * @param data - The data to use when overwriting commands
+ */
+ public async bulkOverwriteGlobalCommands(applicationId: Snowflake, data: RESTPutAPIApplicationCommandsJSONBody) {
+ return this.rest.put(Routes.applicationCommands(applicationId), {
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches all commands for a guild
+ *
+ * @param applicationId - The application id to fetch commands for
+ * @param guildId - The guild id to fetch commands for
+ * @param data - The data to use when fetching commands
+ */
+ public async getGuildCommands(
+ applicationId: Snowflake,
+ guildId: Snowflake,
+ data: RESTGetAPIApplicationGuildCommandsQuery = {},
+ ) {
+ return this.rest.get(Routes.applicationGuildCommands(applicationId, guildId), {
+ query: makeURLSearchParams(data as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Creates a new command for a guild
+ *
+ * @param applicationId - The application id to create the command for
+ * @param guildId - The guild id to create the command for
+ * @param data - The data to use when creating the command
+ */
+ public async createGuildCommand(
+ applicationId: Snowflake,
+ guildId: Snowflake,
+ data: RESTPostAPIApplicationCommandsJSONBody,
+ ) {
+ return this.rest.post(Routes.applicationGuildCommands(applicationId, guildId), {
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches a guild command
+ *
+ * @param applicationId - The application id to fetch the command from
+ * @param guildId - The guild id to fetch the command from
+ * @param commandId - The command id to fetch
+ */
+ public async getGuildCommand(applicationId: Snowflake, guildId: Snowflake, commandId: Snowflake) {
+ return this.rest.get(
+ Routes.applicationGuildCommand(applicationId, guildId, commandId),
+ ) as Promise;
+ }
+
+ /**
+ * Edits a guild command
+ *
+ * @param applicationId - The application id of the command
+ * @param guildId - The guild id of the command
+ * @param commandId - The command id to edit
+ * @param data - The data to use when editing the command
+ */
+ public async editGuildCommand(
+ applicationId: Snowflake,
+ guildId: Snowflake,
+ commandId: Snowflake,
+ data: RESTPatchAPIApplicationCommandJSONBody,
+ ) {
+ return this.rest.patch(Routes.applicationGuildCommand(applicationId, guildId, commandId), {
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes a guild command
+ *
+ * @param applicationId - The application id of the command
+ * @param guildId - The guild id of the command
+ * @param commandId - The id of the command to delete
+ */
+ public async deleteGuildCommand(applicationId: Snowflake, guildId: Snowflake, commandId: Snowflake) {
+ await this.rest.delete(Routes.applicationGuildCommand(applicationId, guildId, commandId));
+ }
+
+ /**
+ * Bulk overwrites guild commands
+ *
+ * @param applicationId - The application id to overwrite commands for
+ * @param guildId - The guild id to overwrite commands for
+ * @param data - The data to use when overwriting commands
+ */
+ public async bulkOverwriteGuildCommands(
+ applicationId: Snowflake,
+ guildId: Snowflake,
+ data: RESTPutAPIApplicationCommandsJSONBody,
+ ) {
+ return this.rest.put(Routes.applicationGuildCommands(applicationId, guildId), {
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches the permissions for a guild command
+ *
+ * @param applicationId - The application id to get the permissions for
+ * @param guildId - The guild id of the command
+ * @param commandId - The command id to get the permissions for
+ */
+ public async getGuildCommandPermissions(applicationId: Snowflake, guildId: Snowflake, commandId: Snowflake) {
+ return this.rest.get(
+ Routes.applicationCommandPermissions(applicationId, guildId, commandId),
+ ) as Promise;
+ }
+
+ /**
+ * Fetches all permissions for all commands in a guild
+ *
+ * @param applicationId - The application id to get the permissions for
+ * @param guildId - The guild id to get the permissions for
+ */
+ public async getGuildCommandsPermissions(applicationId: Snowflake, guildId: Snowflake) {
+ return this.rest.get(
+ Routes.guildApplicationCommandsPermissions(applicationId, guildId),
+ ) as Promise;
+ }
+
+ /**
+ * Edits the permissions for a guild command
+ *
+ * @param userToken - The token of the user to edit permissions on behalf of
+ * @param applicationId - The application id to edit the permissions for
+ * @param guildId - The guild id to edit the permissions for
+ * @param commandId - The id of the command to edit the permissions for
+ * @param data - The data to use when editing the permissions
+ */
+ public async editGuildCommandPermissions(
+ userToken: string,
+ applicationId: Snowflake,
+ guildId: Snowflake,
+ commandId: Snowflake,
+ data: RESTPutAPIApplicationCommandPermissionsJSONBody,
+ ) {
+ return this.rest.put(Routes.applicationCommandPermissions(applicationId, guildId, commandId), {
+ headers: { Authorization: `Bearer ${userToken.replace('Bearer ', '')}` },
+ auth: false,
+ body: data,
+ }) as Promise;
+ }
+}
diff --git a/packages/core/src/api/channel.ts b/packages/core/src/api/channel.ts
new file mode 100644
index 000000000..ea1b12588
--- /dev/null
+++ b/packages/core/src/api/channel.ts
@@ -0,0 +1,326 @@
+import { makeURLSearchParams, type RawFile, type REST } from '@discordjs/rest';
+import {
+ Routes,
+ type RESTDeleteAPIChannelResult,
+ type RESTGetAPIChannelInvitesResult,
+ type RESTGetAPIChannelMessageReactionUsersQuery,
+ type RESTGetAPIChannelMessageReactionUsersResult,
+ type RESTGetAPIChannelMessageResult,
+ type RESTGetAPIChannelMessagesQuery,
+ type RESTGetAPIChannelMessagesResult,
+ type RESTGetAPIChannelPinsResult,
+ type RESTGetAPIChannelResult,
+ type RESTGetAPIChannelThreadsArchivedQuery,
+ type RESTGetAPIChannelUsersThreadsArchivedResult,
+ type RESTPatchAPIChannelJSONBody,
+ type RESTPatchAPIChannelMessageResult,
+ type RESTPatchAPIChannelResult,
+ type RESTPostAPIChannelFollowersResult,
+ type RESTPostAPIChannelInviteJSONBody,
+ type RESTPostAPIChannelInviteResult,
+ type RESTPostAPIChannelMessageCrosspostResult,
+ type RESTPostAPIChannelMessageJSONBody,
+ type RESTPostAPIChannelMessageResult,
+ type Snowflake,
+} from 'discord-api-types/v10';
+
+export class ChannelsAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Sends a message in a channel
+ *
+ * @param channelId - The id of the channel to send the message in
+ * @param data - The data to use when sending the message
+ */
+ public async createMessage(
+ channelId: Snowflake,
+ { files, ...body }: RESTPostAPIChannelMessageJSONBody & { files?: RawFile[] },
+ ) {
+ return this.rest.post(Routes.channelMessages(channelId), {
+ files,
+ body,
+ }) as Promise;
+ }
+
+ /**
+ * Edits a message
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to edit
+ * @param data - The data to use when editing the message
+ */
+ public async editMessage(
+ channelId: Snowflake,
+ messageId: Snowflake,
+ { files, ...body }: RESTPostAPIChannelMessageJSONBody & { files?: RawFile[] },
+ ) {
+ return this.rest.patch(Routes.channelMessage(channelId, messageId), {
+ files,
+ body,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches the reactions for a message
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to get the reactions for
+ * @param emoji - The emoji to get the reactions for
+ * @param options - The options to use when fetching the reactions
+ */
+ public async getMessageReactions(
+ channelId: Snowflake,
+ messageId: Snowflake,
+ emoji: string,
+ options: RESTGetAPIChannelMessageReactionUsersQuery = {},
+ ) {
+ return this.rest.get(Routes.channelMessageReaction(channelId, messageId, encodeURIComponent(emoji)), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Deletes a reaction for the current user
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to delete the reaction for
+ * @param emoji - The emoji to delete the reaction for
+ */
+ public async deleteOwnMessageReaction(channelId: Snowflake, messageId: Snowflake, emoji: string) {
+ await this.rest.delete(Routes.channelMessageOwnReaction(channelId, messageId, encodeURIComponent(emoji)));
+ }
+
+ /**
+ * Deletes a reaction for a user
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to delete the reaction for
+ * @param emoji - The emoji to delete the reaction for
+ * @param userId - The id of the user to delete the reaction for
+ */
+ public async deleteUserMessageReaction(channelId: Snowflake, messageId: Snowflake, emoji: string, userId: Snowflake) {
+ await this.rest.delete(Routes.channelMessageUserReaction(channelId, messageId, encodeURIComponent(emoji), userId));
+ }
+
+ /**
+ * Deletes all reactions for a message
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to delete the reactions for
+ */
+ public async deleteAllMessageReactions(channelId: Snowflake, messageId: Snowflake) {
+ await this.rest.delete(Routes.channelMessageAllReactions(channelId, messageId));
+ }
+
+ /**
+ * Deletes all reactions of an emoji for a message
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to delete the reactions for
+ * @param emoji - The emoji to delete the reactions for
+ */
+ public async deleteAllMessageReactionsForEmoji(channelId: Snowflake, messageId: Snowflake, emoji: string) {
+ await this.rest.delete(Routes.channelMessageReaction(channelId, messageId, encodeURIComponent(emoji)));
+ }
+
+ /**
+ * Adds a reaction to a message
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to add the reaction to
+ * @param emoji - The emoji to add the reaction with
+ */
+ public async addMessageReaction(channelId: Snowflake, messageId: Snowflake, emoji: string) {
+ await this.rest.put(Routes.channelMessageOwnReaction(channelId, messageId, encodeURIComponent(emoji)));
+ }
+
+ /**
+ * Fetches a channel
+ *
+ * @param channelId - The id of the channel
+ */
+ public async get(channelId: Snowflake) {
+ return this.rest.get(Routes.channel(channelId)) as Promise;
+ }
+
+ /**
+ * Edits a channel
+ *
+ * @param channelId - The id of the channel to edit
+ * @param data - The new channel data
+ */
+ public async edit(channelId: Snowflake, data: RESTPatchAPIChannelJSONBody) {
+ return this.rest.patch(Routes.channel(channelId), { body: data }) as Promise;
+ }
+
+ /**
+ * Deletes a channel
+ *
+ * @param channelId - The id of the channel to delete
+ */
+ public async delete(channelId: Snowflake) {
+ return this.rest.delete(Routes.channel(channelId)) as Promise;
+ }
+
+ /**
+ * Fetches the messages of a channel
+ *
+ * @param channelId - The id of the channel to fetch messages from
+ * @param options - The options to use when fetching messages
+ */
+ public async getMessages(channelId: Snowflake, options: RESTGetAPIChannelMessagesQuery = {}) {
+ return this.rest.get(Routes.channelMessages(channelId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Shows a typing indicator in a channel
+ *
+ * @param channelId - The id of the channel to show the typing indicator in
+ */
+ public async showTyping(channelId: Snowflake) {
+ await this.rest.post(Routes.channelTyping(channelId));
+ }
+
+ /**
+ * Fetches the pinned messages of a channel
+ *
+ * @param channelId - The id of the channel to fetch pinned messages from
+ */
+ public async getPins(channelId: Snowflake) {
+ return this.rest.get(Routes.channelPins(channelId)) as Promise;
+ }
+
+ /**
+ * Pins a message in a channel
+ *
+ * @param channelId - The id of the channel to pin the message in
+ * @param messageId - The id of the message to pin
+ * @param reason - The reason for pinning the message
+ */
+ public async pinMessage(channelId: Snowflake, messageId: Snowflake, reason?: string) {
+ await this.rest.put(Routes.channelPin(channelId, messageId), { reason });
+ }
+
+ /**
+ * Deletes a message
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to delete
+ * @param reason - The reason for deleting the message
+ */
+ public async deleteMessage(channelId: Snowflake, messageId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.channelMessage(channelId, messageId), { reason });
+ }
+
+ /**
+ * Bulk deletes messages
+ *
+ * @param channelId - The id of the channel the messages are in
+ * @param messageIds - The ids of the messages to delete
+ */
+ public async bulkDeleteMessages(channelId: Snowflake, messageIds: Snowflake[], reason?: string): Promise {
+ await this.rest.post(Routes.channelBulkDelete(channelId), { reason, body: { messages: messageIds } });
+ }
+
+ /**
+ * Fetches a message
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to fetch
+ */
+ public async getMessage(channelId: Snowflake, messageId: Snowflake) {
+ return this.rest.get(Routes.channelMessage(channelId, messageId)) as Promise;
+ }
+
+ /**
+ * Crossposts a message
+ *
+ * @param channelId - The id of the channel the message is in
+ * @param messageId - The id of the message to crosspost
+ */
+ public async crosspostMessage(channelId: Snowflake, messageId: Snowflake) {
+ return this.rest.post(
+ Routes.channelMessageCrosspost(channelId, messageId),
+ ) as Promise;
+ }
+
+ /**
+ * Unpins a message in a channel
+ *
+ * @param channelId - The id of the channel to unpin the message in
+ * @param messageId - The id of the message to unpin
+ * @param reason - The reason for unpinning the message
+ */
+ public async unpinMessage(channelId: Snowflake, messageId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.channelPin(channelId, messageId), { reason });
+ }
+
+ /**
+ * Follows an announcement channel
+ *
+ * @param channelId - The id of the announcement channel to follow
+ * @param webhookChannelId - The id of the webhook channel to follow the announcements in
+ */
+ public async followAnnouncements(channelId: Snowflake, webhookChannelId: Snowflake) {
+ return this.rest.post(Routes.channelFollowers(channelId), {
+ body: { webhook_channel_id: webhookChannelId },
+ }) as Promise;
+ }
+
+ /**
+ * Creates a new invite for a channel
+ *
+ * @param channelId - The id of the channel to create an invite for
+ * @param data - The data to use when creating the invite
+ */
+ public async createInvite(channelId: Snowflake, data: RESTPostAPIChannelInviteJSONBody, reason?: string) {
+ return this.rest.post(Routes.channelInvites(channelId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches the invites of a channel
+ *
+ * @param channelId - The id of the channel to fetch invites from
+ */
+ public async getInvites(channelId: Snowflake) {
+ return this.rest.get(Routes.channelInvites(channelId)) as Promise;
+ }
+
+ /**
+ * Fetches the archived threads of a channel
+ *
+ * @param channelId - The id of the channel to fetch archived threads from
+ * @param archivedStatus - The archived status of the threads to fetch
+ * @param options - The options to use when fetching archived threads
+ */
+ public async getArchivedThreads(
+ channelId: Snowflake,
+ archivedStatus: 'private' | 'public',
+ options: RESTGetAPIChannelThreadsArchivedQuery = {},
+ ) {
+ return this.rest.get(Routes.channelThreads(channelId, archivedStatus), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Fetches the private joined archived threads of a channel
+ *
+ * @param channelId - The id of the channel to fetch joined archived threads from
+ * @param options - The options to use when fetching joined archived threads
+ */
+ public async getJoinedPrivateArchivedThreads(
+ channelId: Snowflake,
+ options: RESTGetAPIChannelThreadsArchivedQuery = {},
+ ) {
+ return this.rest.get(Routes.channelJoinedArchivedThreads(channelId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+}
diff --git a/packages/core/src/api/guild.ts b/packages/core/src/api/guild.ts
new file mode 100644
index 000000000..609e9b13b
--- /dev/null
+++ b/packages/core/src/api/guild.ts
@@ -0,0 +1,901 @@
+import type { RawFile } from '@discordjs/rest';
+import { makeURLSearchParams, type REST } from '@discordjs/rest';
+import {
+ Routes,
+ type GuildMFALevel,
+ type GuildWidgetStyle,
+ type RESTGetAPIAuditLogQuery,
+ type RESTGetAPIAuditLogResult,
+ type RESTGetAPIAutoModerationRuleResult,
+ type RESTGetAPIAutoModerationRulesResult,
+ type RESTGetAPIGuildBansResult,
+ type RESTGetAPIGuildChannelsResult,
+ type RESTGetAPIGuildEmojiResult,
+ type RESTGetAPIGuildEmojisResult,
+ type RESTGetAPIGuildIntegrationsResult,
+ type RESTGetAPIGuildInvitesResult,
+ type RESTGetAPIGuildMemberResult,
+ type RESTGetAPIGuildMembersQuery,
+ type RESTGetAPIGuildMembersSearchResult,
+ type RESTGetAPIGuildPreviewResult,
+ type RESTGetAPIGuildPruneCountResult,
+ type RESTGetAPIGuildResult,
+ type RESTGetAPIGuildRolesResult,
+ type RESTGetAPIGuildScheduledEventQuery,
+ type RESTGetAPIGuildScheduledEventResult,
+ type RESTGetAPIGuildScheduledEventsQuery,
+ type RESTGetAPIGuildScheduledEventsResult,
+ type RESTGetAPIGuildScheduledEventUsersQuery,
+ type RESTGetAPIGuildScheduledEventUsersResult,
+ type RESTGetAPIGuildStickerResult,
+ type RESTGetAPIGuildStickersResult,
+ type RESTGetAPIGuildTemplatesResult,
+ type RESTGetAPIGuildThreadsResult,
+ type RESTGetAPIGuildVanityUrlResult,
+ type RESTGetAPIGuildVoiceRegionsResult,
+ type RESTGetAPIGuildPruneCountQuery,
+ type RESTPostAPIGuildStickerFormDataBody,
+ type RESTPostAPIGuildStickerResult,
+ type RESTGetAPIGuildMembersSearchQuery,
+ type RESTGetAPIGuildWelcomeScreenResult,
+ type RESTGetAPIGuildWidgetImageResult,
+ type RESTGetAPIGuildWidgetJSONResult,
+ type RESTGetAPITemplateResult,
+ type RESTPatchAPIAutoModerationRuleJSONBody,
+ type RESTPatchAPIGuildChannelPositionsJSONBody,
+ type RESTPatchAPIGuildEmojiJSONBody,
+ type RESTPatchAPIGuildEmojiResult,
+ type RESTPatchAPIGuildJSONBody,
+ type RESTPatchAPIGuildMemberJSONBody,
+ type RESTPatchAPIGuildMemberResult,
+ type RESTPatchAPIGuildResult,
+ type RESTPatchAPIGuildRoleJSONBody,
+ type RESTPatchAPIGuildRolePositionsJSONBody,
+ type RESTPatchAPIGuildRolePositionsResult,
+ type RESTPatchAPIGuildRoleResult,
+ type RESTPatchAPIGuildScheduledEventJSONBody,
+ type RESTPatchAPIGuildScheduledEventResult,
+ type RESTPatchAPIGuildStickerJSONBody,
+ type RESTPatchAPIGuildStickerResult,
+ type RESTPatchAPIGuildTemplateJSONBody,
+ type RESTPatchAPIGuildTemplateResult,
+ type RESTPatchAPIGuildVoiceStateUserJSONBody,
+ type RESTPatchAPIGuildWelcomeScreenJSONBody,
+ type RESTPatchAPIGuildWelcomeScreenResult,
+ type RESTPatchAPIGuildWidgetSettingsJSONBody,
+ type RESTPatchAPIGuildWidgetSettingsResult,
+ type RESTPostAPIAutoModerationRuleJSONBody,
+ type RESTPostAPIAutoModerationRuleResult,
+ type RESTPostAPIGuildChannelJSONBody,
+ type RESTPostAPIGuildChannelResult,
+ type RESTPostAPIGuildEmojiJSONBody,
+ type RESTPostAPIGuildEmojiResult,
+ type RESTPostAPIGuildPruneJSONBody,
+ type RESTPostAPIGuildRoleJSONBody,
+ type RESTPostAPIGuildRoleResult,
+ type RESTPostAPIGuildScheduledEventJSONBody,
+ type RESTPostAPIGuildScheduledEventResult,
+ type RESTPostAPIGuildsJSONBody,
+ type RESTPostAPIGuildsMFAResult,
+ type RESTPostAPIGuildsResult,
+ type RESTPostAPIGuildTemplatesResult,
+ type RESTPostAPITemplateCreateGuildJSONBody,
+ type RESTPutAPIGuildBanJSONBody,
+ type RESTPutAPIGuildTemplateSyncResult,
+ type Snowflake,
+} from 'discord-api-types/v10';
+
+export class GuildsAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Fetches a guild
+ *
+ * @param guildId - The id of the guild
+ */
+ public async get(guildId: string) {
+ return this.rest.get(Routes.guild(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches a guild preview
+ *
+ * @param guildId - The id of the guild to fetch the preview from
+ */
+ public async getPreview(guildId: Snowflake) {
+ return this.rest.get(Routes.guildPreview(guildId)) as Promise;
+ }
+
+ /**
+ * Creates a guild
+ *
+ * @param data - The guild to create
+ */
+ public async create(data: RESTPostAPIGuildsJSONBody) {
+ return this.rest.post(Routes.guilds(), { body: data }) as Promise;
+ }
+
+ /**
+ * Edits a guild
+ *
+ * @param guildId - The id of the guild to edit
+ * @param data - The new guild data
+ * @param reason - The reason for editing this guild
+ */
+ public async edit(guildId: Snowflake, data: RESTPatchAPIGuildJSONBody, reason?: string) {
+ return this.rest.patch(Routes.guild(guildId), { reason, body: data }) as Promise;
+ }
+
+ /**
+ * Deletes a guild
+ *
+ * @param guildId - The id of the guild to delete
+ * @param reason - The reason for deleting this guild
+ */
+ public async delete(guildId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guild(guildId), { reason });
+ }
+
+ /**
+ * Fetches all the members of a guild
+ *
+ * @param guildId - The id of the guild
+ * @param options - The options to use when fetching the guild members
+ */
+ public async getMembers(guildId: Snowflake, options: RESTGetAPIGuildMembersQuery = {}) {
+ return this.rest.get(Routes.guildMembers(guildId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Fetches a guild's channels
+ *
+ * @param guildId - The id of the guild to fetch the channels from
+ */
+ public async getChannels(guildId: Snowflake) {
+ return this.rest.get(Routes.guildChannels(guildId)) as Promise;
+ }
+
+ /**
+ * Creates a guild channel
+ *
+ * @param guildId - The id of the guild to create the channel in
+ * @param data - The data to create the new channel
+ * @param reason - The reason for creating this channel
+ */
+ public async createChannel(guildId: Snowflake, data: RESTPostAPIGuildChannelJSONBody, reason?: string) {
+ return this.rest.post(Routes.guildChannels(guildId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Edits a guild channel's positions
+ *
+ * @param guildId - The id of the guild to edit the channel positions from
+ * @param data - The data to edit the channel positions with
+ * @param reason - The reason for editing the channel positions
+ */
+ public async setChannelPositions(
+ guildId: Snowflake,
+ data: RESTPatchAPIGuildChannelPositionsJSONBody,
+ reason?: string,
+ ) {
+ await this.rest.patch(Routes.guildChannels(guildId), { reason, body: data });
+ }
+
+ /**
+ * Fetches the active threads in a guild
+ *
+ * @param guildId - The id of the guild to fetch the active threads from
+ */
+ public async getActiveThreads(guildId: Snowflake) {
+ return this.rest.get(Routes.guildActiveThreads(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches a guild member ban
+ *
+ * @param guildId - The id of the guild to fetch the ban from
+ */
+ public async getMemberBans(guildId: Snowflake) {
+ return this.rest.get(Routes.guildBans(guildId)) as Promise;
+ }
+
+ /**
+ * Bans a user from a guild
+ *
+ * @param guildId - The id of the guild to ban the member in
+ * @param userId - The id of the user to ban
+ * @param options - Options for banning the user
+ * @param reason - The reason for banning the user
+ */
+ public async banUser(
+ guildId: Snowflake,
+ userId: Snowflake,
+ options: RESTPutAPIGuildBanJSONBody = {},
+ reason?: string,
+ ) {
+ await this.rest.put(Routes.guildBan(guildId, userId), { reason, body: options });
+ }
+
+ /**
+ * Unbans a user from a guild
+ *
+ * @param guildId - The id of the guild to unban the member in
+ * @param userId - The id of the user to unban
+ * @param reason - The reason for unbanning the user
+ */
+ public async unbanUser(guildId: Snowflake, userId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guildBan(guildId, userId), { reason });
+ }
+
+ /**
+ * Gets all the roles in a guild
+ *
+ * @param guildId - The id of the guild to fetch the roles from
+ */
+ public async getRoles(guildId: Snowflake) {
+ return this.rest.get(Routes.guildRoles(guildId)) as Promise;
+ }
+
+ /**
+ * Creates a guild role
+ *
+ * @param guildId - The id of the guild to create the role in
+ * @param data - The data to create the role with
+ * @param reason - The reason for creating the role
+ */
+ public async createRole(guildId: Snowflake, data: RESTPostAPIGuildRoleJSONBody, reason?: string) {
+ return this.rest.post(Routes.guildRoles(guildId), { reason, body: data }) as Promise;
+ }
+
+ /**
+ * Sets role positions in a guild
+ *
+ * @param guildId - The id of the guild to set role positions for
+ * @param data - The data for setting a role position
+ * @param reason - The reason for setting the role position
+ */
+ public async setRolePositions(guildId: Snowflake, data: RESTPatchAPIGuildRolePositionsJSONBody, reason?: string) {
+ return this.rest.patch(Routes.guildRoles(guildId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Edits a guild role
+ *
+ * @param guildId - The id of the guild to edit the role in
+ * @param roleId - The id of the role to edit
+ * @param data - data for editing the role
+ * @param reason - The reason for editing the role
+ */
+ public async editRole(guildId: Snowflake, roleId: Snowflake, data: RESTPatchAPIGuildRoleJSONBody, reason?: string) {
+ return this.rest.patch(Routes.guildRole(guildId, roleId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes a guild role
+ *
+ * @param guildId - The id of the guild to delete the role in
+ * @param roleId - The id of the role to delete
+ * @param reason - The reason for deleting the role
+ */
+ public async deleteRole(guildId: Snowflake, roleId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guildRole(guildId, roleId), { reason });
+ }
+
+ /**
+ * Edits the multi-factor-authentication (MFA) level of a guild
+ *
+ * @param guildId - The id of the guild to edit the MFA level for
+ * @param level - The new MFA level
+ * @param reason - The reason for editing the MFA level
+ */
+ public async editMFALevel(guildId: Snowflake, level: GuildMFALevel, reason?: string) {
+ return this.rest.post(Routes.guildMFA(guildId), {
+ reason,
+ body: { mfa_level: level },
+ }) as Promise;
+ }
+
+ /**
+ * Fetch the number of members that can be pruned from a guild
+ *
+ * @param guildId - The id of the guild to fetch the number of pruned members from
+ * @param options - The options for fetching the number of pruned members
+ */
+ public async getPruneCount(guildId: Snowflake, options: RESTGetAPIGuildPruneCountQuery = {}) {
+ return this.rest.get(Routes.guildPrune(guildId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Prunes members in a guild
+ *
+ * @param guildId - The id of the guild to prune members in
+ * @param options - The options for pruning members
+ * @param reason - The reason for pruning members
+ */
+ public async beginPrune(guildId: Snowflake, options: RESTPostAPIGuildPruneJSONBody = {}, reason?: string) {
+ return this.rest.post(Routes.guildPrune(guildId), {
+ body: options,
+ reason,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches voice regions for a guild
+ *
+ * @param guildId - The id of the guild to fetch the voice regions from
+ */
+ public async getVoiceRegions(guildId: Snowflake) {
+ return this.rest.get(Routes.guildVoiceRegions(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches the invites for a guild
+ *
+ * @param guildId - The id of the guild to fetch the invites from
+ */
+ public async getInvites(guildId: Snowflake) {
+ return this.rest.get(Routes.guildInvites(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches the integrations for a guild
+ *
+ * @param guildId - The id of the guild to fetch the integrations from
+ */
+ public async getIntegrations(guildId: Snowflake) {
+ return this.rest.get(Routes.guildIntegrations(guildId)) as Promise;
+ }
+
+ /**
+ * Deletes an integration from a guild
+ *
+ * @param guildId - The id of the guild to delete the integration from
+ * @param integrationId - The id of the integration to delete
+ * @param reason - The reason for deleting the integration
+ */
+ public async deleteIntegration(guildId: Snowflake, integrationId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guildIntegration(guildId, integrationId), { reason });
+ }
+
+ /**
+ * Fetches the widget settings for a guild
+ *
+ * @param guildId - The id of the guild to fetch the widget settings from
+ */
+ public async getWidgetSettings(guildId: Snowflake) {
+ return this.rest.get(Routes.guildWidgetSettings(guildId)) as Promise;
+ }
+
+ /**
+ * Edits the widget settings for a guild
+ *
+ * @param guildId - The id of the guild to edit the widget settings from
+ * @param data - The new widget settings data
+ * @param reason - The reason for editing the widget settings
+ */
+ public async editWidgetSettings(guildId: Snowflake, data: RESTPatchAPIGuildWidgetSettingsJSONBody, reason?: string) {
+ return this.rest.patch(Routes.guildWidgetSettings(guildId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches the widget for a guild
+ *
+ * @param guildId - The id of the guild to fetch the widget from
+ */
+ public async getWidget(guildId: Snowflake) {
+ return this.rest.get(Routes.guildWidgetJSON(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches the vanity url for a guild
+ *
+ * @param guildId - The id of the guild to fetch the vanity url from
+ */
+ public async getVanityURL(guildId: Snowflake) {
+ return this.rest.get(Routes.guildVanityUrl(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches the widget image for a guild
+ *
+ * @param guildId - The id of the guild to fetch the widget image from
+ * @param style - The style of the widget image
+ */
+ public async getWidgetImage(guildId: Snowflake, style?: GuildWidgetStyle) {
+ return this.rest.get(Routes.guildWidgetImage(guildId), {
+ query: makeURLSearchParams({ style }),
+ }) as Promise;
+ }
+
+ /**
+ * Fetches the welcome screen for a guild
+ *
+ * @param guildId - The id of the guild to fetch the welcome screen from
+ */
+ public async getWelcomeScreen(guildId: Snowflake) {
+ return this.rest.get(Routes.guildWelcomeScreen(guildId)) as Promise;
+ }
+
+ /**
+ * Edits the welcome screen for a guild
+ *
+ * @param guildId - The id of the guild to edit the welcome screen for
+ * @param data - The new welcome screen data
+ * @param reason - The reason for editing the welcome screen
+ */
+ public async editWelcomeScreen(guildId: Snowflake, data?: RESTPatchAPIGuildWelcomeScreenJSONBody, reason?: string) {
+ return this.rest.patch(Routes.guildWelcomeScreen(guildId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Edits a user's voice state in a guild
+ *
+ * @param guildId - The id of the guild to edit the current user's voice state in
+ * @param userId - The id of the user to edit the voice state for
+ * @param data - The data for editing the voice state
+ * @param reason - The reason for editing the voice state
+ */
+ public async editUserVoiceState(
+ guildId: Snowflake,
+ userId: Snowflake,
+ data: RESTPatchAPIGuildVoiceStateUserJSONBody,
+ reason?: string,
+ ) {
+ await this.rest.patch(Routes.guildVoiceState(guildId, userId), { reason, body: data });
+ }
+
+ /**
+ * Fetches all emojis for a guild
+ *
+ * @param guildId - The id of the guild to fetch the emojis from
+ */
+ public async getEmojis(guildId: Snowflake) {
+ return this.rest.get(Routes.guildEmojis(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches an emoji for a guild
+ *
+ * @param guildId - The id of the guild to fetch the emoji from
+ * @param emojiId - The id of the emoji to fetch
+ */
+ public async getEmoji(guildId: Snowflake, emojiId: Snowflake) {
+ return this.rest.get(Routes.guildEmoji(guildId, emojiId)) as Promise;
+ }
+
+ /**
+ * Creates a new emoji for a guild
+ *
+ * @param guildId - The id of the guild to create the emoji from
+ * @param data - The data for creating the emoji
+ * @param reason - The reason for creating the emoji
+ */
+ public async createEmoji(guildId: Snowflake, data: RESTPostAPIGuildEmojiJSONBody, reason?: string) {
+ return this.rest.post(Routes.guildEmojis(guildId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Edits an emoji for a guild
+ *
+ * @param guildId - The id of the guild to edit the emoji from
+ * @param emojiId - The id of the emoji to edit
+ * @param data - The data for editing the emoji
+ * @param reason - The reason for editing the emoji
+ */
+ public async editEmoji(
+ guildId: Snowflake,
+ emojiId: Snowflake,
+ data: RESTPatchAPIGuildEmojiJSONBody,
+ reason?: string,
+ ) {
+ return this.rest.patch(Routes.guildEmoji(guildId, emojiId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes an emoji for a guild
+ *
+ * @param guildId - The id of the guild to delete the emoji from
+ * @param emojiId - The id of the emoji to delete
+ * @param reason - The reason for deleting the emoji
+ */
+ public async deleteEmoji(guildId: Snowflake, emojiId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guildEmoji(guildId, emojiId), { reason });
+ }
+
+ /**
+ * Fetches all scheduled events for a guild
+ *
+ * @param guildId - The id of the guild to fetch the scheduled events from
+ * @param options - The options for fetching the scheduled events
+ */
+ public async getScheduledEvents(guildId: Snowflake, options: RESTGetAPIGuildScheduledEventsQuery = {}) {
+ return this.rest.get(Routes.guildScheduledEvents(guildId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Creates a new scheduled event for a guild
+ *
+ * @param guildId - The id of the guild to create the scheduled event from
+ * @param data - The data to create the event with
+ * @param reason - The reason for creating the scheduled event
+ */
+ public async createScheduledEvent(guildId: Snowflake, data: RESTPostAPIGuildScheduledEventJSONBody, reason?: string) {
+ return this.rest.post(Routes.guildScheduledEvents(guildId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Fetches a scheduled event for a guild
+ *
+ * @param guildId - The id of the guild to fetch the scheduled event from
+ * @param eventId - The id of the scheduled event to fetch
+ * @param options - The options for fetching the scheduled event
+ */
+ public async getScheduledEvent(
+ guildId: Snowflake,
+ eventId: Snowflake,
+ options: RESTGetAPIGuildScheduledEventQuery = {},
+ ) {
+ return this.rest.get(Routes.guildScheduledEvent(guildId, eventId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Edits a scheduled event for a guild
+ *
+ * @param guildId - The id of the guild to edit the scheduled event from
+ * @param eventId - The id of the scheduled event to edit
+ * @param data - The new event data
+ * @param reason - The reason for editing the scheduled event
+ */
+ public async editScheduledEvent(
+ guildId: Snowflake,
+ eventId: Snowflake,
+ data: RESTPatchAPIGuildScheduledEventJSONBody,
+ reason?: string,
+ ) {
+ return this.rest.patch(Routes.guildScheduledEvent(guildId, eventId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes a scheduled event for a guild
+ *
+ * @param guildId - The id of the guild to delete the scheduled event from
+ * @param eventId - The id of the scheduled event to delete
+ * @param reason - The reason for deleting the scheduled event
+ */
+ public async deleteScheduledEvent(guildId: Snowflake, eventId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guildScheduledEvent(guildId, eventId), { reason });
+ }
+
+ /**
+ * Gets all users that are interested in a scheduled event
+ *
+ * @param guildId - The id of the guild to fetch the scheduled event users from
+ * @param eventId - The id of the scheduled event to fetch the users for
+ * @param options - The options for fetching the scheduled event users
+ */
+ public async getScheduledEventUsers(
+ guildId: Snowflake,
+ eventId: Snowflake,
+ options: RESTGetAPIGuildScheduledEventUsersQuery = {},
+ ) {
+ return this.rest.get(Routes.guildScheduledEventUsers(guildId, eventId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Fetches all the templates for a guild
+ *
+ * @param guildId - The id of the guild to fetch the templates from
+ */
+ public async getTemplates(guildId: Snowflake) {
+ return this.rest.get(Routes.guildTemplates(guildId)) as Promise;
+ }
+
+ /**
+ * Syncs a template for a guild
+ *
+ * @param guildId - The id of the guild to sync the template from
+ * @param templateCode - The code of the template to sync
+ */
+ public async syncTemplate(guildId: Snowflake, templateCode: string) {
+ return this.rest.put(Routes.guildTemplate(guildId, templateCode)) as Promise;
+ }
+
+ /**
+ * Edits a template for a guild
+ *
+ * @param guildId - The id of the guild to edit the template from
+ * @param templateCode - The code of the template to edit
+ * @param data - The data for editing the template
+ */
+ public async editTemplate(guildId: Snowflake, templateCode: string, data: RESTPatchAPIGuildTemplateJSONBody) {
+ return this.rest.patch(Routes.guildTemplate(guildId, templateCode), {
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes a template for a guild
+ *
+ * @param guildId - The id of the guild to delete the template from
+ * @param templateCode - The code of the template to delete
+ */
+ public async deleteTemplate(guildId: Snowflake, templateCode: string) {
+ await this.rest.delete(Routes.guildTemplate(guildId, templateCode));
+ }
+
+ /**
+ * Fetches all the stickers for a guild
+ *
+ * @param guildId - The id of the guild to fetch the stickers from
+ */
+ public async getStickers(guildId: Snowflake) {
+ return this.rest.get(Routes.guildStickers(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches a sticker for a guild
+ *
+ * @param guildId - The id of the guild to fetch the sticker from
+ * @param stickerId - The id of the sticker to fetch
+ */
+ public async getSticker(guildId: Snowflake, stickerId: Snowflake) {
+ return this.rest.get(Routes.guildSticker(guildId, stickerId)) as Promise;
+ }
+
+ /**
+ * Creates a sticker for a guild
+ *
+ * @param guildId - The id of the guild to create the sticker for
+ * @param data - The data for creating the sticker
+ * @param reason - The reason for creating the sticker
+ */
+ public async createSticker(
+ guildId: Snowflake,
+ { file, ...body }: Omit & { file: RawFile },
+ reason?: string,
+ ) {
+ const fileData = { ...file, key: 'file' };
+
+ return this.rest.post(Routes.guildStickers(guildId), {
+ appendToFormData: true,
+ body,
+ files: [fileData],
+ reason,
+ }) as Promise;
+ }
+
+ /**
+ * Edits a sticker for a guild
+ *
+ * @param guildId - The id of the guild to edit the sticker from
+ * @param stickerId - The id of the sticker to edit
+ * @param data - The data for editing the sticker
+ * @param reason - The reason for editing the sticker
+ */
+ public async editSticker(
+ guildId: Snowflake,
+ stickerId: Snowflake,
+ data: RESTPatchAPIGuildStickerJSONBody,
+ reason?: string,
+ ) {
+ return this.rest.patch(Routes.guildSticker(guildId, stickerId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes a sticker for a guild
+ *
+ * @param guildId - The id of the guild to delete the sticker from
+ * @param stickerId - The id of the sticker to delete
+ * @param reason - The reason for deleting the sticker
+ */
+ public async deleteSticker(guildId: Snowflake, stickerId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guildSticker(guildId, stickerId), { reason });
+ }
+
+ /**
+ * Fetches the audit logs for a guild
+ *
+ * @param guildId - The id of the guild to fetch the audit logs from
+ * @param options - The options for fetching the audit logs
+ */
+ public async getAuditLogs(guildId: Snowflake, options: RESTGetAPIAuditLogQuery = {}) {
+ return this.rest.get(Routes.guildAuditLog(guildId), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Fetches all auto moderation rules for a guild
+ *
+ * @param guildId - The id of the guild to fetch the auto moderation rules from
+ */
+ public async getAutoModerationRules(guildId: Snowflake) {
+ return this.rest.get(Routes.guildAutoModerationRules(guildId)) as Promise;
+ }
+
+ /**
+ * Fetches an auto moderation rule for a guild
+ *
+ * @param guildId - The id of the guild to fetch the auto moderation rule from
+ * @param ruleId - The id of the auto moderation rule to fetch
+ */
+ public async getAutoModerationRule(guildId: Snowflake, ruleId: Snowflake) {
+ return this.rest.get(
+ Routes.guildAutoModerationRule(guildId, ruleId),
+ ) as Promise;
+ }
+
+ /**
+ * Creates a new auto moderation rule for a guild
+ *
+ * @param guildId - The id of the guild to create the auto moderation rule from
+ * @param data - The data for creating the auto moderation rule
+ */
+ public async createAutoModerationRule(
+ guildId: Snowflake,
+ data: RESTPostAPIAutoModerationRuleJSONBody,
+ reason?: string,
+ ) {
+ return this.rest.post(Routes.guildAutoModerationRules(guildId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Edits an auto moderation rule for a guild
+ *
+ * @param guildId - The id of the guild to edit the auto moderation rule from
+ * @param ruleId - The id of the auto moderation rule to edit
+ * @param data - The data for editing the auto moderation rule
+ * @param reason - The reason for editing the auto moderation rule
+ */
+ public async editAutoModerationRule(
+ guildId: Snowflake,
+ ruleId: Snowflake,
+ data: RESTPatchAPIAutoModerationRuleJSONBody,
+ reason?: string,
+ ) {
+ return this.rest.patch(Routes.guildAutoModerationRule(guildId, ruleId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes an auto moderation rule for a guild
+ *
+ * @param guildId - The id of the guild to delete the auto moderation rule from
+ * @param ruleId - The id of the auto moderation rule to delete
+ * @param reason - The reason for deleting the auto moderation rule
+ */
+ public async deleteAutoModerationRule(guildId: Snowflake, ruleId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guildAutoModerationRule(guildId, ruleId), { reason });
+ }
+
+ /**
+ * Fetches a guild member
+ *
+ * @param guildId - The id of the guild
+ * @param userId - The id of the user
+ */
+ public async getMember(guildId: Snowflake, userId: Snowflake) {
+ return this.rest.get(Routes.guildMember(guildId, userId)) as Promise;
+ }
+
+ /**
+ * Searches for guild members
+ *
+ * @param guildId - The id of the guild to search in
+ * @param query - The query to search for
+ * @param limit - The maximum number of members to return
+ */
+ public async searchForMembers(guildId: Snowflake, options: RESTGetAPIGuildMembersSearchQuery) {
+ return this.rest.get(Routes.guildMembersSearch(guildId), {
+ query: makeURLSearchParams(options as unknown as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Edits a guild member
+ *
+ * @param guildId - The id of the guild
+ * @param userId - The id of the user
+ * @param data - The data to use when editing the guild member
+ * @param reason - The reason for editing this guild member
+ */
+ public async editMember(
+ guildId: Snowflake,
+ userId: Snowflake,
+ data: RESTPatchAPIGuildMemberJSONBody = {},
+ reason?: string,
+ ) {
+ return this.rest.patch(Routes.guildMember(guildId, userId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Adds a role to a guild member
+ *
+ * @param guildId - The id of the guild
+ * @param userId - The id of the user
+ * @param roleId - The id of the role
+ * @param reason - The reason for adding this role to the guild member
+ */
+ public async addRoleToMember(guildId: Snowflake, userId: Snowflake, roleId: Snowflake, reason?: string) {
+ await this.rest.put(Routes.guildMemberRole(guildId, userId, roleId), { reason });
+ }
+
+ /**
+ * Removes a role from a guild member
+ *
+ * @param guildId - The id of the guild
+ * @param userId - The id of the user
+ * @param roleId - The id of the role
+ * @param reason - The reason for removing this role from the guild member
+ */
+ public async removeRoleFromMember(guildId: Snowflake, userId: Snowflake, roleId: Snowflake, reason?: string) {
+ await this.rest.delete(Routes.guildMemberRole(guildId, userId, roleId), { reason });
+ }
+
+ /**
+ * Fetches a guild template
+ *
+ * @param templateCode - The code of the template
+ */
+ public async getTemplate(templateCode: string) {
+ return this.rest.get(Routes.template(templateCode)) as Promise;
+ }
+
+ /**
+ * Creates a new template
+ *
+ * @param templateCode - The code of the template
+ * @param data - The data to use when creating the template
+ */
+ public async createTemplate(templateCode: string, data: RESTPostAPITemplateCreateGuildJSONBody) {
+ return this.rest.post(Routes.template(templateCode), { body: data }) as Promise;
+ }
+}
diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts
new file mode 100644
index 000000000..a8282fd23
--- /dev/null
+++ b/packages/core/src/api/index.ts
@@ -0,0 +1,57 @@
+import type { REST } from '@discordjs/rest';
+import { ApplicationCommandsAPI } from './applicationCommands.js';
+import { ChannelsAPI } from './channel.js';
+import { GuildsAPI } from './guild.js';
+import { InteractionsAPI } from './interactions.js';
+import { InvitesAPI } from './invite.js';
+import { StickersAPI } from './sticker.js';
+import { ThreadsAPI } from './thread.js';
+import { UsersAPI } from './user.js';
+import { VoiceAPI } from './voice.js';
+import { WebhooksAPI } from './webhook.js';
+
+export * from './applicationCommands.js';
+export * from './channel.js';
+export * from './guild.js';
+export * from './interactions.js';
+export * from './invite.js';
+export * from './sticker.js';
+export * from './thread.js';
+export * from './user.js';
+export * from './voice.js';
+export * from './webhook.js';
+
+export class API {
+ public readonly applicationCommands: ApplicationCommandsAPI;
+
+ public readonly channels: ChannelsAPI;
+
+ public readonly guilds: GuildsAPI;
+
+ public readonly interactions: InteractionsAPI;
+
+ public readonly invites: InvitesAPI;
+
+ public readonly stickers: StickersAPI;
+
+ public readonly threads: ThreadsAPI;
+
+ public readonly users: UsersAPI;
+
+ public readonly voice: VoiceAPI;
+
+ public readonly webhooks: WebhooksAPI;
+
+ public constructor(public readonly rest: REST) {
+ this.applicationCommands = new ApplicationCommandsAPI(rest);
+ this.channels = new ChannelsAPI(rest);
+ this.guilds = new GuildsAPI(rest);
+ this.invites = new InvitesAPI(rest);
+ this.stickers = new StickersAPI(rest);
+ this.threads = new ThreadsAPI(rest);
+ this.users = new UsersAPI(rest);
+ this.voice = new VoiceAPI(rest);
+ this.webhooks = new WebhooksAPI(rest);
+ this.interactions = new InteractionsAPI(rest, this.webhooks);
+ }
+}
diff --git a/packages/core/src/api/interactions.ts b/packages/core/src/api/interactions.ts
new file mode 100644
index 000000000..5db65e471
--- /dev/null
+++ b/packages/core/src/api/interactions.ts
@@ -0,0 +1,181 @@
+import type { RawFile, REST } from '@discordjs/rest';
+import type { Snowflake } from 'discord-api-types/v10';
+import {
+ InteractionResponseType,
+ Routes,
+ type APICommandAutocompleteInteractionResponseCallbackData,
+ type APIInteractionResponseCallbackData,
+ type APIModalInteractionResponseCallbackData,
+ type RESTGetAPIWebhookWithTokenMessageResult,
+} from 'discord-api-types/v10';
+import type { WebhooksAPI } from './webhook.js';
+
+export class InteractionsAPI {
+ public constructor(private readonly rest: REST, private readonly webhooks: WebhooksAPI) {}
+
+ /**
+ * Replies to an interaction
+ *
+ * @param interactionId - The id of the interaction
+ * @param interactionToken - The token of the interaction
+ * @param data - The data to use when replying
+ */
+ public async reply(
+ interactionId: Snowflake,
+ interactionToken: string,
+ { files, ...data }: APIInteractionResponseCallbackData & { files?: RawFile[] },
+ ) {
+ await this.rest.post(Routes.interactionCallback(interactionId, interactionToken), {
+ files,
+ body: {
+ type: InteractionResponseType.ChannelMessageWithSource,
+ data,
+ },
+ });
+ }
+
+ /**
+ * Defers the reply to an interaction
+ *
+ * @param interactionId - The id of the interaction
+ * @param interactionToken - The token of the interaction
+ */
+ public async defer(interactionId: Snowflake, interactionToken: string) {
+ await this.rest.post(Routes.interactionCallback(interactionId, interactionToken), {
+ body: {
+ type: InteractionResponseType.DeferredChannelMessageWithSource,
+ },
+ });
+ }
+
+ /**
+ * Defers an update from a message component interaction
+ *
+ * @param interactionId - The id of the interaction
+ * @param interactionToken - The token of the interaction
+ */
+ public async deferMessageUpdate(interactionId: Snowflake, interactionToken: string) {
+ await this.rest.post(Routes.interactionCallback(interactionId, interactionToken), {
+ body: {
+ type: InteractionResponseType.DeferredMessageUpdate,
+ },
+ });
+ }
+
+ /**
+ * Reply to a deferred interaction
+ *
+ * @param applicationId - The application id of the interaction
+ * @param interactionToken - The token of the interaction
+ * @param data - The data to use when replying
+ */
+ public async followUp(
+ applicationId: Snowflake,
+ interactionToken: string,
+ data: APIInteractionResponseCallbackData & { files?: RawFile[] },
+ ) {
+ await this.webhooks.execute(applicationId, interactionToken, data);
+ }
+
+ /**
+ * Edits the initial reply to an interaction
+ *
+ * @param applicationId - The application id of the interaction
+ * @param interactionToken - The token of the interaction
+ * @param data - The data to use when editing the reply
+ * @param messageId - The id of the message to edit. If omitted, the original reply will be edited
+ */
+ public async editReply(
+ applicationId: Snowflake,
+ interactionToken: string,
+ data: APIInteractionResponseCallbackData & { files?: RawFile[] },
+ messageId?: string,
+ ) {
+ return this.webhooks.editMessage(applicationId, interactionToken, messageId ?? '@original', data);
+ }
+
+ /**
+ * Fetches the initial reply to an interaction
+ *
+ * @param applicationId - The application id of the interaction
+ * @param interactionToken - The token of the interaction
+ */
+ public async getOriginalReply(applicationId: Snowflake, interactionToken: string) {
+ return this.webhooks.getMessage(
+ applicationId,
+ interactionToken,
+ '@original',
+ ) as Promise;
+ }
+
+ /**
+ * Deletes the initial reply to an interaction
+ *
+ * @param applicationId - The application id of the interaction
+ * @param interactionToken - The token of the interaction
+ */
+ public async deleteReply(applicationId: Snowflake, interactionToken: string) {
+ await this.webhooks.deleteMessage(applicationId, interactionToken, '@original');
+ }
+
+ /**
+ * Updates the the message the component interaction was triggered on
+ *
+ * @param interactionId - The id of the interaction
+ * @param interactionToken - The token of the interaction
+ * @param data - The data to use when updating the interaction
+ */
+ public async updateMessage(
+ interactionId: Snowflake,
+ interactionToken: string,
+ { files, ...data }: APIInteractionResponseCallbackData & { files?: RawFile[] },
+ ) {
+ await this.rest.post(Routes.interactionCallback(interactionId, interactionToken), {
+ files,
+ body: {
+ type: InteractionResponseType.UpdateMessage,
+ data,
+ },
+ });
+ }
+
+ /**
+ * Sends an autocomplete response to an interaction
+ *
+ * @param interactionId - The id of the interaction
+ * @param interactionToken - The token of the interaction
+ * @param data - Data for the autocomplete response
+ */
+ public async createAutocompleteResponse(
+ interactionId: Snowflake,
+ interactionToken: string,
+ data: APICommandAutocompleteInteractionResponseCallbackData,
+ ) {
+ await this.rest.post(Routes.interactionCallback(interactionId, interactionToken), {
+ body: {
+ type: InteractionResponseType.ApplicationCommandAutocompleteResult,
+ data,
+ },
+ });
+ }
+
+ /**
+ * Sends a modal response to an interaction
+ *
+ * @param interactionId - The id of the interaction
+ * @param interactionToken - The token of the interaction
+ * @param data - The modal to send
+ */
+ public async createModal(
+ interactionId: Snowflake,
+ interactionToken: string,
+ data: APIModalInteractionResponseCallbackData,
+ ) {
+ await this.rest.post(Routes.interactionCallback(interactionId, interactionToken), {
+ body: {
+ type: InteractionResponseType.Modal,
+ data,
+ },
+ });
+ }
+}
diff --git a/packages/core/src/api/invite.ts b/packages/core/src/api/invite.ts
new file mode 100644
index 000000000..44da91506
--- /dev/null
+++ b/packages/core/src/api/invite.ts
@@ -0,0 +1,27 @@
+import { makeURLSearchParams, type REST } from '@discordjs/rest';
+import { Routes, type RESTGetAPIInviteQuery, type RESTGetAPIInviteResult } from 'discord-api-types/v10';
+
+export class InvitesAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Fetches an invite
+ *
+ * @param code - The invite code
+ */
+ public async get(code: string, options: RESTGetAPIInviteQuery = {}) {
+ return this.rest.get(Routes.invite(code), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Deletes an invite
+ *
+ * @param code - The invite code
+ * @param reason - The reason for deleting the invite
+ */
+ public async delete(code: string, reason?: string) {
+ await this.rest.delete(Routes.invite(code), { reason });
+ }
+}
diff --git a/packages/core/src/api/sticker.ts b/packages/core/src/api/sticker.ts
new file mode 100644
index 000000000..7a01dc9b0
--- /dev/null
+++ b/packages/core/src/api/sticker.ts
@@ -0,0 +1,27 @@
+import type { REST } from '@discordjs/rest';
+import {
+ Routes,
+ type RESTGetAPIStickerResult,
+ type RESTGetNitroStickerPacksResult,
+ type Snowflake,
+} from 'discord-api-types/v10';
+
+export class StickersAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Fetches all of the nitro sticker packs
+ */
+ public async getNitroStickers() {
+ return this.rest.get(Routes.nitroStickerPacks()) as Promise;
+ }
+
+ /**
+ * Fetches a sticker
+ *
+ * @param stickerId - The id of the sticker
+ */
+ public async get(stickerId: Snowflake) {
+ return this.rest.get(Routes.sticker(stickerId)) as Promise;
+ }
+}
diff --git a/packages/core/src/api/thread.ts b/packages/core/src/api/thread.ts
new file mode 100644
index 000000000..94f08f1a5
--- /dev/null
+++ b/packages/core/src/api/thread.ts
@@ -0,0 +1,117 @@
+import type { RawFile, REST } from '@discordjs/rest';
+import {
+ Routes,
+ type APIThreadChannel,
+ type APIThreadMember,
+ type RESTGetAPIChannelThreadMembersResult,
+ type RESTPostAPIChannelThreadsJSONBody,
+ type RESTPostAPIChannelThreadsResult,
+ type RESTPostAPIGuildForumThreadsJSONBody,
+ type Snowflake,
+} from 'discord-api-types/v10';
+
+export interface StartThreadOptions extends RESTPostAPIChannelThreadsJSONBody {
+ message_id?: string;
+}
+
+export interface StartForumThreadOptions extends RESTPostAPIGuildForumThreadsJSONBody {
+ message: RESTPostAPIGuildForumThreadsJSONBody['message'] & { files?: RawFile[] };
+}
+
+export class ThreadsAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Fetches a thread
+ *
+ * @param channelId - The id of the channel to fetch the thread from
+ * @param threadId - The id of the thread
+ */
+ public async get(channelId: Snowflake, threadId: Snowflake) {
+ return this.rest.get(Routes.threads(channelId, threadId)) as Promise;
+ }
+
+ /**
+ * Creates a new thread
+ *
+ * @param channelId - The id of the channel to start the thread in
+ * @param data - The data to use when starting the thread
+ */
+ public async create(channelId: Snowflake, { message_id, ...body }: StartThreadOptions) {
+ return this.rest.post(Routes.threads(channelId, message_id), { body }) as Promise;
+ }
+
+ /**
+ * Creates a new forum post
+ *
+ * @param channelId - The id of the forum channel to start the thread in
+ * @param data - The data to use when starting the thread
+ */
+ public async createForumThread(channelId: Snowflake, { message, ...optionsBody }: StartForumThreadOptions) {
+ const { files, ...messageBody } = message;
+
+ const body = {
+ ...optionsBody,
+ message: messageBody,
+ };
+
+ return this.rest.post(Routes.threads(channelId), { files, body }) as Promise;
+ }
+
+ /**
+ * Adds the current user to a thread
+ *
+ * @param threadId - The id of the thread to join
+ */
+ public async join(threadId: Snowflake) {
+ await this.rest.put(Routes.threadMembers(threadId, '@me'));
+ }
+
+ /**
+ * Adds a member to a thread
+ *
+ * @param threadId - The id of the thread to add the member to
+ * @param userId - The id of the user to add to the thread
+ */
+ public async addMember(threadId: Snowflake, userId: Snowflake) {
+ await this.rest.put(Routes.threadMembers(threadId, userId));
+ }
+
+ /**
+ * Removes the current user from a thread
+ *
+ * @param threadId - The id of the thread to leave
+ */
+ public async leave(threadId: Snowflake) {
+ await this.rest.delete(Routes.threadMembers(threadId, '@me'));
+ }
+
+ /**
+ * Removes a member from a thread
+ *
+ * @param threadId - The id of the thread to remove the member from
+ * @param userId - The id of the user to remove from the thread
+ */
+ public async removeMember(threadId: Snowflake, userId: Snowflake) {
+ await this.rest.delete(Routes.threadMembers(threadId, userId));
+ }
+
+ /**
+ * Fetches a member of a thread
+ *
+ * @param threadId - The id of the thread to fetch the member from
+ * @param userId - The id of the user
+ */
+ public async getMember(threadId: Snowflake, userId: Snowflake) {
+ return this.rest.get(Routes.threadMembers(threadId, userId)) as Promise;
+ }
+
+ /**
+ * Fetches all members of a thread
+ *
+ * @param threadId - The id of the thread to fetch the members from
+ */
+ public async getAllMembers(threadId: Snowflake) {
+ return this.rest.get(Routes.threadMembers(threadId)) as Promise;
+ }
+}
diff --git a/packages/core/src/api/user.ts b/packages/core/src/api/user.ts
new file mode 100644
index 000000000..5f03bb1b4
--- /dev/null
+++ b/packages/core/src/api/user.ts
@@ -0,0 +1,112 @@
+import { makeURLSearchParams, type REST } from '@discordjs/rest';
+import {
+ Routes,
+ type RESTGetAPICurrentUserGuildsQuery,
+ type RESTGetAPICurrentUserGuildsResult,
+ type RESTGetAPICurrentUserResult,
+ type RESTGetAPIUserResult,
+ type RESTGetCurrentUserGuildMemberResult,
+ type RESTPatchAPICurrentUserJSONBody,
+ type RESTPatchAPICurrentUserResult,
+ type RESTPatchAPIGuildMemberJSONBody,
+ type RESTPatchAPIGuildMemberResult,
+ type RESTPatchAPIGuildVoiceStateCurrentMemberJSONBody,
+ type RESTPatchAPIGuildVoiceStateCurrentMemberResult,
+ type RESTPostAPICurrentUserCreateDMChannelResult,
+ type Snowflake,
+} from 'discord-api-types/v10';
+
+export class UsersAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Fetches a user by their id
+ *
+ * @param userId - The id of the user to fetch
+ */
+ public async get(userId: Snowflake) {
+ return this.rest.get(Routes.user(userId)) as Promise;
+ }
+
+ /**
+ * Returns the user object of the requester's account
+ */
+ public async getCurrent() {
+ return this.rest.get(Routes.user('@me')) as Promise;
+ }
+
+ /**
+ * Returns a list of partial guild objects the current user is a member of
+ *
+ * @param options - The options to use when fetching the current user's guilds
+ */
+ public async getGuilds(options: RESTGetAPICurrentUserGuildsQuery = {}) {
+ return this.rest.get(Routes.userGuilds(), {
+ query: makeURLSearchParams(options as Record),
+ }) as Promise;
+ }
+
+ /**
+ * Leaves the guild with the given id
+ *
+ * @param guildId - The id of the guild
+ */
+ public async leaveGuild(guildId: Snowflake) {
+ await this.rest.delete(Routes.userGuild(guildId));
+ }
+
+ /**
+ * Edits the current user
+ *
+ * @param user - The new data for the current user
+ */
+ public async edit(user: RESTPatchAPICurrentUserJSONBody) {
+ return this.rest.patch(Routes.user('@me'), { body: user }) as Promise;
+ }
+
+ /**
+ * Fetches the guild member for the current user
+ *
+ * @param guildId - The id of the guild
+ */
+ public async getGuildMember(guildId: Snowflake) {
+ return this.rest.get(Routes.userGuildMember(guildId)) as Promise;
+ }
+
+ /**
+ * Edits the guild member for the current user
+ *
+ * @param guildId - The id of the guild
+ * @param member - The new data for the guild member
+ * @param reason - The reason for editing this guild member
+ */
+ public async editGuildMember(guildId: Snowflake, member: RESTPatchAPIGuildMemberJSONBody = {}, reason?: string) {
+ return this.rest.patch(Routes.guildMember(guildId, '@me'), {
+ reason,
+ body: member,
+ }) as Promise;
+ }
+
+ /**
+ * Sets the voice state for the current user
+ *
+ * @param guildId - The id of the guild
+ * @param options - The options to use when setting the voice state
+ */
+ public async setVoiceState(guildId: Snowflake, options: RESTPatchAPIGuildVoiceStateCurrentMemberJSONBody = {}) {
+ return this.rest.patch(Routes.guildVoiceState(guildId, '@me'), {
+ body: options,
+ }) as Promise;
+ }
+
+ /**
+ * Opens a new DM channel with a user
+ *
+ * @param userId - The id of the user to open a DM channel with
+ */
+ public async createDM(userId: Snowflake) {
+ return this.rest.post(Routes.userChannels(), {
+ body: { recipient_id: userId },
+ }) as Promise;
+ }
+}
diff --git a/packages/core/src/api/voice.ts b/packages/core/src/api/voice.ts
new file mode 100644
index 000000000..e04441822
--- /dev/null
+++ b/packages/core/src/api/voice.ts
@@ -0,0 +1,13 @@
+import type { REST } from '@discordjs/rest';
+import { Routes, type GetAPIVoiceRegionsResult } from 'discord-api-types/v10';
+
+export class VoiceAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Fetches all voice regions
+ */
+ public async getVoiceRegions() {
+ return this.rest.get(Routes.voiceRegions()) as Promise;
+ }
+}
diff --git a/packages/core/src/api/webhook.ts b/packages/core/src/api/webhook.ts
new file mode 100644
index 000000000..b4da4997d
--- /dev/null
+++ b/packages/core/src/api/webhook.ts
@@ -0,0 +1,214 @@
+import { makeURLSearchParams, type RawFile, type REST } from '@discordjs/rest';
+import {
+ Routes,
+ type RESTGetAPIChannelMessageResult,
+ type RESTGetAPIWebhookResult,
+ type RESTPatchAPIWebhookJSONBody,
+ type RESTPatchAPIWebhookResult,
+ type RESTPatchAPIWebhookWithTokenMessageJSONBody,
+ type RESTPatchAPIWebhookWithTokenMessageResult,
+ type RESTPostAPIChannelWebhookJSONBody,
+ type RESTPostAPIWebhookWithTokenGitHubQuery,
+ type RESTPostAPIWebhookWithTokenJSONBody,
+ type RESTPostAPIWebhookWithTokenQuery,
+ type RESTPostAPIWebhookWithTokenResult,
+ type RESTPostAPIWebhookWithTokenSlackQuery,
+ type RESTPostAPIWebhookWithTokenWaitResult,
+ type Snowflake,
+} from 'discord-api-types/v10';
+
+export class WebhooksAPI {
+ public constructor(private readonly rest: REST) {}
+
+ /**
+ * Fetches a webhook
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ */
+ public async get(id: Snowflake, token?: string) {
+ return this.rest.get(Routes.webhook(id, token)) as Promise;
+ }
+
+ /**
+ * Creates a new webhook
+ *
+ * @param channelId - The id of the channel to create the webhook in
+ * @param data - The data to use when creating the webhook
+ * @param reason - The reason for creating the webhook
+ */
+ public async create(channelId: Snowflake, data: RESTPostAPIChannelWebhookJSONBody, reason?: string) {
+ return this.rest.post(Routes.channelWebhooks(channelId), {
+ reason,
+ body: data,
+ }) as Promise;
+ }
+
+ /**
+ * Edits a webhook
+ *
+ * @param id - The id of the webhook to edit
+ * @param webhook - The new webhook data
+ * @param options - The options to use when editing the webhook
+ */
+ public async edit(
+ id: Snowflake,
+ webhook: RESTPatchAPIWebhookJSONBody,
+ { token, reason }: { reason?: string; token?: string } = {},
+ ) {
+ return this.rest.patch(Routes.webhook(id, token), { reason, body: webhook }) as Promise;
+ }
+
+ /**
+ * Deletes a webhook
+ *
+ * @param id - The id of the webhook to delete
+ * @param options - The options to use when deleting the webhook
+ */
+ public async delete(id: Snowflake, { token, reason }: { reason?: string; token?: string } = {}) {
+ await this.rest.delete(Routes.webhook(id, token), { reason });
+ }
+
+ /**
+ * Executes a webhook and returns the created message
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ * @param data - The data to use when executing the webhook
+ */
+ public async execute(
+ id: Snowflake,
+ token: string,
+ data: RESTPostAPIWebhookWithTokenJSONBody & RESTPostAPIWebhookWithTokenQuery & { files?: RawFile[]; wait: true },
+ ): Promise;
+
+ /**
+ * Executes a webhook
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ * @param data - The data to use when executing the webhook
+ */
+ public async execute(
+ id: Snowflake,
+ token: string,
+ data: RESTPostAPIWebhookWithTokenJSONBody & RESTPostAPIWebhookWithTokenQuery & { files?: RawFile[]; wait?: false },
+ ): Promise;
+
+ /**
+ * Executes a webhook
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ * @param data - The data to use when executing the webhook
+ */
+ public async execute(
+ id: Snowflake,
+ token: string,
+ {
+ wait,
+ thread_id,
+ files,
+ ...body
+ }: RESTPostAPIWebhookWithTokenJSONBody & RESTPostAPIWebhookWithTokenQuery & { files?: RawFile[] },
+ ) {
+ return this.rest.post(Routes.webhook(id, token), {
+ query: makeURLSearchParams({ wait, thread_id }),
+ files,
+ body,
+ auth: false,
+ // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
+ }) as Promise;
+ }
+
+ /**
+ * Executes a slack webhook
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ * @param options - The options to use when executing the webhook
+ */
+ public async executeSlack(
+ id: Snowflake,
+ token: string,
+ body: unknown,
+ options: RESTPostAPIWebhookWithTokenSlackQuery = {},
+ ) {
+ await this.rest.post(Routes.webhookPlatform(id, token, 'slack'), {
+ query: makeURLSearchParams(options as Record),
+ body,
+ auth: false,
+ });
+ }
+
+ /**
+ * Executes a github webhook
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ * @param options - The options to use when executing the webhook
+ */
+ public async executeGitHub(
+ id: Snowflake,
+ token: string,
+ body: unknown,
+ options: RESTPostAPIWebhookWithTokenGitHubQuery = {},
+ ) {
+ await this.rest.post(Routes.webhookPlatform(id, token, 'github'), {
+ query: makeURLSearchParams(options as Record),
+ body,
+ auth: false,
+ });
+ }
+
+ /**
+ * Fetches an associated message from a webhook
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ * @param messageId - The id of the message to fetch
+ * @param options - The options to use when fetching the message
+ */
+ public async getMessage(id: Snowflake, token: string, messageId: Snowflake, options: { thread_id?: string } = {}) {
+ return this.rest.get(Routes.webhookMessage(id, token, messageId), {
+ query: makeURLSearchParams(options as Record),
+ auth: false,
+ }) as Promise;
+ }
+
+ /**
+ * Edits an associated message from a webhook
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ * @param messageId - The id of the message to edit
+ * @param data - The data to use when editing the message
+ */
+ public async editMessage(
+ id: Snowflake,
+ token: string,
+ messageId: Snowflake,
+ { thread_id, ...body }: RESTPatchAPIWebhookWithTokenMessageJSONBody & { thread_id?: string },
+ ) {
+ return this.rest.patch(Routes.webhookMessage(id, token, messageId), {
+ query: makeURLSearchParams({ thread_id }),
+ auth: false,
+ body,
+ }) as Promise;
+ }
+
+ /**
+ * Deletes an associated message from a webhook
+ *
+ * @param id - The id of the webhook
+ * @param token - The token of the webhook
+ * @param messageId - The id of the message to delete
+ * @param options - The options to use when deleting the message
+ */
+ public async deleteMessage(id: Snowflake, token: string, messageId: Snowflake, options: { thread_id?: string } = {}) {
+ await this.rest.delete(Routes.webhookMessage(id, token, messageId), {
+ query: makeURLSearchParams(options as Record),
+ auth: false,
+ });
+ }
+}
diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts
new file mode 100644
index 000000000..04652a730
--- /dev/null
+++ b/packages/core/src/client.ts
@@ -0,0 +1,179 @@
+import type { REST } from '@discordjs/rest';
+import { WebSocketShardEvents, type WebSocketManager } from '@discordjs/ws';
+import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
+import type {
+ GatewayAutoModerationActionExecutionDispatchData,
+ GatewayAutoModerationRuleCreateDispatchData,
+ GatewayAutoModerationRuleDeleteDispatchData,
+ GatewayAutoModerationRuleUpdateDispatchData,
+ GatewayChannelCreateDispatchData,
+ GatewayChannelDeleteDispatchData,
+ GatewayChannelPinsUpdateDispatchData,
+ GatewayChannelUpdateDispatchData,
+ GatewayDispatchEvents,
+ GatewayGuildBanAddDispatchData,
+ GatewayGuildBanRemoveDispatchData,
+ GatewayGuildCreateDispatchData,
+ GatewayGuildDeleteDispatchData,
+ GatewayGuildEmojisUpdateDispatchData,
+ GatewayGuildIntegrationsUpdateDispatchData,
+ GatewayGuildMemberAddDispatchData,
+ GatewayGuildMemberRemoveDispatchData,
+ GatewayGuildMembersChunkDispatchData,
+ GatewayGuildMemberUpdateDispatchData,
+ GatewayGuildRoleCreateDispatchData,
+ GatewayGuildRoleDeleteDispatchData,
+ GatewayGuildRoleUpdateDispatchData,
+ GatewayGuildScheduledEventCreateDispatchData,
+ GatewayGuildScheduledEventDeleteDispatchData,
+ GatewayGuildScheduledEventUpdateDispatchData,
+ GatewayGuildScheduledEventUserAddDispatchData,
+ GatewayGuildScheduledEventUserRemoveDispatchData,
+ GatewayGuildStickersUpdateDispatchData,
+ GatewayGuildUpdateDispatchData,
+ GatewayIntegrationCreateDispatchData,
+ GatewayIntegrationDeleteDispatchData,
+ GatewayIntegrationUpdateDispatchData,
+ GatewayInteractionCreateDispatchData,
+ GatewayInviteCreateDispatchData,
+ GatewayInviteDeleteDispatchData,
+ GatewayMessageCreateDispatchData,
+ GatewayMessageDeleteBulkDispatchData,
+ GatewayMessageDeleteDispatchData,
+ GatewayMessageReactionAddDispatchData,
+ GatewayMessageReactionRemoveAllDispatchData,
+ GatewayMessageReactionRemoveDispatchData,
+ GatewayMessageReactionRemoveEmojiDispatchData,
+ GatewayMessageUpdateDispatchData,
+ GatewayPresenceUpdateDispatchData,
+ GatewayReadyDispatchData,
+ GatewayStageInstanceCreateDispatchData,
+ GatewayStageInstanceDeleteDispatchData,
+ GatewayStageInstanceUpdateDispatchData,
+ GatewayThreadCreateDispatchData,
+ GatewayThreadDeleteDispatchData,
+ GatewayThreadListSyncDispatchData,
+ GatewayThreadMembersUpdateDispatchData,
+ GatewayThreadMemberUpdateDispatchData,
+ GatewayThreadUpdateDispatchData,
+ GatewayTypingStartDispatchData,
+ GatewayUserUpdateDispatchData,
+ GatewayVoiceServerUpdateDispatchData,
+ GatewayVoiceStateUpdateDispatchData,
+ GatewayWebhooksUpdateDispatchData,
+} from 'discord-api-types/v10';
+import { API } from './api/index.js';
+
+export interface IntrinsicProps {
+ /**
+ * The REST API
+ */
+ api: API;
+ /**
+ * The id of the shard that emitted the event
+ */
+ shardId: number;
+}
+
+export interface WithIntrinsicProps extends IntrinsicProps {
+ data: T;
+}
+
+export interface MappedEvents {
+ [GatewayDispatchEvents.ChannelCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ChannelDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ChannelPinsUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ChannelUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildBanAdd]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildBanRemove]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildEmojisUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildIntegrationsUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildMemberAdd]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildMemberRemove]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildMemberUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildMembersChunk]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildRoleCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildRoleDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildRoleUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildScheduledEventCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildScheduledEventDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildScheduledEventUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildScheduledEventUserAdd]: [
+ WithIntrinsicProps,
+ ];
+ [GatewayDispatchEvents.GuildScheduledEventUserRemove]: [
+ WithIntrinsicProps,
+ ];
+ [GatewayDispatchEvents.GuildStickersUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.GuildUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.IntegrationCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.IntegrationDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.IntegrationUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.InteractionCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.InviteCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.InviteDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.MessageCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.MessageDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.MessageDeleteBulk]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.MessageReactionAdd]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.MessageReactionRemove]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.MessageReactionRemoveAll]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.MessageReactionRemoveEmoji]: [
+ WithIntrinsicProps,
+ ];
+ [GatewayDispatchEvents.MessageUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.PresenceUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.Ready]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.StageInstanceCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.StageInstanceDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.StageInstanceUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ThreadCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ThreadDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ThreadListSync]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ThreadMemberUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ThreadMembersUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.ThreadUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.UserUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.VoiceServerUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.VoiceStateUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.WebhooksUpdate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.Resumed]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.TypingStart]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.AutoModerationActionExecution]: [
+ WithIntrinsicProps,
+ ];
+ [GatewayDispatchEvents.AutoModerationRuleCreate]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.AutoModerationRuleDelete]: [WithIntrinsicProps];
+ [GatewayDispatchEvents.AutoModerationRuleUpdate]: [WithIntrinsicProps];
+}
+
+export type ManagerShardEventsMap = {
+ [K in keyof MappedEvents]: MappedEvents[K];
+};
+
+export interface ClientOptions {
+ rest: REST;
+ ws: WebSocketManager;
+}
+
+export function createClient({ rest, ws }: ClientOptions) {
+ const api = new API(rest);
+ const emitter = new AsyncEventEmitter();
+
+ function wrapIntrinsicProps(obj: T, shardId: number): WithIntrinsicProps {
+ return {
+ api,
+ shardId,
+ data: obj,
+ };
+ }
+
+ ws.on(WebSocketShardEvents.Dispatch, ({ data: dispatch, shardId }) => {
+ // @ts-expect-error event props can't be resolved properly, but they are correct
+ emitter.emit(dispatch.t, wrapIntrinsicProps(dispatch.d, shardId));
+ });
+
+ return emitter;
+}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
new file mode 100644
index 000000000..86f5c5247
--- /dev/null
+++ b/packages/core/src/index.ts
@@ -0,0 +1,5 @@
+export * from './api/index.js';
+export * from './client.js';
+export * from './util/index.js';
+
+export * from 'discord-api-types/v10';
diff --git a/packages/core/src/util/files.ts b/packages/core/src/util/files.ts
new file mode 100644
index 000000000..7f22fb074
--- /dev/null
+++ b/packages/core/src/util/files.ts
@@ -0,0 +1,29 @@
+import type { RawFile } from '@discordjs/rest';
+import type { APIInteractionResponseCallbackData } from 'discord-api-types/v10';
+
+export interface DescriptiveRawFile extends RawFile {
+ description?: string;
+}
+
+/**
+ * A utility function to create a form data payload given an array of file buffers
+ *
+ * @param files - The files to create a form data payload for
+ * @param options - The additional options for the form data payload
+ */
+export function withFiles(files: DescriptiveRawFile[], options: APIInteractionResponseCallbackData) {
+ const body = {
+ ...options,
+ attachments: files.map((file, index) => ({
+ id: index.toString(),
+ description: file.description,
+ })),
+ };
+
+ const outputFiles = files.map((file, index) => ({
+ name: file.name ?? index.toString(),
+ data: file.data,
+ }));
+
+ return { body, files: outputFiles };
+}
diff --git a/packages/core/src/util/index.ts b/packages/core/src/util/index.ts
new file mode 100644
index 000000000..f2891a7c9
--- /dev/null
+++ b/packages/core/src/util/index.ts
@@ -0,0 +1 @@
+export * from './files.js';
diff --git a/packages/core/tsconfig.eslint.json b/packages/core/tsconfig.eslint.json
new file mode 100644
index 000000000..d04d4be3a
--- /dev/null
+++ b/packages/core/tsconfig.eslint.json
@@ -0,0 +1,20 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true
+ },
+ "include": [
+ "**/*.ts",
+ "**/*.tsx",
+ "**/*.js",
+ "**/*.mjs",
+ "**/*.jsx",
+ "**/*.test.ts",
+ "**/*.test.js",
+ "**/*.test.mjs",
+ "**/*.spec.ts",
+ "**/*.spec.js",
+ "**/*.spec.mjs"
+ ],
+ "exclude": []
+}
diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json
new file mode 100644
index 000000000..fd8b5e417
--- /dev/null
+++ b/packages/core/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../../tsconfig.json",
+ "include": ["src/**/*.ts"]
+}
diff --git a/packages/core/tsup.config.js b/packages/core/tsup.config.js
new file mode 100644
index 000000000..3d4480d6d
--- /dev/null
+++ b/packages/core/tsup.config.js
@@ -0,0 +1,3 @@
+import { createTsupConfig } from '../../tsup.config.js';
+
+export default createTsupConfig({});
diff --git a/packages/rest/src/lib/RequestManager.ts b/packages/rest/src/lib/RequestManager.ts
index 4fa8dab22..18d03ae3d 100644
--- a/packages/rest/src/lib/RequestManager.ts
+++ b/packages/rest/src/lib/RequestManager.ts
@@ -88,7 +88,7 @@ export interface RequestData {
/**
* Reason to show in the audit logs
*/
- reason?: string;
+ reason?: string | undefined;
/**
* The signal to abort the queue entry or the REST call, where applicable
*/
diff --git a/yarn.lock b/yarn.lock
index 244c4d197..e795e3707 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2089,6 +2089,29 @@ __metadata:
languageName: unknown
linkType: soft
+"@discordjs/core@workspace:packages/core":
+ version: 0.0.0-use.local
+ resolution: "@discordjs/core@workspace:packages/core"
+ dependencies:
+ "@discordjs/rest": "workspace:^"
+ "@discordjs/ws": "workspace:^"
+ "@favware/cliff-jumper": ^1.9.0
+ "@microsoft/api-extractor": ^7.33.6
+ "@types/node": 16.18.3
+ "@vitest/coverage-c8": ^0.25.3
+ "@vladfrangu/async_event_emitter": ^2.1.2
+ cross-env: ^7.0.3
+ discord-api-types: ^0.37.20
+ eslint: ^8.28.0
+ eslint-config-neon: ^0.1.40
+ eslint-formatter-pretty: ^4.1.0
+ prettier: ^2.8.0
+ tsup: ^6.5.0
+ typescript: ^4.9.3
+ vitest: ^0.25.3
+ languageName: unknown
+ linkType: soft
+
"@discordjs/discord.js@workspace:.":
version: 0.0.0-use.local
resolution: "@discordjs/discord.js@workspace:."
@@ -2420,7 +2443,7 @@ __metadata:
languageName: unknown
linkType: soft
-"@discordjs/ws@workspace:packages/ws":
+"@discordjs/ws@workspace:^, @discordjs/ws@workspace:packages/ws":
version: 0.0.0-use.local
resolution: "@discordjs/ws@workspace:packages/ws"
dependencies: