Compare commits

...

69 Commits

Author SHA1 Message Date
Jiralite
8065b80cea chore: update 11135 name 2025-10-08 18:15:00 +01:00
Vlad Frangu
3b26680672 chore(discord.js): release discord.js@14.23.0 2025-10-08 20:14:05 +03:00
Jiralite
c4dbd7ee9f chore(core): release @discordjs/core@2.3.0 2025-10-08 18:09:11 +01:00
Almeida
72771b79aa feat: add {add,remove}GroupDMRecipient methods (#11135)
* feat: add `{add,delete}GroupDMRecipient methods`

* fix: requested changes

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-10-08 16:03:50 +01:00
Jiralite
63dbe48055 feat(guild): Support incident actions (#11131)
* feat(guild): add incident actions

* fix: add result

---------

Co-Authored-By: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-10-08 16:02:03 +01:00
Vlad Frangu
67c8953a10 feat: bump builders in v14 (and fix runtime crashes) (#11153)
* feat: bump builders in v14 (and fix runtime crashes)

* chore: bump dtypes

* Update packages/discord.js/src/structures/LabelBuilder.js

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* chore: requested changes

* chore: lint

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-10-08 14:47:39 +01:00
Pavel-Boyazov
30e35d909e types(ClientEventTypes): fix messageDeleteBulk event arg (#11122)
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-10-07 17:44:32 +01:00
Pavel-Boyazov
6a5707c786 types(Webhook): specify message type (#11142)
* types(Webhook): specify message type

* test(Webhook): update types
2025-10-07 17:40:05 +01:00
Jiralite
9b821e5dfc feat(GuildMemberManager): Add new modify self fields (#11112)
* feat(GuildMemberManager): Add new modify self fields (#11089)

* fix: use correct route

* fix: add deprecation

* fix: rewrite message
2025-10-06 08:36:28 +01:00
Jiralite
a04172325a feat: Add gateway endpoints (#11130)
feat: add gateway
Co-Authored-By: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-10-04 17:51:37 +01:00
Almeida
154c00ded9 fix(ThreadMemberFlagsBitField): use ThreadMemberFlags enum in Flags (#11118)
feat(ThreadMemberFlagsBitField): use `ThreadMemberFlags` enum in `Flags`
2025-10-02 21:59:27 +01:00
Almeida
3b927449ae docs: use LocalizationMap where applicable (#11117) 2025-10-02 21:58:28 +01:00
Naiyar
fcce0d95bb fix: backport in operator fix from main (#11127)
fix: use in operator when resolving modal component (#11115)
2025-10-01 15:54:03 +01:00
Naiyar
93e0f4cd10 feat: text display and more selects in modal for v14 (#11096)
* feat: handle recieve label components

* chore: missed fixes

* fix: missing id when transforming

* chore: add missing things

* fix: test

* feat: send label

* fix: un-break it

* chore: test

* feat: more selects in modals

* chore: make resolved read-only

* chore: import order

* chore: add missing cached generic

* style: spacing

* docs: consistency

* docs: make it a type

* docs: Add `APISelectMenuDefaultValue`

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-09-21 22:28:33 +01:00
Naiyar
abaae4ff16 feat: label component and select in modal for v14 (#11090)
* feat: handle recieve label components

* chore: missed fixes

* fix: missing id when transforming

* chore: add missing things

* fix: test

* feat: send label

* fix: un-break it

* chore: test

* chore: missing required in typings
2025-09-15 21:28:07 +01:00
Jiralite
270d9f1047 chore(core): release @discordjs/core@2.2.2 2025-09-10 18:49:28 +01:00
Jiralite
9ae737708b fix(users): Correct type for editing current guild member (#11098)
* fix(users): `RESTPatchAPICurrentGuildMemberJSONBody`

* fix: imports
2025-09-10 18:45:05 +01:00
Jiralite
e382d60421 build: upgrade discord-api-types to 0.38.24 2025-09-10 18:14:11 +01:00
Jiralite
68aa202cd6 build: upgrade discord-api-types to 0.38.23 2025-09-10 11:34:34 +01:00
VAKiliner
d8ad181c19 fix: Ensure discriminator detection respects webhooks too (#11062)
* Replace discriminator === '0' to Number(discriminator)

* Fix

* Replacing !Number() to ['0', '0000'].includes

* chore: fmt

* perf: no array

---------

Co-authored-by: almeidx <github@almeidx.dev>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-09-05 13:46:27 +01:00
Jiralite
0dff969e16 refactor(ActionsManager): Register actions without using class name (#11080)
* refactor: register actions without using class name

* fix: this is `module.exports`
2025-09-02 10:28:15 +01:00
Jiralite
79d999e4c1 feat: Guest invites (#11079)
feat(Invite): add `flags`
2025-09-02 09:21:11 +01:00
Amgelo563
215f8dc5e0 fix: Do not omit falsy default values (#10755)
* fix(docs): fix default falsy values being omitted

* fix(docs): swap defaultValue check to avoid negated condition

* fix: fix pr by removing everything it added and committing something entirely different

---------

Co-authored-by: almeidx <github@almeidx.dev>
2025-09-02 01:11:24 +01:00
Jiralite
b6089e585e build: upgrade discord-api-types to 0.38.22 2025-09-02 00:53:31 +01:00
Jiralite
fe025c0a9f docs(GuildEditOptions): deprecate owner property 2025-08-29 11:01:16 +01:00
Almeida
4a8aeb6aee feat: polls overhaul (#11058)
* feat: polls overhaul (#10328)

* feat(Managers): add PollAnswerVoterManager

* feat(Partials): make Polls partial-safe

* types: add typings

* chore: add tests

* fix: use fetch method in manager instead

* chore: add tests for manager

* feat: add partial support to poll actions

* style: formatting

* fix: change all .users references to .voters

* refactor: add additional logic for partials

* fix: actually add the partials

* fix: fixed issue where event does not emit on first event

* fix: align property type with DAPI documentation

* fix: resolve additional bugs with partials

* typings: update typings to reflect property type change

* fix: tests

* fix: adjust tests

* refactor: combine partials logic into one statement

* docs: mark getter as readonly

* refactor: apply suggestions

Co-authored-by: Almeida <github@almeidx.dev>

* refactor(Actions): apply suggestions

* refactor(PollAnswerVoterManager): apply suggestions

* refactor(Message): check for existing poll before creating a poll

* refactor(Polls): apply suggestions

* revert(types): remove unused method from Poll class

* refactor(Actions): consolidate poll creation logic into action class

* refactor(PollAnswerVoterManager): set default for fetch parameter

* refactor(Message): apply suggestion

* fix: remove partial setter

* refactor(Polls): apply suggestions

* types: apply suggestions

* refactor: remove clones

* docs: spacing

* refactor: move setters from constructor to _patch

* types: adjust partials for poll classes

* test: add more tests for polls

* refactor: move updates around, more correct partial types

* fix: handle more cases

* refactor: requested changes

* fix: missing imports

* fix: update imports

* fix: require file extensions

---------

Co-authored-by: Almeida <github@almeidx.dev>
Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>

* fix(Poll): ensure `this.answers` is set before we reference it (#10809)

* Ensure 	his.answers is set sooner if it's null during a patch

* Move data.answers block up as well to ensure the patched answers are set

* Ensure collection is set in constructor instead

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* fix(PollAnswer): only define _emoji property once (#10811)

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

---------

Co-authored-by: Kevin <uhkevinmc@eedo.app>
Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
Co-authored-by: Jacob Morrison <jake.morrison24@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-08-29 10:58:04 +01:00
Jiralite
3dd57c2eaf docs: Deprecate API related to guild ownership (#11054)
* docs: deprecate API usage for guild ownership

* docs: remove pointless example
2025-08-29 10:53:34 +01:00
Jiralite
740da4ce5e docs: deprecate setting owner 2025-08-29 10:51:13 +01:00
Jiralite
6fb0b1cef6 docs(guild): deprecate API related to guild ownership 2025-08-22 12:48:29 +01:00
Jiralite
ac6ff15b7d fix(guild): Creating a template actually creates a template (#11030)
feat(guild): add creating a template
2025-08-22 12:41:53 +01:00
Jiralite
a294b47db0 chore(discord.js): release discord.js@14.22.1 2025-08-22 12:05:56 +01:00
Souji
ecef7bdf22 fix(GuildChannel): account for everyone base permissions (#11053)
When calculating permissions after overwrites, the base permission of the at-everyone role need to be accounted for.
Role#permissions is not sufficient, as it only describes base permissions of the role itself.

fixes #11052
2025-08-22 12:04:35 +01:00
Jiralite
40578393c3 chore: add @discordjs/collection in api-extractor.json 2025-08-22 08:57:02 +01:00
Vlad Frangu
86ecb37c9e chore(discord.js): correct changelog and version 2025-08-21 00:58:23 +03:00
Vlad Frangu
311e826b12 chore(discord.js): release discord.js@15.0.0 2025-08-21 00:52:53 +03:00
Vlad Frangu
dceac0089d chore(core): release @discordjs/core@2.2.1 2025-08-21 00:48:29 +03:00
Vlad Frangu
a2f7d3ad54 chore(rest): release @discordjs/rest@2.6.0 2025-08-21 00:43:53 +03:00
Jiralite
b532df61ed fix: Remove trailing color references (#11007)
fix: role colour fixes
2025-07-20 18:01:45 +01:00
Jiralite
d60e0bf30b types(Invite): Approximate fields should be nullable (#10997)
types: nullable fields
2025-07-16 23:41:57 +01:00
Danial Raza
baa08b8fbb feat: support user guilds (#10995) 2025-07-16 22:27:54 +02:00
Jiralite
f469f74aca feat(MessageManager): New pinned messages routes (#10993)
feat(MessageManager)!: New pinned messages routes (#10989)

BREAKING CHANGE: `fetchPinned()` has been renamed to `fetchPins()`, which is a paginated endpoint to fetch pinned messages.
2025-07-16 11:33:16 +01:00
Jiralite
90d3b28268 fix(Emoji): remove incorrect nullables, add ApplicationEmoji#available (#10990)
fix: remove incorrect nullables, add `ApplicationEmoji#available`

Co-authored-by: Amgelo563 <61554601+Amgelo563@users.noreply.github.com>
2025-07-15 19:52:47 +01:00
Jiralite
a271e9b51e build: Upgrade discord-api-types (#10991)
build: Upgrade dependencies
2025-07-15 01:30:58 +01:00
Danial Raza
8ac0e1e5d6 feat(User): add collectibles (#10939)
* feat(User): add `collectibles`

* docs: nullable collectibles and nameplate data

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

---------

Co-Authored-By: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-07-15 00:41:57 +01:00
Jiralite
9d6fdf8979 feat: Role gradient colours (#10986)
* feat: backport role gradient colours

* chore: add deprecations
2025-07-14 18:34:52 +01:00
Jiralite
cafe58b3bd feat: Support animated WebP (#10987)
* feat: Support animated WebP (#10911)

* feat: support animated WebP

* refactor: change the rest

* fix: remove redundant code

* fix(CDN): Export `MakeURLOptions`
2025-07-14 18:34:27 +01:00
Jiralite
7eca844f6d chore: remove unused directive 2025-07-14 10:58:55 +01:00
Naiyar
63f5261f4c fix(GuildChannelManager): properly resolve avatar for createWebhook (#10772)
fix(GuildChannelManager): properly resolve avatr for createWebhook
2025-07-14 02:26:55 +01:00
Almeida
5be774db64 docs: remove hardcoded locale from links (#10794)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-07-14 02:24:17 +01:00
Almeida
b36b751bde fix(Message): forwarded messages are not crosspostable (#10821)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-07-14 02:19:03 +01:00
Danial Raza
500712d5ea types(ModalSubmitFields): fix fields type (#10816)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-07-14 02:17:36 +01:00
Danial Raza
040c66ae15 docs(ApplicationCommand): incorrect method in example (#10837) 2025-07-14 02:10:20 +01:00
Jiralite
82378fc2e8 refactor: Deprecate ready event in favor of clientReady (#10969)
* refactor: deprecate ready event

* refactor: tweak message

Co-Authored-By: Vlad Frangu <me@vladfrangu.dev>

* Update packages/discord.js/src/client/websocket/WebSocketManager.js

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>

* chore: disable max-len

---------

Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
2025-07-13 21:32:22 +01:00
Jiralite
d4f742e99e refactor(ThreadChannel): Remove trimming of name (#10984)
refactor(ThreadChannel): no need to trim name

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-07-13 21:16:44 +01:00
Jiralite
51ceb203fa fix(DirectoryChannel): Export class (#10985)
fix: export directory channels
2025-07-13 21:07:09 +01:00
Qjuh
1404e32849 fix(Events): WebhooksUpdate enum value (#10970)
fix: change Events.WebhooksUpdate value
2025-07-11 19:39:12 +01:00
Jiralite
9fc3e5ea72 fix: Adjust reason in methods options (#10977)
fix: Adjust `reason` in methods options (#10976)

* fix(channel): allow reason in editing

* fix(channel): allow reason in `delete()`

* fix(channel): allow reason in creating threads

* chore: run format

* fix(guild): remove incorrect `reason` option

---------

Co-authored-by: Danial Raza <danialrazafb@gmail.com>
2025-07-11 10:00:58 +01:00
TÆMBØ
19e74b1533 types(InteractionCallbackResponse): add missing InGuild generic (#10963) 2025-07-03 00:21:13 +02:00
Vlad Frangu
de22a10038 chore(discord.js): release discord.js@14.21.0 2025-06-26 01:33:02 +03:00
Vlad Frangu
8ab30cdefa chore(core): release @discordjs/core@2.2.0 2025-06-26 01:26:48 +03:00
Danial Raza
c2a43b685e types(ClientEventTypes): add missing guildSoundboardSoundsUpdate (#10928) 2025-06-25 16:22:07 +01:00
Jiralite
507b696792 fix(ClientUser): Remove token assignment (#10953)
fix(ClientUser): remove token assignment
2025-06-25 16:15:35 +01:00
Jiralite
15f7571243 feat(GuildMember): add avatarDecorationData (#10942)
Co-Authored-By: Danial Raza <danialrazafb@gmail.com>
2025-06-25 12:31:43 +01:00
Almeida
3fa429c7df feat(ClientApplication): add approximateUserAuthorizationCount (#10933)
Co-authored-by: Danial Raza <danialrazafb@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-06-25 12:22:36 +01:00
Jiralite
7713627fd1 feat(webhook): Support with_components (#10945)
feat: support `with_components`
2025-06-22 10:48:47 +01:00
Vlad Frangu
6a5c0fb32d chore(discord.js): release discord.js@14.20.0 2025-06-16 15:41:03 +03:00
Vlad Frangu
eb5acd1e30 chore(discord.js): bump ws 2025-06-16 15:39:20 +03:00
Vlad Frangu
127021d5ab chore(core): release @discordjs/core@2.1.1 2025-06-16 15:30:25 +03:00
Vlad Frangu
0943bc2efb chore(ws): release @discordjs/ws@2.0.3 2025-06-16 15:29:02 +03:00
80 changed files with 2463 additions and 499 deletions

2
.github/CODEOWNERS vendored
View File

@@ -1,5 +1,5 @@
# Learn how to add code owners here:
# https://help.github.com/en/articles/about-code-owners
# https://help.github.com/articles/about-code-owners
* @iCrawl

View File

@@ -1270,7 +1270,7 @@ export class ApiModelGenerator {
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString(
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}${jsDoc.default ? ` (default: ${this._escapeSpecialChars(jsDoc.default)})` : ''}\n${
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}${jsDoc.default === undefined ? '' : ` (default: ${this._escapeSpecialChars(jsDoc.default)})`}\n${
'see' in jsDoc ? jsDoc.see.map((see) => ` * @see ${see}\n`).join('') : ''
}${'readonly' in jsDoc && jsDoc.readonly ? ' * @readonly\n' : ''}${
'deprecated' in jsDoc && jsDoc.deprecated
@@ -1348,7 +1348,7 @@ export class ApiModelGenerator {
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString(
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}${jsDoc.default ? `\n * @defaultValue ${this._escapeSpecialChars(jsDoc.default)}` : ''}\n${
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}${jsDoc.default === undefined ? '' : `\n * @defaultValue ${this._escapeSpecialChars(jsDoc.default)}`}\n${
'see' in jsDoc ? jsDoc.see.map((see) => ` * @see ${see}\n`).join('') : ''
}${'readonly' in jsDoc && jsDoc.readonly ? ' * @readonly\n' : ''}${
'deprecated' in jsDoc && jsDoc.deprecated

View File

@@ -83,7 +83,7 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder<APIStringSele
*
* @remarks
* This method behaves similarly
* to {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice | Array.prototype.splice()}.
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/slice | Array.prototype.splice()}.
* It's useful for modifying and adjusting the order of existing options.
* @example
* Remove the first option:

View File

@@ -125,7 +125,7 @@ export class EmbedBuilder {
*
* @remarks
* This method behaves similarly
* to {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
* The maximum amount of fields that can be added is 25.
*
* It's useful for modifying and adjusting order of the already-existing fields of an embed.

View File

@@ -2,6 +2,43 @@
All notable changes to this project will be documented in this file.
# [@discordjs/core@2.3.0](https://github.com/discordjs/discord.js/compare/@discordjs/core@2.2.2...@discordjs/core@2.3.0) - (2025-10-08)
## Features
- Add `{add,remove}GroupDMRecipient` methods (#11135) ([72771b7](https://github.com/discordjs/discord.js/commit/72771b79aa3a78967be92ea2e4c523755d0d2ec0))
- **guild:** Support incident actions (#11131) ([63dbe48](https://github.com/discordjs/discord.js/commit/63dbe48055347413ec70f36bce4f645688776413))
- Add gateway endpoints (#11130) ([a041723](https://github.com/discordjs/discord.js/commit/a04172325af5a3a9880253bb8dc7c057a0426d83))
# [@discordjs/core@2.2.2](https://github.com/discordjs/discord.js/compare/@discordjs/core@2.2.1...@discordjs/core@2.2.2) - (2025-09-10)
## Bug Fixes
- **users:** Correct type for editing current guild member (#11098) ([9ae7377](https://github.com/discordjs/discord.js/commit/9ae737708b24400320a2da4a85a6c977475a05a5))
- **guild:** Creating a template actually creates a template (#11030) ([ac6ff15](https://github.com/discordjs/discord.js/commit/ac6ff15b7dc4a88753b7cfdf1bca1b4bcc1cc260))
## Documentation
- **guild:** Deprecate API related to guild ownership ([6fb0b1c](https://github.com/discordjs/discord.js/commit/6fb0b1cef6e479be3d47370438d8a588a7c2b850))
# [@discordjs/core@2.2.1](https://github.com/discordjs/discord.js/compare/@discordjs/core@2.2.0...@discordjs/core@2.2.1) - (2025-08-20)
## Bug Fixes
- Adjust `reason` in methods options (#10977) ([9fc3e5e](https://github.com/discordjs/discord.js/commit/9fc3e5ea72a2714d81cc57cbac4f378a49934446))
# [@discordjs/core@2.2.0](https://github.com/discordjs/discord.js/compare/@discordjs/core@2.1.1...@discordjs/core@2.2.0) - (2025-06-25)
## Features
- **webhook:** Support `with_components` (#10945) ([7713627](https://github.com/discordjs/discord.js/commit/7713627fd18599a6187b325e1e4bc9a17cf23e21))
# [@discordjs/core@2.1.1](https://github.com/discordjs/discord.js/compare/@discordjs/core@2.1.0...@discordjs/core@2.1.1) - (2025-06-16)
## Bug Fixes
- **guild:** Fix incorrectly-detected deprecated overload ([d0a535e](https://github.com/discordjs/discord.js/commit/d0a535ea6a66861276691a51547adfb2bcef0384))
# [@discordjs/core@2.1.0](https://github.com/discordjs/discord.js/compare/@discordjs/core@2.0.1...@discordjs/core@2.1.0) - (2025-04-25)
## Features

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/core",
"version": "2.1.0",
"version": "2.3.0",
"description": "A thinly abstracted wrapper around the rest API, and gateway.",
"scripts": {
"test": "vitest run",
@@ -70,7 +70,7 @@
"@discordjs/ws": "workspace:^",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.1"
"discord-api-types": "^0.38.29"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",

View File

@@ -0,0 +1,34 @@
import { Routes } from 'discord-api-types/v10';
import { glob, readFile } from 'node:fs/promises';
const usedRoutes = new Set();
const ignoredRoutes = new Set([
// Deprecated
'channelPins',
'channelPin',
'guilds',
'guildCurrentMemberNickname',
'guildMFA',
'nitroStickerPacks',
]);
for await (const file of glob('src/api/*.ts')) {
const content = await readFile(file, 'utf-8');
const routes = content.matchAll(/Routes\.([\w\d_]+)/g);
for (const route of routes) {
usedRoutes.add(route[1]);
}
}
const unusedRoutes = Object.keys(Routes).filter((route) => !usedRoutes.has(route) && !ignoredRoutes.has(route));
if (unusedRoutes.length > 0) {
console.warn('The following routes are not implemented:');
for (const route of unusedRoutes) {
console.warn(` - ${route}`);
}
} else {
console.log('No missing routes.');
}

View File

@@ -1,10 +1,9 @@
/* eslint-disable jsdoc/check-param-names */
import { makeURLSearchParams, type RawFile, type REST, type RequestData } from '@discordjs/rest';
import { makeURLSearchParams, type RawFile, type RequestData, type REST } from '@discordjs/rest';
import {
Routes,
type RESTPostAPIChannelWebhookJSONBody,
type RESTPostAPIChannelWebhookResult,
type APIThreadChannel,
type RESTDeleteAPIChannelResult,
type RESTGetAPIChannelInvitesResult,
type RESTGetAPIChannelMessageReactionUsersQuery,
@@ -17,8 +16,8 @@ import {
type RESTGetAPIChannelThreadsArchivedQuery,
type RESTGetAPIChannelUsersThreadsArchivedResult,
type RESTGetAPIChannelWebhooksResult,
type RESTPatchAPIChannelMessageJSONBody,
type RESTPatchAPIChannelJSONBody,
type RESTPatchAPIChannelMessageJSONBody,
type RESTPatchAPIChannelMessageResult,
type RESTPatchAPIChannelResult,
type RESTPostAPIChannelFollowersResult,
@@ -27,12 +26,14 @@ import {
type RESTPostAPIChannelMessageCrosspostResult,
type RESTPostAPIChannelMessageJSONBody,
type RESTPostAPIChannelMessageResult,
type RESTPutAPIChannelPermissionJSONBody,
type Snowflake,
type RESTPostAPIChannelThreadsJSONBody,
type RESTPostAPIChannelThreadsResult,
type APIThreadChannel,
type RESTPostAPIChannelWebhookJSONBody,
type RESTPostAPIChannelWebhookResult,
type RESTPostAPIGuildForumThreadsJSONBody,
type RESTPutAPIChannelPermissionJSONBody,
type RESTPutAPIChannelRecipientJSONBody,
type Snowflake,
} from 'discord-api-types/v10';
export interface StartForumThreadOptions extends RESTPostAPIGuildForumThreadsJSONBody {
@@ -223,9 +224,13 @@ export class ChannelsAPI {
public async edit(
channelId: Snowflake,
body: RESTPatchAPIChannelJSONBody,
{ signal }: Pick<RequestData, 'signal'> = {},
{ signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {},
) {
return this.rest.patch(Routes.channel(channelId), { body, signal }) as Promise<RESTPatchAPIChannelResult>;
return this.rest.patch(Routes.channel(channelId), {
reason,
body,
signal,
}) as Promise<RESTPatchAPIChannelResult>;
}
/**
@@ -235,8 +240,8 @@ export class ChannelsAPI {
* @param channelId - The id of the channel to delete
* @param options - The options for deleting the channel
*/
public async delete(channelId: Snowflake, { signal }: Pick<RequestData, 'signal'> = {}) {
return this.rest.delete(Routes.channel(channelId), { signal }) as Promise<RESTDeleteAPIChannelResult>;
public async delete(channelId: Snowflake, { signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {}) {
return this.rest.delete(Routes.channel(channelId), { signal, reason }) as Promise<RESTDeleteAPIChannelResult>;
}
/**
@@ -441,11 +446,12 @@ export class ChannelsAPI {
channelId: Snowflake,
body: RESTPostAPIChannelThreadsJSONBody,
messageId?: Snowflake,
{ signal }: Pick<RequestData, 'signal'> = {},
{ signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {},
) {
return this.rest.post(Routes.threads(channelId, messageId), {
body,
signal,
reason,
}) as Promise<RESTPostAPIChannelThreadsResult>;
}
@@ -460,7 +466,7 @@ export class ChannelsAPI {
public async createForumThread(
channelId: Snowflake,
{ message, ...optionsBody }: StartForumThreadOptions,
{ signal }: Pick<RequestData, 'signal'> = {},
{ signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {},
) {
const { files, ...messageBody } = message;
@@ -469,7 +475,12 @@ export class ChannelsAPI {
message: messageBody,
};
return this.rest.post(Routes.threads(channelId), { files, body, signal }) as Promise<APIThreadChannel>;
return this.rest.post(Routes.threads(channelId), {
files,
body,
reason,
signal,
}) as Promise<APIThreadChannel>;
}
/**
@@ -583,4 +594,43 @@ export class ChannelsAPI {
signal,
});
}
/**
* Adds a recipient to a group DM channel
*
* @see {@link https://discord.com/developers/docs/resources/channel#group-dm-add-recipient}
* @param channelId - The id of the channel to add the recipient to
* @param userId - The id of the user to add as a recipient
* @param body - The data for adding the recipient
* @param options - The options for adding the recipient
*/
public async addGroupDMRecipient(
channelId: Snowflake,
userId: Snowflake,
body: RESTPutAPIChannelRecipientJSONBody,
{ signal }: Pick<RequestData, 'signal'> = {},
) {
await this.rest.put(Routes.channelRecipient(channelId, userId), {
body,
signal,
});
}
/**
* Removes a recipient from a group DM channel
*
* @see {@link https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient}
* @param channelId - The id of the channel to remove the recipient from
* @param userId - The id of the user to remove as a recipient
* @param options - The options for removing the recipient
*/
public async removeGroupDMRecipient(
channelId: Snowflake,
userId: Snowflake,
{ signal }: Pick<RequestData, 'signal'> = {},
) {
await this.rest.delete(Routes.channelRecipient(channelId, userId), {
signal,
});
}
}

View File

@@ -0,0 +1,31 @@
/* eslint-disable jsdoc/check-param-names */
import type { RequestData, REST } from '@discordjs/rest';
import { Routes, type RESTGetAPIGatewayBotResult, type RESTGetAPIGatewayResult } from 'discord-api-types/v10';
export class GatewayAPI {
public constructor(private readonly rest: REST) {}
/**
* Gets gateway information.
*
* @see {@link https://discord.com/developers/docs/events/gateway#get-gateway}
* @param options - The options for fetching the gateway information
*/
public async get({ signal }: Pick<RequestData, 'signal'> = {}) {
return this.rest.get(Routes.gateway(), {
auth: false,
signal,
}) as Promise<RESTGetAPIGatewayResult>;
}
/**
* Gets gateway information with additional metadata.
*
* @see {@link https://discord.com/developers/docs/events/gateway#get-gateway-bot}
* @param options - The options for fetching the gateway information
*/
public async getBot({ signal }: Pick<RequestData, 'signal'> = {}) {
return this.rest.get(Routes.gatewayBot(), { signal }) as Promise<RESTGetAPIGatewayBotResult>;
}
}

View File

@@ -95,6 +95,8 @@ import {
type RESTPostAPIGuildsMFAResult,
type RESTPostAPIGuildsResult,
type RESTPutAPIGuildBanJSONBody,
type RESTPutAPIGuildIncidentActionsJSONBody,
type RESTPutAPIGuildIncidentActionsResult,
type RESTPutAPIGuildMemberJSONBody,
type RESTPutAPIGuildMemberResult,
type RESTPutAPIGuildOnboardingJSONBody,
@@ -166,6 +168,7 @@ export class GuildsAPI {
* @see {@link https://discord.com/developers/docs/resources/guild#create-guild}
* @param body - The guild to create
* @param options - The options for creating the guild
* @deprecated API related to guild ownership may no longer be used.
*/
public async create(body: RESTPostAPIGuildsJSONBody, { signal }: Pick<RequestData, 'signal'> = {}) {
return this.rest.post(Routes.guilds(), { body, signal }) as Promise<RESTPostAPIGuildsResult>;
@@ -197,9 +200,10 @@ export class GuildsAPI {
* @see {@link https://discord.com/developers/docs/resources/guild#delete-guild}
* @param guildId - The id of the guild to delete
* @param options - The options for deleting this guild
* @deprecated API related to guild ownership may no longer be used.
*/
public async delete(guildId: Snowflake, { signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {}) {
await this.rest.delete(Routes.guild(guildId), { reason, signal });
public async delete(guildId: Snowflake, { signal }: Pick<RequestData, 'signal'> = {}) {
await this.rest.delete(Routes.guild(guildId), { signal });
}
/**
@@ -491,6 +495,7 @@ export class GuildsAPI {
* @param guildId - The id of the guild to edit the MFA level for
* @param level - The new MFA level
* @param options - The options for editing the MFA level
* @deprecated API related to guild ownership may no longer be used.
*/
public async editMFALevel(
guildId: Snowflake,
@@ -1287,16 +1292,16 @@ export class GuildsAPI {
* Creates a new template
*
* @see {@link https://discord.com/developers/docs/resources/guild-template#create-guild-template}
* @param templateCode - The code of the template
* @param guildId - The id of the guild
* @param body - The data for creating the template
* @param options - The options for creating the template
*/
public async createTemplate(
templateCode: string,
guildId: Snowflake,
body: RESTPostAPIGuildTemplatesJSONBody,
{ signal }: Pick<RequestData, 'signal'> = {},
) {
return this.rest.post(Routes.template(templateCode), { body, signal }) as Promise<RESTPostAPIGuildTemplatesResult>;
return this.rest.post(Routes.guildTemplates(guildId), { body, signal }) as Promise<RESTPostAPIGuildTemplatesResult>;
}
/**
@@ -1356,4 +1361,23 @@ export class GuildsAPI {
signal,
}) as Promise<RESTPutAPIGuildOnboardingResult>;
}
/**
* Modifies incident actions for a guild.
*
* @see {@link https://discord.com/developers/docs/resources/guild#modify-guild-incident-actions}
* @param guildId - The id of the guild
* @param body - The data for modifying guild incident actions
* @param options - The options for modifying guild incident actions
*/
public async editIncidentActions(
guildId: Snowflake,
body: RESTPutAPIGuildIncidentActionsJSONBody,
{ signal }: Pick<RequestData, 'signal'> = {},
) {
return this.rest.put(Routes.guildIncidentActions(guildId), {
body,
signal,
}) as Promise<RESTPutAPIGuildIncidentActionsResult>;
}
}

View File

@@ -2,6 +2,7 @@ import type { REST } from '@discordjs/rest';
import { ApplicationCommandsAPI } from './applicationCommands.js';
import { ApplicationsAPI } from './applications.js';
import { ChannelsAPI } from './channel.js';
import { GatewayAPI } from './gateway.js';
import { GuildsAPI } from './guild.js';
import { InteractionsAPI } from './interactions.js';
import { InvitesAPI } from './invite.js';
@@ -19,6 +20,7 @@ import { WebhooksAPI } from './webhook.js';
export * from './applicationCommands.js';
export * from './applications.js';
export * from './channel.js';
export * from './gateway.js';
export * from './guild.js';
export * from './interactions.js';
export * from './invite.js';
@@ -40,6 +42,8 @@ export class API {
public readonly channels: ChannelsAPI;
public readonly gateway: GatewayAPI;
public readonly guilds: GuildsAPI;
public readonly interactions: InteractionsAPI;
@@ -70,6 +74,7 @@ export class API {
this.applicationCommands = new ApplicationCommandsAPI(rest);
this.applications = new ApplicationsAPI(rest);
this.channels = new ChannelsAPI(rest);
this.gateway = new GatewayAPI(rest);
this.guilds = new GuildsAPI(rest);
this.invites = new InvitesAPI(rest);
this.monetization = new MonetizationAPI(rest);

View File

@@ -44,7 +44,7 @@ export class OAuth2API {
{ signal }: Pick<RequestData, 'signal'> = {},
) {
return this.rest.post(Routes.oauth2TokenExchange(), {
body: makeURLSearchParams(body),
body: makeURLSearchParams<RESTPostOAuth2AccessTokenURLEncodedData>(body),
passThroughBody: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
@@ -66,7 +66,7 @@ export class OAuth2API {
{ signal }: Pick<RequestData, 'signal'> = {},
) {
return this.rest.post(Routes.oauth2TokenExchange(), {
body: makeURLSearchParams(body),
body: makeURLSearchParams<RESTPostOAuth2RefreshTokenURLEncodedData>(body),
passThroughBody: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',

View File

@@ -10,9 +10,9 @@ import {
type RESTGetAPICurrentUserResult,
type RESTGetAPIUserResult,
type RESTGetCurrentUserGuildMemberResult,
type RESTPatchAPICurrentGuildMemberJSONBody,
type RESTPatchAPICurrentUserJSONBody,
type RESTPatchAPICurrentUserResult,
type RESTPatchAPIGuildMemberJSONBody,
type RESTPatchAPIGuildMemberResult,
type RESTPostAPICurrentUserCreateDMChannelResult,
type RESTPutAPICurrentUserApplicationRoleConnectionJSONBody,
@@ -101,7 +101,7 @@ export class UsersAPI {
*/
public async editCurrentGuildMember(
guildId: Snowflake,
body: RESTPatchAPIGuildMemberJSONBody = {},
body: RESTPatchAPICurrentGuildMemberJSONBody = {},
{ reason, signal }: Pick<RequestData, 'reason' | 'signal'> = {},
) {
return this.rest.patch(Routes.guildMember(guildId, '@me'), {

View File

@@ -9,6 +9,7 @@ import {
type RESTPatchAPIWebhookJSONBody,
type RESTPatchAPIWebhookResult,
type RESTPatchAPIWebhookWithTokenMessageJSONBody,
type RESTPatchAPIWebhookWithTokenMessageQuery,
type RESTPatchAPIWebhookWithTokenMessageResult,
type RESTPostAPIWebhookWithTokenGitHubQuery,
type RESTPostAPIWebhookWithTokenJSONBody,
@@ -127,13 +128,14 @@ export class WebhooksAPI {
{
wait,
thread_id,
with_components,
files,
...body
}: RESTPostAPIWebhookWithTokenJSONBody & RESTPostAPIWebhookWithTokenQuery & { files?: RawFile[] },
{ signal }: Pick<RequestData, 'signal'> = {},
) {
return this.rest.post(Routes.webhook(id, token), {
query: makeURLSearchParams({ wait, thread_id }),
query: makeURLSearchParams({ wait, thread_id, with_components }),
files,
body,
auth: false,
@@ -232,13 +234,14 @@ export class WebhooksAPI {
messageId: Snowflake,
{
thread_id,
with_components,
files,
...body
}: RESTPatchAPIWebhookWithTokenMessageJSONBody & { files?: RawFile[]; thread_id?: string },
}: RESTPatchAPIWebhookWithTokenMessageJSONBody & RESTPatchAPIWebhookWithTokenMessageQuery & { files?: RawFile[] },
{ signal }: Pick<RequestData, 'signal'> = {},
) {
return this.rest.patch(Routes.webhookMessage(id, token, messageId), {
query: makeURLSearchParams({ thread_id }),
query: makeURLSearchParams({ thread_id, with_components }),
auth: false,
body,
signal,

View File

@@ -2,6 +2,116 @@
All notable changes to this project will be documented in this file.
# [14.23.0](https://github.com/discordjs/discord.js/compare/14.22.1...14.23.0) - (2025-10-08)
## Bug Fixes
- **ThreadMemberFlagsBitField:** Use `ThreadMemberFlags` enum in `Flags` (#11118) ([154c00d](https://github.com/discordjs/discord.js/commit/154c00ded932109c59ff0759609424fcb95140a0))
- Backport in operator fix from main (#11127) ([fcce0d9](https://github.com/discordjs/discord.js/commit/fcce0d95bb6cd415f40f9f7a052e01ddcf625ed0))
- Ensure discriminator detection respects webhooks too (#11062) ([d8ad181](https://github.com/discordjs/discord.js/commit/d8ad181c191e3a908e3c8e133ccb1d961d9d79e0))
## Documentation
- Use LocalizationMap where applicable (#11117) ([3b92744](https://github.com/discordjs/discord.js/commit/3b927449ae728175f04d67376642b20ba4a93069))
- **GuildEditOptions:** Deprecate owner property ([fe025c0](https://github.com/discordjs/discord.js/commit/fe025c0a9f722c6225fff6501e9b3981cfe134ba))
- Deprecate API related to guild ownership (#11054) ([3dd57c2](https://github.com/discordjs/discord.js/commit/3dd57c2eaf220b08f2b6f6562c34acf8524b5b17))
- Deprecate setting owner ([740da4c](https://github.com/discordjs/discord.js/commit/740da4ce5e189391c7a0904da32a96fe1c8534e6))
## Features
- Bump builders in v14 (and fix runtime crashes) (#11153) ([67c8953](https://github.com/discordjs/discord.js/commit/67c8953a10d150074ba848cd8bfb30961d46b662))
- **GuildMemberManager:** Add new modify self fields (#11112) ([9b821e5](https://github.com/discordjs/discord.js/commit/9b821e5dfcfb92a9d23ef96dd947c0bd11ee7b86))
- Text display and more selects in modal for v14 (#11096) ([93e0f4c](https://github.com/discordjs/discord.js/commit/93e0f4cd10af6d85ccdcb6a6aeae3e1a9f14a8fe))
- Guest invites (#11079) ([79d999e](https://github.com/discordjs/discord.js/commit/79d999e4c10e36330ee897065987ad99d558edca))
- Polls overhaul (#11058) ([4a8aeb6](https://github.com/discordjs/discord.js/commit/4a8aeb6aee78b23a25e8d5be1309cc7c64b066fb))
## Refactor
- **ActionsManager:** Register actions without using class name (#11080) ([0dff969](https://github.com/discordjs/discord.js/commit/0dff969e16a8879a0fc889567bd540cb1b82a682))
## Typings
- **ClientEventTypes:** Fix `messageDeleteBulk` event arg (#11122) ([30e35d9](https://github.com/discordjs/discord.js/commit/30e35d909e0058db701c82744b13da26ddefcf0e))
- **Webhook:** Specify message type (#11142) ([6a5707c](https://github.com/discordjs/discord.js/commit/6a5707c78669bb65d03ae76ab591e053787891f1))
# [14.22.1](https://github.com/discordjs/discord.js/compare/14.22.0...14.22.1) - (2025-08-22)
## Bug Fixes
- **GuildChannel:** Account for everyone base permissions (#11053) ([ecef7bd](https://github.com/discordjs/discord.js/commit/ecef7bdf22cc3e7c1fc47d828a55f6139672b2a8))
# [14.22.0](https://github.com/discordjs/discord.js/compare/14.21.0...14.22.0) - (2025-08-20)
## Bug Fixes
- Remove trailing `color` references (#11007) ([b532df6](https://github.com/discordjs/discord.js/commit/b532df61ed346e2289831f2905984583a53d3fa5))
- **Emoji:** Remove incorrect nullables, add `ApplicationEmoji#available` (#10990) ([90d3b28](https://github.com/discordjs/discord.js/commit/90d3b282684f5a4bbf9ce2672fa5efd5ec802e95))
- **GuildChannelManager:** Properly resolve avatar for createWebhook (#10772) ([63f5261](https://github.com/discordjs/discord.js/commit/63f5261f4c351cd1b6befca1163254300f7424f4))
- **Message:** Forwarded messages are not `crosspostable` (#10821) ([b36b751](https://github.com/discordjs/discord.js/commit/b36b751bde5f5de286d748465619147619959d4f))
- **DirectoryChannel:** Export class (#10985) ([51ceb20](https://github.com/discordjs/discord.js/commit/51ceb203fa218accab47f604f91f74b49af7e51f))
- **Events:** `WebhooksUpdate` enum value (#10970) ([1404e32](https://github.com/discordjs/discord.js/commit/1404e328492549f4a17ab997eb648f3263e8ec25))
## Documentation
- Remove hardcoded locale from links (#10794) ([5be774d](https://github.com/discordjs/discord.js/commit/5be774db641b60669505645861d721200d335a7b))
- **ApplicationCommand:** Incorrect method in example (#10837) ([040c66a](https://github.com/discordjs/discord.js/commit/040c66ae15fd3cc7cb40bdbb5384ede445c40973))
## Features
- Support user guilds (#10995) ([baa08b8](https://github.com/discordjs/discord.js/commit/baa08b8fbb64abd8265890413c95f81e2c61303f))
- **MessageManager:** New pinned messages routes (#10993) ([f469f74](https://github.com/discordjs/discord.js/commit/f469f74acaa14f126fd02af4a032ca8e56dbb534))
- `fetchPinned()` has been renamed to `fetchPins()`, which is a paginated endpoint to fetch pinned messages.
- **User:** Add `collectibles` (#10939) ([8ac0e1e](https://github.com/discordjs/discord.js/commit/8ac0e1e5d6df9c51fd3a41d9f8c9dbe8f786229a))
- Role gradient colours (#10986) ([9d6fdf8](https://github.com/discordjs/discord.js/commit/9d6fdf8979d29787a13912cfa55f1ff3961c6b68))
- Support animated WebP (#10987) ([cafe58b](https://github.com/discordjs/discord.js/commit/cafe58b3bd9defb5050a7a90bd07568f3b509c89))
## Refactor
- Deprecate `ready` event in favor of `clientReady` (#10969) ([82378fc](https://github.com/discordjs/discord.js/commit/82378fc2e8f4fd7e56b5a80eb6a6d3a8315e388e))
- **ThreadChannel:** Remove trimming of name (#10984) ([d4f742e](https://github.com/discordjs/discord.js/commit/d4f742e99e31b4eb9e84afce3bab48ed549f8ae3))
## Typings
- **Invite:** Approximate fields should be nullable (#10997) ([d60e0bf](https://github.com/discordjs/discord.js/commit/d60e0bf30bac05921063d010fab253ca829ae3bb))
- **ModalSubmitFields:** Fix `fields` type (#10816) ([500712d](https://github.com/discordjs/discord.js/commit/500712d5eaef1a75133e088e0f260842d12f3419))
- **InteractionCallbackResponse:** Add missing InGuild generic (#10963) ([19e74b1](https://github.com/discordjs/discord.js/commit/19e74b153317cf8b910317c56beb95a698acc00c))
# [14.21.0](https://github.com/discordjs/discord.js/compare/14.20.0...14.21.0) - (2025-06-25)
## Bug Fixes
- **ClientUser:** Remove token assignment (#10953) ([507b696](https://github.com/discordjs/discord.js/commit/507b696792d61ae49565b4613439aceb08dcf38a))
## Features
- **GuildMember:** Add `avatarDecorationData` (#10942) ([15f7571](https://github.com/discordjs/discord.js/commit/15f7571243d5b206141290478fd5164d299c0850))
- **ClientApplication:** Add `approximateUserAuthorizationCount` (#10933) ([3fa429c](https://github.com/discordjs/discord.js/commit/3fa429c7dfa3bb3e6f099cd2f068c474a01677b1))
## Typings
- **ClientEventTypes:** Add missing `guildSoundboardSoundsUpdate` (#10928) ([c2a43b6](https://github.com/discordjs/discord.js/commit/c2a43b685e01eff878a399e8c00df8e473c185ad))
# [14.20.0](https://github.com/discordjs/discord.js/compare/14.19.3...14.20.0) - (2025-06-16)
## Bug Fixes
- Use resolvePartialEmoji on MessagePayload#options#components (#10910) ([f2f757c](https://github.com/discordjs/discord.js/commit/f2f757ce52b76d5e571f47f9bf1c5188627b80d5))
- **ChannelManager:** Remove threads from cache upon deletion (#10883) ([ee2eb73](https://github.com/discordjs/discord.js/commit/ee2eb7349f2467451880baaea54e02074916015f))
- **PartialGroupDMChannel:** Prevent `undefined` values (#10889) ([9bca4af](https://github.com/discordjs/discord.js/commit/9bca4af5fdb78fae7b970498db7f93df31f55ef9))
## Features
- Backport entrypoint command (#10908) ([c0eae34](https://github.com/discordjs/discord.js/commit/c0eae344c2ed43fa67be9fda8e3d3e479693d2d1))
- **BaseInteraction:** Add `attachmentSizeLimit` (#10830) ([7e21a94](https://github.com/discordjs/discord.js/commit/7e21a9474e532c5b22c6e0600c446fca47352617))
## Performance
- **Components:** Hash table (#10893) ([2d19163](https://github.com/discordjs/discord.js/commit/2d19163d764705667691430e438499fd330ffb65))
## Refactor
- **Client:** Remove `with_expiration` query parameter (#10800) ([c8f6066](https://github.com/discordjs/discord.js/commit/c8f6066d6a0a1fc6ac23a49d66604d95b588af3e))
# [14.19.3](https://github.com/discordjs/discord.js/compare/14.19.2...14.19.3) - (2025-05-02)
## Bug Fixes

View File

@@ -3,6 +3,7 @@
"mainEntryPointFilePath": "<projectFolder>/typings/index.d.ts",
"bundledPackages": [
"discord-api-types",
"@discordjs/collection",
"@discordjs/builders",
"@discordjs/formatters",
"@discordjs/rest",

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "discord.js",
"version": "14.19.3",
"version": "14.23.0",
"description": "A powerful library for interacting with the Discord API",
"scripts": {
"test": "pnpm run docs:test && pnpm run test:typescript",
@@ -66,14 +66,14 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@discordjs/builders": "^1.11.2",
"@discordjs/builders": "^1.12.0",
"@discordjs/collection": "1.5.3",
"@discordjs/formatters": "^0.6.1",
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"@discordjs/ws": "^1.2.2",
"@discordjs/ws": "^1.2.3",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "^0.38.1",
"discord-api-types": "^0.38.29",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"magic-bytes.js": "^1.10.0",

View File

@@ -36,12 +36,14 @@ async function writeClientActionImports() {
for (const file of (await readdir(actionsDirectory)).sort()) {
if (file === 'Action.js' || file === 'ActionsManager.js') continue;
lines.push(` this.register(require('./${file.slice(0, -3)}'));`);
const actionName = file.slice(0, -3);
lines.push(` this.${actionName} = this.load(require('./${file}'));`);
}
lines.push(' }\n');
lines.push(' register(Action) {');
lines.push(" this[Action.name.replace(/Action$/, '')] = new Action(this.client);");
lines.push(' load(Action) {');
lines.push(' return new Action(this.client);');
lines.push(' }');
lines.push('}\n');
lines.push('module.exports = ActionsManager;\n');

View File

@@ -522,7 +522,7 @@ class Client extends BaseClient {
}
/**
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
* Calls {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
* with the client as `this`.
* @param {string} script Script to eval
* @returns {*}
@@ -604,7 +604,7 @@ module.exports = Client;
*/
/**
* A {@link https://developer.twitter.com/en/docs/twitter-ids Twitter snowflake},
* A {@link https://docs.x.com/resources/fundamentals/x-ids Twitter snowflake},
* except the epoch is 2015-01-01T00:00:00.000Z.
*
* If we have a snowflake '266241948824764416' we can represent it as binary:
@@ -638,6 +638,11 @@ module.exports = Client;
* @see {@link https://discord.js.org/docs/packages/rest/stable/ImageURLOptions:Interface}
*/
/**
* @external EmojiURLOptions
* @see {@link https://discord.js.org/docs/packages/rest/stable/EmojiURLOptions:TypeAlias}
*/
/**
* @external BaseImageURLOptions
* @see {@link https://discord.js.org/docs/packages/rest/stable/BaseImageURLOptions:Interface}

View File

@@ -1,6 +1,8 @@
'use strict';
const Partials = require('../../util/Partials');
const { Poll } = require('../../structures/Poll.js');
const { PollAnswer } = require('../../structures/PollAnswer.js');
const Partials = require('../../util/Partials.js');
/*
@@ -63,6 +65,23 @@ class GenericAction {
);
}
getPoll(data, message, channel) {
const includePollPartial = this.client.options.partials.includes(Partials.Poll);
const includePollAnswerPartial = this.client.options.partials.includes(Partials.PollAnswer);
if (message.partial && (!includePollPartial || !includePollAnswerPartial)) return null;
if (!message.poll && includePollPartial) {
message.poll = new Poll(this.client, data, message, channel);
}
if (message.poll && !message.poll.answers.has(data.answer_id) && includePollAnswerPartial) {
const pollAnswer = new PollAnswer(this.client, data, message.poll);
message.poll.answers.set(data.answer_id, pollAnswer);
}
return message.poll;
}
getReaction(data, message, user) {
const id = data.emoji.id ?? decodeURIComponent(data.emoji.name);
return this.getPayload(

View File

@@ -11,74 +11,74 @@ class ActionsManager {
constructor(client) {
this.client = client;
this.register(require('./ApplicationCommandPermissionsUpdate'));
this.register(require('./AutoModerationActionExecution'));
this.register(require('./AutoModerationRuleCreate'));
this.register(require('./AutoModerationRuleDelete'));
this.register(require('./AutoModerationRuleUpdate'));
this.register(require('./ChannelCreate'));
this.register(require('./ChannelDelete'));
this.register(require('./ChannelUpdate'));
this.register(require('./EntitlementCreate'));
this.register(require('./EntitlementDelete'));
this.register(require('./EntitlementUpdate'));
this.register(require('./GuildAuditLogEntryCreate'));
this.register(require('./GuildBanAdd'));
this.register(require('./GuildBanRemove'));
this.register(require('./GuildChannelsPositionUpdate'));
this.register(require('./GuildDelete'));
this.register(require('./GuildEmojiCreate'));
this.register(require('./GuildEmojiDelete'));
this.register(require('./GuildEmojiUpdate'));
this.register(require('./GuildEmojisUpdate'));
this.register(require('./GuildIntegrationsUpdate'));
this.register(require('./GuildMemberRemove'));
this.register(require('./GuildMemberUpdate'));
this.register(require('./GuildRoleCreate'));
this.register(require('./GuildRoleDelete'));
this.register(require('./GuildRoleUpdate'));
this.register(require('./GuildRolesPositionUpdate'));
this.register(require('./GuildScheduledEventCreate'));
this.register(require('./GuildScheduledEventDelete'));
this.register(require('./GuildScheduledEventUpdate'));
this.register(require('./GuildScheduledEventUserAdd'));
this.register(require('./GuildScheduledEventUserRemove'));
this.register(require('./GuildSoundboardSoundDelete.js'));
this.register(require('./GuildStickerCreate'));
this.register(require('./GuildStickerDelete'));
this.register(require('./GuildStickerUpdate'));
this.register(require('./GuildStickersUpdate'));
this.register(require('./GuildUpdate'));
this.register(require('./InteractionCreate'));
this.register(require('./InviteCreate'));
this.register(require('./InviteDelete'));
this.register(require('./MessageCreate'));
this.register(require('./MessageDelete'));
this.register(require('./MessageDeleteBulk'));
this.register(require('./MessagePollVoteAdd'));
this.register(require('./MessagePollVoteRemove'));
this.register(require('./MessageReactionAdd'));
this.register(require('./MessageReactionRemove'));
this.register(require('./MessageReactionRemoveAll'));
this.register(require('./MessageReactionRemoveEmoji'));
this.register(require('./MessageUpdate'));
this.register(require('./PresenceUpdate'));
this.register(require('./StageInstanceCreate'));
this.register(require('./StageInstanceDelete'));
this.register(require('./StageInstanceUpdate'));
this.register(require('./ThreadCreate'));
this.register(require('./ThreadDelete'));
this.register(require('./ThreadListSync'));
this.register(require('./ThreadMemberUpdate'));
this.register(require('./ThreadMembersUpdate'));
this.register(require('./TypingStart'));
this.register(require('./UserUpdate'));
this.register(require('./VoiceStateUpdate'));
this.register(require('./WebhooksUpdate'));
this.ApplicationCommandPermissionsUpdate = this.load(require('./ApplicationCommandPermissionsUpdate.js'));
this.AutoModerationActionExecution = this.load(require('./AutoModerationActionExecution.js'));
this.AutoModerationRuleCreate = this.load(require('./AutoModerationRuleCreate.js'));
this.AutoModerationRuleDelete = this.load(require('./AutoModerationRuleDelete.js'));
this.AutoModerationRuleUpdate = this.load(require('./AutoModerationRuleUpdate.js'));
this.ChannelCreate = this.load(require('./ChannelCreate.js'));
this.ChannelDelete = this.load(require('./ChannelDelete.js'));
this.ChannelUpdate = this.load(require('./ChannelUpdate.js'));
this.EntitlementCreate = this.load(require('./EntitlementCreate.js'));
this.EntitlementDelete = this.load(require('./EntitlementDelete.js'));
this.EntitlementUpdate = this.load(require('./EntitlementUpdate.js'));
this.GuildAuditLogEntryCreate = this.load(require('./GuildAuditLogEntryCreate.js'));
this.GuildBanAdd = this.load(require('./GuildBanAdd.js'));
this.GuildBanRemove = this.load(require('./GuildBanRemove.js'));
this.GuildChannelsPositionUpdate = this.load(require('./GuildChannelsPositionUpdate.js'));
this.GuildDelete = this.load(require('./GuildDelete.js'));
this.GuildEmojiCreate = this.load(require('./GuildEmojiCreate.js'));
this.GuildEmojiDelete = this.load(require('./GuildEmojiDelete.js'));
this.GuildEmojiUpdate = this.load(require('./GuildEmojiUpdate.js'));
this.GuildEmojisUpdate = this.load(require('./GuildEmojisUpdate.js'));
this.GuildIntegrationsUpdate = this.load(require('./GuildIntegrationsUpdate.js'));
this.GuildMemberRemove = this.load(require('./GuildMemberRemove.js'));
this.GuildMemberUpdate = this.load(require('./GuildMemberUpdate.js'));
this.GuildRoleCreate = this.load(require('./GuildRoleCreate.js'));
this.GuildRoleDelete = this.load(require('./GuildRoleDelete.js'));
this.GuildRoleUpdate = this.load(require('./GuildRoleUpdate.js'));
this.GuildRolesPositionUpdate = this.load(require('./GuildRolesPositionUpdate.js'));
this.GuildScheduledEventCreate = this.load(require('./GuildScheduledEventCreate.js'));
this.GuildScheduledEventDelete = this.load(require('./GuildScheduledEventDelete.js'));
this.GuildScheduledEventUpdate = this.load(require('./GuildScheduledEventUpdate.js'));
this.GuildScheduledEventUserAdd = this.load(require('./GuildScheduledEventUserAdd.js'));
this.GuildScheduledEventUserRemove = this.load(require('./GuildScheduledEventUserRemove.js'));
this.GuildSoundboardSoundDelete = this.load(require('./GuildSoundboardSoundDelete.js'));
this.GuildStickerCreate = this.load(require('./GuildStickerCreate.js'));
this.GuildStickerDelete = this.load(require('./GuildStickerDelete.js'));
this.GuildStickerUpdate = this.load(require('./GuildStickerUpdate.js'));
this.GuildStickersUpdate = this.load(require('./GuildStickersUpdate.js'));
this.GuildUpdate = this.load(require('./GuildUpdate.js'));
this.InteractionCreate = this.load(require('./InteractionCreate.js'));
this.InviteCreate = this.load(require('./InviteCreate.js'));
this.InviteDelete = this.load(require('./InviteDelete.js'));
this.MessageCreate = this.load(require('./MessageCreate.js'));
this.MessageDelete = this.load(require('./MessageDelete.js'));
this.MessageDeleteBulk = this.load(require('./MessageDeleteBulk.js'));
this.MessagePollVoteAdd = this.load(require('./MessagePollVoteAdd.js'));
this.MessagePollVoteRemove = this.load(require('./MessagePollVoteRemove.js'));
this.MessageReactionAdd = this.load(require('./MessageReactionAdd.js'));
this.MessageReactionRemove = this.load(require('./MessageReactionRemove.js'));
this.MessageReactionRemoveAll = this.load(require('./MessageReactionRemoveAll.js'));
this.MessageReactionRemoveEmoji = this.load(require('./MessageReactionRemoveEmoji.js'));
this.MessageUpdate = this.load(require('./MessageUpdate.js'));
this.PresenceUpdate = this.load(require('./PresenceUpdate.js'));
this.StageInstanceCreate = this.load(require('./StageInstanceCreate.js'));
this.StageInstanceDelete = this.load(require('./StageInstanceDelete.js'));
this.StageInstanceUpdate = this.load(require('./StageInstanceUpdate.js'));
this.ThreadCreate = this.load(require('./ThreadCreate.js'));
this.ThreadDelete = this.load(require('./ThreadDelete.js'));
this.ThreadListSync = this.load(require('./ThreadListSync.js'));
this.ThreadMemberUpdate = this.load(require('./ThreadMemberUpdate.js'));
this.ThreadMembersUpdate = this.load(require('./ThreadMembersUpdate.js'));
this.TypingStart = this.load(require('./TypingStart.js'));
this.UserUpdate = this.load(require('./UserUpdate.js'));
this.VoiceStateUpdate = this.load(require('./VoiceStateUpdate.js'));
this.WebhooksUpdate = this.load(require('./WebhooksUpdate.js'));
}
register(Action) {
this[Action.name.replace(/Action$/, '')] = new Action(this.client);
load(Action) {
return new Action(this.client);
}
}

View File

@@ -11,11 +11,18 @@ class MessagePollVoteAddAction extends Action {
const message = this.getMessage(data, channel);
if (!message) return false;
const { poll } = message;
const poll = this.getPoll(data, message, channel);
if (!poll) return false;
const answer = poll?.answers.get(data.answer_id);
const answer = poll.answers.get(data.answer_id);
if (!answer) return false;
const user = this.getUser(data);
if (user) {
answer.voters._add(user);
}
answer.voteCount++;
/**

View File

@@ -11,12 +11,17 @@ class MessagePollVoteRemoveAction extends Action {
const message = this.getMessage(data, channel);
if (!message) return false;
const { poll } = message;
const poll = this.getPoll(data, message, channel);
if (!poll) return false;
const answer = poll?.answers.get(data.answer_id);
const answer = poll.answers.get(data.answer_id);
if (!answer) return false;
answer.voteCount--;
answer.voters.cache.delete(data.user_id);
if (answer.voteCount > 0) {
answer.voteCount--;
}
/**
* Emitted whenever a user removes their vote in a poll.

View File

@@ -19,6 +19,7 @@ const Status = require('../../util/Status');
const WebSocketShardEvents = require('../../util/WebSocketShardEvents');
let zlib;
let deprecationEmitted = false;
try {
zlib = require('zlib-sync');
@@ -379,6 +380,22 @@ class WebSocketManager extends EventEmitter {
/**
* Emitted when the client becomes ready to start working.
* @event Client#ready
* @deprecated Use {@link Client#event:clientReady} instead.
* @param {Client} client The client
*/
if (this.client.emit('ready', this.client) && !deprecationEmitted) {
deprecationEmitted = true;
process.emitWarning(
// eslint-disable-next-line max-len
'The ready event has been renamed to clientReady to distinguish it from the gateway READY event and will only emit under that name in v15. Please use clientReady instead.',
'DeprecationWarning',
);
}
/**
* Emitted when the client becomes ready to start working.
* @event Client#clientReady
* @param {Client} client The client
*/
this.client.emit(Events.ClientReady, this.client);

View File

@@ -32,10 +32,10 @@ const handlers = Object.fromEntries([
['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')],
['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')],
['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')],
['GUILD_SOUNDBOARD_SOUNDS_UPDATE', require('./GUILD_SOUNDBOARD_SOUNDS_UPDATE.js')],
['GUILD_SOUNDBOARD_SOUND_CREATE', require('./GUILD_SOUNDBOARD_SOUND_CREATE.js')],
['GUILD_SOUNDBOARD_SOUND_DELETE', require('./GUILD_SOUNDBOARD_SOUND_DELETE.js')],
['GUILD_SOUNDBOARD_SOUND_UPDATE', require('./GUILD_SOUNDBOARD_SOUND_UPDATE.js')],
['GUILD_SOUNDBOARD_SOUNDS_UPDATE', require('./GUILD_SOUNDBOARD_SOUNDS_UPDATE')],
['GUILD_SOUNDBOARD_SOUND_CREATE', require('./GUILD_SOUNDBOARD_SOUND_CREATE')],
['GUILD_SOUNDBOARD_SOUND_DELETE', require('./GUILD_SOUNDBOARD_SOUND_DELETE')],
['GUILD_SOUNDBOARD_SOUND_UPDATE', require('./GUILD_SOUNDBOARD_SOUND_UPDATE')],
['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')],
['GUILD_UPDATE', require('./GUILD_UPDATE')],
['INTERACTION_CREATE', require('./INTERACTION_CREATE')],
@@ -54,7 +54,7 @@ const handlers = Object.fromEntries([
['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')],
['READY', require('./READY')],
['RESUMED', require('./RESUMED')],
['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS.js')],
['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS')],
['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')],
['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')],
['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')],

View File

@@ -168,6 +168,8 @@
* @property {'ModalSubmitInteractionFieldNotFound'} ModalSubmitInteractionFieldNotFound
* @property {'ModalSubmitInteractionFieldType'} ModalSubmitInteractionFieldType
* @property {'ModalSubmitInteractionFieldEmpty'} ModalSubmitInteractionFieldEmpty
* @property {'ModalSubmitInteractionFieldInvalidChannelType'} ModalSubmitInteractionFieldInvalidChannelType
* @property {'InvalidMissingScopes'} InvalidMissingScopes
* @property {'InvalidScopesWithPermissions'} InvalidScopesWithPermissions
@@ -327,6 +329,8 @@ const keys = [
'ModalSubmitInteractionFieldNotFound',
'ModalSubmitInteractionFieldType',
'ModalSubmitInteractionFieldEmpty',
'ModalSubmitInteractionFieldInvalidChannelType',
'InvalidMissingScopes',
'InvalidScopesWithPermissions',

View File

@@ -161,6 +161,10 @@ const Messages = {
`Required field with custom id "${customId}" not found.`,
[DjsErrorCodes.ModalSubmitInteractionFieldType]: (customId, type, expected) =>
`Field with custom id "${customId}" is of type: ${type}; expected ${expected}.`,
[DjsErrorCodes.ModalSubmitInteractionFieldEmpty]: (customId, type) =>
`Required field with custom id "${customId}" is of type: ${type}; expected a non-empty value.`,
[DjsErrorCodes.ModalSubmitInteractionFieldInvalidChannelType]: (customId, type, expected) =>
`The type of channel of the field with custom id "${customId}" is: ${type}; expected ${expected}.`,
[DjsErrorCodes.InvalidMissingScopes]: 'At least one valid scope must be provided for the invite',
[DjsErrorCodes.InvalidScopesWithPermissions]: 'Permissions cannot be set without the bot scope.',

View File

@@ -33,6 +33,7 @@ exports.Events = require('./util/Events');
exports.Formatters = require('./util/Formatters');
exports.GuildMemberFlagsBitField = require('./util/GuildMemberFlagsBitField').GuildMemberFlagsBitField;
exports.IntentsBitField = require('./util/IntentsBitField');
exports.InviteFlagsBitField = require('./util/InviteFlagsBitField.js').InviteFlagsBitField;
exports.LimitedCollection = require('./util/LimitedCollection');
exports.MessageFlagsBitField = require('./util/MessageFlagsBitField');
exports.Options = require('./util/Options');
@@ -80,6 +81,7 @@ exports.GuildStickerManager = require('./managers/GuildStickerManager');
exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager');
exports.MessageManager = require('./managers/MessageManager');
exports.PermissionOverwriteManager = require('./managers/PermissionOverwriteManager');
exports.PollAnswerVoterManager = require('./managers/PollAnswerVoterManager.js').PollAnswerVoterManager;
exports.PresenceManager = require('./managers/PresenceManager');
exports.ReactionManager = require('./managers/ReactionManager');
exports.ReactionUserManager = require('./managers/ReactionUserManager');
@@ -126,6 +128,7 @@ exports.CommandInteractionOptionResolver = require('./structures/CommandInteract
exports.Component = require('./structures/Component');
exports.ContainerComponent = require('./structures/ContainerComponent');
exports.ContextMenuCommandInteraction = require('./structures/ContextMenuCommandInteraction');
exports.DirectoryChannel = require('./structures/DirectoryChannel');
exports.DMChannel = require('./structures/DMChannel');
exports.Embed = require('./structures/Embed');
exports.EmbedBuilder = require('./structures/EmbedBuilder');
@@ -159,6 +162,7 @@ exports.InteractionWebhook = require('./structures/InteractionWebhook');
exports.Invite = require('./structures/Invite');
exports.InviteStageInstance = require('./structures/InviteStageInstance');
exports.InviteGuild = require('./structures/InviteGuild');
exports.LabelComponent = require('./structures/LabelComponent');
exports.Message = require('./structures/Message').Message;
exports.Attachment = require('./structures/Attachment');
exports.AttachmentBuilder = require('./structures/AttachmentBuilder');

View File

@@ -230,13 +230,13 @@ class GuildChannelManager extends CachedManager {
async createWebhook({ channel, name, avatar, reason }) {
const id = this.resolveId(channel);
if (!id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable');
if (typeof avatar === 'string' && !avatar.startsWith('data:')) {
avatar = await resolveImage(avatar);
}
const resolvedImage = await resolveImage(avatar);
const data = await this.client.rest.post(Routes.channelWebhooks(id), {
body: {
name,
avatar,
avatar: resolvedImage,
},
reason,
});

View File

@@ -163,6 +163,7 @@ class GuildManager extends CachedManager {
* Creates a guild.
* <warn>This is only available to bots in fewer than 10 guilds.</warn>
* @param {GuildCreateOptions} options Options for creating the guild
* @deprecated API related to guild ownership may no longer be used.
* @returns {Promise<Guild>} The guild that was created
*/
async create({

View File

@@ -1,5 +1,6 @@
'use strict';
const { process } = require('node:process');
const { setTimeout, clearTimeout } = require('node:timers');
const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
@@ -10,10 +11,13 @@ const { DiscordjsError, DiscordjsTypeError, DiscordjsRangeError, ErrorCodes } =
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
const { GuildMember } = require('../structures/GuildMember');
const { Role } = require('../structures/Role');
const { resolveImage } = require('../util/DataResolver');
const Events = require('../util/Events');
const { GuildMemberFlagsBitField } = require('../util/GuildMemberFlagsBitField');
const Partials = require('../util/Partials');
let deprecatedEmittedForEditSoleNickname = false;
/**
* Manages API methods for GuildMembers and stores their cache.
* @extends {CachedManager}
@@ -336,8 +340,8 @@ class GuildMemberManager extends CachedManager {
*/
/**
* Edits a member of the guild.
* <info>The user must be a member of the guild</info>
* Edits a member of a guild.
*
* @param {UserResolvable} user The member to edit
* @param {GuildMemberEditOptions} options The options to provide
* @returns {Promise<GuildMember>}
@@ -372,13 +376,30 @@ class GuildMemberManager extends CachedManager {
}
let endpoint;
if (id === this.client.user.id) {
const keys = Object.keys(options);
if (keys.length === 1 && keys[0] === 'nick') endpoint = Routes.guildMember(this.guild.id);
else endpoint = Routes.guildMember(this.guild.id, id);
} else {
endpoint = Routes.guildMember(this.guild.id, id);
if (keys.length === 1 && keys[0] === 'nick') {
// For modifying the current application's nickname only, we use the /guilds/{guild.id}/members/@me endpoint.
// This endpoint only requires the CHANGE_NICKNAME permission.
// The other endpoint would require the MANAGE_NICKNAMES permission.
// In v15, this will be split out, so emit a deprecation.
endpoint = Routes.guildMember(this.guild.id, '@me');
if (!deprecatedEmittedForEditSoleNickname) {
process.emitWarning(
// eslint-disable-next-line max-len
"You should use GuildMemberManager#editMe() when changing your nickname. Due to Discord's API changes, GuildMemberManager#edit() will end up requiring MANAGE_NICKNAMES in v15.",
'DeprecationWarning',
);
deprecatedEmittedForEditSoleNickname = true;
}
}
}
endpoint ??= Routes.guildMember(this.guild.id, id);
const d = await this.client.rest.patch(endpoint, { body: options, reason });
const clone = this.cache.get(id)?._clone();
@@ -386,6 +407,38 @@ class GuildMemberManager extends CachedManager {
return clone ?? this._add(d, false);
}
/**
* The data for editing the current application's guild member.
*
* @typedef {Object} GuildMemberEditMeOptions
* @property {?string} [nick] The nickname to set
* @property {?(BufferResolvable|Base64Resolvable)} [banner] The banner to set
* @property {?(BufferResolvable|Base64Resolvable)} [avatar] The avatar to set
* @property {?string} [bio] The bio to set
* @property {string} [reason] The reason to use
*/
/**
* Edits the current application's guild member in a guild.
*
* @param {GuildMemberEditMeOptions} options The options to provide
* @returns {Promise<GuildMember>}
*/
async editMe({ reason, ...options }) {
const data = await this.client.rest.patch(Routes.guildMember(this.guild.id, '@me'), {
body: {
...options,
banner: options.banner && (await resolveImage(options.banner)),
avatar: options.avatar && (await resolveImage(options.avatar)),
},
reason,
});
const clone = this.me?._clone();
clone?._patch(data);
return clone ?? this._add(data, false);
}
/**
* Options used for pruning guild members.
* <info>It's recommended to set {@link GuildPruneMembersOptions#count options.count}

View File

@@ -65,7 +65,7 @@ class GuildMemberRoleManager extends DataManager {
* @readonly
*/
get color() {
const coloredRoles = this.cache.filter(role => role.color);
const coloredRoles = this.cache.filter(role => role.colors.primaryColor);
if (!coloredRoles.size) return null;
return coloredRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev));
}

View File

@@ -1,5 +1,6 @@
'use strict';
const process = require('node:process');
const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v10');
@@ -10,6 +11,8 @@ const MessagePayload = require('../structures/MessagePayload');
const { MakeCacheOverrideSymbol } = require('../util/Symbols');
const { resolvePartialEmoji } = require('../util/Util');
let deprecationEmittedForFetchPinned = false;
/**
* Manages API methods for Messages and holds their cache.
* @extends {CachedManager}
@@ -116,19 +119,83 @@ class MessageManager extends CachedManager {
return data.reduce((_data, message) => _data.set(message.id, this._add(message, options.cache)), new Collection());
}
/**
* Options used to fetch pinned messages.
*
* @typedef {Object} FetchPinnedMessagesOptions
* @property {DateResolvable} [before] Consider only pinned messages before this time
* @property {number} [limit] The maximum number of pinned messages to return
* @property {boolean} [cache] Whether to cache the pinned messages
*/
/**
* Data returned from fetching pinned messages.
*
* @typedef {Object} FetchPinnedMessagesResponse
* @property {MessagePin[]} items The pinned messages
* @property {boolean} hasMore Whether there are additional pinned messages that require a subsequent call
*/
/**
* Pinned message data returned from fetching pinned messages.
*
* @typedef {Object} MessagePin
* @property {Date} pinnedAt The time the message was pinned at
* @property {number} pinnedTimestamp The timestamp the message was pinned at
* @property {Message} message The pinned message
*/
/**
* Fetches the pinned messages of this channel and returns a collection of them.
* <info>The returned Collection does not contain any reaction data of the messages.
* Those need to be fetched separately.</info>
*
* @param {FetchPinnedMessagesOptions} [options={}] Options for fetching pinned messages
* @returns {Promise<FetchPinnedMessagesResponse>}
* @example
* // Get pinned messages
* channel.messages.fetchPins()
* .then(messages => console.log(`Received ${messages.items.length} messages`))
* .catch(console.error);
*/
async fetchPins(options = {}) {
const data = await this.client.rest.get(Routes.channelMessagesPins(this.channel.id), {
query: makeURLSearchParams({
...options,
before: options.before && new Date(options.before).toISOString(),
}),
});
return {
items: data.items.map(item => ({
pinnedTimestamp: Date.parse(item.pinned_at),
get pinnedAt() {
return new Date(this.pinnedTimestamp);
},
message: this._add(item.message, options.cache),
})),
hasMore: data.has_more,
};
}
/**
* Fetches the pinned messages of this channel and returns a collection of them.
* <info>The returned Collection does not contain any reaction data of the messages.
* Those need to be fetched separately.</info>
* @param {boolean} [cache=true] Whether to cache the message(s)
* @deprecated Use {@link MessageManager#fetchPins} instead.
* @returns {Promise<Collection<Snowflake, Message>>}
* @example
* // Get pinned messages
* channel.messages.fetchPinned()
* .then(messages => console.log(`Received ${messages.size} messages`))
* .catch(console.error);
*/
async fetchPinned(cache = true) {
if (!deprecationEmittedForFetchPinned) {
process.emitWarning(
'The MessageManager#fetchPinned() method is deprecated. Use MessageManager#fetchPins() instead.',
'DeprecationWarning',
);
deprecationEmittedForFetchPinned = true;
}
const data = await this.client.rest.get(Routes.channelPins(this.channel.id));
const messages = new Collection();
for (const message of data) messages.set(message.id, this._add(message, cache));
@@ -219,7 +286,7 @@ class MessageManager extends CachedManager {
message = this.resolveId(message);
if (!message) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable');
await this.client.rest.put(Routes.channelPin(this.channel.id, message), { reason });
await this.client.rest.put(Routes.channelMessagesPin(this.channel.id, message), { reason });
}
/**
@@ -232,7 +299,7 @@ class MessageManager extends CachedManager {
message = this.resolveId(message);
if (!message) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable');
await this.client.rest.delete(Routes.channelPin(this.channel.id, message), { reason });
await this.client.rest.delete(Routes.channelMessagesPin(this.channel.id, message), { reason });
}
/**

View File

@@ -0,0 +1,50 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager.js');
const User = require('../structures/User.js');
/**
* Manages API methods for users who voted on a poll and stores their cache.
* @extends {CachedManager}
*/
class PollAnswerVoterManager extends CachedManager {
constructor(answer) {
super(answer.client, User);
/**
* The poll answer that this manager belongs to
* @type {PollAnswer}
*/
this.answer = answer;
}
/**
* The cache of this manager
* @type {Collection<Snowflake, User>}
* @name PollAnswerVoterManager#cache
*/
/**
* Fetches the users that voted on this poll answer. Resolves with a collection of users, mapped by their ids.
* @param {BaseFetchPollAnswerVotersOptions} [options={}] Options for fetching the users
* @returns {Promise<Collection<Snowflake, User>>}
*/
async fetch({ after, limit } = {}) {
const poll = this.answer.poll;
const query = makeURLSearchParams({ limit, after });
const data = await this.client.rest.get(Routes.pollAnswerVoters(poll.channelId, poll.messageId, this.answer.id), {
query,
});
return data.users.reduce((coll, rawUser) => {
const user = this.client.users._add(rawUser);
this.cache.set(user.id, user);
return coll.set(user.id, user);
}, new Collection());
}
}
exports.PollAnswerVoterManager = PollAnswerVoterManager;

View File

@@ -12,6 +12,8 @@ const PermissionsBitField = require('../util/PermissionsBitField');
const { setPosition, resolveColor } = require('../util/Util');
let cacheWarningEmitted = false;
let deprecationEmittedForCreate = false;
let deprecationEmittedForEdit = false;
/**
* Manages API methods for roles and stores their cache.
@@ -58,7 +60,7 @@ class RoleManager extends CachedManager {
* @example
* // Fetch a single role
* message.guild.roles.fetch('222078108977594368')
* .then(role => console.log(`The role color is: ${role.color}`))
* .then(role => console.log(`The role color is: ${role.colors.primaryColor}`))
* .catch(console.error);
*/
async fetch(id, { cache = true, force = false } = {}) {
@@ -112,11 +114,24 @@ class RoleManager extends CachedManager {
* @returns {?Snowflake}
*/
/**
* @typedef {Object} RoleColorsResolvable
* @property {ColorResolvable} primaryColor The primary color of the role
* @property {ColorResolvable} [secondaryColor] The secondary color of the role.
* This will make the role a gradient between the other provided colors
* @property {ColorResolvable} [tertiaryColor] The tertiary color of the role.
* When sending `tertiaryColor` the API enforces the role color to be a holographic style
* with values of `primaryColor = 11127295`, `secondaryColor = 16759788`, and `tertiaryColor = 16761760`.
* These values are available as a constant: `Constants.HolographicStyle`
*/
/**
* Options used to create a new role.
* @typedef {Object} RoleCreateOptions
* @property {string} [name] The name of the new role
* @property {ColorResolvable} [color] The data to create the role with
* <warn>This property is deprecated. Use `colors` instead.</warn>
* @property {RoleColorsResolvable} [colors] The colors to create the role with
* @property {boolean} [hoist] Whether or not the new role should be hoisted
* @property {PermissionResolvable} [permissions] The permissions for the new role
* @property {number} [position] The position of the new role
@@ -142,15 +157,30 @@ class RoleManager extends CachedManager {
* // Create a new role with data and a reason
* guild.roles.create({
* name: 'Super Cool Blue People',
* color: Colors.Blue,
* reason: 'we needed a role for Super Cool People',
* colors: {
* primaryColor: Colors.Blue,
* },
* })
* .then(console.log)
* .catch(console.error);
* @example
* // Create a role with holographic colors
* guild.roles.create({
* name: 'Holographic Role',
* reason: 'Creating a role with holographic effect',
* colors: {
* primaryColor: Constants.HolographicStyle.Primary,
* secondaryColor: Constants.HolographicStyle.Secondary,
* tertiaryColor: Constants.HolographicStyle.Tertiary,
* },
* })
* .then(console.log)
* .catch(console.error);
*/
async create(options = {}) {
let { name, color, hoist, permissions, position, mentionable, reason, icon, unicodeEmoji } = options;
color &&= resolveColor(color);
let { permissions, icon } = options;
const { name, color, hoist, position, mentionable, reason, unicodeEmoji } = options;
if (permissions !== undefined) permissions = new PermissionsBitField(permissions);
if (icon) {
const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL();
@@ -158,10 +188,30 @@ class RoleManager extends CachedManager {
if (typeof icon !== 'string') icon = undefined;
}
let colors = options.colors && {
primary_color: resolveColor(options.colors.primaryColor),
secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
};
if (color !== undefined) {
if (!deprecationEmittedForCreate) {
process.emitWarning(`Passing "color" to RoleManager#create() is deprecated. Use "colors" instead.`);
}
deprecationEmittedForCreate = true;
colors = {
primary_color: resolveColor(color),
secondary_color: null,
tertiary_color: null,
};
}
const data = await this.client.rest.post(Routes.guildRoles(this.guild.id), {
body: {
name,
color,
colors,
hoist,
permissions,
mentionable,
@@ -210,9 +260,29 @@ class RoleManager extends CachedManager {
if (typeof icon !== 'string') icon = undefined;
}
let colors = options.colors && {
primary_color: resolveColor(options.colors.primaryColor),
secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
};
if (options.color !== undefined) {
if (!deprecationEmittedForEdit) {
process.emitWarning(`Passing "color" to RoleManager#edit() is deprecated. Use "colors" instead.`);
}
deprecationEmittedForEdit = true;
colors = {
primary_color: resolveColor(options.color),
secondary_color: null,
tertiary_color: null,
};
}
const body = {
name: options.name,
color: options.color === undefined ? undefined : resolveColor(options.color),
colors,
hoist: options.hoist,
permissions: options.permissions === undefined ? undefined : new PermissionsBitField(options.permissions),
mentionable: options.mentionable,

View File

@@ -73,7 +73,7 @@ class ApplicationCommand extends Base {
if ('name_localizations' in data) {
/**
* The name localizations for this command
* @type {?Object<Locale, string>}
* @type {?LocalizationMap}
*/
this.nameLocalizations = data.name_localizations;
} else {
@@ -101,7 +101,7 @@ class ApplicationCommand extends Base {
if ('description_localizations' in data) {
/**
* The description localizations for this command
* @type {?Object<Locale, string>}
* @type {?LocalizationMap}
*/
this.descriptionLocalizations = data.description_localizations;
} else {
@@ -227,11 +227,11 @@ class ApplicationCommand extends Base {
* @typedef {Object} ApplicationCommandData
* @property {string} name The name of the command, must be in all lowercase if type is
* {@link ApplicationCommandType.ChatInput}
* @property {Object<Locale, string>} [nameLocalizations] The localizations for the command name
* @property {LocalizationMap} [nameLocalizations] The localizations for the command name
* @property {string} description The description of the command,
* if type is {@link ApplicationCommandType.ChatInput} or {@link ApplicationCommandType.PrimaryEntryPoint}
* @property {boolean} [nsfw] Whether the command is age-restricted
* @property {Object<Locale, string>} [descriptionLocalizations] The localizations for the command description,
* @property {LocalizationMap} [descriptionLocalizations] The localizations for the command description,
* if type is {@link ApplicationCommandType.ChatInput} or {@link ApplicationCommandType.PrimaryEntryPoint}
* @property {ApplicationCommandType} [type=ApplicationCommandType.ChatInput] The type of the command
* @property {ApplicationCommandOptionData[]} [options] Options for the command
@@ -253,9 +253,9 @@ class ApplicationCommand extends Base {
* @typedef {Object} ApplicationCommandOptionData
* @property {ApplicationCommandOptionType} type The type of the option
* @property {string} name The name of the option
* @property {Object<Locale, string>} [nameLocalizations] The name localizations for the option
* @property {LocalizationMap} [nameLocalizations] The name localizations for the option
* @property {string} description The description of the option
* @property {Object<Locale, string>} [descriptionLocalizations] The description localizations for the option
* @property {LocalizationMap} [descriptionLocalizations] The description localizations for the option
* @property {boolean} [autocomplete] Whether the autocomplete interaction is enabled for a
* {@link ApplicationCommandOptionType.String}, {@link ApplicationCommandOptionType.Integer} or
* {@link ApplicationCommandOptionType.Number} option
@@ -277,7 +277,7 @@ class ApplicationCommand extends Base {
/**
* @typedef {Object} ApplicationCommandOptionChoiceData
* @property {string} name The name of the choice
* @property {Object<Locale, string>} [nameLocalizations] The localized names for this choice
* @property {LocalizationMap} [nameLocalizations] The localized names for this choice
* @property {string|number} value The value of the choice
*/
@@ -308,11 +308,11 @@ class ApplicationCommand extends Base {
/**
* Edits the localized names of this ApplicationCommand
* @param {Object<Locale, string>} nameLocalizations The new localized names for the command
* @param {LocalizationMap} nameLocalizations The new localized names for the command
* @returns {Promise<ApplicationCommand>}
* @example
* // Edit the name localizations of this command
* command.setLocalizedNames({
* command.setNameLocalizations({
* 'en-GB': 'test',
* 'pt-BR': 'teste',
* })
@@ -334,7 +334,7 @@ class ApplicationCommand extends Base {
/**
* Edits the localized descriptions of this ApplicationCommand
* @param {Object<Locale, string>} descriptionLocalizations The new localized descriptions for the command
* @param {LocalizationMap} descriptionLocalizations The new localized descriptions for the command
* @returns {Promise<ApplicationCommand>}
* @example
* // Edit the description localizations of this command
@@ -550,10 +550,10 @@ class ApplicationCommand extends Base {
* @typedef {Object} ApplicationCommandOption
* @property {ApplicationCommandOptionType} type The type of the option
* @property {string} name The name of the option
* @property {Object<Locale, string>} [nameLocalizations] The localizations for the option name
* @property {LocalizationMap} [nameLocalizations] The localizations for the option name
* @property {string} [nameLocalized] The localized name for this option
* @property {string} description The description of the option
* @property {Object<Locale, string>} [descriptionLocalizations] The localizations for the option description
* @property {LocalizationMap} [descriptionLocalizations] The localizations for the option description
* @property {string} [descriptionLocalized] The localized description for this option
* @property {boolean} [required] Whether the option is required
* @property {boolean} [autocomplete] Whether the autocomplete interaction is enabled for a

View File

@@ -16,37 +16,42 @@ class ApplicationEmoji extends Emoji {
*/
this.application = application;
/**
* The user who created this emoji
* @type {?User}
*/
this.author = null;
this.managed = null;
this.requiresColons = null;
this._patch(data);
}
_patch(data) {
if ('name' in data) this.name = data.name;
if (data.user) this.author = this.client.users._add(data.user);
if (data.user) {
/**
* The user who created this emoji
* @type {User}
*/
this.author = this.client.users._add(data.user);
}
if ('managed' in data) {
/**
* Whether this emoji is managed by an external service
* @type {?boolean}
* Whether this emoji is managed by an external service. Always `false` for application emojis
* @type {false}
*/
this.managed = data.managed;
}
if ('require_colons' in data) {
/**
* Whether or not this emoji requires colons surrounding it
* @type {?boolean}
* Whether this emoji requires colons surrounding it. Always `true` for application emojis
* @type {true}
*/
this.requiresColons = data.require_colons;
}
if ('available' in data) {
/**
* Whether this emoji is available. Always `true` for application emojis
* @type {true}
*/
this.available = data.available;
}
}
/**
@@ -107,7 +112,8 @@ class ApplicationEmoji extends Emoji {
other.id === this.id &&
other.name === this.name &&
other.managed === this.managed &&
other.requiresColons === this.requiresColons
other.requiresColons === this.requiresColons &&
other.available === this.available
);
}
@@ -115,4 +121,49 @@ class ApplicationEmoji extends Emoji {
}
}
/**
* The emoji's name
* @name name
* @memberof ApplicationEmoji
* @instance
* @type {string}
* @readonly
*/
/**
* Whether the emoji is animated
* @name animated
* @memberof ApplicationEmoji
* @instance
* @type {boolean}
* @readonly
*/
/**
* Returns a URL for the emoji.
* @method imageURL
* @memberof ApplicationEmoji
* @instance
* @param {EmojiURLOptions} [options] Options for the image URL
* @returns {string}
*/
/**
* The time the emoji was created at
* @name createdAt
* @memberof ApplicationEmoji
* @instance
* @type {Date}
* @readonly
*/
/**
* The timestamp the emoji was created at
* @name createdTimestamp
* @memberof ApplicationEmoji
* @instance
* @type {number}
* @readonly
*/
module.exports = ApplicationEmoji;

View File

@@ -13,7 +13,7 @@ class ApplicationRoleConnectionMetadata {
/**
* The name localizations for this metadata field
* @type {?Object<Locale, string>}
* @type {?LocalizationMap}
*/
this.nameLocalizations = data.name_localizations ?? null;
@@ -25,7 +25,7 @@ class ApplicationRoleConnectionMetadata {
/**
* The description localizations for this metadata field
* @type {?Object<Locale, string>}
* @type {?LocalizationMap}
*/
this.descriptionLocalizations = data.description_localizations ?? null;

View File

@@ -58,7 +58,7 @@ class BaseGuildEmoji extends Emoji {
* @method imageURL
* @memberof BaseGuildEmoji
* @instance
* @param {BaseImageURLOptions} [options] Options for the image URL
* @param {EmojiURLOptions} [options] Options for the emoji URL
* @returns {string}
*/
@@ -72,4 +72,40 @@ class BaseGuildEmoji extends Emoji {
* @deprecated Use {@link BaseGuildEmoji#imageURL} instead.
*/
/**
* The emoji's name
* @name name
* @memberof BaseGuildEmoji
* @instance
* @type {string}
* @readonly
*/
/**
* Whether or not the emoji is animated
* @name animated
* @memberof BaseGuildEmoji
* @instance
* @type {boolean}
* @readonly
*/
/**
* The time the emoji was created at.
* @name createdAt
* @memberof BaseGuildEmoji
* @instance
* @type {Date}
* @readonly
*/
/**
* The timestamp the emoji was created at.
* @name createdTimestamp
* @memberof BaseGuildEmoji
* @instance
* @type {number}
* @readonly
*/
module.exports = BaseGuildEmoji;

View File

@@ -163,6 +163,17 @@ class ClientApplication extends Application {
this.approximateUserInstallCount ??= null;
}
if ('approximate_user_authorization_count' in data) {
/**
* An approximate amount of users that have OAuth2 authorizations for this application.
*
* @type {?number}
*/
this.approximateUserAuthorizationCount = data.approximate_user_authorization_count;
} else {
this.approximateUserAuthorizationCount ??= null;
}
if ('guild_id' in data) {
/**
* The id of the guild associated with this application.
@@ -364,9 +375,9 @@ class ClientApplication extends Application {
* Data for creating or editing an application role connection metadata.
* @typedef {Object} ApplicationRoleConnectionMetadataEditOptions
* @property {string} name The name of the metadata field
* @property {?Object<Locale, string>} [nameLocalizations] The name localizations for the metadata field
* @property {?LocalizationMap} [nameLocalizations] The name localizations for the metadata field
* @property {string} description The description of the metadata field
* @property {?Object<Locale, string>} [descriptionLocalizations] The description localizations for the metadata field
* @property {?LocalizationMap} [descriptionLocalizations] The description localizations for the metadata field
* @property {string} key The dictionary key of the metadata field
* @property {ApplicationRoleConnectionMetadataType} type The type of the metadata field
*/

View File

@@ -64,8 +64,6 @@ class ClientUser extends User {
},
});
this.client.token = data.token;
this.client.rest.setToken(data.token);
const { updated } = this.client.actions.UserUpdate.handle(data);
return updated ?? this;
}

View File

@@ -80,12 +80,17 @@ class CommandInteraction extends BaseInteraction {
}
/**
* Represents the resolved data of a received command interaction.
* @typedef {Object} CommandInteractionResolvedData
* @typedef {Object} BaseInteractionResolvedData
* @property {Collection<Snowflake, User>} [users] The resolved users
* @property {Collection<Snowflake, GuildMember|APIGuildMember>} [members] The resolved guild members
* @property {Collection<Snowflake, Role|APIRole>} [roles] The resolved roles
* @property {Collection<Snowflake, BaseChannel|APIChannel>} [channels] The resolved channels
*/
/**
* Represents the resolved data of a received command interaction.
*
* @typedef {BaseInteractionResolvedData} CommandInteractionResolvedData
* @property {Collection<Snowflake, Message|APIMessage>} [messages] The resolved messages
* @property {Collection<Snowflake, Attachment>} [attachments] The resolved attachments
*/

View File

@@ -45,7 +45,7 @@ class Emoji extends Base {
/**
* Returns a URL for the emoji or `null` if this is not a custom emoji.
* @param {BaseImageURLOptions} [options] Options for the image URL
* @param {EmojiURLOptions} [options] Options for the emoji URL
* @returns {?string}
*/
imageURL(options) {

View File

@@ -832,6 +832,7 @@ class Guild extends AnonymousGuild {
* @property {number} [afkTimeout] The AFK timeout of the guild
* @property {?(BufferResolvable|Base64Resolvable)} [icon] The icon of the guild
* @property {GuildMemberResolvable} [owner] The owner of the guild
* <warn>This property is **deprecated** as API related to guild ownership may no longer be used.</warn>
* @property {?(BufferResolvable|Base64Resolvable)} [splash] The invite splash image of the guild
* @property {?(BufferResolvable|Base64Resolvable)} [discoverySplash] The discovery splash image of the guild
* @property {?(BufferResolvable|Base64Resolvable)} [banner] The banner of the guild
@@ -1197,12 +1198,7 @@ class Guild extends AnonymousGuild {
* @param {GuildMemberResolvable} owner The new owner of the guild
* @param {string} [reason] Reason for setting the new owner
* @returns {Promise<Guild>}
* @example
* // Edit the guild owner
* guild.setOwner(guild.members.cache.first())
* .then(guild => guild.fetchOwner())
* .then(owner => console.log(`Updated the guild owner to ${owner.displayName}`))
* .catch(console.error);
* @deprecated API related to guild ownership may no longer be used.
*/
setOwner(owner, reason) {
return this.edit({ owner, reason });
@@ -1345,11 +1341,7 @@ class Guild extends AnonymousGuild {
* @param {GuildMFALevel} level The MFA level
* @param {string} [reason] Reason for changing the guild's MFA level
* @returns {Promise<Guild>}
* @example
* // Set the MFA level of the guild to Elevated
* guild.setMFALevel(GuildMFALevel.Elevated)
* .then(guild => console.log("Set guild's MFA level to Elevated"))
* .catch(console.error);
* @deprecated API related to guild ownership may no longer be used.
*/
async setMFALevel(level, reason) {
await this.client.rest.post(Routes.guildMFA(this.id), {
@@ -1379,6 +1371,7 @@ class Guild extends AnonymousGuild {
/**
* Deletes the guild.
* @returns {Promise<Guild>}
* @deprecated API related to guild ownership may no longer be used.
* @example
* // Delete a guild
* guild.delete()

View File

@@ -250,10 +250,11 @@ class GuildChannel extends BaseChannel {
return new PermissionsBitField(PermissionsBitField.All).freeze();
}
const basePermissions = new PermissionsBitField([role.permissions, role.guild.roles.everyone.permissions]);
const everyoneOverwrites = this.permissionOverwrites.cache.get(this.guild.id);
const roleOverwrites = this.permissionOverwrites.cache.get(role.id);
return role.permissions
return basePermissions
.remove(everyoneOverwrites?.deny ?? PermissionsBitField.DefaultBit)
.add(everyoneOverwrites?.allow ?? PermissionsBitField.DefaultBit)
.remove(roleOverwrites?.deny ?? PermissionsBitField.DefaultBit)

View File

@@ -122,6 +122,20 @@ class GuildMember extends Base {
} else {
this.flags ??= new GuildMemberFlagsBitField().freeze();
}
if (data.avatar_decoration_data) {
/**
* The member avatar decoration's data
*
* @type {?AvatarDecorationData}
*/
this.avatarDecorationData = {
asset: data.avatar_decoration_data.asset,
skuId: data.avatar_decoration_data.sku_id,
};
} else {
this.avatarDecorationData = null;
}
}
_clone() {
@@ -166,6 +180,15 @@ class GuildMember extends Base {
return this.avatar && this.client.rest.cdn.guildMemberAvatar(this.guild.id, this.id, this.avatar, options);
}
/**
* A link to the member's avatar decoration.
*
* @returns {?string}
*/
avatarDecorationURL() {
return this.avatarDecorationData ? this.client.rest.cdn.avatarDecoration(this.avatarDecorationData.asset) : null;
}
/**
* A link to the member's banner.
* @param {ImageURLOptions} [options={}] Options for the banner URL
@@ -195,6 +218,16 @@ class GuildMember extends Base {
return this.bannerURL(options) ?? this.user.bannerURL(options);
}
/**
* A link to the member's guild avatar decoration if they have one.
* Otherwise, a link to their {@link User#avatarDecorationURL} will be returned.
*
* @returns {?string}
*/
displayAvatarDecorationURL() {
return this.avatarDecorationURL() ?? this.user.avatarDecorationURL();
}
/**
* The time this member joined the guild
* @type {?Date}
@@ -237,7 +270,7 @@ class GuildMember extends Base {
* @readonly
*/
get displayColor() {
return this.roles.color?.color ?? 0;
return this.roles.color?.colors.primaryColor ?? 0;
}
/**
@@ -389,7 +422,9 @@ class GuildMember extends Base {
* .catch(console.error);
*/
setNickname(nick, reason) {
return this.edit({ nick, reason });
return this.user.id === this.client.user.id
? this.guild.members.editMe({ nick, reason })
: this.edit({ nick, reason });
}
/**
@@ -499,7 +534,10 @@ class GuildMember extends Base {
this.communicationDisabledUntilTimestamp === member.communicationDisabledUntilTimestamp &&
this.flags.bitfield === member.flags.bitfield &&
(this._roles === member._roles ||
(this._roles.length === member._roles.length && this._roles.every((role, i) => role === member._roles[i])))
(this._roles.length === member._roles.length &&
this._roles.every((role, index) => role === member._roles[index]))) &&
this.avatarDecorationData?.asset === member.avatarDecorationData?.asset &&
this.avatarDecorationData?.skuId === member.avatarDecorationData?.skuId
);
}
@@ -525,6 +563,7 @@ class GuildMember extends Base {
json.bannerURL = this.bannerURL();
json.displayAvatarURL = this.displayAvatarURL();
json.displayBannerURL = this.displayBannerURL();
json.avatarDecorationURL = this.avatarDecorationURL();
return json;
}
}

View File

@@ -120,6 +120,7 @@ class GuildTemplate extends Base {
* @param {string} name The name of the guild
* @param {BufferResolvable|Base64Resolvable} [icon] The icon for the guild
* @returns {Promise<Guild>}
* @deprecated API related to guild ownership may no longer be used.
*/
async createGuild(name, icon) {
const { client } = this;

View File

@@ -6,6 +6,7 @@ const { GuildScheduledEvent } = require('./GuildScheduledEvent');
const IntegrationApplication = require('./IntegrationApplication');
const InviteStageInstance = require('./InviteStageInstance');
const { DiscordjsError, ErrorCodes } = require('../errors');
const { InviteFlagsBitField } = require('../util/InviteFlagsBitField.js');
/**
* Represents an invitation to a guild channel.
@@ -222,6 +223,17 @@ class Invite extends Base {
} else {
this.guildScheduledEvent ??= null;
}
if ('flags' in data) {
/**
* The flags of this invite.
*
* @type {Readonly<InviteFlagsBitField>}
*/
this.flags = new InviteFlagsBitField(data.flags).freeze();
} else {
this.flags ??= new InviteFlagsBitField().freeze();
}
}
/**

View File

@@ -0,0 +1,54 @@
'use strict';
const Component = require('./Component');
const { createComponent } = require('../util/Components');
/**
* Represents a label component
*
* @extends {Component}
*/
class LabelComponent extends Component {
constructor({ component, ...data }) {
super(data);
/**
* The component in this label
*
* @type {Component}
* @readonly
*/
this.component = createComponent(component);
}
/**
* The label of the component
*
* @type {string}
* @readonly
*/
get label() {
return this.data.label;
}
/**
* The description of this component
*
* @type {?string}
* @readonly
*/
get description() {
return this.data.description ?? null;
}
/**
* Returns the API-compatible JSON for this component
*
* @returns {APILabelComponent}
*/
toJSON() {
return { ...this.data, component: this.component.toJSON() };
}
}
module.exports = LabelComponent;

View File

@@ -442,11 +442,15 @@ class Message extends Base {
}
if (data.poll) {
/**
* The poll that was sent with the message
* @type {?Poll}
*/
this.poll = new Poll(this.client, data.poll, this);
if (this.poll) {
this.poll._patch(data.poll);
} else {
/**
* The poll that was sent with the message
* @type {?Poll}
*/
this.poll = new Poll(this.client, data.poll, this, this.channel);
}
} else {
this.poll ??= null;
}
@@ -619,7 +623,7 @@ class Message extends Base {
* Similar to createReactionCollector but in promise form.
* Resolves with a collection of reactions that pass the specified filter.
* @param {AwaitReactionsOptions} [options={}] Optional options to pass to the internal collector
* @returns {Promise<Collection<string | Snowflake, MessageReaction>>}
* @returns {Promise<Collection<string|Snowflake, MessageReaction>>}
* @example
* // Create a reaction collector
* const filter = (reaction, user) => reaction.emoji.name === '👌' && user.id === 'someId'
@@ -812,6 +816,7 @@ class Message extends Base {
return Boolean(
channel?.type === ChannelType.GuildAnnouncement &&
!this.flags.has(MessageFlags.Crossposted) &&
this.reference?.type !== MessageReferenceType.Forward &&
this.type === MessageType.Default &&
!this.poll &&
channel.viewable &&

View File

@@ -4,23 +4,48 @@ const { Collection } = require('@discordjs/collection');
const { ComponentType } = require('discord-api-types/v10');
const { DiscordjsTypeError, ErrorCodes } = require('../errors');
/**
* @typedef {Object} ModalSelectedMentionables
* @property {Collection<Snowflake, User>} users The selected users
* @property {Collection<Snowflake, GuildMember | APIGuildMember>} members The selected members
* @property {Collection<Snowflake, Role | APIRole>} roles The selected roles
*/
/**
* Represents the serialized fields from a modal submit interaction
*/
class ModalSubmitFields {
constructor(components) {
constructor(components, resolved) {
/**
* The components within the modal
* @type {ActionRowModalData[]}
*
* @type {Array<ActionRowModalData|LabelModalData|TextDisplayModalData>}
*/
this.components = components;
/**
* The interaction resolved data
*
* @name ModalSubmitFields#resolved
* @type {?Readonly<BaseInteractionResolvedData>}
*/
Object.defineProperty(this, 'resolved', { value: resolved ? Object.freeze(resolved) : null });
/**
* The extracted fields from the modal
* @type {Collection<string, ModalData>}
*/
this.fields = components.reduce((accumulator, next) => {
next.components.forEach(component => accumulator.set(component.customId, component));
// For legacy support of action rows
if ('components' in next) {
for (const component of next.components) accumulator.set(component.customId, component);
}
// For label components
if ('component' in next) {
accumulator.set(next.component.customId, next.component);
}
return accumulator;
}, new Collection());
}
@@ -42,13 +67,154 @@ class ModalSubmitFields {
return field;
}
/**
* Gets a component by custom id and property and checks its type.
*
* @param {string} customId The custom id of the component.
* @param {ComponentType[]} allowedTypes The allowed types of the component.
* @param {string[]} properties The properties to check for for `required`.
* @param {boolean} required Whether to throw an error if the component value(s) are not found.
* @returns {ModalData} The option, if found.
* @private
*/
_getTypedComponent(customId, allowedTypes, properties, required) {
const component = this.getField(customId);
if (!allowedTypes.includes(component.type)) {
throw new DiscordjsTypeError(
ErrorCodes.ModalSubmitInteractionFieldNotFound,
customId,
component.type,
allowedTypes.join(', '),
);
} else if (required && properties.every(prop => component[prop] === null || component[prop] === undefined)) {
throw new DiscordjsTypeError(ErrorCodes.ModalSubmitInteractionFieldEmpty, customId, component.type);
}
return component;
}
/**
* Gets the value of a text input component given a custom id
* @param {string} customId The custom id of the text input component
* @returns {string}
*/
getTextInputValue(customId) {
return this.getField(customId, ComponentType.TextInput).value;
return this._getTypedComponent(customId, [ComponentType.TextInput]).value;
}
/**
* Gets the values of a string select component given a custom id
*
* @param {string} customId The custom id of the string select component
* @returns {string[]}
*/
getStringSelectValues(customId) {
return this._getTypedComponent(customId, [ComponentType.StringSelect]).values;
}
/**
* Gets users component
*
* @param {string} customId The custom id of the component
* @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty
* @returns {?Collection<Snowflake, User>} The selected users, or null if none were selected and not required
*/
getSelectedUsers(customId, required = false) {
const component = this._getTypedComponent(
customId,
[ComponentType.UserSelect, ComponentType.MentionableSelect],
['users'],
required,
);
return component.users ?? null;
}
/**
* Gets roles component
*
* @param {string} customId The custom id of the component
* @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty
* @returns {?Collection<Snowflake, Role|APIRole>} The selected roles, or null if none were selected and not required
*/
getSelectedRoles(customId, required = false) {
const component = this._getTypedComponent(
customId,
[ComponentType.RoleSelect, ComponentType.MentionableSelect],
['roles'],
required,
);
return component.roles ?? null;
}
/**
* Gets channels component
*
* @param {string} customId The custom id of the component
* @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty
* @param {ChannelType[]} [channelTypes=[]] The allowed types of channels. If empty, all channel types are allowed.
* @returns {?Collection<Snowflake, GuildChannel|ThreadChannel|APIChannel>} The selected channels,
* or null if none were selected and not required
*/
getSelectedChannels(customId, required = false, channelTypes = []) {
const component = this._getTypedComponent(customId, [ComponentType.ChannelSelect], ['channels'], required);
const channels = component.channels;
if (channels && channelTypes.length > 0) {
for (const channel of channels.values()) {
if (!channelTypes.includes(channel.type)) {
throw new DiscordjsTypeError(
ErrorCodes.ModalSubmitInteractionComponentInvalidChannelType,
customId,
channel.type,
channelTypes.join(', '),
);
}
}
}
return channels ?? null;
}
/**
* Gets members component
*
* @param {string} customId The custom id of the component
* @returns {?Collection<Snowflake, GuildMember|APIGuildMember>} The selected members,
* or null if none were selected or the users were not present in the guild
*/
getSelectedMembers(customId) {
const component = this._getTypedComponent(
customId,
[ComponentType.UserSelect, ComponentType.MentionableSelect],
['members'],
false,
);
return component.members ?? null;
}
/**
* Gets mentionables component
*
* @param {string} customId The custom id of the component
* @param {boolean} [required=false] Whether to throw an error if the component value is not found or empty
* @returns {?ModalSelectedMentionables} The selected mentionables, or null if none were selected and not required
*/
getSelectedMentionables(customId, required = false) {
const component = this._getTypedComponent(
customId,
[ComponentType.MentionableSelect],
['users', 'members', 'roles'],
required,
);
if (component.users || component.members || component.roles) {
return {
users: component.users ?? new Collection(),
members: component.members ?? new Collection(),
roles: component.roles ?? new Collection(),
};
}
return null;
}
}

View File

@@ -1,24 +1,53 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { lazy } = require('@discordjs/util');
const BaseInteraction = require('./BaseInteraction');
const InteractionWebhook = require('./InteractionWebhook');
const ModalSubmitFields = require('./ModalSubmitFields');
const InteractionResponses = require('./interfaces/InteractionResponses');
const { transformResolved } = require('../util/Util');
const getMessage = lazy(() => require('./Message').Message);
/**
* @typedef {Object} ModalData
* @property {string} value The value of the field
* @typedef {Object} BaseModalData
* @property {ComponentType} type The component type of the field
* @property {string} customId The custom id of the field
* @property {number} id The id of the field
*/
/**
* @typedef {Object} ActionRowModalData
* @property {ModalData[]} components The components of this action row
* @property {ComponentType} type The component type of the action row
* @typedef {BaseModalData} TextInputModalData
* @property {string} customId The custom id of the field
* @property {string} value The value of the field
*/
/**
* @typedef {BaseModalData} SelectMenuModalData
* @property {string} customId The custom id of the field
* @property {string[]} values The values of the field
* @property {Collection<string, GuildMember|APIGuildMember>} [members] The resolved members
* @property {Collection<string, User|APIUser>} [users] The resolved users
* @property {Collection<string, Role|APIRole>} [roles] The resolved roles
* @property {Collection<string, BaseChannel|APIChannel>} [channels] The resolved channels
*/
/**
* @typedef {BaseModalData} TextDisplayModalData
*/
/**
* @typedef {SelectMenuModalData|TextInputModalData} ModalData
*/
/**
* @typedef {BaseModalData} LabelModalData
* @property {ModalData} component The component within the label
*/
/**
* @typedef {BaseModalData} ActionRowModalData
* @property {TextInputModalData[]} components The components of this action row
*/
/**
@@ -47,15 +76,21 @@ class ModalSubmitInteraction extends BaseInteraction {
/**
* The components within the modal
* @type {ActionRowModalData[]}
*
* @type {Array<ActionRowModalData | LabelModalData | TextDisplayModalData>}
*/
this.components = data.data.components?.map(component => ModalSubmitInteraction.transformComponent(component));
this.components = data.data.components?.map(component =>
ModalSubmitInteraction.transformComponent(component, data.data.resolved),
);
/**
* The fields within the modal
* @type {ModalSubmitFields}
*/
this.fields = new ModalSubmitFields(this.components);
this.fields = new ModalSubmitFields(
this.components,
transformResolved({ client: this.client, guild: this.guild, channel: this.channel }, data.data.resolved),
);
/**
* Whether the reply to this interaction has been deferred
@@ -85,19 +120,68 @@ class ModalSubmitInteraction extends BaseInteraction {
/**
* Transforms component data to discord.js-compatible data
* @param {*} rawComponent The data to transform
* @param {APIInteractionDataResolved} [resolved] The resolved data for the interaction
* @returns {ModalData[]}
*/
static transformComponent(rawComponent) {
return rawComponent.components
? {
type: rawComponent.type,
components: rawComponent.components.map(component => this.transformComponent(component)),
}
: {
value: rawComponent.value,
type: rawComponent.type,
customId: rawComponent.custom_id,
static transformComponent(rawComponent, resolved) {
if ('components' in rawComponent) {
return {
type: rawComponent.type,
id: rawComponent.id,
components: rawComponent.components.map(component => this.transformComponent(component, resolved)),
};
}
if ('component' in rawComponent) {
return {
type: rawComponent.type,
id: rawComponent.id,
component: this.transformComponent(rawComponent.component, resolved),
};
}
const data = {
type: rawComponent.type,
id: rawComponent.id,
};
// Text display components do not have custom ids.
if ('custom_id' in rawComponent) data.customId = rawComponent.custom_id;
if ('value' in rawComponent) data.value = rawComponent.value;
if (rawComponent.values) {
data.values = rawComponent.values;
if (resolved) {
const resolveCollection = (resolvedData, resolver) => {
const collection = new Collection();
for (const value of data.values) {
if (resolvedData?.[value]) {
collection.set(value, resolver(resolvedData[value]));
}
}
return collection.size ? collection : null;
};
const users = resolveCollection(resolved.users, user => this.client.users._add(user));
if (users) data.users = users;
const channels = resolveCollection(
resolved.channels,
channel => this.client.channels._add(channel, this.guild) ?? channel,
);
if (channels) data.channels = channels;
const members = resolveCollection(resolved.members, member => this.guild?.members._add(member) ?? member);
if (members) data.members = members;
const roles = resolveCollection(resolved.roles, role => this.guild?.roles._add(role) ?? role);
if (roles) data.roles = roles;
}
}
return data;
}
/**

View File

@@ -11,9 +11,30 @@ const { ErrorCodes } = require('../errors/index');
* @extends {Base}
*/
class Poll extends Base {
constructor(client, data, message) {
constructor(client, data, message, channel) {
super(client);
/**
* The id of the channel that this poll is in
* @type {Snowflake}
*/
this.channelId = data.channel_id ?? channel.id;
/**
* The channel that this poll is in
* @name Poll#channel
* @type {TextBasedChannel}
* @readonly
*/
Object.defineProperty(this, 'channel', { value: channel });
/**
* The id of the message that started this poll
* @type {Snowflake}
*/
this.messageId = data.message_id ?? message.id;
/**
* The message that started this poll
* @name Poll#message
@@ -23,51 +44,27 @@ class Poll extends Base {
Object.defineProperty(this, 'message', { value: message });
/**
* The media for a poll's question
* @typedef {Object} PollQuestionMedia
* @property {string} text The text of this question
*/
/**
* The media for this poll's question
* @type {PollQuestionMedia}
*/
this.question = {
text: data.question.text,
};
/**
* The answers of this poll
* @type {Collection<number, PollAnswer>}
* @type {Collection<number, PollAnswer|PartialPollAnswer>}
*/
this.answers = data.answers.reduce(
(acc, answer) => acc.set(answer.answer_id, new PollAnswer(this.client, answer, this)),
new Collection(),
);
/**
* The timestamp when this poll expires
* @type {number}
*/
this.expiresTimestamp = Date.parse(data.expiry);
/**
* Whether this poll allows multiple answers
* @type {boolean}
*/
this.allowMultiselect = data.allow_multiselect;
/**
* The layout type of this poll
* @type {PollLayoutType}
*/
this.layoutType = data.layout_type;
this.answers = new Collection();
this._patch(data);
}
_patch(data) {
if (data.answers) {
for (const answer of data.answers) {
const existing = this.answers.get(answer.answer_id);
if (existing) {
existing._patch(answer);
} else {
this.answers.set(answer.answer_id, new PollAnswer(this.client, answer, this));
}
}
}
if (data.results) {
/**
* Whether this poll's results have been precisely counted
@@ -82,15 +79,84 @@ class Poll extends Base {
} else {
this.resultsFinalized ??= false;
}
if ('allow_multiselect' in data) {
/**
* Whether this poll allows multiple answers
* @type {boolean}
*/
this.allowMultiselect = data.allow_multiselect;
} else {
this.allowMultiselect ??= null;
}
if ('layout_type' in data) {
/**
* The layout type of this poll
* @type {PollLayoutType}
*/
this.layoutType = data.layout_type;
} else {
this.layoutType ??= null;
}
if ('expiry' in data) {
/**
* The timestamp when this poll expires
* @type {?number}
*/
this.expiresTimestamp = data.expiry && Date.parse(data.expiry);
} else {
this.expiresTimestamp ??= null;
}
if (data.question) {
/**
* The media for a poll's question
* @typedef {Object} PollQuestionMedia
* @property {?string} text The text of this question
*/
/**
* The media for this poll's question
* @type {PollQuestionMedia}
*/
this.question = {
text: data.question.text,
};
} else {
this.question ??= {
text: null,
};
}
}
/**
* The date when this poll expires
* @type {Date}
* @type {?Date}
* @readonly
*/
get expiresAt() {
return new Date(this.expiresTimestamp);
return this.expiresTimestamp && new Date(this.expiresTimestamp);
}
/**
* Whether this poll is a partial
* @type {boolean}
* @readonly
*/
get partial() {
return this.allowMultiselect === null;
}
/**
* Fetches the message that started this poll, then updates the poll from the fetched message.
* @returns {Promise<Poll>}
*/
async fetch() {
await this.channel.messages.fetch(this.messageId);
return this;
}
/**
@@ -102,7 +168,7 @@ class Poll extends Base {
throw new DiscordjsError(ErrorCodes.PollAlreadyExpired);
}
return this.message.channel.messages.endPoll(this.message.id);
return this.channel.messages.endPoll(this.messageId);
}
}

View File

@@ -1,7 +1,11 @@
'use strict';
const Base = require('./Base');
const { Emoji } = require('./Emoji');
const process = require('node:process');
const Base = require('./Base.js');
const { Emoji } = require('./Emoji.js');
const { PollAnswerVoterManager } = require('../managers/PollAnswerVoterManager.js');
let deprecationEmittedForFetchVoters = false;
/**
* Represents an answer to a {@link Poll}
@@ -14,7 +18,7 @@ class PollAnswer extends Base {
/**
* The {@link Poll} this answer is part of
* @name PollAnswer#poll
* @type {Poll}
* @type {Poll|PartialPoll}
* @readonly
*/
Object.defineProperty(this, 'poll', { value: poll });
@@ -26,10 +30,10 @@ class PollAnswer extends Base {
this.id = data.answer_id;
/**
* The text of this answer
* @type {?string}
* The manager of the voters for this answer
* @type {PollAnswerVoterManager}
*/
this.text = data.poll_media.text ?? null;
this.voters = new PollAnswerVoterManager(this);
/**
* The raw emoji of this answer
@@ -37,7 +41,7 @@ class PollAnswer extends Base {
* @type {?APIPartialEmoji}
* @private
*/
Object.defineProperty(this, '_emoji', { value: data.poll_media.emoji ?? null });
Object.defineProperty(this, '_emoji', { value: null, writable: true });
this._patch(data);
}
@@ -51,7 +55,17 @@ class PollAnswer extends Base {
*/
this.voteCount = data.count;
} else {
this.voteCount ??= 0;
this.voteCount ??= this.voters.cache.size;
}
/**
* The text of this answer
* @type {?string}
*/
this.text ??= data.poll_media?.text ?? null;
if (data.poll_media?.emoji) {
this._emoji = data.poll_media.emoji;
}
}
@@ -64,6 +78,15 @@ class PollAnswer extends Base {
return this.client.emojis.cache.get(this._emoji.id) ?? new Emoji(this.client, this._emoji);
}
/**
* Whether this poll answer is a partial.
* @type {boolean}
* @readonly
*/
get partial() {
return this.poll.partial || (this.text === null && this.emoji === null);
}
/**
* Options used for fetching voters of a poll answer.
* @typedef {Object} BaseFetchPollAnswerVotersOptions
@@ -75,14 +98,16 @@ class PollAnswer extends Base {
* Fetches the users that voted for this answer.
* @param {BaseFetchPollAnswerVotersOptions} [options={}] The options for fetching voters
* @returns {Promise<Collection<Snowflake, User>>}
* @deprecated Use {@link PollAnswerVoterManager#fetch} instead
*/
fetchVoters({ after, limit } = {}) {
return this.poll.message.channel.messages.fetchPollAnswerVoters({
messageId: this.poll.message.id,
answerId: this.id,
after,
limit,
});
if (!deprecationEmittedForFetchVoters) {
process.emitWarning('PollAnswer#fetchVoters is deprecated. Use PollAnswer#voters#fetch instead.');
deprecationEmittedForFetchVoters = true;
}
return this.voters.fetch({ after, limit });
}
}

View File

@@ -54,11 +54,37 @@ class Role extends Base {
if ('color' in data) {
/**
* The base 10 color of the role
*
* @type {number}
* @deprecated Use {@link Role#colors} instead.
*/
this.color = data.color;
}
/**
* @typedef {Object} RoleColors
* @property {number} primaryColor The primary color of the role
* @property {?number} secondaryColor The secondary color of the role.
* This will make the role a gradient between the other provided colors
* @property {?number} tertiaryColor The tertiary color of the role.
* When sending `tertiaryColor` the API enforces the role color to be a holographic style
* with values of `primaryColor = 11127295`, `secondaryColor = 16759788`, and `tertiaryColor = 16761760`.
* These values are available as a constant: `Constants.HolographicStyle`
*/
if ('colors' in data) {
/**
* The colors of the role
*
* @type {RoleColors}
*/
this.colors = {
primaryColor: data.colors.primary_color,
secondaryColor: data.colors.secondary_color,
tertiaryColor: data.colors.tertiary_color,
};
}
if ('hoist' in data) {
/**
* If true, users that are part of this role will appear in a separate category in the users list
@@ -170,7 +196,7 @@ class Role extends Base {
* @readonly
*/
get hexColor() {
return `#${this.color.toString(16).padStart(6, '0')}`;
return `#${this.colors.primaryColor.toString(16).padStart(6, '0')}`;
}
/**
@@ -231,6 +257,8 @@ class Role extends Base {
* @typedef {Object} RoleData
* @property {string} [name] The name of the role
* @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number
* <warn>This property is deprecated. Use `colors` instead.</warn>
* @property {RoleColorsResolvable} [colors] The colors of the role
* @property {boolean} [hoist] Whether or not the role should be hoisted
* @property {number} [position] The position of the role
* @property {PermissionResolvable} [permissions] The permissions of the role
@@ -286,17 +314,39 @@ class Role extends Base {
/**
* Sets a new color for the role.
*
* @param {ColorResolvable} color The color of the role
* @param {string} [reason] Reason for changing the role's color
* @returns {Promise<Role>}
* @deprecated Use {@link Role#setColors} instead.
*/
async setColor(color, reason) {
return this.edit({ color, reason });
}
/**
* Sets new colors for the role.
*
* @param {RoleColorsResolvable} colors The colors of the role
* @param {string} [reason] Reason for changing the role's colors
* @returns {Promise<Role>}
* @example
* // Set the color of a role
* role.setColor('#FF0000')
* .then(updated => console.log(`Set color of role to ${updated.color}`))
* // Set the colors of a role
* role.setColors({ primaryColor: '#FF0000', secondaryColor: '#00FF00', tertiaryColor: '#0000FF' })
* .then(updated => console.log(`Set colors of role to ${updated.colors}`))
* .catch(console.error);
* @example
* // Set holographic colors using constants
* role.setColors({
* primaryColor: Constants.HolographicStyle.Primary,
* secondaryColor: Constants.HolographicStyle.Secondary,
* tertiaryColor: Constants.HolographicStyle.Tertiary,
* })
* .then(updated => console.log(`Set holographic colors for role ${updated.name}`))
* .catch(console.error);
*/
setColor(color, reason) {
return this.edit({ color, reason });
async setColors(colors, reason) {
return this.edit({ colors, reason });
}
/**
@@ -434,7 +484,9 @@ class Role extends Base {
role &&
this.id === role.id &&
this.name === role.name &&
this.color === role.color &&
this.colors.primaryColor === role.colors.primaryColor &&
this.colors.secondaryColor === role.colors.secondaryColor &&
this.colors.tertiaryColor === role.colors.tertiaryColor &&
this.hoist === role.hoist &&
this.position === role.position &&
this.permissions.bitfield === role.permissions.bitfield &&

View File

@@ -351,7 +351,7 @@ class ThreadChannel extends BaseChannel {
async edit(options) {
const newData = await this.client.rest.patch(Routes.channel(this.id), {
body: {
name: (options.name ?? this.name).trim(),
name: options.name,
archived: options.archived,
auto_archive_duration: options.autoArchiveDuration,
rate_limit_per_user: options.rateLimitPerUser,

View File

@@ -5,6 +5,7 @@ const { calculateUserDefaultAvatarIndex } = require('@discordjs/rest');
const { DiscordSnowflake } = require('@sapphire/snowflake');
const Base = require('./Base');
const TextBasedChannel = require('./interfaces/TextBasedChannel');
const { _transformCollectibles } = require('../util/Transformers.js');
const UserFlagsBitField = require('../util/UserFlagsBitField');
const { emitDeprecationWarningForUserFetchFlags } = require('../util/Util');
@@ -140,18 +141,74 @@ class User extends Base {
* @property {string} asset The avatar decoration hash
* @property {Snowflake} skuId The id of the avatar decoration's SKU
*/
if (data.avatar_decoration_data) {
/**
* The user avatar decoration's data
* @type {?AvatarDecorationData}
*/
this.avatarDecorationData = {
asset: data.avatar_decoration_data.asset,
skuId: data.avatar_decoration_data.sku_id,
};
if ('avatar_decoration_data' in data) {
if (data.avatar_decoration_data) {
/**
* The user avatar decoration's data
*
* @type {?AvatarDecorationData}
*/
this.avatarDecorationData = {
asset: data.avatar_decoration_data.asset,
skuId: data.avatar_decoration_data.sku_id,
};
} else {
this.avatarDecorationData = null;
}
} else {
this.avatarDecorationData = null;
this.avatarDecorationData ??= null;
}
/**
* @typedef {Object} NameplateData
* @property {Snowflake} skuId The id of the nameplate's SKU
* @property {string} asset The nameplate's asset path
* @property {string} label The nameplate's label
* @property {NameplatePalette} palette Background color of the nameplate
*/
/**
* @typedef {Object} Collectibles
* @property {?NameplateData} nameplate The user's nameplate data
*/
if (data.collectibles) {
/**
* The user's collectibles
*
* @type {?Collectibles}
*/
this.collectibles = _transformCollectibles(data.collectibles);
} else {
this.collectibles = null;
}
/**
* @typedef {Object} UserPrimaryGuild
* @property {?Snowflake} identityGuildId The id of the user's primary guild
* @property {?boolean} identityEnabled Whether the user is displaying the primary guild's tag
* @property {?string} tag The user's guild tag. Limited to 4 characters
* @property {?string} badge The guild tag badge hash
*/
if ('primary_guild' in data) {
if (data.primary_guild) {
/**
* The primary guild of the user
*
* @type {?UserPrimaryGuild}
*/
this.primaryGuild = {
identityGuildId: data.primary_guild.identity_guild_id,
identityEnabled: data.primary_guild.identity_enabled,
tag: data.primary_guild.tag,
badge: data.primary_guild.badge,
};
} else {
this.primaryGuild = null;
}
} else {
this.primaryGuild ??= null;
}
}
@@ -210,7 +267,11 @@ class User extends Base {
* @readonly
*/
get defaultAvatarURL() {
const index = this.discriminator === '0' ? calculateUserDefaultAvatarIndex(this.id) : this.discriminator % 5;
const index =
this.discriminator === '0' || this.discriminator === '0000'
? calculateUserDefaultAvatarIndex(this.id)
: this.discriminator % 5;
return this.client.rest.cdn.defaultAvatar(index);
}
@@ -244,6 +305,18 @@ class User extends Base {
return this.banner && this.client.rest.cdn.banner(this.id, this.banner, options);
}
/**
* A link to the user's guild tag badge.
*
* @param {ImageURLOptions} [options={}] Options for the image URL
* @returns {?string}
*/
guildTagBadgeURL(options = {}) {
return this.primaryGuild?.badge
? this.client.rest.cdn.guildTagBadge(this.primaryGuild.identityGuildId, this.primaryGuild.badge, options)
: null;
}
/**
* The tag of this user
* <info>This user's username, or their legacy tag (e.g. `hydrabolt#0001`)
@@ -253,7 +326,7 @@ class User extends Base {
*/
get tag() {
return typeof this.username === 'string'
? this.discriminator === '0'
? this.discriminator === '0' || this.discriminator === '0000'
? this.username
: `${this.username}#${this.discriminator}`
: null;
@@ -314,7 +387,15 @@ class User extends Base {
this.accentColor === user.accentColor &&
this.avatarDecoration === user.avatarDecoration &&
this.avatarDecorationData?.asset === user.avatarDecorationData?.asset &&
this.avatarDecorationData?.skuId === user.avatarDecorationData?.skuId
this.avatarDecorationData?.skuId === user.avatarDecorationData?.skuId &&
this.collectibles?.nameplate?.skuId === user.collectibles?.nameplate?.skuId &&
this.collectibles?.nameplate?.asset === user.collectibles?.nameplate?.asset &&
this.collectibles?.nameplate?.label === user.collectibles?.nameplate?.label &&
this.collectibles?.nameplate?.palette === user.collectibles?.nameplate?.palette &&
this.primaryGuild?.identityGuildId === user.primaryGuild?.identityGuildId &&
this.primaryGuild?.identityEnabled === user.primaryGuild?.identityEnabled &&
this.primaryGuild?.tag === user.primaryGuild?.tag &&
this.primaryGuild?.badge === user.primaryGuild?.badge
);
}
@@ -339,6 +420,18 @@ class User extends Base {
('avatar_decoration_data' in user
? this.avatarDecorationData?.asset === user.avatar_decoration_data?.asset &&
this.avatarDecorationData?.skuId === user.avatar_decoration_data?.sku_id
: true) &&
('collectibles' in user
? this.collectibles?.nameplate?.skuId === user.collectibles?.nameplate?.sku_id &&
this.collectibles?.nameplate?.asset === user.collectibles?.nameplate?.asset &&
this.collectibles?.nameplate?.label === user.collectibles?.nameplate?.label &&
this.collectibles?.nameplate?.palette === user.collectibles?.nameplate?.palette
: true) &&
('primary_guild' in user
? this.primaryGuild?.identityGuildId === user.primary_guild?.identity_guild_id &&
this.primaryGuild?.identityEnabled === user.primary_guild?.identity_enabled &&
this.primaryGuild?.tag === user.primary_guild?.tag &&
this.primaryGuild?.badge === user.primary_guild?.badge
: true)
);
}
@@ -388,6 +481,7 @@ class User extends Base {
json.avatarURL = this.avatarURL();
json.displayAvatarURL = this.displayAvatarURL();
json.bannerURL = this.banner ? this.bannerURL() : this.banner;
json.guildTagBadgeURL = this.guildTagBadgeURL();
return json;
}
}

View File

@@ -267,7 +267,7 @@ class Collector extends EventEmitter {
/**
* Allows collectors to be consumed with for-await-of loops
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of}
* @see {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/for-await...of}
*/
async *[Symbol.asyncIterator]() {
const queue = [];

View File

@@ -15,13 +15,6 @@ const MessagePayload = require('../MessagePayload');
let deprecationEmittedForEphemeralOption = false;
let deprecationEmittedForFetchReplyOption = false;
/**
* @typedef {Object} ModalComponentData
* @property {string} title The title of the modal
* @property {string} customId The custom id of the modal
* @property {ActionRow[]} components The components within this modal
*/
/**
* Interface for classes that support shared interaction response types.
* @interface

View File

@@ -245,6 +245,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APISelectMenuOption}
*/
/**
* @external APISelectMenuDefaultValue
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APISelectMenuDefaultValue}
*/
/**
* @external APISticker
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APISticker}
@@ -515,6 +520,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/InteractionResponseType}
*/
/**
* @external InviteFlags
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/InviteFlags}
*/
/**
* @external InviteType
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/InviteType}
@@ -530,6 +540,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/Locale}
*/
/**
* @external LocalizationMap
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10#LocalizationMap}
*/
/**
* @external MessageActivityType
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/MessageActivityType}
@@ -550,6 +565,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/MessageFlags}
*/
/**
* @external NameplatePalette
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/NameplatePalette}
*/
/**
* @external OAuth2Scopes
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/OAuth2Scopes}
@@ -640,6 +660,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ThreadAutoArchiveDuration}
*/
/**
* @external ThreadMemberFlags
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ThreadMemberFlags}
*/
/**
* @external UserFlags
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/UserFlags}

View File

@@ -14,6 +14,25 @@ const { ComponentType } = require('discord-api-types/v10');
* @property {ComponentData[]} components The components in this action row
*/
/**
* @typedef {Object} ModalComponentData
* @property {string} title The title of the modal
* @property {string} customId The custom id of the modal
* @property {Array<ActionRow|TextDisplayComponentData|LabelData>} components The components within this modal
*/
/**
* @typedef {StringSelectMenuComponentData|TextInputComponentData|UserSelectMenuComponentData|
* RoleSelectMenuComponentData|MentionableSelectMenuComponentData|ChannelSelectMenuComponentData} ComponentInLabelData
*/
/**
* @typedef {BaseComponentData} LabelData
* @property {string} label The label to use
* @property {string} [description] The optional description for the label
* @property {ComponentInLabelData} component The component within the label
*/
/**
* @typedef {BaseComponentData} ButtonComponentData
* @property {ButtonStyle} style The style of the button
@@ -24,6 +43,42 @@ const { ComponentType } = require('discord-api-types/v10');
* @property {string} [url] The URL of the button
*/
/**
* @typedef {BaseComponentData} BaseSelectMenuComponentData
* @property {string} customId The custom id of the select menu
* @property {boolean} [disabled] Whether the select menu is disabled or not
* @property {number} [maxValues] The maximum amount of options that can be selected
* @property {number} [minValues] The minimum amount of options that can be selected
* @property {string} [placeholder] The placeholder of the select menu
* @property {boolean} [required] Whether this component is required in modals
*/
/**
* @typedef {BaseSelectMenuComponentData} StringSelectMenuComponentData
* @property {SelectMenuComponentOptionData[]} [options] The options in this select menu
*/
/**
* @typedef {BaseSelectMenuComponentData} UserSelectMenuComponentData
* @property {APISelectMenuDefaultValue[]} [defaultValues] The default selected values in this select menu
*/
/**
* @typedef {BaseSelectMenuComponentData} RoleSelectMenuComponentData
* @property {APISelectMenuDefaultValue[]} [defaultValues] The default selected values in this select menu
*/
/**
* @typedef {BaseSelectMenuComponentData} MentionableSelectMenuComponentData
* @property {APISelectMenuDefaultValue[]} [defaultValues] The default selected values in this select menu
*/
/**
* @typedef {BaseSelectMenuComponentData} ChannelSelectMenuComponentData
* @property {APISelectMenuDefaultValue[]} [defaultValues] The default selected values in this select menu
* @property {ChannelType[]} [channelTypes] The types of channels that can be selected
*/
/**
* @typedef {object} SelectMenuComponentOptionData
* @property {string} label The label of the option
@@ -199,6 +254,7 @@ const ChannelSelectMenuComponent = require('../structures/ChannelSelectMenuCompo
const Component = require('../structures/Component');
const ContainerComponent = require('../structures/ContainerComponent');
const FileComponent = require('../structures/FileComponent');
const LabelComponent = require('../structures/LabelComponent');
const MediaGalleryComponent = require('../structures/MediaGalleryComponent');
const MentionableSelectMenuBuilder = require('../structures/MentionableSelectMenuBuilder');
const MentionableSelectMenuComponent = require('../structures/MentionableSelectMenuComponent');
@@ -231,6 +287,7 @@ const ComponentTypeToComponent = {
[ComponentType.Section]: SectionComponent,
[ComponentType.Separator]: SeparatorComponent,
[ComponentType.Thumbnail]: ThumbnailComponent,
[ComponentType.Label]: LabelComponent,
};
const ComponentTypeToBuilder = {

View File

@@ -254,6 +254,21 @@ exports.StickerFormatExtensionMap = {
[StickerFormatType.GIF]: ImageFormat.GIF,
};
/**
* Holographic color values for role styling.
* When using `tertiaryColor`, the API enforces these specific values for holographic effect.
*
* @typedef {Object} HolographicStyle
* @property {number} Primary 11127295 (0xA9FFFF)
* @property {number} Secondary 16759788 (0xFFCCCC)
* @property {number} Tertiary 16761760 (0xFFE0A0)
*/
exports.HolographicStyle = {
Primary: 11_127_295,
Secondary: 16_759_788,
Tertiary: 16_761_760,
};
/**
* @typedef {Object} Constants Constants that can be used in an enum or object-like way.
* @property {number} MaxBulkDeletableMessageAge Max bulk deletable message age
@@ -264,4 +279,5 @@ exports.StickerFormatExtensionMap = {
* @property {VoiceBasedChannelTypes} VoiceBasedChannelTypes The types of channels that are voice-based
* @property {SelectMenuTypes} SelectMenuTypes The types of components that are select menus.
* @property {Object} StickerFormatExtensionMap A mapping between sticker formats and their respective image formats.
* @property {HolographicStyle} HolographicStyle Holographic color values for role styling.
*/

View File

@@ -12,7 +12,7 @@
* @property {string} ChannelDelete channelDelete
* @property {string} ChannelPinsUpdate channelPinsUpdate
* @property {string} ChannelUpdate channelUpdate
* @property {string} ClientReady ready
* @property {string} ClientReady clientReady
* @property {string} Debug debug
* @property {string} EntitlementCreate entitlementCreate
* @property {string} EntitlementUpdate entitlementUpdate
@@ -89,7 +89,7 @@
* @property {string} VoiceServerUpdate voiceServerUpdate
* @property {string} VoiceStateUpdate voiceStateUpdate
* @property {string} Warn warn
* @property {string} WebhooksUpdate webhookUpdate
* @property {string} WebhooksUpdate webhooksUpdate
*/
// JSDoc for IntelliSense purposes
@@ -108,7 +108,7 @@ module.exports = {
ChannelDelete: 'channelDelete',
ChannelPinsUpdate: 'channelPinsUpdate',
ChannelUpdate: 'channelUpdate',
ClientReady: 'ready',
ClientReady: 'clientReady',
Debug: 'debug',
EntitlementCreate: 'entitlementCreate',
EntitlementUpdate: 'entitlementUpdate',
@@ -186,5 +186,5 @@ module.exports = {
VoiceServerUpdate: 'voiceServerUpdate',
VoiceStateUpdate: 'voiceStateUpdate',
Warn: 'warn',
WebhooksUpdate: 'webhookUpdate',
WebhooksUpdate: 'webhooksUpdate',
};

View File

@@ -0,0 +1,28 @@
'use strict';
const { InviteFlags } = require('discord-api-types/v10');
const BitField = require('./BitField');
/**
* Data structure that makes it easy to interact with a {@link GuildInvite#flags} bit field.
*
* @extends {BitField}
*/
class InviteFlagsBitField extends BitField {
/**
* Numeric invite flags.
*
* @type {InviteFlags}
* @memberof InviteFlagsBitField
*/
static Flags = InviteFlags;
}
/**
* @name InviteFlagsBitField
* @kind constructor
* @memberof InviteFlagsBitField
* @param {BitFieldResolvable} [bits=0] Bit(s) to read from
*/
exports.InviteFlagsBitField = InviteFlagsBitField;

View File

@@ -27,6 +27,8 @@ const { createEnum } = require('./Enums');
* @property {number} GuildScheduledEvent The partial to receive uncached guild scheduled events.
* @property {number} ThreadMember The partial to receive uncached thread members.
* @property {number} SoundboardSound The partial to receive uncached soundboard sounds.
* @property {number} Poll The partial to receive uncached polls.
* @property {number} PollAnswer The partial to receive uncached poll answers.
*/
// JSDoc for IntelliSense purposes
@@ -43,4 +45,6 @@ module.exports = createEnum([
'GuildScheduledEvent',
'ThreadMember',
'SoundboardSound',
'Poll',
'PollAnswer',
]);

View File

@@ -1,5 +1,6 @@
'use strict';
const { ThreadMemberFlags } = require('discord-api-types/v10');
const BitField = require('./BitField');
/**
@@ -9,10 +10,10 @@ const BitField = require('./BitField');
class ThreadMemberFlagsBitField extends BitField {
/**
* Numeric thread member flags. There are currently no bitflags relevant to bots for this.
* @type {Object<string, number>}
* @type {ThreadMemberFlags}
* @memberof ThreadMemberFlagsBitField
*/
static Flags = {};
static Flags = ThreadMemberFlags;
}
/**

View File

@@ -95,10 +95,31 @@ function _transformAPIIncidentsData(data) {
};
}
/**
* Transforms a collectibles object to a camel-cased variant.
*
* @param {APICollectibles} collectibles The collectibles to transform
* @returns {Collectibles}
* @ignore
*/
function _transformCollectibles(collectibles) {
if (!collectibles.nameplate) return { nameplate: null };
return {
nameplate: {
skuId: collectibles.nameplate.sku_id,
asset: collectibles.nameplate.asset,
label: collectibles.nameplate.label,
palette: collectibles.nameplate.palette,
},
};
}
module.exports = {
toSnakeCase,
_transformAPIAutoModerationAction,
_transformAPIMessageInteractionMetadata,
_transformGuildScheduledEventRecurrenceRule,
_transformAPIIncidentsData,
_transformCollectibles,
};

View File

@@ -37,7 +37,15 @@ import {
} from '@discordjs/formatters';
import { Awaitable, JSONEncodable } from '@discordjs/util';
import { Collection, ReadonlyCollection } from '@discordjs/collection';
import { BaseImageURLOptions, ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
import {
BaseImageURLOptions,
EmojiURLOptions,
ImageURLOptions,
RawFile,
REST,
RESTOptions,
ImageSize,
} from '@discordjs/rest';
import {
WebSocketManager as WSWebSocketManager,
IShardingStrategy,
@@ -55,6 +63,7 @@ import {
APIInteractionDataResolvedChannel,
APIInteractionDataResolvedGuildMember,
APIInteractionGuildMember,
APILabelComponent,
APIMessage,
APIMessageComponent,
APIOverwrite,
@@ -80,6 +89,7 @@ import {
InteractionType,
InviteTargetType,
MessageType,
NameplatePalette,
OAuth2Scopes,
RESTPostAPIApplicationCommandsJSONBody,
Snowflake,
@@ -214,6 +224,7 @@ import {
APIFileComponent,
APIMessageTopLevelComponent,
EntryPointCommandHandlerType,
InviteFlags,
} from 'discord-api-types/v10';
import { ChildProcess } from 'node:child_process';
import { EventEmitter } from 'node:events';
@@ -350,6 +361,21 @@ export class ActionRowBuilder<
): ActionRowBuilder<ComponentType>;
}
export type ComponentInLabelData =
| StringSelectMenuComponentData
| TextInputComponentData
| UserSelectMenuComponentData
| ChannelSelectMenuComponentData
| RoleSelectMenuComponentData
| MentionableSelectMenuComponentData;
export interface LabelComponentData extends BaseComponentData {
type: ComponentType.Label;
component: ComponentInLabelData;
description?: string;
label: string;
}
export type MessageActionRowComponent =
| ButtonComponent
| StringSelectMenuComponent
@@ -599,7 +625,7 @@ export abstract class CommandInteraction<Cached extends CacheType = CacheType> e
public inRawGuild(): this is CommandInteraction<'raw'>;
public deferReply(
options: InteractionDeferReplyOptions & { withResponse: true },
): Promise<InteractionCallbackResponse>;
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public deferReply(
options: InteractionDeferReplyOptions & { fetchReply: true },
@@ -611,29 +637,35 @@ export abstract class CommandInteraction<Cached extends CacheType = CacheType> e
): Promise<Message<BooleanCache<Cached>>>;
public fetchReply(message?: Snowflake | '@original'): Promise<Message<BooleanCache<Cached>>>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message<BooleanCache<Cached>>>;
public reply(options: InteractionReplyOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public reply(
options: InteractionReplyOptions & { withResponse: true },
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<Message<BooleanCache<Cached>>>;
public reply(
options: string | MessagePayload | InteractionReplyOptions,
): Promise<InteractionResponse<BooleanCache<Cached>>>;
public launchActivity(options: LaunchActivityOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public launchActivity(
options: LaunchActivityOptions & { withResponse: true },
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
public launchActivity(options?: LaunchActivityOptions & { withResponse?: false }): Promise<undefined>;
public launchActivity(options?: LaunchActivityOptions): Promise<InteractionCallbackResponse | undefined>;
public launchActivity(
options?: LaunchActivityOptions,
): Promise<InteractionCallbackResponse<BooleanCache<Cached>> | undefined>;
public showModal(
modal:
| JSONEncodable<APIModalInteractionResponseCallbackData>
| ModalComponentData
| APIModalInteractionResponseCallbackData,
options: ShowModalOptions & { withResponse: true },
): Promise<InteractionCallbackResponse>;
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
public showModal(
modal:
| JSONEncodable<APIModalInteractionResponseCallbackData>
| ModalComponentData
| APIModalInteractionResponseCallbackData,
options?: ShowModalOptions & { withResponse: true },
): Promise<InteractionCallbackResponse>;
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
public showModal(
modal:
| JSONEncodable<APIModalInteractionResponseCallbackData>
@@ -691,14 +723,16 @@ export abstract class BaseGuild extends Base {
export class BaseGuildEmoji extends Emoji {
protected constructor(client: Client<true>, data: RawGuildEmojiData, guild: Guild | GuildPreview);
public imageURL(options?: BaseImageURLOptions): string;
public imageURL(options?: EmojiURLOptions): string;
public get url(): string;
public available: boolean | null;
public get createdAt(): Date;
public get createdTimestamp(): number;
public guild: Guild | GuildPreview;
public id: Snowflake;
public managed: boolean | null;
public name: string;
public animated: boolean;
public managed: boolean;
public requiresColons: boolean | null;
}
@@ -902,6 +936,12 @@ export class TextInputComponent extends Component<APITextInputComponent> {
public get value(): string;
}
export class LabelComponent extends Component<APILabelComponent> {
public component: StringSelectMenuComponent | TextInputComponent;
public get label(): string;
public get description(): string | null;
}
export class BaseSelectMenuComponent<Data extends APISelectMenuComponent> extends Component<Data> {
protected constructor(data: Data);
public get placeholder(): string | null;
@@ -1159,6 +1199,7 @@ export class ClientApplication extends Application {
public flags: Readonly<ApplicationFlagsBitField>;
public approximateGuildCount: number | null;
public approximateUserInstallCount: number | null;
public approximateUserAuthorizationCount: number | null;
public tags: string[];
public installParams: ClientApplicationInstallParams | null;
public integrationTypesConfig: IntegrationTypesConfiguration | null;
@@ -1485,7 +1526,7 @@ export class Emoji extends Base {
public id: Snowflake | null;
public name: string | null;
public get identifier(): string;
public imageURL(options?: BaseImageURLOptions): string | null;
public imageURL(options?: EmojiURLOptions): string | null;
public get url(): string | null;
public toJSON(): unknown;
public toString(): string;
@@ -1504,10 +1545,16 @@ export class ApplicationEmoji extends Emoji {
private constructor(client: Client<true>, data: RawApplicationEmojiData, application: ClientApplication);
public application: ClientApplication;
public author: User | null;
public author: User;
public id: Snowflake;
public managed: boolean | null;
public requiresColons: boolean | null;
public managed: false;
public requiresColons: true;
public name: string;
public animated: boolean;
public available: true;
public get createdAt(): Date;
public get createdTimestamp(): number;
public imageURL(options?: EmojiURLOptions): string;
public delete(): Promise<ApplicationEmoji>;
public edit(options: ApplicationEmojiEditOptions): Promise<ApplicationEmoji>;
public equals(other: ApplicationEmoji | unknown): boolean;
@@ -1655,6 +1702,7 @@ export class Guild extends AnonymousGuild {
): Promise<Guild>;
public setIcon(icon: BufferResolvable | Base64Resolvable | null, reason?: string): Promise<Guild>;
public setName(name: string, reason?: string): Promise<Guild>;
/** @deprecated API related to guild ownership may no longer be used. */
public setOwner(owner: GuildMemberResolvable, reason?: string): Promise<Guild>;
public setPreferredLocale(preferredLocale: Locale | null, reason?: string): Promise<Guild>;
public setPublicUpdatesChannel(publicUpdatesChannel: TextChannelResolvable | null, reason?: string): Promise<Guild>;
@@ -1666,6 +1714,7 @@ export class Guild extends AnonymousGuild {
public setVerificationLevel(verificationLevel: GuildVerificationLevel | null, reason?: string): Promise<Guild>;
public setPremiumProgressBarEnabled(enabled?: boolean, reason?: string): Promise<Guild>;
public setWidgetSettings(settings: GuildWidgetSettingsData, reason?: string): Promise<Guild>;
/** @deprecated API related to guild ownership may no longer be used. */
public setMFALevel(level: GuildMFALevel, reason?: string): Promise<Guild>;
public toJSON(): unknown;
}
@@ -1798,6 +1847,7 @@ export class GuildMember extends Base {
private constructor(client: Client<true>, data: RawGuildMemberData, guild: Guild);
private _roles: Snowflake[];
public avatar: string | null;
public avatarDecorationData: AvatarDecorationData | null;
public banner: string | null;
public get bannable(): boolean;
public get dmChannel(): DMChannel | null;
@@ -1825,6 +1875,7 @@ export class GuildMember extends Base {
public user: User;
public get voice(): VoiceState;
public avatarURL(options?: ImageURLOptions): string | null;
public avatarDecorationURL(): string | null;
public bannerURL(options?: ImageURLOptions): string | null;
public ban(options?: BanOptions): Promise<GuildMember>;
public disableCommunicationUntil(timeout: DateResolvable | null, reason?: string): Promise<GuildMember>;
@@ -1834,6 +1885,7 @@ export class GuildMember extends Base {
public deleteDM(): Promise<DMChannel>;
public displayAvatarURL(options?: ImageURLOptions): string;
public displayBannerURL(options?: ImageURLOptions): string | null;
public displayAvatarDecorationURL(): string | null;
public edit(options: GuildMemberEditOptions): Promise<GuildMember>;
public isCommunicationDisabled(): this is GuildMember & {
communicationDisabledUntilTimestamp: number;
@@ -2001,6 +2053,7 @@ export class GuildTemplate extends Base {
public guildId: Snowflake;
public serializedGuild: APITemplateSerializedSourceGuild;
public unSynced: boolean | null;
/** @deprecated API related to guild ownership may no longer be used. */
public createGuild(name: string, icon?: BufferResolvable | Base64Resolvable): Promise<Guild>;
public delete(): Promise<GuildTemplate>;
public edit(options?: GuildTemplateEditOptions): Promise<GuildTemplate>;
@@ -2155,17 +2208,17 @@ export class InteractionCallback {
public type: InteractionType;
}
export class InteractionCallbackResponse {
export class InteractionCallbackResponse<InGuild extends boolean = boolean> {
private constructor(client: Client<true>, data: RESTPostAPIInteractionCallbackWithResponseResult);
public readonly client: Client<true>;
public interaction: InteractionCallback;
public resource: InteractionCallbackResource | null;
public resource: InteractionCallbackResource<InGuild> | null;
}
export class InteractionCallbackResource {
export class InteractionCallbackResource<InGuild extends boolean = boolean> {
private constructor(client: Client<true>, data: RESTAPIInteractionCallbackResourceObject);
public activityInstance: RESTAPIInteractionCallbackActivityInstanceResource | null;
public message: Message | null;
public message: Message<InGuild> | null;
public type: InteractionResponseType;
}
@@ -2236,8 +2289,8 @@ export class Invite extends Base {
public inviterId: Snowflake | null;
public maxAge: number | null;
public maxUses: number | null;
public memberCount: number;
public presenceCount: number;
public memberCount: number | null;
public presenceCount: number | null;
public targetApplication: IntegrationApplication | null;
public targetUser: User | null;
public targetType: InviteTargetType | null;
@@ -2252,6 +2305,7 @@ export class Invite extends Base {
/** @deprecated Public Stage Instances don't exist anymore */
public stageInstance: InviteStageInstance | null;
public guildScheduledEvent: GuildScheduledEvent | null;
public flags: Readonly<InviteFlagsBitField>;
}
/** @deprecated Public Stage Instances don't exist anymore */
@@ -2272,6 +2326,13 @@ export class InviteGuild extends AnonymousGuild {
public welcomeScreen: WelcomeScreen | null;
}
export type InviteFlagsString = keyof typeof InviteFlags;
export class InviteFlagsBitField extends BitField<InviteFlagsString> {
public static Flags: typeof InviteFlags;
public static resolve(bit?: BitFieldResolvable<InviteFlagsString, number>): number;
}
export class LimitedCollection<Key, Value> extends Collection<Key, Value> {
public constructor(options?: LimitedCollectionOptions<Key, Value>, iterable?: Iterable<readonly [Key, Value]>);
public maxSize: number;
@@ -2522,7 +2583,7 @@ export class MessageComponentInteraction<Cached extends CacheType = CacheType> e
public inRawGuild(): this is MessageComponentInteraction<'raw'>;
public deferReply(
options: InteractionDeferReplyOptions & { withResponse: true },
): Promise<InteractionCallbackResponse>;
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public deferReply(
options: InteractionDeferReplyOptions & { fetchReply: true },
@@ -2530,7 +2591,7 @@ export class MessageComponentInteraction<Cached extends CacheType = CacheType> e
public deferReply(options?: InteractionDeferReplyOptions): Promise<InteractionResponse<BooleanCache<Cached>>>;
public deferUpdate(
options: InteractionDeferUpdateOptions & { withResponse: true },
): Promise<InteractionCallbackResponse>;
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public deferUpdate(
options: InteractionDeferUpdateOptions & { fetchReply: true },
@@ -2542,28 +2603,36 @@ export class MessageComponentInteraction<Cached extends CacheType = CacheType> e
): Promise<Message<BooleanCache<Cached>>>;
public fetchReply(message?: Snowflake | '@original'): Promise<Message<BooleanCache<Cached>>>;
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message<BooleanCache<Cached>>>;
public reply(options: InteractionReplyOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public reply(
options: InteractionReplyOptions & { withResponse: true },
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<Message<BooleanCache<Cached>>>;
public reply(
options: string | MessagePayload | InteractionReplyOptions,
): Promise<InteractionResponse<BooleanCache<Cached>>>;
public update(options: InteractionUpdateOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public update(
options: InteractionUpdateOptions & { withResponse: true },
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<Message<BooleanCache<Cached>>>;
public update(
options: string | MessagePayload | InteractionUpdateOptions,
): Promise<InteractionResponse<BooleanCache<Cached>>>;
public launchActivity(options: LaunchActivityOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public launchActivity(
options: LaunchActivityOptions & { withResponse: true },
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
public launchActivity(options?: LaunchActivityOptions & { withResponse?: false }): Promise<undefined>;
public launchActivity(options?: LaunchActivityOptions): Promise<InteractionCallbackResponse | undefined>;
public launchActivity(
options?: LaunchActivityOptions,
): Promise<InteractionCallbackResponse<BooleanCache<Cached>> | undefined>;
public showModal(
modal:
| JSONEncodable<APIModalInteractionResponseCallbackData>
| ModalComponentData
| APIModalInteractionResponseCallbackData,
options: ShowModalOptions & { withResponse: true },
): Promise<InteractionCallbackResponse>;
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
public showModal(
modal:
| JSONEncodable<APIModalInteractionResponseCallbackData>
@@ -2718,40 +2787,128 @@ export interface ModalComponentData {
customId: string;
title: string;
components: readonly (
| JSONEncodable<APIActionRowComponent<APIComponentInModalActionRow>>
| JSONEncodable<APIActionRowComponent<APIComponentInModalActionRow> | APILabelComponent>
| ActionRowData<ModalActionRowComponentData>
| LabelComponentData
| TextDisplayComponentData
)[];
}
export interface BaseModalData {
customId: string;
type: ComponentType;
export interface BaseModalData<Type extends ComponentType> {
id: number;
type: Type;
}
export interface TextInputModalData extends BaseModalData {
type: ComponentType.TextInput;
export interface TextInputModalData extends BaseModalData<ComponentType.TextInput> {
customId: string;
value: string;
}
export interface ActionRowModalData {
type: ComponentType.ActionRow;
export interface SelectMenuModalData<Cached extends CacheType = CacheType>
extends BaseModalData<
| ComponentType.ChannelSelect
| ComponentType.MentionableSelect
| ComponentType.RoleSelect
| ComponentType.StringSelect
| ComponentType.UserSelect
> {
channels?: ReadonlyCollection<
Snowflake,
CacheTypeReducer<Cached, GuildBasedChannel, APIInteractionDataResolvedChannel>
>;
customId: string;
members?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, GuildMember, APIInteractionDataResolvedGuildMember>>;
roles?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Role, APIRole>>;
users?: ReadonlyCollection<Snowflake, User>;
values: readonly string[];
}
export type ModalData = SelectMenuModalData | TextInputModalData;
export interface LabelModalData extends BaseModalData<ComponentType.Label> {
component: readonly ModalData[];
}
export interface ActionRowModalData extends BaseModalData<ComponentType.ActionRow> {
components: readonly TextInputModalData[];
}
export class ModalSubmitFields {
private constructor(components: readonly (readonly ModalActionRowComponent[])[]);
public components: ActionRowModalData[];
public fields: Collection<string, ModalActionRowComponent>;
public getField<Type extends ComponentType>(customId: string, type: Type): { type: Type } & TextInputModalData;
public getField(customId: string, type?: ComponentType): TextInputModalData;
export interface TextDisplayModalData extends BaseModalData<ComponentType.TextDisplay> {}
export interface ModalSelectedMentionables<Cached extends CacheType = CacheType> {
members: NonNullable<SelectMenuModalData<Cached>['members']>;
roles: NonNullable<SelectMenuModalData<Cached>['roles']>;
users: NonNullable<SelectMenuModalData<Cached>['users']>;
}
export class ModalSubmitFields<Cached extends CacheType = CacheType> {
private constructor(
components: readonly (ActionRowModalData | LabelModalData | TextDisplayModalData)[],
resolved?: BaseInteractionResolvedData,
);
public components: (ActionRowModalData | LabelModalData | TextDisplayModalData)[];
public resolved: Readonly<BaseInteractionResolvedData<Cached>> | null;
public fields: Collection<string, ModalData>;
public getField<Type extends ComponentType>(customId: string, type: Type): Extract<ModalData, { type: Type }>;
public getField(customId: string, type?: ComponentType): ModalData;
private _getTypedComponent(
customId: string,
allowedTypes: readonly ComponentType[],
properties: string,
required: boolean,
): ModalData;
public getTextInputValue(customId: string): string;
public getStringSelectValues(customId: string): readonly string[];
public getSelectedUsers(customId: string, required: true): ReadonlyCollection<Snowflake, User>;
public getSelectedUsers(customId: string, required?: boolean): ReadonlyCollection<Snowflake, User> | null;
public getSelectedMembers(customId: string): NonNullable<SelectMenuModalData<Cached>['members']> | null;
public getSelectedChannels<const Type extends ChannelType = ChannelType>(
customId: string,
required: true,
channelTypes?: readonly Type[],
): ReadonlyCollection<
Snowflake,
Extract<
NonNullable<CommandInteractionOption<Cached>['channel']>,
{
type: Type extends ChannelType.AnnouncementThread | ChannelType.PublicThread
? ChannelType.AnnouncementThread | ChannelType.PublicThread
: Type;
}
>
>;
public getSelectedChannels<const Type extends ChannelType = ChannelType>(
customId: string,
required?: boolean,
channelTypes?: readonly Type[],
): ReadonlyCollection<
Snowflake,
Extract<
NonNullable<CommandInteractionOption<Cached>['channel']>,
{
type: Type extends ChannelType.AnnouncementThread | ChannelType.PublicThread
? ChannelType.AnnouncementThread | ChannelType.PublicThread
: Type;
}
>
> | null;
public getSelectedRoles(customId: string, required: true): NonNullable<SelectMenuModalData<Cached>['roles']>;
public getSelectedRoles(
customId: string,
required?: boolean,
): NonNullable<SelectMenuModalData<Cached>['roles']> | null;
public getSelectedMentionables(customId: string, required: true): ModalSelectedMentionables<Cached>;
public getSelectedMentionables(customId: string, required?: boolean): ModalSelectedMentionables<Cached> | null;
}
export interface ModalMessageModalSubmitInteraction<Cached extends CacheType = CacheType>
extends ModalSubmitInteraction<Cached> {
message: Message<BooleanCache<Cached>>;
channelId: Snowflake;
update(options: InteractionUpdateOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
update(
options: InteractionUpdateOptions & { withResponse: true },
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<Message>;
update(
@@ -2766,14 +2923,16 @@ export class ModalSubmitInteraction<Cached extends CacheType = CacheType> extend
private constructor(client: Client<true>, data: APIModalSubmitInteraction);
public type: InteractionType.ModalSubmit;
public readonly customId: string;
public readonly components: ActionRowModalData[];
public readonly fields: ModalSubmitFields;
public readonly components: (ActionRowModalData | LabelModalData)[];
public readonly fields: ModalSubmitFields<Cached>;
public deferred: boolean;
public ephemeral: boolean | null;
public message: Message<BooleanCache<Cached>> | null;
public replied: boolean;
public readonly webhook: InteractionWebhook;
public reply(options: InteractionReplyOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public reply(
options: InteractionReplyOptions & { withResponse: true },
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<Message<BooleanCache<Cached>>>;
public reply(
@@ -2785,7 +2944,7 @@ export class ModalSubmitInteraction<Cached extends CacheType = CacheType> extend
): Promise<Message<BooleanCache<Cached>>>;
public deferReply(
options: InteractionDeferReplyOptions & { withResponse: true },
): Promise<InteractionCallbackResponse>;
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public deferReply(
options: InteractionDeferReplyOptions & { fetchReply: true },
@@ -2795,7 +2954,7 @@ export class ModalSubmitInteraction<Cached extends CacheType = CacheType> extend
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<Message<BooleanCache<Cached>>>;
public deferUpdate(
options: InteractionDeferUpdateOptions & { withResponse: true },
): Promise<InteractionCallbackResponse>;
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
/** @deprecated `fetchReply` is deprecated. Use `withResponse` instead or fetch the response after using the method. */
public deferUpdate(
options: InteractionDeferUpdateOptions & { fetchReply: true },
@@ -2803,9 +2962,13 @@ export class ModalSubmitInteraction<Cached extends CacheType = CacheType> extend
public deferUpdate(options?: InteractionDeferUpdateOptions): Promise<InteractionResponse<BooleanCache<Cached>>>;
/** @deprecated Sending a premium-style button is the new Discord behaviour. */
public sendPremiumRequired(): Promise<void>;
public launchActivity(options: LaunchActivityOptions & { withResponse: true }): Promise<InteractionCallbackResponse>;
public launchActivity(
options: LaunchActivityOptions & { withResponse: true },
): Promise<InteractionCallbackResponse<BooleanCache<Cached>>>;
public launchActivity(options?: LaunchActivityOptions & { withResponse?: false }): Promise<undefined>;
public launchActivity(options?: LaunchActivityOptions): Promise<InteractionCallbackResponse | undefined>;
public launchActivity(
options?: LaunchActivityOptions,
): Promise<InteractionCallbackResponse<BooleanCache<Cached>> | undefined>;
public inGuild(): this is ModalSubmitInteraction<'raw' | 'cached'>;
public inCachedGuild(): this is ModalSubmitInteraction<'cached'>;
public inRawGuild(): this is ModalSubmitInteraction<'raw'>;
@@ -2967,19 +3130,30 @@ export class Presence extends Base {
}
export interface PollQuestionMedia {
text: string;
text: string | null;
}
export class PollAnswerVoterManager extends CachedManager<Snowflake, User, UserResolvable> {
private constructor(answer: PollAnswer);
public answer: PollAnswer;
public fetch(options?: BaseFetchPollAnswerVotersOptions): Promise<Collection<Snowflake, User>>;
}
export class Poll extends Base {
private constructor(client: Client<true>, data: APIPoll, message: Message);
private constructor(client: Client<true>, data: APIPoll, message: Message, channel: TextBasedChannel);
public readonly channel: TextBasedChannel;
public channelId: Snowflake;
public readonly message: Message;
public messageId: Snowflake;
public question: PollQuestionMedia;
public answers: Collection<number, PollAnswer>;
public expiresTimestamp: number;
public get expiresAt(): Date;
public answers: Collection<number, PollAnswer | PartialPollAnswer>;
public expiresTimestamp: number | null;
public get expiresAt(): Date | null;
public allowMultiselect: boolean;
public layoutType: PollLayoutType;
public resultsFinalized: boolean;
public get partial(): false;
public fetch(): Promise<this>;
public end(): Promise<Message>;
}
@@ -2991,11 +3165,14 @@ export interface BaseFetchPollAnswerVotersOptions {
export class PollAnswer extends Base {
private constructor(client: Client<true>, data: APIPollAnswer & { count?: number }, poll: Poll);
private _emoji: APIPartialEmoji | null;
public readonly poll: Poll;
public readonly poll: Poll | PartialPoll;
public id: number;
public text: string | null;
public voteCount: number;
public voters: PollAnswerVoterManager;
public get emoji(): GuildEmoji | Emoji | null;
public get partial(): false;
/** @deprecated Use {@link PollAnswerVoterManager.fetch} instead */
public fetchVoters(options?: BaseFetchPollAnswerVotersOptions): Promise<Collection<Snowflake, User>>;
}
@@ -3054,9 +3231,23 @@ export class RichPresenceAssets {
public smallImageURL(options?: ImageURLOptions): string | null;
}
export interface RoleColors {
primaryColor: number;
secondaryColor: number | null;
tertiaryColor: number | null;
}
export interface RoleColorsResolvable {
primaryColor: ColorResolvable;
secondaryColor?: ColorResolvable;
tertiaryColor?: ColorResolvable;
}
export class Role extends Base {
private constructor(client: Client<true>, data: RawRoleData, guild: Guild);
/** @deprecated Use {@link Role.colors} instead. */
public color: number;
public colors: RoleColors;
public get createdAt(): Date;
public get createdTimestamp(): number;
public get editable(): boolean;
@@ -3084,7 +3275,9 @@ export class Role extends Base {
channel: NonThreadGuildBasedChannel | Snowflake,
checkAdmin?: boolean,
): Readonly<PermissionsBitField>;
/** @deprecated Use {@link Role.setColors} instead. */
public setColor(color: ColorResolvable, reason?: string): Promise<Role>;
public setColors(colors: RoleColorsResolvable, reason?: string): Promise<Role>;
public setHoist(hoist?: boolean, reason?: string): Promise<Role>;
public setMentionable(mentionable?: boolean, reason?: string): Promise<Role>;
public setName(name: string, reason?: string): Promise<Role>;
@@ -3752,6 +3945,24 @@ export interface AvatarDecorationData {
skuId: Snowflake;
}
export interface Collectibles {
nameplate: NameplateData | null;
}
export interface UserPrimaryGuild {
badge: string | null;
identityEnabled: boolean | null;
identityGuildId: Snowflake | null;
tag: string | null;
}
export interface NameplateData {
asset: string;
label: string;
palette: NameplatePalette;
skuId: Snowflake;
}
export interface UnfurledMediaItemData {
url: string;
}
@@ -3777,6 +3988,7 @@ export class User extends Base {
public bot: boolean;
public get createdAt(): Date;
public get createdTimestamp(): number;
public collectibles: Collectibles | null;
public discriminator: string;
public get displayName(): string;
public get defaultAvatarURL(): string;
@@ -3786,12 +3998,14 @@ export class User extends Base {
public get hexAccentColor(): HexColorString | null | undefined;
public id: Snowflake;
public get partial(): false;
public primaryGuild: UserPrimaryGuild | null;
public system: boolean;
public get tag(): string;
public username: string;
public avatarURL(options?: ImageURLOptions): string | null;
public avatarDecorationURL(options?: BaseImageURLOptions): string | null;
public bannerURL(options?: ImageURLOptions): string | null | undefined;
public guildTagBadgeURL(options?: ImageURLOptions): string | null;
public createDM(force?: boolean): Promise<DMChannel>;
public deleteDM(): Promise<DMChannel>;
public displayAvatarURL(options?: ImageURLOptions): string;
@@ -3938,6 +4152,8 @@ export class Formatters extends null {
export type ComponentData =
| MessageActionRowComponentData
| ModalActionRowComponentData
| LabelComponentData
| ComponentInLabelData
| ComponentInContainerData
| ContainerComponentData
| ThumbnailComponentData;
@@ -4037,9 +4253,9 @@ export class Webhook<Type extends WebhookType = WebhookType> {
public editMessage(
message: MessageResolvable,
options: string | MessagePayload | WebhookMessageEditOptions,
): Promise<Message>;
public fetchMessage(message: Snowflake, options?: WebhookFetchMessageOptions): Promise<Message>;
public send(options: string | MessagePayload | WebhookMessageCreateOptions): Promise<Message>;
): Promise<Message<true>>;
public fetchMessage(message: Snowflake, options?: WebhookFetchMessageOptions): Promise<Message<true>>;
public send(options: string | MessagePayload | WebhookMessageCreateOptions): Promise<Message<true>>;
}
// tslint:disable-next-line no-empty-interface
@@ -4236,6 +4452,11 @@ export type DeletableMessageType =
| MessageType.UserJoin;
export const Constants: {
HolographicStyle: {
Primary: 11_127_295;
Secondary: 16_759_788;
Tertiary: 16_761_760;
};
MaxBulkDeletableMessageAge: 1_209_600_000;
SweeperKeys: SweeperKey[];
NonSystemMessageTypes: NonSystemMessageType[];
@@ -4420,6 +4641,8 @@ export enum DiscordjsErrorCodes {
ModalSubmitInteractionFieldNotFound = 'ModalSubmitInteractionFieldNotFound',
ModalSubmitInteractionFieldType = 'ModalSubmitInteractionFieldType',
ModalSubmitInteractionFieldEmpty = 'ModalSubmitInteractionComponentEmpty',
ModalSubmitInteractionFieldInvalidChannelType = 'ModalSubmitInteractionFieldInvalidChannelType',
InvalidMissingScopes = 'InvalidMissingScopes',
InvalidScopesWithPermissions = 'InvalidScopesWithPermissions',
@@ -4768,6 +4991,7 @@ export interface FetchSoundboardSoundsOptions {
export class GuildManager extends CachedManager<Snowflake, Guild, GuildResolvable> {
private constructor(client: Client<true>, iterable?: Iterable<RawGuildData>);
/** @deprecated API related to guild ownership may no longer be used. */
public create(options: GuildCreateOptions): Promise<Guild>;
public fetch(options: Snowflake | FetchGuildOptions): Promise<Guild>;
public fetch(options?: FetchGuildsOptions): Promise<Collection<Snowflake, OAuth2Guild>>;
@@ -4802,6 +5026,7 @@ export class GuildMemberManager extends CachedManager<Snowflake, GuildMember, Gu
options?: BulkBanOptions,
): Promise<BulkBanResult>;
public edit(user: UserResolvable, options: GuildMemberEditOptions): Promise<GuildMember>;
public editMe(options: GuildMemberEditMeOptions): Promise<GuildMember>;
public fetch(
options: UserResolvable | FetchMemberOptions | (FetchMembersOptions & { user: UserResolvable }),
): Promise<GuildMember>;
@@ -4957,7 +5182,9 @@ export abstract class MessageManager<InGuild extends boolean = boolean> extends
): Promise<Message<InGuild>>;
public fetch(options: MessageResolvable | FetchMessageOptions): Promise<Message<InGuild>>;
public fetch(options?: FetchMessagesOptions): Promise<Collection<Snowflake, Message<InGuild>>>;
/** @deprecated Use {@link MessageManager.fetchPins} instead. */
public fetchPinned(cache?: boolean): Promise<Collection<Snowflake, Message<InGuild>>>;
public fetchPins(options?: FetchPinnedMessagesOptions): Promise<FetchPinnedMessagesResponse<InGuild>>;
public react(message: MessageResolvable, emoji: EmojiIdentifierResolvable): Promise<void>;
public pin(message: MessageResolvable, reason?: string): Promise<void>;
public unpin(message: MessageResolvable, reason?: string): Promise<void>;
@@ -5227,7 +5454,9 @@ export type AllowedPartial =
| MessageReaction
| GuildScheduledEvent
| ThreadMember
| SoundboardSound;
| SoundboardSound
| Poll
| PollAnswer;
export type AllowedThreadTypeForNewsChannel = ChannelType.AnnouncementThread;
@@ -5764,6 +5993,7 @@ export interface ClientEvents {
oldChannel: DMChannel | NonThreadGuildBasedChannel,
newChannel: DMChannel | NonThreadGuildBasedChannel,
];
clientReady: [client: Client<true>];
debug: [message: string];
warn: [message: string];
emojiCreate: [emoji: GuildEmoji];
@@ -5787,22 +6017,26 @@ export interface ClientEvents {
guildMembersChunk: [members: ReadonlyCollection<Snowflake, GuildMember>, guild: Guild, data: GuildMembersChunk];
guildMemberUpdate: [oldMember: GuildMember | PartialGuildMember, newMember: GuildMember];
guildUpdate: [oldGuild: Guild, newGuild: Guild];
guildSoundboardSoundCreate: [soundboardSound: SoundboardSound];
guildSoundboardSoundDelete: [soundboardSound: SoundboardSound | PartialSoundboardSound];
guildSoundboardSoundUpdate: [oldSoundboardSound: SoundboardSound | null, newSoundboardSound: SoundboardSound];
guildSoundboardSoundCreate: [soundboardSound: GuildSoundboardSound];
guildSoundboardSoundDelete: [soundboardSound: GuildSoundboardSound | PartialSoundboardSound];
guildSoundboardSoundUpdate: [
oldSoundboardSound: GuildSoundboardSound | null,
newSoundboardSound: GuildSoundboardSound,
];
guildSoundboardSoundsUpdate: [soundboardSounds: ReadonlyCollection<Snowflake, GuildSoundboardSound>, guild: Guild];
inviteCreate: [invite: Invite];
inviteDelete: [invite: Invite];
messageCreate: [message: OmitPartialGroupDMChannel<Message>];
messageDelete: [message: OmitPartialGroupDMChannel<Message | PartialMessage>];
messagePollVoteAdd: [pollAnswer: PollAnswer, userId: Snowflake];
messagePollVoteRemove: [pollAnswer: PollAnswer, userId: Snowflake];
messagePollVoteAdd: [pollAnswer: PollAnswer | PartialPollAnswer, userId: Snowflake];
messagePollVoteRemove: [pollAnswer: PollAnswer | PartialPollAnswer, userId: Snowflake];
messageReactionRemoveAll: [
message: OmitPartialGroupDMChannel<Message | PartialMessage>,
reactions: ReadonlyCollection<string | Snowflake, MessageReaction>,
];
messageReactionRemoveEmoji: [reaction: MessageReaction | PartialMessageReaction];
messageDeleteBulk: [
messages: ReadonlyCollection<Snowflake, OmitPartialGroupDMChannel<Message | PartialMessage>>,
messages: ReadonlyCollection<Snowflake, Message<true> | PartialMessage<true>>,
channel: GuildTextBasedChannel,
];
messageReactionAdd: [
@@ -5820,6 +6054,7 @@ export interface ClientEvents {
newMessage: OmitPartialGroupDMChannel<Message>,
];
presenceUpdate: [oldPresence: Presence | null, newPresence: Presence];
/** @deprecated Use {@link ClientEvents.ClientReady} instead. */
ready: [client: Client<true>];
invalidated: [];
roleCreate: [role: Role];
@@ -5865,7 +6100,7 @@ export interface ClientEvents {
guildScheduledEventDelete: [guildScheduledEvent: GuildScheduledEvent | PartialGuildScheduledEvent];
guildScheduledEventUserAdd: [guildScheduledEvent: GuildScheduledEvent | PartialGuildScheduledEvent, user: User];
guildScheduledEventUserRemove: [guildScheduledEvent: GuildScheduledEvent | PartialGuildScheduledEvent, user: User];
soundboardSounds: [soundboardSounds: ReadonlyCollection<Snowflake, SoundboardSound>, guild: Guild];
soundboardSounds: [soundboardSounds: ReadonlyCollection<Snowflake, GuildSoundboardSound>, guild: Guild];
}
export interface ClientFetchInviteOptions {
@@ -5948,13 +6183,17 @@ export interface CommandInteractionOption<Cached extends CacheType = CacheType>
message?: Message<BooleanCache<Cached>>;
}
export interface CommandInteractionResolvedData<Cached extends CacheType = CacheType> {
users?: ReadonlyCollection<Snowflake, User>;
export interface BaseInteractionResolvedData<Cached extends CacheType = CacheType> {
channels?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Channel, APIInteractionDataResolvedChannel>>;
members?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, GuildMember, APIInteractionDataResolvedGuildMember>>;
roles?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Role, APIRole>>;
channels?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Channel, APIInteractionDataResolvedChannel>>;
messages?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Message, APIMessage>>;
users?: ReadonlyCollection<Snowflake, User>;
}
export interface CommandInteractionResolvedData<Cached extends CacheType = CacheType>
extends BaseInteractionResolvedData<Cached> {
attachments?: ReadonlyCollection<Snowflake, Attachment>;
messages?: ReadonlyCollection<Snowflake, CacheTypeReducer<Cached, Message, APIMessage>>;
}
export interface AutocompleteFocusedOption extends Pick<CommandInteractionOption, 'name'> {
@@ -6005,7 +6244,7 @@ export enum Events {
AutoModerationRuleCreate = 'autoModerationRuleCreate',
AutoModerationRuleDelete = 'autoModerationRuleDelete',
AutoModerationRuleUpdate = 'autoModerationRuleUpdate',
ClientReady = 'ready',
ClientReady = 'clientReady',
EntitlementCreate = 'entitlementCreate',
EntitlementDelete = 'entitlementDelete',
EntitlementUpdate = 'entitlementUpdate',
@@ -6057,7 +6296,7 @@ export enum Events {
VoiceServerUpdate = 'voiceServerUpdate',
VoiceStateUpdate = 'voiceStateUpdate',
TypingStart = 'typingStart',
WebhooksUpdate = 'webhookUpdate',
WebhooksUpdate = 'webhooksUpdate',
InteractionCreate = 'interactionCreate',
Error = 'error',
Warn = 'warn',
@@ -6275,6 +6514,23 @@ export interface FetchMessagesOptions {
cache?: boolean;
}
export interface FetchPinnedMessagesOptions {
before?: DateResolvable;
cache?: boolean;
limit?: number;
}
export interface FetchPinnedMessagesResponse<InGuild extends boolean = boolean> {
hasMore: boolean;
items: readonly MessagePin<InGuild>[];
}
export interface MessagePin<InGuild extends boolean = boolean> {
message: Message<InGuild>;
get pinnedAt(): Date;
pinnedTimestamp: number;
}
export interface FetchReactionUsersOptions {
type?: ReactionType;
limit?: number;
@@ -6543,6 +6799,7 @@ export interface GuildChannelOverwriteOptions {
type?: OverwriteType;
}
/** @deprecated API related to guild ownership may no longer be used. */
export interface GuildCreateOptions {
name: string;
icon?: BufferResolvable | Base64Resolvable | null;
@@ -6570,6 +6827,7 @@ export interface GuildEditOptions {
afkTimeout?: number;
afkChannel?: VoiceChannelResolvable | null;
icon?: BufferResolvable | Base64Resolvable | null;
/** @deprecated API related to guild ownership may no longer be used. */
owner?: GuildMemberResolvable;
splash?: BufferResolvable | Base64Resolvable | null;
discoverySplash?: BufferResolvable | Base64Resolvable | null;
@@ -6627,6 +6885,14 @@ export interface GuildMemberEditOptions {
export type GuildMemberResolvable = GuildMember | UserResolvable;
export interface GuildMemberEditMeOptions {
avatar?: Base64Resolvable | BufferResolvable | null;
banner?: Base64Resolvable | BufferResolvable | null;
bio?: string | null;
nick?: string | null;
reason?: string;
}
export type GuildResolvable = Guild | NonThreadGuildBasedChannel | GuildMember | GuildEmoji | Invite | Role | Snowflake;
export interface GuildPruneMembersOptions {
@@ -7100,6 +7366,7 @@ export interface BaseSelectMenuComponentData extends BaseComponentData {
maxValues?: number;
minValues?: number;
placeholder?: string;
required?: true;
}
export interface StringSelectMenuComponentData extends BaseSelectMenuComponentData {
@@ -7218,6 +7485,7 @@ export interface PresenceData {
export type PresenceResolvable = Presence | UserResolvable | Snowflake;
/** @deprecated API related to guild ownership may no longer be used. */
export interface PartialChannelData {
id?: Snowflake | number;
parentId?: Snowflake | number;
@@ -7264,11 +7532,28 @@ export interface PartialDMChannel extends Partialize<DMChannel, null, null, 'las
export interface PartialGuildMember extends Partialize<GuildMember, 'joinedAt' | 'joinedTimestamp' | 'pending'> {}
export interface PartialMessage
extends Partialize<Message, 'type' | 'system' | 'pinned' | 'tts', 'content' | 'cleanContent' | 'author'> {}
export interface PartialMessage<InGuild extends boolean = boolean>
extends Partialize<Message<InGuild>, 'type' | 'system' | 'pinned' | 'tts', 'content' | 'cleanContent' | 'author'> {}
export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> {}
export interface PartialPoll
extends Partialize<
Poll,
'allowMultiselect' | 'layoutType' | 'expiresTimestamp',
null,
'question' | 'message' | 'answers'
> {
question: { text: null };
message: PartialMessage;
// eslint-disable-next-line no-restricted-syntax
answers: Collection<number, PartialPollAnswer>;
}
export interface PartialPollAnswer extends Partialize<PollAnswer, 'emoji' | 'text', null, 'poll'> {
readonly poll: PartialPoll;
}
export interface PartialGuildScheduledEvent
extends Partialize<GuildScheduledEvent, 'userCount', 'status' | 'privacyLevel' | 'name' | 'entityType'> {}
@@ -7276,6 +7561,7 @@ export interface PartialThreadMember extends Partialize<ThreadMember, 'flags' |
export interface PartialSoundboardSound extends Partialize<SoundboardSound, 'available' | 'name' | 'volume'> {}
/** @deprecated API related to guild ownership may no longer be used. */
export interface PartialOverwriteData {
id: Snowflake | number;
type?: OverwriteType;
@@ -7283,6 +7569,7 @@ export interface PartialOverwriteData {
deny?: PermissionResolvable;
}
/** @deprecated API related to guild ownership may no longer be used. */
export interface PartialRoleData extends RoleData {
id?: Snowflake | number;
}
@@ -7296,6 +7583,8 @@ export enum Partials {
GuildScheduledEvent,
ThreadMember,
SoundboardSound,
Poll,
PollAnswer,
}
export interface PartialUser extends Partialize<User, 'username' | 'tag' | 'discriminator'> {}
@@ -7340,7 +7629,9 @@ export interface ResolvedOverwriteOptions {
export interface RoleData {
name?: string;
/** @deprecated Use {@link RoleData.colors} instead. */
color?: ColorResolvable;
colors?: RoleColorsResolvable;
hoist?: boolean;
position?: number;
permissions?: PermissionResolvable;
@@ -7721,3 +8012,6 @@ export * from '@discordjs/formatters';
export * from '@discordjs/rest';
export * from '@discordjs/util';
export * from '@discordjs/ws';
// Solve TS compile error
export type { ImageSize };

View File

@@ -231,7 +231,12 @@ import {
FileComponentData,
ContainerComponentData,
InteractionResponse,
} from '.';
FetchPinnedMessagesResponse,
PartialPoll,
PartialPollAnswer,
PollAnswer,
PollAnswerVoterManager,
} from './index.js';
import {
expectAssignable,
expectDeprecated,
@@ -463,7 +468,7 @@ client.on('messageCreate', async message => {
const component = await message.awaitMessageComponent({ componentType: ComponentType.Button });
expectType<ButtonInteraction<'cached'>>(component);
expectType<Message<true>>(await component.reply({ fetchReply: true }));
expectType<InteractionCallbackResponse>(await component.reply({ withResponse: true }));
expectType<InteractionCallbackResponse<true>>(await component.reply({ withResponse: true }));
const buttonCollector = message.createMessageComponentCollector({ componentType: ComponentType.Button });
expectType<InteractionCollector<ButtonInteraction<'cached'>>>(buttonCollector);
@@ -714,6 +719,48 @@ client.on('messageDeleteBulk', (messages, { client }) => {
expectType<Client<true>>(client);
});
client.on('messagePollVoteAdd', async (answer, userId) => {
expectType<Client<true>>(answer.client);
expectType<Snowflake>(userId);
if (answer.partial) {
expectType<null>(answer.emoji);
expectType<null>(answer.text);
expectNotType<null>(answer.id);
expectNotType<null>(answer.poll);
await answer.poll.fetch();
answer = answer.poll.answers?.get(answer.id) ?? answer;
expectType<User>(answer.voters.cache.get(userId)!);
}
expectType<string | null>(answer.text);
expectType<GuildEmoji | Emoji | null>(answer.emoji);
expectType<number>(answer.id);
expectType<number>(answer.voteCount!);
});
client.on('messagePollVoteRemove', async (answer, userId) => {
expectType<Client<true>>(answer.client);
expectType<Snowflake>(userId);
if (answer.partial) {
expectType<null>(answer.emoji);
expectType<null>(answer.text);
expectNotType<null>(answer.id);
expectNotType<null>(answer.poll);
await answer.poll.fetch();
answer = answer.poll.answers?.get(answer.id) ?? answer;
}
expectType<string | null>(answer.text);
expectType<GuildEmoji | Emoji | null>(answer.emoji);
expectType<number>(answer.id);
expectType<number>(answer.voteCount!);
});
client.on('messageReactionAdd', async (reaction, { client }) => {
expectType<Client<true>>(reaction.client);
expectType<Client<true>>(client);
@@ -1690,6 +1737,7 @@ declare const guildChannelManager: GuildChannelManager;
expectType<Promise<Message<true>>>(messages.edit('1234567890', 'text'));
expectType<Promise<Message<true>>>(messages.fetch('1234567890'));
expectType<Promise<Collection<Snowflake, Message<true>>>>(messages.fetchPinned());
expectType<Promise<FetchPinnedMessagesResponse<true>>>(messages.fetchPins());
expectType<Guild>(message.guild);
expectType<Snowflake>(message.guildId);
expectType<GuildTextBasedChannel>(message.channel.messages.channel);
@@ -1703,6 +1751,7 @@ declare const guildChannelManager: GuildChannelManager;
expectType<Promise<Message>>(messages.edit('1234567890', 'text'));
expectType<Promise<Message>>(messages.fetch('1234567890'));
expectType<Promise<Collection<Snowflake, Message>>>(messages.fetchPinned());
expectType<Promise<FetchPinnedMessagesResponse>>(messages.fetchPins());
expectType<Guild | null>(message.guild);
expectType<Snowflake | null>(message.guildId);
expectType<DMChannel | PartialGroupDMChannel | GuildTextBasedChannel>(message.channel.messages.channel);
@@ -1777,6 +1826,12 @@ declare const messageManager: MessageManager;
messageManager.fetch({ message: '1234567890', after: '1234567890', cache: true, force: false });
}
declare const pollAnswerVoterManager: PollAnswerVoterManager;
{
expectType<Promise<Collection<Snowflake, User>>>(pollAnswerVoterManager.fetch());
expectType<PollAnswer>(pollAnswerVoterManager.answer);
}
declare const roleManager: RoleManager;
expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch());
expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch(undefined, {}));
@@ -1882,18 +1937,18 @@ client.on('interactionCreate', async interaction => {
expectType<Guild>(interaction.guild);
expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<true>>>(interaction.fetchReply());
expectType<Promise<Message<true>>>(interaction.update({ content: 'a', fetchReply: true }));
expectType<Promise<Message<true>>>(interaction.deferUpdate({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.update({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferUpdate({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.update({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferUpdate({ withResponse: true }));
expectType<Promise<Message<true>>>(interaction.followUp({ content: 'a' }));
expectType<Promise<InteractionCallbackResponse>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<undefined>>(interaction.launchActivity({ withResponse: false }));
expectType<Promise<InteractionCallbackResponse | undefined>>(
expectType<Promise<InteractionCallbackResponse<true> | undefined>>(
interaction.launchActivity({ withResponse: booleanValue }),
);
} else if (interaction.inRawGuild()) {
@@ -1903,18 +1958,18 @@ client.on('interactionCreate', async interaction => {
expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<false>>>(interaction.fetchReply());
expectType<Promise<Message<false>>>(interaction.update({ content: 'a', fetchReply: true }));
expectType<Promise<Message<false>>>(interaction.deferUpdate({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.update({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferUpdate({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.update({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferUpdate({ withResponse: true }));
expectType<Promise<Message<false>>>(interaction.followUp({ content: 'a' }));
expectType<Promise<InteractionCallbackResponse>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<undefined>>(interaction.launchActivity({ withResponse: false }));
expectType<Promise<InteractionCallbackResponse | undefined>>(
expectType<Promise<InteractionCallbackResponse<false> | undefined>>(
interaction.launchActivity({ withResponse: booleanValue }),
);
} else if (interaction.inGuild()) {
@@ -1972,14 +2027,14 @@ client.on('interactionCreate', async interaction => {
expectAssignable<CommandInteraction<'cached'>>(interaction);
expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<true>>>(interaction.fetchReply());
expectType<Promise<Message<true>>>(interaction.followUp({ content: 'a' }));
expectType<Promise<InteractionCallbackResponse>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<undefined>>(interaction.launchActivity({ withResponse: false }));
expectType<Promise<InteractionCallbackResponse | undefined>>(
expectType<Promise<InteractionCallbackResponse<true> | undefined>>(
interaction.launchActivity({ withResponse: booleanValue }),
);
} else if (interaction.inRawGuild()) {
@@ -1987,14 +2042,14 @@ client.on('interactionCreate', async interaction => {
expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<false>>>(interaction.fetchReply());
expectType<Promise<Message<false>>>(interaction.followUp({ content: 'a' }));
expectType<Promise<InteractionCallbackResponse>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<undefined>>(interaction.launchActivity({ withResponse: false }));
expectType<Promise<InteractionCallbackResponse | undefined>>(
expectType<Promise<InteractionCallbackResponse<false> | undefined>>(
interaction.launchActivity({ withResponse: booleanValue }),
);
} else if (interaction.inGuild()) {
@@ -2063,14 +2118,14 @@ client.on('interactionCreate', async interaction => {
expectType<Message<true>>(interaction.message);
expectType<Guild>(interaction.guild);
expectType<Promise<Message<true>>>(interaction.reply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ withResponse: true }));
} else if (interaction.inRawGuild()) {
expectAssignable<ButtonInteraction>(interaction);
expectType<APIButtonComponent>(interaction.component);
expectType<Message<false>>(interaction.message);
expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ withResponse: true }));
} else if (interaction.inGuild()) {
expectAssignable<ButtonInteraction>(interaction);
expectType<ButtonComponent | APIButtonComponent>(interaction.component);
@@ -2094,14 +2149,14 @@ client.on('interactionCreate', async interaction => {
expectType<Message<true>>(interaction.message);
expectType<Guild>(interaction.guild);
expectType<Promise<Message<true>>>(interaction.reply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ withResponse: true }));
} else if (interaction.inRawGuild()) {
expectAssignable<StringSelectMenuInteraction>(interaction);
expectType<APIStringSelectComponent>(interaction.component);
expectType<Message<false>>(interaction.message);
expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ withResponse: true }));
} else if (interaction.inGuild()) {
expectAssignable<StringSelectMenuInteraction>(interaction);
expectType<SelectMenuComponent | APIStringSelectComponent>(interaction.component);
@@ -2121,7 +2176,7 @@ client.on('interactionCreate', async interaction => {
expectNotAssignable<Interaction<'cached'>>(interaction);
expectAssignable<ChatInputCommandInteraction>(interaction);
expectType<Promise<Message<false>>>(interaction.reply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ withResponse: true }));
expectType<APIInteractionDataResolvedGuildMember | null>(interaction.options.getMember('test'));
expectType<APIInteractionDataResolvedChannel>(interaction.options.getChannel('test', true));
@@ -2136,7 +2191,7 @@ client.on('interactionCreate', async interaction => {
expectType<GuildMember | null>(interaction.options.getMember('test'));
expectAssignable<ChatInputCommandInteraction>(interaction);
expectType<Promise<Message<true>>>(interaction.reply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ withResponse: true }));
expectType<GuildBasedChannel>(interaction.options.getChannel('test', true));
expectType<Role>(interaction.options.getRole('test', true));
@@ -2208,28 +2263,28 @@ client.on('interactionCreate', async interaction => {
expectAssignable<PrimaryEntryPointCommandInteraction>(interaction);
expectAssignable<Guild>(interaction.guild);
expectAssignable<CommandInteraction<'cached'>>(interaction);
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionResponse<true>>>(interaction.deferReply());
expectType<Promise<InteractionResponse<true>>>(interaction.reply({ content: 'a', withResponse: false }));
expectType<Promise<InteractionResponse<true>>>(interaction.deferReply({ withResponse: false }));
expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<true>>>(interaction.fetchReply());
expectType<Promise<Message<true>>>(interaction.followUp({ content: 'a' }));
expectType<Promise<InteractionCallbackResponse>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<undefined>>(interaction.launchActivity({ withResponse: false }));
} else if (interaction.inRawGuild()) {
expectAssignable<PrimaryEntryPointCommandInteraction>(interaction);
expectType<null>(interaction.guild);
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionResponse<false>>>(interaction.deferReply());
expectType<Promise<InteractionResponse<false>>>(interaction.reply({ content: 'a', withResponse: false }));
expectType<Promise<InteractionResponse<false>>>(interaction.deferReply({ withResponse: false }));
expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<false>>>(interaction.fetchReply());
expectType<Promise<Message<false>>>(interaction.followUp({ content: 'a' }));
expectType<Promise<InteractionCallbackResponse>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<undefined>>(interaction.launchActivity({ withResponse: false }));
} else if (interaction.inGuild()) {
expectAssignable<PrimaryEntryPointCommandInteraction>(interaction);
@@ -2268,27 +2323,27 @@ client.on('interactionCreate', async interaction => {
expectType<Guild>(interaction.guild);
expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<true>>>(interaction.fetchReply());
expectType<Promise<Message<true>>>(interaction.deferUpdate({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferUpdate({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferUpdate({ withResponse: true }));
expectType<Promise<Message<true>>>(interaction.followUp({ content: 'a' }));
expectType<Promise<InteractionCallbackResponse>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<true>>>(interaction.launchActivity({ withResponse: true }));
} else if (interaction.inRawGuild()) {
expectAssignable<ModalSubmitInteraction>(interaction);
expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<false>>>(interaction.fetchReply());
expectType<Promise<Message<false>>>(interaction.deferUpdate({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferUpdate({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferUpdate({ withResponse: true }));
expectType<Promise<Message<false>>>(interaction.followUp({ content: 'a' }));
expectType<Promise<InteractionCallbackResponse>>(interaction.launchActivity({ withResponse: true }));
expectType<Promise<InteractionCallbackResponse<false>>>(interaction.launchActivity({ withResponse: true }));
} else if (interaction.inGuild()) {
expectAssignable<ModalSubmitInteraction>(interaction);
expectType<Guild | null>(interaction.guild);
@@ -2511,6 +2566,59 @@ chatInputInteraction.showModal({
],
});
chatInputInteraction.showModal({
title: 'abc',
customId: 'abc',
components: [
{
type: ComponentType.Label,
label: 'label',
component: {
customId: 'aa',
type: ComponentType.TextInput,
style: TextInputStyle.Short,
label: 'label',
},
},
{
components: [
{
customId: 'aa',
label: 'label',
style: TextInputStyle.Short,
type: ComponentType.TextInput,
},
],
type: ComponentType.ActionRow,
},
{
type: ComponentType.Label,
label: 'Lll',
component: {
customId: 'aa',
type: ComponentType.UserSelect,
},
},
{
type: ComponentType.Label,
label: 'Lll',
component: {
customId: 'aa',
type: ComponentType.ChannelSelect,
channelTypes: [ChannelType.GuildText, ChannelType.GuildVoice],
},
},
{
type: ComponentType.Label,
label: 'Lll',
component: {
customId: 'aa',
type: ComponentType.RoleSelect,
},
},
],
});
declare const stringSelectMenuData: APIStringSelectComponent;
StringSelectMenuBuilder.from(stringSelectMenuData);
@@ -2594,9 +2702,9 @@ declare const webhookClient: WebhookClient;
declare const interactionWebhook: InteractionWebhook;
declare const snowflake: Snowflake;
expectType<Promise<Message>>(webhook.send('content'));
expectType<Promise<Message>>(webhook.editMessage(snowflake, 'content'));
expectType<Promise<Message>>(webhook.fetchMessage(snowflake));
expectType<Promise<Message<true>>>(webhook.send('content'));
expectType<Promise<Message<true>>>(webhook.editMessage(snowflake, 'content'));
expectType<Promise<Message<true>>>(webhook.fetchMessage(snowflake));
expectType<Promise<Webhook>>(webhook.edit({ name: 'name' }));
expectType<Promise<APIMessage>>(webhookClient.send('content'));
@@ -2799,16 +2907,42 @@ await textChannel.send({
},
});
declare const partialPoll: PartialPoll;
{
if (partialPoll.partial) {
expectType<null>(partialPoll.question.text);
expectType<PartialMessage>(partialPoll.message);
expectType<null>(partialPoll.allowMultiselect);
expectType<null>(partialPoll.layoutType);
expectType<null>(partialPoll.expiresTimestamp);
expectType<Collection<number, PartialPollAnswer>>(partialPoll.answers);
}
}
declare const partialPollAnswer: PartialPollAnswer;
{
if (partialPollAnswer.partial) {
expectType<PartialPoll>(partialPollAnswer.poll);
expectType<null>(partialPollAnswer.emoji);
expectType<null>(partialPollAnswer.text);
}
}
declare const poll: Poll;
declare const message: Message;
declare const pollData: PollData;
{
expectType<Message>(await poll.end());
expectType<false>(poll.partial);
expectNotType<Collection<number, PartialPollAnswer>>(poll.answers);
const answer = poll.answers.first()!;
expectType<number>(answer.voteCount);
expectType<Collection<Snowflake, User>>(await answer.fetchVoters({ after: snowflake, limit: 10 }));
if (!answer.partial) {
expectType<number>(answer.voteCount);
expectType<number>(answer.id);
expectType<PollAnswerVoterManager>(answer.voters);
expectType<Collection<Snowflake, User>>(await answer.voters.fetch({ after: snowflake, limit: 10 }));
}
await messageManager.endPoll(snowflake);
await messageManager.fetchPollAnswerVoters({

View File

@@ -55,7 +55,7 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"discord-api-types": "^0.37.119"
"discord-api-types": "^0.38.24"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",

View File

@@ -72,7 +72,7 @@
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"@discordjs/ws": "workspace:^",
"discord-api-types": "^0.37.119"
"discord-api-types": "^0.38.24"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",

View File

@@ -2,6 +2,17 @@
All notable changes to this project will be documented in this file.
# [@discordjs/rest@2.6.0](https://github.com/discordjs/discord.js/compare/@discordjs/rest@2.5.1...@discordjs/rest@2.6.0) - (2025-08-20)
## Documentation
- Remove hardcoded locale from links (#10794) ([5be774d](https://github.com/discordjs/discord.js/commit/5be774db641b60669505645861d721200d335a7b))
## Features
- Support user guilds (#10995) ([baa08b8](https://github.com/discordjs/discord.js/commit/baa08b8fbb64abd8265890413c95f81e2c61303f))
- Support animated WebP (#10987) ([cafe58b](https://github.com/discordjs/discord.js/commit/cafe58b3bd9defb5050a7a90bd07568f3b509c89))
# [@discordjs/rest@2.5.0](https://github.com/discordjs/discord.js/compare/@discordjs/rest@2.4.3...@discordjs/rest@2.5.0) - (2025-04-25)
## Features

View File

@@ -54,7 +54,7 @@ test('discoverySplash default', () => {
expect(cdn.discoverySplash(id, hash)).toEqual(`${baseCDN}/discovery-splashes/${id}/${hash}.webp`);
});
test('emoji default', () => {
test('emoji', () => {
expect(cdn.emoji(id)).toEqual(`${baseCDN}/emojis/${id}.webp`);
});
@@ -62,6 +62,14 @@ test('emoji gif', () => {
expect(cdn.emoji(id, 'gif')).toEqual(`${baseCDN}/emojis/${id}.gif`);
});
test('emoji animated', () => {
expect(cdn.emoji(id, { animated: true })).toEqual(`${baseCDN}/emojis/${id}.webp?animated=true`);
});
test('emoji with GIF format', () => {
expect(cdn.emoji(id, { extension: 'gif' })).toEqual(`${baseCDN}/emojis/${id}.gif`);
});
test('guildMemberAvatar default', () => {
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${baseCDN}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
});
@@ -134,8 +142,11 @@ test('soundboardSound', () => {
expect(cdn.soundboardSound(id)).toEqual(`${baseCDN}/soundboard-sounds/${id}`);
});
test('guildTagBadge', () => {
expect(cdn.guildTagBadge(id, hash)).toEqual(`${baseCDN}/guild-tag-badges/${id}/${hash}.webp`);
});
test('makeURL throws on invalid size', () => {
// @ts-expect-error: Invalid size
expect(() => cdn.avatar(id, animatedHash, { size: 5 })).toThrow(RangeError);
});

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/rest",
"version": "2.5.1",
"version": "2.6.0",
"description": "The REST API for discord.js",
"scripts": {
"test": "vitest run",
@@ -88,7 +88,7 @@
"@sapphire/async-queue": "^1.5.3",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.1",
"discord-api-types": "^0.38.24",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.21.3"

View File

@@ -14,27 +14,44 @@ import { deprecationWarning } from './utils/utils.js';
let deprecationEmittedForEmoji = false;
/**
* The options used for image URLs
* The options used for image URLs.
*/
export interface BaseImageURLOptions {
/**
* The extension to use for the image URL
* The extension to use for the image URL.
*
* @defaultValue `'webp'`
*/
extension?: ImageExtension;
/**
* The size specified in the image URL
* The size specified in the image URL.
*/
size?: ImageSize;
}
export interface EmojiURLOptionsWebp extends BaseImageURLOptions {
/**
* Whether to use the `animated` query parameter.
*/
animated?: boolean;
extension?: 'webp';
}
export interface EmojiURLOptionsNotWebp extends BaseImageURLOptions {
extension: Exclude<ImageExtension, 'webp'>;
}
/**
* The options used for image URLs with animated content
* The options used for emoji URLs.
*/
export type EmojiURLOptions = EmojiURLOptionsNotWebp | EmojiURLOptionsWebp;
/**
* The options used for image URLs that may be animated.
*/
export interface ImageURLOptions extends BaseImageURLOptions {
/**
* Whether or not to prefer the static version of an image asset.
* Whether to prefer the static asset.
*/
forceStatic?: boolean;
}
@@ -47,6 +64,10 @@ export interface MakeURLOptions {
* The allowed extensions that can be used
*/
allowedExtensions?: readonly string[];
/**
* Whether to use the `animated` query parameter
*/
animated?: boolean;
/**
* The base URL.
*
@@ -192,7 +213,7 @@ export class CDN {
* @param emojiId - The emoji id
* @param options - Optional options for the emoji
*/
public emoji(emojiId: string, options?: Readonly<BaseImageURLOptions>): string;
public emoji(emojiId: string, options?: Readonly<EmojiURLOptions>): string;
/**
* Generates an emoji's URL for an emoji.
@@ -204,7 +225,7 @@ export class CDN {
// eslint-disable-next-line @typescript-eslint/unified-signatures
public emoji(emojiId: string, extension?: ImageExtension): string;
public emoji(emojiId: string, options?: ImageExtension | Readonly<BaseImageURLOptions>): string {
public emoji(emojiId: string, options?: ImageExtension | Readonly<EmojiURLOptions>): string {
let resolvedOptions;
if (typeof options === 'string') {
@@ -353,6 +374,17 @@ export class CDN {
return `${this.cdn}${CDNRoutes.soundboardSound(soundId)}`;
}
/**
* Generates a URL for a guild tag badge.
*
* @param guildId - The guild id
* @param badgeHash - The hash of the badge
* @param options - Optional options for the badge
*/
public guildTagBadge(guildId: string, badgeHash: string, options?: Readonly<BaseImageURLOptions>): string {
return this.makeURL(`/guild-tag-badges/${guildId}/${badgeHash}`, options);
}
/**
* Constructs the URL for the resource, checking whether or not `hash` starts with `a_` if `dynamic` is set to `true`.
*
@@ -381,6 +413,7 @@ export class CDN {
base = this.cdn,
extension = 'webp',
size,
animated,
}: Readonly<MakeURLOptions> = {},
): string {
// eslint-disable-next-line no-param-reassign
@@ -396,6 +429,10 @@ export class CDN {
const url = new URL(`${base}${route}.${extension}`);
if (animated !== undefined) {
url.searchParams.set('animated', String(animated));
}
if (size) {
url.searchParams.set('size', String(size));
}

View File

@@ -336,9 +336,9 @@ export class REST extends AsyncEventEmitter<RestEvents> {
for (const [index, file] of request.files.entries()) {
const fileKey = file.key ?? `files[${index}]`;
// https://developer.mozilla.org/en-US/docs/Web/API/FormData/append#parameters
// https://developer.mozilla.org/docs/Web/API/FormData/append#parameters
// FormData.append only accepts a string or Blob.
// https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#parameters
// https://developer.mozilla.org/docs/Web/API/Blob/Blob#parameters
// The Blob constructor accepts TypedArray/ArrayBuffer, strings, and Blobs.
if (isBufferLike(file.data)) {
// Try to infer the content type from the buffer if one isn't passed

View File

@@ -64,7 +64,7 @@
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@types/ws": "^8.5.12",
"discord-api-types": "^0.37.119",
"discord-api-types": "^0.38.24",
"prism-media": "^1.3.5",
"tslib": "^2.6.3",
"ws": "^8.18.0"

View File

@@ -2,6 +2,37 @@
All notable changes to this project will be documented in this file.
# [@discordjs/ws@2.0.3](https://github.com/discordjs/discord.js/compare/@discordjs/ws@2.0.2...@discordjs/ws@2.0.3) - (2025-06-16)
## Bug Fixes
- Regression in allowedMentions when replying (#10866) ([2ebb5cb](https://github.com/discordjs/discord.js/commit/2ebb5cbd53d869a52cba4549e7acc417963741cd))
# [@discordjs/ws@2.0.0](https://github.com/discordjs/discord.js/compare/@discordjs/ws@1.1.1...@discordjs/ws@2.0.0) - (2024-09-02)
## Bug Fixes
- **WebSocketShard:** Buffer native zlib decompression payload (#10416) ([defb083](https://github.com/discordjs/discord.js/commit/defb083528ef31383778187a04ced8b00d886242)) by @didinele
- **WebSocketManager:** Heartbeat event had outdated types (#10417) ([5eabec1](https://github.com/discordjs/discord.js/commit/5eabec14d45ef7bdd7f610e84234eb63e726eacd)) by @didinele
- Retry for EAI_AGAIN I/O error (#10383) ([be04acd](https://github.com/discordjs/discord.js/commit/be04acd534d7d0c3fb7f6bd174e4a6482aae0d73)) by @didinele
- Consistent debug log spacing (#10349) ([38c699b](https://github.com/discordjs/discord.js/commit/38c699bc8a2ca40f37f70c93e08067e00f12ee81)) by @Jiralite
## Features
- **WebsocketManager:** Retroactive token setting (#10418) ([de94eaf](https://github.com/discordjs/discord.js/commit/de94eaf351a69fab57ec766bd9e90e8c05e8c3d1)) by @didinele
- **WebSocketShard:** Explicit time out network error handling (#10375) ([093ac92](https://github.com/discordjs/discord.js/commit/093ac924aef1bf328feadb49876bfbe26052fe1a)) by @didinele
## Refactor
- **WebSocketShard:** Error event handling (#10436) ([a6de270](https://github.com/discordjs/discord.js/commit/a6de2707fc1107262b12491f73b5b6887df91c67)) by @didinele
- **ws:** Event layout (#10376) ([bf6761a](https://github.com/discordjs/discord.js/commit/bf6761a44adec1fe5017f6bf5d8bc0734916961f)) by @didinele
- **BREAKING CHANGE:** All events now emit shard id as its own param
* fix: worker event forwarding
---------
- Native zlib support (#10316) ([94cc02a](https://github.com/discordjs/discord.js/commit/94cc02a2580496774d75673abc0caabc765d9ee0)) by @sdanialraza
# [@discordjs/ws@2.0.0](https://github.com/discordjs/discord.js/compare/@discordjs/ws@1.1.1...@discordjs/ws@2.0.0) - (2024-09-02)
## Bug Fixes

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/ws",
"version": "2.0.2",
"version": "2.0.3",
"description": "Wrapper around Discord's gateway",
"scripts": {
"test": "vitest run",
@@ -79,7 +79,7 @@
"@sapphire/async-queue": "^1.5.3",
"@types/ws": "^8.5.12",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.1",
"discord-api-types": "^0.38.24",
"tslib": "^2.6.3",
"ws": "^8.18.0"
},

97
pnpm-lock.yaml generated
View File

@@ -816,8 +816,8 @@ importers:
specifier: ^2.4.6
version: 2.4.6
discord-api-types:
specifier: ^0.38.1
version: 0.38.1
specifier: ^0.38.29
version: 0.38.29
devDependencies:
'@discordjs/api-extractor':
specifier: workspace:^
@@ -932,8 +932,8 @@ importers:
packages/discord.js:
dependencies:
'@discordjs/builders':
specifier: ^1.11.2
version: 1.11.2
specifier: ^1.12.0
version: 1.12.0
'@discordjs/collection':
specifier: 1.5.3
version: 1.5.3
@@ -947,14 +947,14 @@ importers:
specifier: workspace:^
version: link:../util
'@discordjs/ws':
specifier: ^1.2.2
version: 1.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
specifier: ^1.2.3
version: 1.2.3(bufferutil@4.0.8)(utf-8-validate@6.0.4)
'@sapphire/snowflake':
specifier: 3.5.3
version: 3.5.3
discord-api-types:
specifier: ^0.38.1
version: 0.38.1
specifier: ^0.38.29
version: 0.38.29
fast-deep-equal:
specifier: 3.1.3
version: 3.1.3
@@ -1075,8 +1075,8 @@ importers:
packages/formatters:
dependencies:
discord-api-types:
specifier: ^0.37.119
version: 0.37.119
specifier: ^0.38.24
version: 0.38.24
devDependencies:
'@discordjs/api-extractor':
specifier: workspace:^
@@ -1148,8 +1148,8 @@ importers:
specifier: workspace:^
version: link:../ws
discord-api-types:
specifier: ^0.37.119
version: 0.37.119
specifier: ^0.38.24
version: 0.38.24
devDependencies:
'@discordjs/api-extractor':
specifier: workspace:^
@@ -1322,8 +1322,8 @@ importers:
specifier: ^2.4.6
version: 2.4.6
discord-api-types:
specifier: ^0.38.1
version: 0.38.1
specifier: ^0.38.24
version: 0.38.24
magic-bytes.js:
specifier: ^1.10.0
version: 1.10.0
@@ -1619,8 +1619,8 @@ importers:
specifier: ^8.5.12
version: 8.5.12
discord-api-types:
specifier: ^0.37.119
version: 0.37.119
specifier: ^0.38.24
version: 0.38.24
prism-media:
specifier: ^1.3.5
version: 1.3.5
@@ -1716,8 +1716,8 @@ importers:
specifier: ^2.4.6
version: 2.4.6
discord-api-types:
specifier: ^0.38.1
version: 0.38.1
specifier: ^0.38.24
version: 0.38.24
tslib:
specifier: ^2.6.3
version: 2.6.3
@@ -2777,20 +2777,20 @@ packages:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
'@definitelytyped/header-parser@0.2.19':
resolution: {integrity: sha512-zu+RxQpUCgorYUQZoyyrRIn9CljL1CeM4qak3NDeMO1r7tjAkodfpAGnVzx/6JR2OUk0tAgwmZxNMSwd9LVgxw==}
'@definitelytyped/header-parser@0.2.20':
resolution: {integrity: sha512-97YPAlUo8XjWNtZ+6k+My+50/ljE2iX6KEPjOZ1Az1RsZdKwJ6taAX3F5g6SY1SJr50bzdm2RZzyQNdRmHcs4w==}
engines: {node: '>=18.18.0'}
'@definitelytyped/typescript-versions@0.1.8':
resolution: {integrity: sha512-iz6q9aTwWW7CzN2g8jFQfZ955D63LA+wdIAKz4+2pCc/7kokmEHie1/jVWSczqLFOlmH+69bWQxIurryBP/sig==}
'@definitelytyped/typescript-versions@0.1.9':
resolution: {integrity: sha512-Qjalw9eNlcTjXhzx0Q6kHKuRCOUt/M5RGGRGKsiYlm/nveGvPX9liZSQlGXZVwyQ5I9qvq/GdaWiPchQ+ZXOrQ==}
engines: {node: '>=18.18.0'}
'@definitelytyped/utils@0.1.8':
resolution: {integrity: sha512-4JINx4Rttha29f50PBsJo48xZXx/He5yaIWJRwVarhYAN947+S84YciHl+AIhQNRPAFkg8+5qFngEGtKxQDWXA==}
engines: {node: '>=18.18.0'}
'@discordjs/builders@1.11.2':
resolution: {integrity: sha512-F1WTABdd8/R9D1icJzajC4IuLyyS8f3rTOz66JsSI3pKvpCAtsMBweu8cyNYsIyvcrKAVn9EPK+Psoymq+XC0A==}
'@discordjs/builders@1.12.0':
resolution: {integrity: sha512-jQ0m/fVOg6j3w2Rrzrg5VfqPpkslYNnpdTAyQzQXQ7S/5y0iScBnMlVZfu/AFLP9C34shQ4I+EpTGZV1VlN7RQ==}
engines: {node: '>=16.11.0'}
'@discordjs/collection@1.5.3':
@@ -2805,16 +2805,16 @@ packages:
resolution: {integrity: sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg==}
engines: {node: '>=16.11.0'}
'@discordjs/rest@2.5.0':
resolution: {integrity: sha512-PWhchxTzpn9EV3vvPRpwS0EE2rNYB9pvzDU/eLLW3mByJl0ZHZjHI2/wA8EbH2gRMQV7nu+0FoDF84oiPl8VAQ==}
'@discordjs/rest@2.5.1':
resolution: {integrity: sha512-Tg9840IneBcbrAjcGaQzHUJWFNq1MMWZjTdjJ0WS/89IffaNKc++iOvffucPxQTF/gviO9+9r8kEPea1X5J2Dw==}
engines: {node: '>=18'}
'@discordjs/util@1.1.1':
resolution: {integrity: sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==}
engines: {node: '>=18'}
'@discordjs/ws@1.2.2':
resolution: {integrity: sha512-dyfq7yn0wO0IYeYOs3z79I6/HumhmKISzFL0Z+007zQJMtAFGtt3AEoq1nuLXtcunUE5YYYQqgKvybXukAK8/w==}
'@discordjs/ws@1.2.3':
resolution: {integrity: sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==}
engines: {node: '>=16.11.0'}
'@edge-runtime/format@2.2.1':
@@ -8357,8 +8357,11 @@ packages:
discord-api-types@0.37.119:
resolution: {integrity: sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg==}
discord-api-types@0.38.1:
resolution: {integrity: sha512-vsjsqjAuxsPhiwbPjTBeGQaDPlizFmSkU0mTzFGMgRxqCDIRBR7iTY74HacpzrDV0QtERHRKQEk1tq7drZUtHg==}
discord-api-types@0.38.24:
resolution: {integrity: sha512-P7/DkcFIiIoaBogStnhhcGRX7KR+gIFp0SpmwsZUIM0bgDkYMEUx+8l+t3quYc/KSgg92wvE9w/4mabO57EMug==}
discord-api-types@0.38.29:
resolution: {integrity: sha512-+5BfrjLJN1hrrcK0MxDQli6NSv5lQH7Y3/qaOfk9+k7itex8RkA/UcevVMMLe8B4IKIawr4ITBTb2fBB2vDORg==}
dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
@@ -16299,13 +16302,13 @@ snapshots:
dependencies:
'@jridgewell/trace-mapping': 0.3.9
'@definitelytyped/header-parser@0.2.19':
'@definitelytyped/header-parser@0.2.20':
dependencies:
'@definitelytyped/typescript-versions': 0.1.8
'@definitelytyped/typescript-versions': 0.1.9
'@definitelytyped/utils': 0.1.8
semver: 7.7.1
'@definitelytyped/typescript-versions@0.1.8': {}
'@definitelytyped/typescript-versions@0.1.9': {}
'@definitelytyped/utils@0.1.8':
dependencies:
@@ -16318,12 +16321,12 @@ snapshots:
tar-stream: 3.1.7
which: 4.0.0
'@discordjs/builders@1.11.2':
'@discordjs/builders@1.12.0':
dependencies:
'@discordjs/formatters': 0.6.1
'@discordjs/util': 1.1.1
'@sapphire/shapeshift': 4.0.0
discord-api-types: 0.38.1
discord-api-types: 0.38.29
fast-deep-equal: 3.1.3
ts-mixer: 6.0.4
tslib: 2.8.1
@@ -16334,31 +16337,31 @@ snapshots:
'@discordjs/formatters@0.6.1':
dependencies:
discord-api-types: 0.38.1
discord-api-types: 0.38.29
'@discordjs/rest@2.5.0':
'@discordjs/rest@2.5.1':
dependencies:
'@discordjs/collection': 2.1.1
'@discordjs/util': 1.1.1
'@sapphire/async-queue': 1.5.3
'@sapphire/snowflake': 3.5.3
'@vladfrangu/async_event_emitter': 2.4.6
discord-api-types: 0.38.1
discord-api-types: 0.38.29
magic-bytes.js: 1.10.0
tslib: 2.8.1
undici: 6.21.1
undici: 6.21.3
'@discordjs/util@1.1.1': {}
'@discordjs/ws@1.2.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
'@discordjs/ws@1.2.3(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
dependencies:
'@discordjs/collection': 2.1.1
'@discordjs/rest': 2.5.0
'@discordjs/rest': 2.5.1
'@discordjs/util': 1.1.1
'@sapphire/async-queue': 1.5.3
'@types/ws': 8.5.12
'@vladfrangu/async_event_emitter': 2.4.6
discord-api-types: 0.38.1
discord-api-types: 0.38.29
tslib: 2.8.1
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
transitivePeerDependencies:
@@ -23711,7 +23714,9 @@ snapshots:
discord-api-types@0.37.119: {}
discord-api-types@0.38.1: {}
discord-api-types@0.38.24: {}
discord-api-types@0.38.29: {}
dlv@1.1.3: {}
@@ -23762,7 +23767,7 @@ snapshots:
dts-critic@3.3.11(typescript@5.5.4):
dependencies:
'@definitelytyped/header-parser': 0.2.19
'@definitelytyped/header-parser': 0.2.20
command-exists: 1.2.9
rimraf: 3.0.2
semver: 6.3.1
@@ -23772,8 +23777,8 @@ snapshots:
dtslint@4.2.1(typescript@5.5.4):
dependencies:
'@definitelytyped/header-parser': 0.2.19
'@definitelytyped/typescript-versions': 0.1.8
'@definitelytyped/header-parser': 0.2.20
'@definitelytyped/typescript-versions': 0.1.9
'@definitelytyped/utils': 0.1.8
dts-critic: 3.3.11(typescript@5.5.4)
fs-extra: 6.0.1