Compare commits

...

76 Commits

Author SHA1 Message Date
Jiralite
083f6abb38 chore(discord.js): release discord.js@14.23.2 2025-10-09 02:10:39 +01:00
Jiralite
5cc13b735c fix(ModalSubmitInteraction): Better resolving of components (#11162)
* fix: fix value crash

* fix: use a set

* fix: `const`
2025-10-09 02:05:29 +01:00
Jiralite
1e4d1dc04f fix: handle DM modals 2025-10-08 23:02:34 +01:00
Jiralite
177d81f596 chore(discord.js): release discord.js@14.23.1 2025-10-08 22:13:52 +01:00
Jiralite
bf4cfeb4bf build: upgrade builders to 1.12.1 2025-10-08 22:08:03 +01:00
Jiralite
11b236ff65 fix(ModalSubmitInteraction): Resolve crash on handling populated select menus (#11158)
* fix: handle receiving new selects

* fix: handle missing user object
2025-10-08 22:07:48 +01:00
Almeida
1d5b9837de fix: ending uncached polls (#11157) 2025-10-08 22:04:23 +01:00
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 2506 additions and 500 deletions

2
.github/CODEOWNERS vendored
View File

@@ -1,5 +1,5 @@
# Learn how to add code owners here: # 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 * @iCrawl

View File

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

View File

@@ -83,7 +83,7 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder<APIStringSele
* *
* @remarks * @remarks
* This method behaves similarly * 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. * It's useful for modifying and adjusting the order of existing options.
* @example * @example
* Remove the first option: * Remove the first option:

View File

@@ -125,7 +125,7 @@ export class EmbedBuilder {
* *
* @remarks * @remarks
* This method behaves similarly * 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. * 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. * 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. 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) # [@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 ## Features

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/core", "name": "@discordjs/core",
"version": "2.1.0", "version": "2.3.0",
"description": "A thinly abstracted wrapper around the rest API, and gateway.", "description": "A thinly abstracted wrapper around the rest API, and gateway.",
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run",
@@ -70,7 +70,7 @@
"@discordjs/ws": "workspace:^", "@discordjs/ws": "workspace:^",
"@sapphire/snowflake": "^3.5.3", "@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6", "@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.1" "discord-api-types": "^0.38.29"
}, },
"devDependencies": { "devDependencies": {
"@discordjs/api-extractor": "workspace:^", "@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 */ /* 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 { import {
Routes, Routes,
type RESTPostAPIChannelWebhookJSONBody, type APIThreadChannel,
type RESTPostAPIChannelWebhookResult,
type RESTDeleteAPIChannelResult, type RESTDeleteAPIChannelResult,
type RESTGetAPIChannelInvitesResult, type RESTGetAPIChannelInvitesResult,
type RESTGetAPIChannelMessageReactionUsersQuery, type RESTGetAPIChannelMessageReactionUsersQuery,
@@ -17,8 +16,8 @@ import {
type RESTGetAPIChannelThreadsArchivedQuery, type RESTGetAPIChannelThreadsArchivedQuery,
type RESTGetAPIChannelUsersThreadsArchivedResult, type RESTGetAPIChannelUsersThreadsArchivedResult,
type RESTGetAPIChannelWebhooksResult, type RESTGetAPIChannelWebhooksResult,
type RESTPatchAPIChannelMessageJSONBody,
type RESTPatchAPIChannelJSONBody, type RESTPatchAPIChannelJSONBody,
type RESTPatchAPIChannelMessageJSONBody,
type RESTPatchAPIChannelMessageResult, type RESTPatchAPIChannelMessageResult,
type RESTPatchAPIChannelResult, type RESTPatchAPIChannelResult,
type RESTPostAPIChannelFollowersResult, type RESTPostAPIChannelFollowersResult,
@@ -27,12 +26,14 @@ import {
type RESTPostAPIChannelMessageCrosspostResult, type RESTPostAPIChannelMessageCrosspostResult,
type RESTPostAPIChannelMessageJSONBody, type RESTPostAPIChannelMessageJSONBody,
type RESTPostAPIChannelMessageResult, type RESTPostAPIChannelMessageResult,
type RESTPutAPIChannelPermissionJSONBody,
type Snowflake,
type RESTPostAPIChannelThreadsJSONBody, type RESTPostAPIChannelThreadsJSONBody,
type RESTPostAPIChannelThreadsResult, type RESTPostAPIChannelThreadsResult,
type APIThreadChannel, type RESTPostAPIChannelWebhookJSONBody,
type RESTPostAPIChannelWebhookResult,
type RESTPostAPIGuildForumThreadsJSONBody, type RESTPostAPIGuildForumThreadsJSONBody,
type RESTPutAPIChannelPermissionJSONBody,
type RESTPutAPIChannelRecipientJSONBody,
type Snowflake,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
export interface StartForumThreadOptions extends RESTPostAPIGuildForumThreadsJSONBody { export interface StartForumThreadOptions extends RESTPostAPIGuildForumThreadsJSONBody {
@@ -223,9 +224,13 @@ export class ChannelsAPI {
public async edit( public async edit(
channelId: Snowflake, channelId: Snowflake,
body: RESTPatchAPIChannelJSONBody, 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 channelId - The id of the channel to delete
* @param options - The options for deleting the channel * @param options - The options for deleting the channel
*/ */
public async delete(channelId: Snowflake, { signal }: Pick<RequestData, 'signal'> = {}) { public async delete(channelId: Snowflake, { signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {}) {
return this.rest.delete(Routes.channel(channelId), { signal }) as Promise<RESTDeleteAPIChannelResult>; return this.rest.delete(Routes.channel(channelId), { signal, reason }) as Promise<RESTDeleteAPIChannelResult>;
} }
/** /**
@@ -441,11 +446,12 @@ export class ChannelsAPI {
channelId: Snowflake, channelId: Snowflake,
body: RESTPostAPIChannelThreadsJSONBody, body: RESTPostAPIChannelThreadsJSONBody,
messageId?: Snowflake, messageId?: Snowflake,
{ signal }: Pick<RequestData, 'signal'> = {}, { signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {},
) { ) {
return this.rest.post(Routes.threads(channelId, messageId), { return this.rest.post(Routes.threads(channelId, messageId), {
body, body,
signal, signal,
reason,
}) as Promise<RESTPostAPIChannelThreadsResult>; }) as Promise<RESTPostAPIChannelThreadsResult>;
} }
@@ -460,7 +466,7 @@ export class ChannelsAPI {
public async createForumThread( public async createForumThread(
channelId: Snowflake, channelId: Snowflake,
{ message, ...optionsBody }: StartForumThreadOptions, { message, ...optionsBody }: StartForumThreadOptions,
{ signal }: Pick<RequestData, 'signal'> = {}, { signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {},
) { ) {
const { files, ...messageBody } = message; const { files, ...messageBody } = message;
@@ -469,7 +475,12 @@ export class ChannelsAPI {
message: messageBody, 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, 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 RESTPostAPIGuildsMFAResult,
type RESTPostAPIGuildsResult, type RESTPostAPIGuildsResult,
type RESTPutAPIGuildBanJSONBody, type RESTPutAPIGuildBanJSONBody,
type RESTPutAPIGuildIncidentActionsJSONBody,
type RESTPutAPIGuildIncidentActionsResult,
type RESTPutAPIGuildMemberJSONBody, type RESTPutAPIGuildMemberJSONBody,
type RESTPutAPIGuildMemberResult, type RESTPutAPIGuildMemberResult,
type RESTPutAPIGuildOnboardingJSONBody, type RESTPutAPIGuildOnboardingJSONBody,
@@ -166,6 +168,7 @@ export class GuildsAPI {
* @see {@link https://discord.com/developers/docs/resources/guild#create-guild} * @see {@link https://discord.com/developers/docs/resources/guild#create-guild}
* @param body - The guild to create * @param body - The guild to create
* @param options - The options for creating the guild * @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'> = {}) { public async create(body: RESTPostAPIGuildsJSONBody, { signal }: Pick<RequestData, 'signal'> = {}) {
return this.rest.post(Routes.guilds(), { body, signal }) as Promise<RESTPostAPIGuildsResult>; 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} * @see {@link https://discord.com/developers/docs/resources/guild#delete-guild}
* @param guildId - The id of the guild to delete * @param guildId - The id of the guild to delete
* @param options - The options for deleting this guild * @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'> = {}) { public async delete(guildId: Snowflake, { signal }: Pick<RequestData, 'signal'> = {}) {
await this.rest.delete(Routes.guild(guildId), { reason, 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 guildId - The id of the guild to edit the MFA level for
* @param level - The new MFA level * @param level - The new MFA level
* @param options - The options for editing the 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( public async editMFALevel(
guildId: Snowflake, guildId: Snowflake,
@@ -1287,16 +1292,16 @@ export class GuildsAPI {
* Creates a new template * Creates a new template
* *
* @see {@link https://discord.com/developers/docs/resources/guild-template#create-guild-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 body - The data for creating the template
* @param options - The options for creating the template * @param options - The options for creating the template
*/ */
public async createTemplate( public async createTemplate(
templateCode: string, guildId: Snowflake,
body: RESTPostAPIGuildTemplatesJSONBody, body: RESTPostAPIGuildTemplatesJSONBody,
{ signal }: Pick<RequestData, 'signal'> = {}, { 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, signal,
}) as Promise<RESTPutAPIGuildOnboardingResult>; }) 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 { ApplicationCommandsAPI } from './applicationCommands.js';
import { ApplicationsAPI } from './applications.js'; import { ApplicationsAPI } from './applications.js';
import { ChannelsAPI } from './channel.js'; import { ChannelsAPI } from './channel.js';
import { GatewayAPI } from './gateway.js';
import { GuildsAPI } from './guild.js'; import { GuildsAPI } from './guild.js';
import { InteractionsAPI } from './interactions.js'; import { InteractionsAPI } from './interactions.js';
import { InvitesAPI } from './invite.js'; import { InvitesAPI } from './invite.js';
@@ -19,6 +20,7 @@ import { WebhooksAPI } from './webhook.js';
export * from './applicationCommands.js'; export * from './applicationCommands.js';
export * from './applications.js'; export * from './applications.js';
export * from './channel.js'; export * from './channel.js';
export * from './gateway.js';
export * from './guild.js'; export * from './guild.js';
export * from './interactions.js'; export * from './interactions.js';
export * from './invite.js'; export * from './invite.js';
@@ -40,6 +42,8 @@ export class API {
public readonly channels: ChannelsAPI; public readonly channels: ChannelsAPI;
public readonly gateway: GatewayAPI;
public readonly guilds: GuildsAPI; public readonly guilds: GuildsAPI;
public readonly interactions: InteractionsAPI; public readonly interactions: InteractionsAPI;
@@ -70,6 +74,7 @@ export class API {
this.applicationCommands = new ApplicationCommandsAPI(rest); this.applicationCommands = new ApplicationCommandsAPI(rest);
this.applications = new ApplicationsAPI(rest); this.applications = new ApplicationsAPI(rest);
this.channels = new ChannelsAPI(rest); this.channels = new ChannelsAPI(rest);
this.gateway = new GatewayAPI(rest);
this.guilds = new GuildsAPI(rest); this.guilds = new GuildsAPI(rest);
this.invites = new InvitesAPI(rest); this.invites = new InvitesAPI(rest);
this.monetization = new MonetizationAPI(rest); this.monetization = new MonetizationAPI(rest);

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,130 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
# [14.23.2](https://github.com/discordjs/discord.js/compare/14.23.1...14.23.2) - (2025-10-09)
## Bug Fixes
- **ModalSubmitInteraction:** Better resolving of components (#11162) ([5cc13b7](https://github.com/discordjs/discord.js/commit/5cc13b735c78384a3488da527985cded92f67d41))
- Handle DM modals ([1e4d1dc](https://github.com/discordjs/discord.js/commit/1e4d1dc04f7dabfb0575441957a6278675f02871))
# [14.23.1](https://github.com/discordjs/discord.js/compare/14.23.0...14.23.1) - (2025-10-08)
## Bug Fixes
- **ModalSubmitInteraction:** Resolve crash on handling populated select menus (#11158) ([11b236f](https://github.com/discordjs/discord.js/commit/11b236ff6539f91f11caa3d5a2cc7ae23070aaec))
- Ending uncached polls (#11157) ([1d5b983](https://github.com/discordjs/discord.js/commit/1d5b9837de4036ca6f07f22f714f534463cc35ec))
# [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) # [14.19.3](https://github.com/discordjs/discord.js/compare/14.19.2...14.19.3) - (2025-05-02)
## Bug Fixes ## Bug Fixes

View File

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

View File

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

View File

@@ -36,12 +36,14 @@ async function writeClientActionImports() {
for (const file of (await readdir(actionsDirectory)).sort()) { for (const file of (await readdir(actionsDirectory)).sort()) {
if (file === 'Action.js' || file === 'ActionsManager.js') continue; 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(' }\n');
lines.push(' register(Action) {'); lines.push(' load(Action) {');
lines.push(" this[Action.name.replace(/Action$/, '')] = new Action(this.client);"); lines.push(' return new Action(this.client);');
lines.push(' }'); lines.push(' }');
lines.push('}\n'); lines.push('}\n');
lines.push('module.exports = ActionsManager;\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`. * with the client as `this`.
* @param {string} script Script to eval * @param {string} script Script to eval
* @returns {*} * @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. * except the epoch is 2015-01-01T00:00:00.000Z.
* *
* If we have a snowflake '266241948824764416' we can represent it as binary: * 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} * @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 * @external BaseImageURLOptions
* @see {@link https://discord.js.org/docs/packages/rest/stable/BaseImageURLOptions:Interface} * @see {@link https://discord.js.org/docs/packages/rest/stable/BaseImageURLOptions:Interface}

View File

@@ -1,6 +1,8 @@
'use strict'; '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) { getReaction(data, message, user) {
const id = data.emoji.id ?? decodeURIComponent(data.emoji.name); const id = data.emoji.id ?? decodeURIComponent(data.emoji.name);
return this.getPayload( return this.getPayload(

View File

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

View File

@@ -11,11 +11,18 @@ class MessagePollVoteAddAction extends Action {
const message = this.getMessage(data, channel); const message = this.getMessage(data, channel);
if (!message) return false; 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; if (!answer) return false;
const user = this.getUser(data);
if (user) {
answer.voters._add(user);
}
answer.voteCount++; answer.voteCount++;
/** /**

View File

@@ -11,12 +11,17 @@ class MessagePollVoteRemoveAction extends Action {
const message = this.getMessage(data, channel); const message = this.getMessage(data, channel);
if (!message) return false; 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; 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. * 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'); const WebSocketShardEvents = require('../../util/WebSocketShardEvents');
let zlib; let zlib;
let deprecationEmitted = false;
try { try {
zlib = require('zlib-sync'); zlib = require('zlib-sync');
@@ -379,6 +380,22 @@ class WebSocketManager extends EventEmitter {
/** /**
* Emitted when the client becomes ready to start working. * Emitted when the client becomes ready to start working.
* @event Client#ready * @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 * @param {Client} client The client
*/ */
this.client.emit(Events.ClientReady, this.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_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')],
['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')], ['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')],
['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')], ['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')],
['GUILD_SOUNDBOARD_SOUNDS_UPDATE', require('./GUILD_SOUNDBOARD_SOUNDS_UPDATE.js')], ['GUILD_SOUNDBOARD_SOUNDS_UPDATE', require('./GUILD_SOUNDBOARD_SOUNDS_UPDATE')],
['GUILD_SOUNDBOARD_SOUND_CREATE', require('./GUILD_SOUNDBOARD_SOUND_CREATE.js')], ['GUILD_SOUNDBOARD_SOUND_CREATE', require('./GUILD_SOUNDBOARD_SOUND_CREATE')],
['GUILD_SOUNDBOARD_SOUND_DELETE', require('./GUILD_SOUNDBOARD_SOUND_DELETE.js')], ['GUILD_SOUNDBOARD_SOUND_DELETE', require('./GUILD_SOUNDBOARD_SOUND_DELETE')],
['GUILD_SOUNDBOARD_SOUND_UPDATE', require('./GUILD_SOUNDBOARD_SOUND_UPDATE.js')], ['GUILD_SOUNDBOARD_SOUND_UPDATE', require('./GUILD_SOUNDBOARD_SOUND_UPDATE')],
['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')], ['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')],
['GUILD_UPDATE', require('./GUILD_UPDATE')], ['GUILD_UPDATE', require('./GUILD_UPDATE')],
['INTERACTION_CREATE', require('./INTERACTION_CREATE')], ['INTERACTION_CREATE', require('./INTERACTION_CREATE')],
@@ -54,7 +54,7 @@ const handlers = Object.fromEntries([
['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')], ['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')],
['READY', require('./READY')], ['READY', require('./READY')],
['RESUMED', require('./RESUMED')], ['RESUMED', require('./RESUMED')],
['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS.js')], ['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS')],
['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')], ['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')],
['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')], ['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')],
['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')], ['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')],

View File

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

View File

@@ -161,6 +161,10 @@ const Messages = {
`Required field with custom id "${customId}" not found.`, `Required field with custom id "${customId}" not found.`,
[DjsErrorCodes.ModalSubmitInteractionFieldType]: (customId, type, expected) => [DjsErrorCodes.ModalSubmitInteractionFieldType]: (customId, type, expected) =>
`Field with custom id "${customId}" is of type: ${type}; expected ${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.InvalidMissingScopes]: 'At least one valid scope must be provided for the invite',
[DjsErrorCodes.InvalidScopesWithPermissions]: 'Permissions cannot be set without the bot scope.', [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.Formatters = require('./util/Formatters');
exports.GuildMemberFlagsBitField = require('./util/GuildMemberFlagsBitField').GuildMemberFlagsBitField; exports.GuildMemberFlagsBitField = require('./util/GuildMemberFlagsBitField').GuildMemberFlagsBitField;
exports.IntentsBitField = require('./util/IntentsBitField'); exports.IntentsBitField = require('./util/IntentsBitField');
exports.InviteFlagsBitField = require('./util/InviteFlagsBitField.js').InviteFlagsBitField;
exports.LimitedCollection = require('./util/LimitedCollection'); exports.LimitedCollection = require('./util/LimitedCollection');
exports.MessageFlagsBitField = require('./util/MessageFlagsBitField'); exports.MessageFlagsBitField = require('./util/MessageFlagsBitField');
exports.Options = require('./util/Options'); exports.Options = require('./util/Options');
@@ -80,6 +81,7 @@ exports.GuildStickerManager = require('./managers/GuildStickerManager');
exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager'); exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager');
exports.MessageManager = require('./managers/MessageManager'); exports.MessageManager = require('./managers/MessageManager');
exports.PermissionOverwriteManager = require('./managers/PermissionOverwriteManager'); exports.PermissionOverwriteManager = require('./managers/PermissionOverwriteManager');
exports.PollAnswerVoterManager = require('./managers/PollAnswerVoterManager.js').PollAnswerVoterManager;
exports.PresenceManager = require('./managers/PresenceManager'); exports.PresenceManager = require('./managers/PresenceManager');
exports.ReactionManager = require('./managers/ReactionManager'); exports.ReactionManager = require('./managers/ReactionManager');
exports.ReactionUserManager = require('./managers/ReactionUserManager'); exports.ReactionUserManager = require('./managers/ReactionUserManager');
@@ -126,6 +128,7 @@ exports.CommandInteractionOptionResolver = require('./structures/CommandInteract
exports.Component = require('./structures/Component'); exports.Component = require('./structures/Component');
exports.ContainerComponent = require('./structures/ContainerComponent'); exports.ContainerComponent = require('./structures/ContainerComponent');
exports.ContextMenuCommandInteraction = require('./structures/ContextMenuCommandInteraction'); exports.ContextMenuCommandInteraction = require('./structures/ContextMenuCommandInteraction');
exports.DirectoryChannel = require('./structures/DirectoryChannel');
exports.DMChannel = require('./structures/DMChannel'); exports.DMChannel = require('./structures/DMChannel');
exports.Embed = require('./structures/Embed'); exports.Embed = require('./structures/Embed');
exports.EmbedBuilder = require('./structures/EmbedBuilder'); exports.EmbedBuilder = require('./structures/EmbedBuilder');
@@ -159,6 +162,7 @@ exports.InteractionWebhook = require('./structures/InteractionWebhook');
exports.Invite = require('./structures/Invite'); exports.Invite = require('./structures/Invite');
exports.InviteStageInstance = require('./structures/InviteStageInstance'); exports.InviteStageInstance = require('./structures/InviteStageInstance');
exports.InviteGuild = require('./structures/InviteGuild'); exports.InviteGuild = require('./structures/InviteGuild');
exports.LabelComponent = require('./structures/LabelComponent');
exports.Message = require('./structures/Message').Message; exports.Message = require('./structures/Message').Message;
exports.Attachment = require('./structures/Attachment'); exports.Attachment = require('./structures/Attachment');
exports.AttachmentBuilder = require('./structures/AttachmentBuilder'); exports.AttachmentBuilder = require('./structures/AttachmentBuilder');

View File

@@ -230,13 +230,13 @@ class GuildChannelManager extends CachedManager {
async createWebhook({ channel, name, avatar, reason }) { async createWebhook({ channel, name, avatar, reason }) {
const id = this.resolveId(channel); const id = this.resolveId(channel);
if (!id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable'); 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), { const data = await this.client.rest.post(Routes.channelWebhooks(id), {
body: { body: {
name, name,
avatar, avatar: resolvedImage,
}, },
reason, reason,
}); });

View File

@@ -163,6 +163,7 @@ class GuildManager extends CachedManager {
* Creates a guild. * Creates a guild.
* <warn>This is only available to bots in fewer than 10 guilds.</warn> * <warn>This is only available to bots in fewer than 10 guilds.</warn>
* @param {GuildCreateOptions} options Options for creating the guild * @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 * @returns {Promise<Guild>} The guild that was created
*/ */
async create({ async create({

View File

@@ -1,5 +1,6 @@
'use strict'; 'use strict';
const { process } = require('node:process');
const { setTimeout, clearTimeout } = require('node:timers'); const { setTimeout, clearTimeout } = require('node:timers');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest'); const { makeURLSearchParams } = require('@discordjs/rest');
@@ -10,10 +11,13 @@ const { DiscordjsError, DiscordjsTypeError, DiscordjsRangeError, ErrorCodes } =
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel'); const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
const { GuildMember } = require('../structures/GuildMember'); const { GuildMember } = require('../structures/GuildMember');
const { Role } = require('../structures/Role'); const { Role } = require('../structures/Role');
const { resolveImage } = require('../util/DataResolver');
const Events = require('../util/Events'); const Events = require('../util/Events');
const { GuildMemberFlagsBitField } = require('../util/GuildMemberFlagsBitField'); const { GuildMemberFlagsBitField } = require('../util/GuildMemberFlagsBitField');
const Partials = require('../util/Partials'); const Partials = require('../util/Partials');
let deprecatedEmittedForEditSoleNickname = false;
/** /**
* Manages API methods for GuildMembers and stores their cache. * Manages API methods for GuildMembers and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
@@ -336,8 +340,8 @@ class GuildMemberManager extends CachedManager {
*/ */
/** /**
* Edits a member of the guild. * Edits a member of a guild.
* <info>The user must be a member of the guild</info> *
* @param {UserResolvable} user The member to edit * @param {UserResolvable} user The member to edit
* @param {GuildMemberEditOptions} options The options to provide * @param {GuildMemberEditOptions} options The options to provide
* @returns {Promise<GuildMember>} * @returns {Promise<GuildMember>}
@@ -372,13 +376,30 @@ class GuildMemberManager extends CachedManager {
} }
let endpoint; let endpoint;
if (id === this.client.user.id) { if (id === this.client.user.id) {
const keys = Object.keys(options); 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); if (keys.length === 1 && keys[0] === 'nick') {
} else { // For modifying the current application's nickname only, we use the /guilds/{guild.id}/members/@me endpoint.
endpoint = Routes.guildMember(this.guild.id, id); // 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 d = await this.client.rest.patch(endpoint, { body: options, reason });
const clone = this.cache.get(id)?._clone(); const clone = this.cache.get(id)?._clone();
@@ -386,6 +407,38 @@ class GuildMemberManager extends CachedManager {
return clone ?? this._add(d, false); 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. * Options used for pruning guild members.
* <info>It's recommended to set {@link GuildPruneMembersOptions#count options.count} * <info>It's recommended to set {@link GuildPruneMembersOptions#count options.count}

View File

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

View File

@@ -1,5 +1,6 @@
'use strict'; 'use strict';
const process = require('node:process');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest'); const { makeURLSearchParams } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v10'); const { Routes } = require('discord-api-types/v10');
@@ -10,6 +11,8 @@ const MessagePayload = require('../structures/MessagePayload');
const { MakeCacheOverrideSymbol } = require('../util/Symbols'); const { MakeCacheOverrideSymbol } = require('../util/Symbols');
const { resolvePartialEmoji } = require('../util/Util'); const { resolvePartialEmoji } = require('../util/Util');
let deprecationEmittedForFetchPinned = false;
/** /**
* Manages API methods for Messages and holds their cache. * Manages API methods for Messages and holds their cache.
* @extends {CachedManager} * @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()); 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. * 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. * <info>The returned Collection does not contain any reaction data of the messages.
* Those need to be fetched separately.</info> * Those need to be fetched separately.</info>
* @param {boolean} [cache=true] Whether to cache the message(s) * @param {boolean} [cache=true] Whether to cache the message(s)
* @deprecated Use {@link MessageManager#fetchPins} instead.
* @returns {Promise<Collection<Snowflake, Message>>} * @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) { 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 data = await this.client.rest.get(Routes.channelPins(this.channel.id));
const messages = new Collection(); const messages = new Collection();
for (const message of data) messages.set(message.id, this._add(message, cache)); 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); message = this.resolveId(message);
if (!message) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); 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); message = this.resolveId(message);
if (!message) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'message', 'MessageResolvable'); 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'); const { setPosition, resolveColor } = require('../util/Util');
let cacheWarningEmitted = false; let cacheWarningEmitted = false;
let deprecationEmittedForCreate = false;
let deprecationEmittedForEdit = false;
/** /**
* Manages API methods for roles and stores their cache. * Manages API methods for roles and stores their cache.
@@ -58,7 +60,7 @@ class RoleManager extends CachedManager {
* @example * @example
* // Fetch a single role * // Fetch a single role
* message.guild.roles.fetch('222078108977594368') * 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); * .catch(console.error);
*/ */
async fetch(id, { cache = true, force = false } = {}) { async fetch(id, { cache = true, force = false } = {}) {
@@ -112,11 +114,24 @@ class RoleManager extends CachedManager {
* @returns {?Snowflake} * @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. * Options used to create a new role.
* @typedef {Object} RoleCreateOptions * @typedef {Object} RoleCreateOptions
* @property {string} [name] The name of the new role * @property {string} [name] The name of the new role
* @property {ColorResolvable} [color] The data to create the role with * @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 {boolean} [hoist] Whether or not the new role should be hoisted
* @property {PermissionResolvable} [permissions] The permissions for the new role * @property {PermissionResolvable} [permissions] The permissions for the new role
* @property {number} [position] The position of 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 * // Create a new role with data and a reason
* guild.roles.create({ * guild.roles.create({
* name: 'Super Cool Blue People', * name: 'Super Cool Blue People',
* color: Colors.Blue,
* reason: 'we needed a role for Super Cool People', * 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) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async create(options = {}) { async create(options = {}) {
let { name, color, hoist, permissions, position, mentionable, reason, icon, unicodeEmoji } = options; let { permissions, icon } = options;
color &&= resolveColor(color); const { name, color, hoist, position, mentionable, reason, unicodeEmoji } = options;
if (permissions !== undefined) permissions = new PermissionsBitField(permissions); if (permissions !== undefined) permissions = new PermissionsBitField(permissions);
if (icon) { if (icon) {
const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL(); const guildEmojiURL = this.guild.emojis.resolve(icon)?.imageURL();
@@ -158,10 +188,30 @@ class RoleManager extends CachedManager {
if (typeof icon !== 'string') icon = undefined; 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), { const data = await this.client.rest.post(Routes.guildRoles(this.guild.id), {
body: { body: {
name, name,
color, colors,
hoist, hoist,
permissions, permissions,
mentionable, mentionable,
@@ -210,9 +260,29 @@ class RoleManager extends CachedManager {
if (typeof icon !== 'string') icon = undefined; 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 = { const body = {
name: options.name, name: options.name,
color: options.color === undefined ? undefined : resolveColor(options.color), colors,
hoist: options.hoist, hoist: options.hoist,
permissions: options.permissions === undefined ? undefined : new PermissionsBitField(options.permissions), permissions: options.permissions === undefined ? undefined : new PermissionsBitField(options.permissions),
mentionable: options.mentionable, mentionable: options.mentionable,

View File

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

View File

@@ -16,37 +16,42 @@ class ApplicationEmoji extends Emoji {
*/ */
this.application = application; this.application = application;
/**
* The user who created this emoji
* @type {?User}
*/
this.author = null;
this.managed = null;
this.requiresColons = null;
this._patch(data); this._patch(data);
} }
_patch(data) { _patch(data) {
if ('name' in data) this.name = data.name; 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) { if ('managed' in data) {
/** /**
* Whether this emoji is managed by an external service * Whether this emoji is managed by an external service. Always `false` for application emojis
* @type {?boolean} * @type {false}
*/ */
this.managed = data.managed; this.managed = data.managed;
} }
if ('require_colons' in data) { if ('require_colons' in data) {
/** /**
* Whether or not this emoji requires colons surrounding it * Whether this emoji requires colons surrounding it. Always `true` for application emojis
* @type {?boolean} * @type {true}
*/ */
this.requiresColons = data.require_colons; 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.id === this.id &&
other.name === this.name && other.name === this.name &&
other.managed === this.managed && 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; module.exports = ApplicationEmoji;

View File

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

View File

@@ -58,7 +58,7 @@ class BaseGuildEmoji extends Emoji {
* @method imageURL * @method imageURL
* @memberof BaseGuildEmoji * @memberof BaseGuildEmoji
* @instance * @instance
* @param {BaseImageURLOptions} [options] Options for the image URL * @param {EmojiURLOptions} [options] Options for the emoji URL
* @returns {string} * @returns {string}
*/ */
@@ -72,4 +72,40 @@ class BaseGuildEmoji extends Emoji {
* @deprecated Use {@link BaseGuildEmoji#imageURL} instead. * @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; module.exports = BaseGuildEmoji;

View File

@@ -163,6 +163,17 @@ class ClientApplication extends Application {
this.approximateUserInstallCount ??= null; 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) { if ('guild_id' in data) {
/** /**
* The id of the guild associated with this application. * 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. * Data for creating or editing an application role connection metadata.
* @typedef {Object} ApplicationRoleConnectionMetadataEditOptions * @typedef {Object} ApplicationRoleConnectionMetadataEditOptions
* @property {string} name The name of the metadata field * @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 {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 {string} key The dictionary key of the metadata field
* @property {ApplicationRoleConnectionMetadataType} type The type 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); const { updated } = this.client.actions.UserUpdate.handle(data);
return updated ?? this; return updated ?? this;
} }

View File

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

View File

@@ -832,6 +832,7 @@ class Guild extends AnonymousGuild {
* @property {number} [afkTimeout] The AFK timeout of the guild * @property {number} [afkTimeout] The AFK timeout of the guild
* @property {?(BufferResolvable|Base64Resolvable)} [icon] The icon of the guild * @property {?(BufferResolvable|Base64Resolvable)} [icon] The icon of the guild
* @property {GuildMemberResolvable} [owner] The owner 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)} [splash] The invite splash image of the guild
* @property {?(BufferResolvable|Base64Resolvable)} [discoverySplash] The discovery 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 * @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 {GuildMemberResolvable} owner The new owner of the guild
* @param {string} [reason] Reason for setting the new owner * @param {string} [reason] Reason for setting the new owner
* @returns {Promise<Guild>} * @returns {Promise<Guild>}
* @example * @deprecated API related to guild ownership may no longer be used.
* // 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);
*/ */
setOwner(owner, reason) { setOwner(owner, reason) {
return this.edit({ owner, reason }); return this.edit({ owner, reason });
@@ -1345,11 +1341,7 @@ class Guild extends AnonymousGuild {
* @param {GuildMFALevel} level The MFA level * @param {GuildMFALevel} level The MFA level
* @param {string} [reason] Reason for changing the guild's MFA level * @param {string} [reason] Reason for changing the guild's MFA level
* @returns {Promise<Guild>} * @returns {Promise<Guild>}
* @example * @deprecated API related to guild ownership may no longer be used.
* // 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);
*/ */
async setMFALevel(level, reason) { async setMFALevel(level, reason) {
await this.client.rest.post(Routes.guildMFA(this.id), { await this.client.rest.post(Routes.guildMFA(this.id), {
@@ -1379,6 +1371,7 @@ class Guild extends AnonymousGuild {
/** /**
* Deletes the guild. * Deletes the guild.
* @returns {Promise<Guild>} * @returns {Promise<Guild>}
* @deprecated API related to guild ownership may no longer be used.
* @example * @example
* // Delete a guild * // Delete a guild
* guild.delete() * guild.delete()

View File

@@ -250,10 +250,11 @@ class GuildChannel extends BaseChannel {
return new PermissionsBitField(PermissionsBitField.All).freeze(); 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 everyoneOverwrites = this.permissionOverwrites.cache.get(this.guild.id);
const roleOverwrites = this.permissionOverwrites.cache.get(role.id); const roleOverwrites = this.permissionOverwrites.cache.get(role.id);
return role.permissions return basePermissions
.remove(everyoneOverwrites?.deny ?? PermissionsBitField.DefaultBit) .remove(everyoneOverwrites?.deny ?? PermissionsBitField.DefaultBit)
.add(everyoneOverwrites?.allow ?? PermissionsBitField.DefaultBit) .add(everyoneOverwrites?.allow ?? PermissionsBitField.DefaultBit)
.remove(roleOverwrites?.deny ?? PermissionsBitField.DefaultBit) .remove(roleOverwrites?.deny ?? PermissionsBitField.DefaultBit)

View File

@@ -122,6 +122,20 @@ class GuildMember extends Base {
} else { } else {
this.flags ??= new GuildMemberFlagsBitField().freeze(); 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() { _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); 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. * A link to the member's banner.
* @param {ImageURLOptions} [options={}] Options for the banner URL * @param {ImageURLOptions} [options={}] Options for the banner URL
@@ -195,6 +218,16 @@ class GuildMember extends Base {
return this.bannerURL(options) ?? this.user.bannerURL(options); 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 * The time this member joined the guild
* @type {?Date} * @type {?Date}
@@ -237,7 +270,7 @@ class GuildMember extends Base {
* @readonly * @readonly
*/ */
get displayColor() { 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); * .catch(console.error);
*/ */
setNickname(nick, reason) { 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.communicationDisabledUntilTimestamp === member.communicationDisabledUntilTimestamp &&
this.flags.bitfield === member.flags.bitfield && this.flags.bitfield === member.flags.bitfield &&
(this._roles === member._roles || (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.bannerURL = this.bannerURL();
json.displayAvatarURL = this.displayAvatarURL(); json.displayAvatarURL = this.displayAvatarURL();
json.displayBannerURL = this.displayBannerURL(); json.displayBannerURL = this.displayBannerURL();
json.avatarDecorationURL = this.avatarDecorationURL();
return json; return json;
} }
} }

View File

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

View File

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

View File

@@ -4,23 +4,48 @@ const { Collection } = require('@discordjs/collection');
const { ComponentType } = require('discord-api-types/v10'); const { ComponentType } = require('discord-api-types/v10');
const { DiscordjsTypeError, ErrorCodes } = require('../errors'); 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 * Represents the serialized fields from a modal submit interaction
*/ */
class ModalSubmitFields { class ModalSubmitFields {
constructor(components) { constructor(components, resolved) {
/** /**
* The components within the modal * The components within the modal
* @type {ActionRowModalData[]} *
* @type {Array<ActionRowModalData|LabelModalData|TextDisplayModalData>}
*/ */
this.components = components; 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 * The extracted fields from the modal
* @type {Collection<string, ModalData>} * @type {Collection<string, ModalData>}
*/ */
this.fields = components.reduce((accumulator, next) => { 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; return accumulator;
}, new Collection()); }, new Collection());
} }
@@ -42,13 +67,154 @@ class ModalSubmitFields {
return field; 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 * Gets the value of a text input component given a custom id
* @param {string} customId The custom id of the text input component * @param {string} customId The custom id of the text input component
* @returns {string} * @returns {string}
*/ */
getTextInputValue(customId) { 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'; 'use strict';
const { Collection } = require('@discordjs/collection');
const { lazy } = require('@discordjs/util'); const { lazy } = require('@discordjs/util');
const BaseInteraction = require('./BaseInteraction'); const BaseInteraction = require('./BaseInteraction');
const InteractionWebhook = require('./InteractionWebhook'); const InteractionWebhook = require('./InteractionWebhook');
const ModalSubmitFields = require('./ModalSubmitFields'); const ModalSubmitFields = require('./ModalSubmitFields');
const InteractionResponses = require('./interfaces/InteractionResponses'); const InteractionResponses = require('./interfaces/InteractionResponses');
const { transformResolved } = require('../util/Util');
const getMessage = lazy(() => require('./Message').Message); const getMessage = lazy(() => require('./Message').Message);
/** /**
* @typedef {Object} ModalData * @typedef {Object} BaseModalData
* @property {string} value The value of the field
* @property {ComponentType} type The component type of the field * @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 * @typedef {BaseModalData} TextInputModalData
* @property {ModalData[]} components The components of this action row * @property {string} customId The custom id of the field
* @property {ComponentType} type The component type of the action row * @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,24 @@ class ModalSubmitInteraction extends BaseInteraction {
/** /**
* The components within the modal * 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, {
client: this.client,
guild: this.guild,
}),
);
/** /**
* The fields within the modal * The fields within the modal
* @type {ModalSubmitFields} * @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 * Whether the reply to this interaction has been deferred
@@ -85,19 +123,93 @@ class ModalSubmitInteraction extends BaseInteraction {
/** /**
* Transforms component data to discord.js-compatible data * Transforms component data to discord.js-compatible data
* @param {*} rawComponent The data to transform * @param {*} rawComponent The data to transform
* @param {APIInteractionDataResolved} [resolved] The resolved data for the interaction
* @param {*} [extra] Extra data required for the transformation
* @returns {ModalData[]} * @returns {ModalData[]}
*/ */
static transformComponent(rawComponent) { static transformComponent(rawComponent, resolved, { client, guild } = {}) {
return rawComponent.components if ('components' in rawComponent) {
? { return {
type: rawComponent.type, type: rawComponent.type,
components: rawComponent.components.map(component => this.transformComponent(component)), id: rawComponent.id,
components: rawComponent.components.map(component =>
this.transformComponent(component, resolved, { client, guild }),
),
};
}
if ('component' in rawComponent) {
return {
type: rawComponent.type,
id: rawComponent.id,
component: this.transformComponent(rawComponent.component, resolved, { client, guild }),
};
}
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;
/* eslint-disable max-depth */
if (resolved) {
const { members, users, channels, roles } = resolved;
const valueSet = new Set(rawComponent.values);
if (users) {
data.users = new Collection();
for (const [id, user] of Object.entries(users)) {
if (valueSet.has(id)) {
data.users.set(id, client.users._add(user));
}
}
} }
: {
value: rawComponent.value, if (channels) {
type: rawComponent.type, data.channels = new Collection();
customId: rawComponent.custom_id,
}; for (const [id, apiChannel] of Object.entries(channels)) {
if (valueSet.has(id)) {
data.channels.set(id, client.channels._add(apiChannel, guild) ?? apiChannel);
}
}
}
if (members) {
data.members = new Collection();
for (const [id, member] of Object.entries(members)) {
if (valueSet.has(id)) {
const user = users?.[id];
data.members.set(id, guild?.members._add({ user, ...member }) ?? member);
}
}
}
if (roles) {
data.roles = new Collection();
for (const [id, role] of Object.entries(roles)) {
if (valueSet.has(id)) {
data.roles.set(id, guild?.roles._add(role) ?? role);
}
}
}
}
/* eslint-enable max-depth */
}
return data;
} }
/** /**

View File

@@ -11,9 +11,30 @@ const { ErrorCodes } = require('../errors/index');
* @extends {Base} * @extends {Base}
*/ */
class Poll extends Base { class Poll extends Base {
constructor(client, data, message) { constructor(client, data, message, channel) {
super(client); 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 * The message that started this poll
* @name Poll#message * @name Poll#message
@@ -23,51 +44,27 @@ class Poll extends Base {
Object.defineProperty(this, 'message', { value: message }); 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 * The answers of this poll
* @type {Collection<number, PollAnswer>} * @type {Collection<number, PollAnswer|PartialPollAnswer>}
*/ */
this.answers = data.answers.reduce( this.answers = new Collection();
(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._patch(data); this._patch(data);
} }
_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) { if (data.results) {
/** /**
* Whether this poll's results have been precisely counted * Whether this poll's results have been precisely counted
@@ -82,15 +79,84 @@ class Poll extends Base {
} else { } else {
this.resultsFinalized ??= false; 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 * The date when this poll expires
* @type {Date} * @type {?Date}
* @readonly * @readonly
*/ */
get expiresAt() { 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;
} }
/** /**
@@ -98,11 +164,11 @@ class Poll extends Base {
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
async end() { async end() {
if (Date.now() > this.expiresTimestamp) { if (this.expiresTimestamp !== null && Date.now() > this.expiresTimestamp) {
throw new DiscordjsError(ErrorCodes.PollAlreadyExpired); 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'; 'use strict';
const Base = require('./Base'); const process = require('node:process');
const { Emoji } = require('./Emoji'); 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} * Represents an answer to a {@link Poll}
@@ -14,7 +18,7 @@ class PollAnswer extends Base {
/** /**
* The {@link Poll} this answer is part of * The {@link Poll} this answer is part of
* @name PollAnswer#poll * @name PollAnswer#poll
* @type {Poll} * @type {Poll|PartialPoll}
* @readonly * @readonly
*/ */
Object.defineProperty(this, 'poll', { value: poll }); Object.defineProperty(this, 'poll', { value: poll });
@@ -26,10 +30,10 @@ class PollAnswer extends Base {
this.id = data.answer_id; this.id = data.answer_id;
/** /**
* The text of this answer * The manager of the voters for this answer
* @type {?string} * @type {PollAnswerVoterManager}
*/ */
this.text = data.poll_media.text ?? null; this.voters = new PollAnswerVoterManager(this);
/** /**
* The raw emoji of this answer * The raw emoji of this answer
@@ -37,7 +41,7 @@ class PollAnswer extends Base {
* @type {?APIPartialEmoji} * @type {?APIPartialEmoji}
* @private * @private
*/ */
Object.defineProperty(this, '_emoji', { value: data.poll_media.emoji ?? null }); Object.defineProperty(this, '_emoji', { value: null, writable: true });
this._patch(data); this._patch(data);
} }
@@ -51,7 +55,17 @@ class PollAnswer extends Base {
*/ */
this.voteCount = data.count; this.voteCount = data.count;
} else { } 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); 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. * Options used for fetching voters of a poll answer.
* @typedef {Object} BaseFetchPollAnswerVotersOptions * @typedef {Object} BaseFetchPollAnswerVotersOptions
@@ -75,14 +98,16 @@ class PollAnswer extends Base {
* Fetches the users that voted for this answer. * Fetches the users that voted for this answer.
* @param {BaseFetchPollAnswerVotersOptions} [options={}] The options for fetching voters * @param {BaseFetchPollAnswerVotersOptions} [options={}] The options for fetching voters
* @returns {Promise<Collection<Snowflake, User>>} * @returns {Promise<Collection<Snowflake, User>>}
* @deprecated Use {@link PollAnswerVoterManager#fetch} instead
*/ */
fetchVoters({ after, limit } = {}) { fetchVoters({ after, limit } = {}) {
return this.poll.message.channel.messages.fetchPollAnswerVoters({ if (!deprecationEmittedForFetchVoters) {
messageId: this.poll.message.id, process.emitWarning('PollAnswer#fetchVoters is deprecated. Use PollAnswer#voters#fetch instead.');
answerId: this.id,
after, deprecationEmittedForFetchVoters = true;
limit, }
});
return this.voters.fetch({ after, limit });
} }
} }

View File

@@ -54,11 +54,37 @@ class Role extends Base {
if ('color' in data) { if ('color' in data) {
/** /**
* The base 10 color of the role * The base 10 color of the role
*
* @type {number} * @type {number}
* @deprecated Use {@link Role#colors} instead.
*/ */
this.color = data.color; 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 ('hoist' in data) {
/** /**
* If true, users that are part of this role will appear in a separate category in the users list * 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 * @readonly
*/ */
get hexColor() { 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 * @typedef {Object} RoleData
* @property {string} [name] The name of the role * @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 * @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 {boolean} [hoist] Whether or not the role should be hoisted
* @property {number} [position] The position of the role * @property {number} [position] The position of the role
* @property {PermissionResolvable} [permissions] The permissions 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. * Sets a new color for the role.
*
* @param {ColorResolvable} color The color of the role * @param {ColorResolvable} color The color of the role
* @param {string} [reason] Reason for changing the role's color * @param {string} [reason] Reason for changing the role's color
* @returns {Promise<Role>} * @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 * @example
* // Set the color of a role * // Set the colors of a role
* role.setColor('#FF0000') * role.setColors({ primaryColor: '#FF0000', secondaryColor: '#00FF00', tertiaryColor: '#0000FF' })
* .then(updated => console.log(`Set color of role to ${updated.color}`)) * .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); * .catch(console.error);
*/ */
setColor(color, reason) { async setColors(colors, reason) {
return this.edit({ color, reason }); return this.edit({ colors, reason });
} }
/** /**
@@ -434,7 +484,9 @@ class Role extends Base {
role && role &&
this.id === role.id && this.id === role.id &&
this.name === role.name && 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.hoist === role.hoist &&
this.position === role.position && this.position === role.position &&
this.permissions.bitfield === role.permissions.bitfield && this.permissions.bitfield === role.permissions.bitfield &&

View File

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

View File

@@ -5,6 +5,7 @@ const { calculateUserDefaultAvatarIndex } = require('@discordjs/rest');
const { DiscordSnowflake } = require('@sapphire/snowflake'); const { DiscordSnowflake } = require('@sapphire/snowflake');
const Base = require('./Base'); const Base = require('./Base');
const TextBasedChannel = require('./interfaces/TextBasedChannel'); const TextBasedChannel = require('./interfaces/TextBasedChannel');
const { _transformCollectibles } = require('../util/Transformers.js');
const UserFlagsBitField = require('../util/UserFlagsBitField'); const UserFlagsBitField = require('../util/UserFlagsBitField');
const { emitDeprecationWarningForUserFetchFlags } = require('../util/Util'); const { emitDeprecationWarningForUserFetchFlags } = require('../util/Util');
@@ -140,18 +141,74 @@ class User extends Base {
* @property {string} asset The avatar decoration hash * @property {string} asset The avatar decoration hash
* @property {Snowflake} skuId The id of the avatar decoration's SKU * @property {Snowflake} skuId The id of the avatar decoration's SKU
*/ */
if ('avatar_decoration_data' in data) {
if (data.avatar_decoration_data) { if (data.avatar_decoration_data) {
/** /**
* The user avatar decoration's data * The user avatar decoration's data
* @type {?AvatarDecorationData} *
*/ * @type {?AvatarDecorationData}
this.avatarDecorationData = { */
asset: data.avatar_decoration_data.asset, this.avatarDecorationData = {
skuId: data.avatar_decoration_data.sku_id, asset: data.avatar_decoration_data.asset,
}; skuId: data.avatar_decoration_data.sku_id,
};
} else {
this.avatarDecorationData = null;
}
} else { } 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 * @readonly
*/ */
get defaultAvatarURL() { 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); 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); 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 * The tag of this user
* <info>This user's username, or their legacy tag (e.g. `hydrabolt#0001`) * <info>This user's username, or their legacy tag (e.g. `hydrabolt#0001`)
@@ -253,7 +326,7 @@ class User extends Base {
*/ */
get tag() { get tag() {
return typeof this.username === 'string' return typeof this.username === 'string'
? this.discriminator === '0' ? this.discriminator === '0' || this.discriminator === '0000'
? this.username ? this.username
: `${this.username}#${this.discriminator}` : `${this.username}#${this.discriminator}`
: null; : null;
@@ -314,7 +387,15 @@ class User extends Base {
this.accentColor === user.accentColor && this.accentColor === user.accentColor &&
this.avatarDecoration === user.avatarDecoration && this.avatarDecoration === user.avatarDecoration &&
this.avatarDecorationData?.asset === user.avatarDecorationData?.asset && 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 ('avatar_decoration_data' in user
? this.avatarDecorationData?.asset === user.avatar_decoration_data?.asset && ? this.avatarDecorationData?.asset === user.avatar_decoration_data?.asset &&
this.avatarDecorationData?.skuId === user.avatar_decoration_data?.sku_id 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) : true)
); );
} }
@@ -388,6 +481,7 @@ class User extends Base {
json.avatarURL = this.avatarURL(); json.avatarURL = this.avatarURL();
json.displayAvatarURL = this.displayAvatarURL(); json.displayAvatarURL = this.displayAvatarURL();
json.bannerURL = this.banner ? this.bannerURL() : this.banner; json.bannerURL = this.banner ? this.bannerURL() : this.banner;
json.guildTagBadgeURL = this.guildTagBadgeURL();
return json; return json;
} }
} }

View File

@@ -267,7 +267,7 @@ class Collector extends EventEmitter {
/** /**
* Allows collectors to be consumed with for-await-of loops * 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]() { async *[Symbol.asyncIterator]() {
const queue = []; const queue = [];

View File

@@ -15,13 +15,6 @@ const MessagePayload = require('../MessagePayload');
let deprecationEmittedForEphemeralOption = false; let deprecationEmittedForEphemeralOption = false;
let deprecationEmittedForFetchReplyOption = 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 for classes that support shared interaction response types.
* @interface * @interface

View File

@@ -245,6 +245,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APISelectMenuOption} * @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 * @external APISticker
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/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} * @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 * @external InviteType
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/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} * @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 * @external MessageActivityType
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/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} * @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 * @external OAuth2Scopes
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/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} * @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 * @external UserFlags
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/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 * @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 * @typedef {BaseComponentData} ButtonComponentData
* @property {ButtonStyle} style The style of the button * @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 * @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 * @typedef {object} SelectMenuComponentOptionData
* @property {string} label The label of the option * @property {string} label The label of the option
@@ -199,6 +254,7 @@ const ChannelSelectMenuComponent = require('../structures/ChannelSelectMenuCompo
const Component = require('../structures/Component'); const Component = require('../structures/Component');
const ContainerComponent = require('../structures/ContainerComponent'); const ContainerComponent = require('../structures/ContainerComponent');
const FileComponent = require('../structures/FileComponent'); const FileComponent = require('../structures/FileComponent');
const LabelComponent = require('../structures/LabelComponent');
const MediaGalleryComponent = require('../structures/MediaGalleryComponent'); const MediaGalleryComponent = require('../structures/MediaGalleryComponent');
const MentionableSelectMenuBuilder = require('../structures/MentionableSelectMenuBuilder'); const MentionableSelectMenuBuilder = require('../structures/MentionableSelectMenuBuilder');
const MentionableSelectMenuComponent = require('../structures/MentionableSelectMenuComponent'); const MentionableSelectMenuComponent = require('../structures/MentionableSelectMenuComponent');
@@ -231,6 +287,7 @@ const ComponentTypeToComponent = {
[ComponentType.Section]: SectionComponent, [ComponentType.Section]: SectionComponent,
[ComponentType.Separator]: SeparatorComponent, [ComponentType.Separator]: SeparatorComponent,
[ComponentType.Thumbnail]: ThumbnailComponent, [ComponentType.Thumbnail]: ThumbnailComponent,
[ComponentType.Label]: LabelComponent,
}; };
const ComponentTypeToBuilder = { const ComponentTypeToBuilder = {

View File

@@ -254,6 +254,21 @@ exports.StickerFormatExtensionMap = {
[StickerFormatType.GIF]: ImageFormat.GIF, [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. * @typedef {Object} Constants Constants that can be used in an enum or object-like way.
* @property {number} MaxBulkDeletableMessageAge Max bulk deletable message age * @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 {VoiceBasedChannelTypes} VoiceBasedChannelTypes The types of channels that are voice-based
* @property {SelectMenuTypes} SelectMenuTypes The types of components that are select menus. * @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 {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} ChannelDelete channelDelete
* @property {string} ChannelPinsUpdate channelPinsUpdate * @property {string} ChannelPinsUpdate channelPinsUpdate
* @property {string} ChannelUpdate channelUpdate * @property {string} ChannelUpdate channelUpdate
* @property {string} ClientReady ready * @property {string} ClientReady clientReady
* @property {string} Debug debug * @property {string} Debug debug
* @property {string} EntitlementCreate entitlementCreate * @property {string} EntitlementCreate entitlementCreate
* @property {string} EntitlementUpdate entitlementUpdate * @property {string} EntitlementUpdate entitlementUpdate
@@ -89,7 +89,7 @@
* @property {string} VoiceServerUpdate voiceServerUpdate * @property {string} VoiceServerUpdate voiceServerUpdate
* @property {string} VoiceStateUpdate voiceStateUpdate * @property {string} VoiceStateUpdate voiceStateUpdate
* @property {string} Warn warn * @property {string} Warn warn
* @property {string} WebhooksUpdate webhookUpdate * @property {string} WebhooksUpdate webhooksUpdate
*/ */
// JSDoc for IntelliSense purposes // JSDoc for IntelliSense purposes
@@ -108,7 +108,7 @@ module.exports = {
ChannelDelete: 'channelDelete', ChannelDelete: 'channelDelete',
ChannelPinsUpdate: 'channelPinsUpdate', ChannelPinsUpdate: 'channelPinsUpdate',
ChannelUpdate: 'channelUpdate', ChannelUpdate: 'channelUpdate',
ClientReady: 'ready', ClientReady: 'clientReady',
Debug: 'debug', Debug: 'debug',
EntitlementCreate: 'entitlementCreate', EntitlementCreate: 'entitlementCreate',
EntitlementUpdate: 'entitlementUpdate', EntitlementUpdate: 'entitlementUpdate',
@@ -186,5 +186,5 @@ module.exports = {
VoiceServerUpdate: 'voiceServerUpdate', VoiceServerUpdate: 'voiceServerUpdate',
VoiceStateUpdate: 'voiceStateUpdate', VoiceStateUpdate: 'voiceStateUpdate',
Warn: 'warn', 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} GuildScheduledEvent The partial to receive uncached guild scheduled events.
* @property {number} ThreadMember The partial to receive uncached thread members. * @property {number} ThreadMember The partial to receive uncached thread members.
* @property {number} SoundboardSound The partial to receive uncached soundboard sounds. * @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 // JSDoc for IntelliSense purposes
@@ -43,4 +45,6 @@ module.exports = createEnum([
'GuildScheduledEvent', 'GuildScheduledEvent',
'ThreadMember', 'ThreadMember',
'SoundboardSound', 'SoundboardSound',
'Poll',
'PollAnswer',
]); ]);

View File

@@ -1,5 +1,6 @@
'use strict'; 'use strict';
const { ThreadMemberFlags } = require('discord-api-types/v10');
const BitField = require('./BitField'); const BitField = require('./BitField');
/** /**
@@ -9,10 +10,10 @@ const BitField = require('./BitField');
class ThreadMemberFlagsBitField extends BitField { class ThreadMemberFlagsBitField extends BitField {
/** /**
* Numeric thread member flags. There are currently no bitflags relevant to bots for this. * Numeric thread member flags. There are currently no bitflags relevant to bots for this.
* @type {Object<string, number>} * @type {ThreadMemberFlags}
* @memberof ThreadMemberFlagsBitField * @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 = { module.exports = {
toSnakeCase, toSnakeCase,
_transformAPIAutoModerationAction, _transformAPIAutoModerationAction,
_transformAPIMessageInteractionMetadata, _transformAPIMessageInteractionMetadata,
_transformGuildScheduledEventRecurrenceRule, _transformGuildScheduledEventRecurrenceRule,
_transformAPIIncidentsData, _transformAPIIncidentsData,
_transformCollectibles,
}; };

View File

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

View File

@@ -231,7 +231,12 @@ import {
FileComponentData, FileComponentData,
ContainerComponentData, ContainerComponentData,
InteractionResponse, InteractionResponse,
} from '.'; FetchPinnedMessagesResponse,
PartialPoll,
PartialPollAnswer,
PollAnswer,
PollAnswerVoterManager,
} from './index.js';
import { import {
expectAssignable, expectAssignable,
expectDeprecated, expectDeprecated,
@@ -463,7 +468,7 @@ client.on('messageCreate', async message => {
const component = await message.awaitMessageComponent({ componentType: ComponentType.Button }); const component = await message.awaitMessageComponent({ componentType: ComponentType.Button });
expectType<ButtonInteraction<'cached'>>(component); expectType<ButtonInteraction<'cached'>>(component);
expectType<Message<true>>(await component.reply({ fetchReply: true })); 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 }); const buttonCollector = message.createMessageComponentCollector({ componentType: ComponentType.Button });
expectType<InteractionCollector<ButtonInteraction<'cached'>>>(buttonCollector); expectType<InteractionCollector<ButtonInteraction<'cached'>>>(buttonCollector);
@@ -714,6 +719,48 @@ client.on('messageDeleteBulk', (messages, { client }) => {
expectType<Client<true>>(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 }) => { client.on('messageReactionAdd', async (reaction, { client }) => {
expectType<Client<true>>(reaction.client); expectType<Client<true>>(reaction.client);
expectType<Client<true>>(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.edit('1234567890', 'text'));
expectType<Promise<Message<true>>>(messages.fetch('1234567890')); expectType<Promise<Message<true>>>(messages.fetch('1234567890'));
expectType<Promise<Collection<Snowflake, Message<true>>>>(messages.fetchPinned()); expectType<Promise<Collection<Snowflake, Message<true>>>>(messages.fetchPinned());
expectType<Promise<FetchPinnedMessagesResponse<true>>>(messages.fetchPins());
expectType<Guild>(message.guild); expectType<Guild>(message.guild);
expectType<Snowflake>(message.guildId); expectType<Snowflake>(message.guildId);
expectType<GuildTextBasedChannel>(message.channel.messages.channel); 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.edit('1234567890', 'text'));
expectType<Promise<Message>>(messages.fetch('1234567890')); expectType<Promise<Message>>(messages.fetch('1234567890'));
expectType<Promise<Collection<Snowflake, Message>>>(messages.fetchPinned()); expectType<Promise<Collection<Snowflake, Message>>>(messages.fetchPinned());
expectType<Promise<FetchPinnedMessagesResponse>>(messages.fetchPins());
expectType<Guild | null>(message.guild); expectType<Guild | null>(message.guild);
expectType<Snowflake | null>(message.guildId); expectType<Snowflake | null>(message.guildId);
expectType<DMChannel | PartialGroupDMChannel | GuildTextBasedChannel>(message.channel.messages.channel); 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 }); 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; declare const roleManager: RoleManager;
expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch()); expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch());
expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch(undefined, {})); expectType<Promise<Collection<Snowflake, Role>>>(roleManager.fetch(undefined, {}));
@@ -1882,18 +1937,18 @@ client.on('interactionCreate', async interaction => {
expectType<Guild>(interaction.guild); expectType<Guild>(interaction.guild);
expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true })); expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true })); expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' })); expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<true>>>(interaction.fetchReply()); expectType<Promise<Message<true>>>(interaction.fetchReply());
expectType<Promise<Message<true>>>(interaction.update({ content: 'a', fetchReply: true })); expectType<Promise<Message<true>>>(interaction.update({ content: 'a', fetchReply: true }));
expectType<Promise<Message<true>>>(interaction.deferUpdate({ fetchReply: true })); expectType<Promise<Message<true>>>(interaction.deferUpdate({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.update({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.update({ content: 'a', withResponse: 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<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<undefined>>(interaction.launchActivity({ withResponse: false }));
expectType<Promise<InteractionCallbackResponse | undefined>>( expectType<Promise<InteractionCallbackResponse<true> | undefined>>(
interaction.launchActivity({ withResponse: booleanValue }), interaction.launchActivity({ withResponse: booleanValue }),
); );
} else if (interaction.inRawGuild()) { } else if (interaction.inRawGuild()) {
@@ -1903,18 +1958,18 @@ client.on('interactionCreate', async interaction => {
expectType<null>(interaction.guild); expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true })); expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true })); expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' })); expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<false>>>(interaction.fetchReply()); expectType<Promise<Message<false>>>(interaction.fetchReply());
expectType<Promise<Message<false>>>(interaction.update({ content: 'a', fetchReply: true })); expectType<Promise<Message<false>>>(interaction.update({ content: 'a', fetchReply: true }));
expectType<Promise<Message<false>>>(interaction.deferUpdate({ fetchReply: true })); expectType<Promise<Message<false>>>(interaction.deferUpdate({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.update({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.update({ content: 'a', withResponse: 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<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<undefined>>(interaction.launchActivity({ withResponse: false }));
expectType<Promise<InteractionCallbackResponse | undefined>>( expectType<Promise<InteractionCallbackResponse<false> | undefined>>(
interaction.launchActivity({ withResponse: booleanValue }), interaction.launchActivity({ withResponse: booleanValue }),
); );
} else if (interaction.inGuild()) { } else if (interaction.inGuild()) {
@@ -1972,14 +2027,14 @@ client.on('interactionCreate', async interaction => {
expectAssignable<CommandInteraction<'cached'>>(interaction); expectAssignable<CommandInteraction<'cached'>>(interaction);
expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true })); expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true })); expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' })); expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<true>>>(interaction.fetchReply()); expectType<Promise<Message<true>>>(interaction.fetchReply());
expectType<Promise<Message<true>>>(interaction.followUp({ content: 'a' })); 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<undefined>>(interaction.launchActivity({ withResponse: false }));
expectType<Promise<InteractionCallbackResponse | undefined>>( expectType<Promise<InteractionCallbackResponse<true> | undefined>>(
interaction.launchActivity({ withResponse: booleanValue }), interaction.launchActivity({ withResponse: booleanValue }),
); );
} else if (interaction.inRawGuild()) { } else if (interaction.inRawGuild()) {
@@ -1987,14 +2042,14 @@ client.on('interactionCreate', async interaction => {
expectType<null>(interaction.guild); expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true })); expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true })); expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' })); expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<false>>>(interaction.fetchReply()); expectType<Promise<Message<false>>>(interaction.fetchReply());
expectType<Promise<Message<false>>>(interaction.followUp({ content: 'a' })); 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<undefined>>(interaction.launchActivity({ withResponse: false }));
expectType<Promise<InteractionCallbackResponse | undefined>>( expectType<Promise<InteractionCallbackResponse<false> | undefined>>(
interaction.launchActivity({ withResponse: booleanValue }), interaction.launchActivity({ withResponse: booleanValue }),
); );
} else if (interaction.inGuild()) { } else if (interaction.inGuild()) {
@@ -2063,14 +2118,14 @@ client.on('interactionCreate', async interaction => {
expectType<Message<true>>(interaction.message); expectType<Message<true>>(interaction.message);
expectType<Guild>(interaction.guild); expectType<Guild>(interaction.guild);
expectType<Promise<Message<true>>>(interaction.reply({ fetchReply: true })); 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()) { } else if (interaction.inRawGuild()) {
expectAssignable<ButtonInteraction>(interaction); expectAssignable<ButtonInteraction>(interaction);
expectType<APIButtonComponent>(interaction.component); expectType<APIButtonComponent>(interaction.component);
expectType<Message<false>>(interaction.message); expectType<Message<false>>(interaction.message);
expectType<null>(interaction.guild); expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ fetchReply: true })); 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()) { } else if (interaction.inGuild()) {
expectAssignable<ButtonInteraction>(interaction); expectAssignable<ButtonInteraction>(interaction);
expectType<ButtonComponent | APIButtonComponent>(interaction.component); expectType<ButtonComponent | APIButtonComponent>(interaction.component);
@@ -2094,14 +2149,14 @@ client.on('interactionCreate', async interaction => {
expectType<Message<true>>(interaction.message); expectType<Message<true>>(interaction.message);
expectType<Guild>(interaction.guild); expectType<Guild>(interaction.guild);
expectType<Promise<Message<true>>>(interaction.reply({ fetchReply: true })); 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()) { } else if (interaction.inRawGuild()) {
expectAssignable<StringSelectMenuInteraction>(interaction); expectAssignable<StringSelectMenuInteraction>(interaction);
expectType<APIStringSelectComponent>(interaction.component); expectType<APIStringSelectComponent>(interaction.component);
expectType<Message<false>>(interaction.message); expectType<Message<false>>(interaction.message);
expectType<null>(interaction.guild); expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ fetchReply: true })); 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()) { } else if (interaction.inGuild()) {
expectAssignable<StringSelectMenuInteraction>(interaction); expectAssignable<StringSelectMenuInteraction>(interaction);
expectType<SelectMenuComponent | APIStringSelectComponent>(interaction.component); expectType<SelectMenuComponent | APIStringSelectComponent>(interaction.component);
@@ -2121,7 +2176,7 @@ client.on('interactionCreate', async interaction => {
expectNotAssignable<Interaction<'cached'>>(interaction); expectNotAssignable<Interaction<'cached'>>(interaction);
expectAssignable<ChatInputCommandInteraction>(interaction); expectAssignable<ChatInputCommandInteraction>(interaction);
expectType<Promise<Message<false>>>(interaction.reply({ fetchReply: true })); 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<APIInteractionDataResolvedGuildMember | null>(interaction.options.getMember('test'));
expectType<APIInteractionDataResolvedChannel>(interaction.options.getChannel('test', true)); expectType<APIInteractionDataResolvedChannel>(interaction.options.getChannel('test', true));
@@ -2136,7 +2191,7 @@ client.on('interactionCreate', async interaction => {
expectType<GuildMember | null>(interaction.options.getMember('test')); expectType<GuildMember | null>(interaction.options.getMember('test'));
expectAssignable<ChatInputCommandInteraction>(interaction); expectAssignable<ChatInputCommandInteraction>(interaction);
expectType<Promise<Message<true>>>(interaction.reply({ fetchReply: true })); 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<GuildBasedChannel>(interaction.options.getChannel('test', true));
expectType<Role>(interaction.options.getRole('test', true)); expectType<Role>(interaction.options.getRole('test', true));
@@ -2208,28 +2263,28 @@ client.on('interactionCreate', async interaction => {
expectAssignable<PrimaryEntryPointCommandInteraction>(interaction); expectAssignable<PrimaryEntryPointCommandInteraction>(interaction);
expectAssignable<Guild>(interaction.guild); expectAssignable<Guild>(interaction.guild);
expectAssignable<CommandInteraction<'cached'>>(interaction); expectAssignable<CommandInteraction<'cached'>>(interaction);
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionResponse<true>>>(interaction.deferReply()); expectType<Promise<InteractionResponse<true>>>(interaction.deferReply());
expectType<Promise<InteractionResponse<true>>>(interaction.reply({ content: 'a', withResponse: false })); expectType<Promise<InteractionResponse<true>>>(interaction.reply({ content: 'a', withResponse: false }));
expectType<Promise<InteractionResponse<true>>>(interaction.deferReply({ withResponse: false })); expectType<Promise<InteractionResponse<true>>>(interaction.deferReply({ withResponse: false }));
expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' })); expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<true>>>(interaction.fetchReply()); expectType<Promise<Message<true>>>(interaction.fetchReply());
expectType<Promise<Message<true>>>(interaction.followUp({ content: 'a' })); 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<undefined>>(interaction.launchActivity({ withResponse: false }));
} else if (interaction.inRawGuild()) { } else if (interaction.inRawGuild()) {
expectAssignable<PrimaryEntryPointCommandInteraction>(interaction); expectAssignable<PrimaryEntryPointCommandInteraction>(interaction);
expectType<null>(interaction.guild); expectType<null>(interaction.guild);
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<InteractionResponse<false>>>(interaction.deferReply()); expectType<Promise<InteractionResponse<false>>>(interaction.deferReply());
expectType<Promise<InteractionResponse<false>>>(interaction.reply({ content: 'a', withResponse: false })); expectType<Promise<InteractionResponse<false>>>(interaction.reply({ content: 'a', withResponse: false }));
expectType<Promise<InteractionResponse<false>>>(interaction.deferReply({ withResponse: false })); expectType<Promise<InteractionResponse<false>>>(interaction.deferReply({ withResponse: false }));
expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' })); expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<false>>>(interaction.fetchReply()); expectType<Promise<Message<false>>>(interaction.fetchReply());
expectType<Promise<Message<false>>>(interaction.followUp({ content: 'a' })); 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<undefined>>(interaction.launchActivity({ withResponse: false }));
} else if (interaction.inGuild()) { } else if (interaction.inGuild()) {
expectAssignable<PrimaryEntryPointCommandInteraction>(interaction); expectAssignable<PrimaryEntryPointCommandInteraction>(interaction);
@@ -2268,27 +2323,27 @@ client.on('interactionCreate', async interaction => {
expectType<Guild>(interaction.guild); expectType<Guild>(interaction.guild);
expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true })); expectType<Promise<Message<true>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true })); expectType<Promise<Message<true>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true })); expectType<Promise<InteractionCallbackResponse<true>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' })); expectType<Promise<Message<true>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<true>>>(interaction.fetchReply()); expectType<Promise<Message<true>>>(interaction.fetchReply());
expectType<Promise<Message<true>>>(interaction.deferUpdate({ fetchReply: true })); 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<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()) { } else if (interaction.inRawGuild()) {
expectAssignable<ModalSubmitInteraction>(interaction); expectAssignable<ModalSubmitInteraction>(interaction);
expectType<null>(interaction.guild); expectType<null>(interaction.guild);
expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true })); expectType<Promise<Message<false>>>(interaction.reply({ content: 'a', fetchReply: true }));
expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true })); expectType<Promise<Message<false>>>(interaction.deferReply({ fetchReply: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.reply({ content: 'a', withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.reply({ content: 'a', withResponse: true }));
expectType<Promise<InteractionCallbackResponse>>(interaction.deferReply({ withResponse: true })); expectType<Promise<InteractionCallbackResponse<false>>>(interaction.deferReply({ withResponse: true }));
expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' })); expectType<Promise<Message<false>>>(interaction.editReply({ content: 'a' }));
expectType<Promise<Message<false>>>(interaction.fetchReply()); expectType<Promise<Message<false>>>(interaction.fetchReply());
expectType<Promise<Message<false>>>(interaction.deferUpdate({ fetchReply: true })); 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<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()) { } else if (interaction.inGuild()) {
expectAssignable<ModalSubmitInteraction>(interaction); expectAssignable<ModalSubmitInteraction>(interaction);
expectType<Guild | null>(interaction.guild); 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; declare const stringSelectMenuData: APIStringSelectComponent;
StringSelectMenuBuilder.from(stringSelectMenuData); StringSelectMenuBuilder.from(stringSelectMenuData);
@@ -2594,9 +2702,9 @@ declare const webhookClient: WebhookClient;
declare const interactionWebhook: InteractionWebhook; declare const interactionWebhook: InteractionWebhook;
declare const snowflake: Snowflake; declare const snowflake: Snowflake;
expectType<Promise<Message>>(webhook.send('content')); expectType<Promise<Message<true>>>(webhook.send('content'));
expectType<Promise<Message>>(webhook.editMessage(snowflake, 'content')); expectType<Promise<Message<true>>>(webhook.editMessage(snowflake, 'content'));
expectType<Promise<Message>>(webhook.fetchMessage(snowflake)); expectType<Promise<Message<true>>>(webhook.fetchMessage(snowflake));
expectType<Promise<Webhook>>(webhook.edit({ name: 'name' })); expectType<Promise<Webhook>>(webhook.edit({ name: 'name' }));
expectType<Promise<APIMessage>>(webhookClient.send('content')); 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 poll: Poll;
declare const message: Message; declare const message: Message;
declare const pollData: PollData; declare const pollData: PollData;
{ {
expectType<Message>(await poll.end()); expectType<Message>(await poll.end());
expectType<false>(poll.partial);
expectNotType<Collection<number, PartialPollAnswer>>(poll.answers);
const answer = poll.answers.first()!; 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.endPoll(snowflake);
await messageManager.fetchPollAnswerVoters({ await messageManager.fetchPollAnswerVoters({

View File

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

View File

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

View File

@@ -2,6 +2,17 @@
All notable changes to this project will be documented in this file. 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) # [@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 ## Features

View File

@@ -54,7 +54,7 @@ test('discoverySplash default', () => {
expect(cdn.discoverySplash(id, hash)).toEqual(`${baseCDN}/discovery-splashes/${id}/${hash}.webp`); 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`); 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`); 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', () => { test('guildMemberAvatar default', () => {
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${baseCDN}/guilds/${id}/users/${id}/avatars/${hash}.webp`); 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}`); 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', () => { test('makeURL throws on invalid size', () => {
// @ts-expect-error: Invalid size
expect(() => cdn.avatar(id, animatedHash, { size: 5 })).toThrow(RangeError); expect(() => cdn.avatar(id, animatedHash, { size: 5 })).toThrow(RangeError);
}); });

View File

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

View File

@@ -14,27 +14,44 @@ import { deprecationWarning } from './utils/utils.js';
let deprecationEmittedForEmoji = false; let deprecationEmittedForEmoji = false;
/** /**
* The options used for image URLs * The options used for image URLs.
*/ */
export interface BaseImageURLOptions { export interface BaseImageURLOptions {
/** /**
* The extension to use for the image URL * The extension to use for the image URL.
* *
* @defaultValue `'webp'` * @defaultValue `'webp'`
*/ */
extension?: ImageExtension; extension?: ImageExtension;
/** /**
* The size specified in the image URL * The size specified in the image URL.
*/ */
size?: ImageSize; 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 { 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; forceStatic?: boolean;
} }
@@ -47,6 +64,10 @@ export interface MakeURLOptions {
* The allowed extensions that can be used * The allowed extensions that can be used
*/ */
allowedExtensions?: readonly string[]; allowedExtensions?: readonly string[];
/**
* Whether to use the `animated` query parameter
*/
animated?: boolean;
/** /**
* The base URL. * The base URL.
* *
@@ -192,7 +213,7 @@ export class CDN {
* @param emojiId - The emoji id * @param emojiId - The emoji id
* @param options - Optional options for the emoji * @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. * Generates an emoji's URL for an emoji.
@@ -204,7 +225,7 @@ export class CDN {
// eslint-disable-next-line @typescript-eslint/unified-signatures // eslint-disable-next-line @typescript-eslint/unified-signatures
public emoji(emojiId: string, extension?: ImageExtension): string; 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; let resolvedOptions;
if (typeof options === 'string') { if (typeof options === 'string') {
@@ -353,6 +374,17 @@ export class CDN {
return `${this.cdn}${CDNRoutes.soundboardSound(soundId)}`; 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`. * 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, base = this.cdn,
extension = 'webp', extension = 'webp',
size, size,
animated,
}: Readonly<MakeURLOptions> = {}, }: Readonly<MakeURLOptions> = {},
): string { ): string {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@@ -396,6 +429,10 @@ export class CDN {
const url = new URL(`${base}${route}.${extension}`); const url = new URL(`${base}${route}.${extension}`);
if (animated !== undefined) {
url.searchParams.set('animated', String(animated));
}
if (size) { if (size) {
url.searchParams.set('size', String(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()) { for (const [index, file] of request.files.entries()) {
const fileKey = file.key ?? `files[${index}]`; 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. // 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. // The Blob constructor accepts TypedArray/ArrayBuffer, strings, and Blobs.
if (isBufferLike(file.data)) { if (isBufferLike(file.data)) {
// Try to infer the content type from the buffer if one isn't passed // 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", "funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": { "dependencies": {
"@types/ws": "^8.5.12", "@types/ws": "^8.5.12",
"discord-api-types": "^0.37.119", "discord-api-types": "^0.38.24",
"prism-media": "^1.3.5", "prism-media": "^1.3.5",
"tslib": "^2.6.3", "tslib": "^2.6.3",
"ws": "^8.18.0" "ws": "^8.18.0"

View File

@@ -2,6 +2,37 @@
All notable changes to this project will be documented in this file. 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) # [@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 ## Bug Fixes

View File

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

97
pnpm-lock.yaml generated
View File

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