Compare commits

..

246 Commits

Author SHA1 Message Date
Jiralite
c5b71a756b chore(ws): release @discordjs/ws@2.0.4 2025-11-10 13:16:13 +00:00
Denis-Adrian Cristea
6c781ede30 fix(WebSocketShard): bad error re-throw (#11151)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-11-10 13:04:07 +00:00
Denis-Adrian Cristea
be38128ea1 fix(SimpleIdentifyThrottler): don't sleep negative amounts (#10669)
* fix(SimpleIdentifyThrottler): don't sleep negative amounts

* fix: test
2025-11-10 12:59:18 +00:00
Denis-Adrian Cristea
737a97d068 refactor(IContextFetchingStrategy): explicitly name forwarded properties (#10652) 2025-11-10 12:58:28 +00:00
Danial Raza
b26af3cf38 feat: add linked roles formatters (#10461)
* feat: add linked roles formatters

* docs: requested changes

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

* docs: remove locale

---------

Co-authored-by: Almeida <github@almeidx.dev>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-11-09 11:02:08 +00:00
cobalt
169b05f319 refactor(formatters): Change :_: emoji name placeholder (#10567)
* Change `:_:` emoji name placeholder

* Update tests

* Format
2025-11-09 11:01:04 +00:00
Almeida
bf0430f998 feat: add email and phoneNumber formatters (#11050)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-11-09 10:56:24 +00:00
Josef
2da2fa01b2 feat: optimize role manager cache getter (#11239)
Co-authored-by: Almeida <github@almeidx.dev>
2025-11-09 10:52:23 +00:00
Kendell R
1c5701651a fix: improve handling of italics in the presence of links (#11064)
* fix(formatters): don't escape * in links

* add test for * -> * in url

* `\S+` -> `\S*` (tested locally)

* @Qjuh: handle italics both inside+outside <url>s

* more accurate <link> matcher

---------

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
2025-11-09 10:51:50 +00:00
Jiralite
b6a8264d6b chore: Merge builders/1.x into v14 (#11260)
* chore: merge builders (and formatters)

* chore: match cliff.toml

* chore: update README.mds

* build: discord-api-types 0.38.32
2025-11-08 23:31:07 +00:00
Naiyar
a7196dc969 fix: backport only passing relevant options to API when fetching (#11230)
* fix: only pass relevant options to API when fetching

* chore: update changes
2025-10-31 21:35:51 +00:00
Jiralite
a03661844f chore(discord.js): release discord.js@14.24.2 2025-10-30 21:20:01 +00:00
Jiralite
fb2b7281e0 fix(GuildMember): joinedAt possibly being NaN
Co-Authored-By: TÆMBØØ <69138346+TAEMBO@users.noreply.github.com>
2025-10-30 21:01:17 +00:00
Jiralite
c303bf3329 chore(discord.js): release discord.js@14.24.1 2025-10-28 15:06:27 +00:00
Danial Raza
c2c8cce1d7 fix(Message): check if in voice based channel for pinnable (#11215)
* fix(Message): check if in voice based channel for `pinnable`

* chore: changes from main

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-10-28 14:59:42 +00:00
Jiralite
abb84ce88f docs(GuildMemberFlagsBitField): remove duplicate word 2025-10-26 10:47:53 +00:00
Jiralite
d317ca1053 types(FileUploadModalData): Correct fields (#11209)
* types(FileUploadModalData): update fields

* docs(FileUploadModalData): `Snowflake`

* types(FileUploadModalData): `Snowflake`

* docs: add more
2025-10-25 23:57:39 +01:00
Jiralite
072fbb228a types(LabelModalData): Singular ModalData (#11207)
types(LabelModalData): no array
2025-10-25 20:13:26 +01:00
Jiralite
548c25488a types(FileUploadComponentData): boolean 2025-10-25 17:17:32 +01:00
Jiralite
16a44f83e5 chore(discord.js): release discord.js@14.24.0 2025-10-24 17:12:01 +01:00
Jiralite
0dda270ea5 build: bump @discordjs/builders 2025-10-24 16:22:20 +01:00
Danial Raza
ee988e3e75 fix(Message): update pinnable to check for migrated guilds (#11189)
* fix(Message): update `pinnable` to check for migrated guilds

* refactor: requested changes

* refactor: no checkAdmin and clean up the mess
2025-10-24 16:05:12 +01:00
Naiyar
104ad754f3 feat: handle file upload component for v14 (#11179)
* feat: handle file upload component

* chore: fix import

* chore: typings

* fix: `Snowflake`

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-10-24 13:19:37 +01:00
Jiralite
0ff239a602 build: bump discord-api-types to 0.38.31 2025-10-23 23:04:14 +01:00
Jiralite
89fd19e08a build: upgrade @discordjs/builders to 1.12.2 2025-10-15 23:16:36 +01:00
Jiralite
6a6c7d0333 build: upgrade discord-api-types to 0.38.30 2025-10-15 23:16:03 +01:00
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
Vlad Frangu
a1c83c17d6 chore(rest): release @discordjs/rest@2.5.1 2025-06-16 14:55:15 +03:00
Qjuh
c0eae344c2 feat: backport entrypoint command (#10908) 2025-05-27 21:50:26 +02:00
Qjuh
f2f757ce52 fix: use resolvePartialEmoji on MessagePayload#options#components (#10910)
fix: use resolvePartialEmoji on MessagePayload#components again
2025-05-25 13:35:28 +02:00
Jiralite
65cfa3ffd3 build: Update Undici to 6.21.3 (#10906)
build: update undici
2025-05-20 11:52:49 +01:00
Jiralite
ee2eb7349f fix(ChannelManager): Remove threads from cache upon deletion (#10883)
* fix(ChannelManager): remove threads from cache upon deletion

* refactor: loop over thread ids

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-05-09 14:09:35 +01:00
Jiralite
2d19163d76 perf(Components): Hash table (#10893)
perf(Components): hash table
2025-05-09 08:43:04 +01:00
Jiralite
9bca4af5fd fix(PartialGroupDMChannel): Prevent undefined values (#10889)
fix(PartialGroupDMChannel): prevent `undefined` values

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-05-07 19:09:26 +01:00
Almeida
fe5e344adc build: exclude type tests from pack (#10886)
* build: exclude type tests from pack

* fix: requested changes

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-05-07 17:36:47 +01:00
Jiralite
c8f6066d6a refactor(Client): Remove with_expiration query parameter (#10800)
refactor(Client): remove `with_expiration`

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-05-06 15:25:32 +01:00
Almeida
7e21a9474e feat(BaseInteraction): add attachmentSizeLimit (#10830)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-05-05 03:54:12 +01:00
Jiralite
d0a535ea6a fix(guild): fix incorrectly-detected deprecated overload 2025-05-04 14:19:24 +01:00
Vlad Frangu
8124fc68be chore(discord.js): release discord.js@14.19.3 2025-05-03 01:08:35 +03:00
Vlad Frangu
dbd5354056 chore: bump builders 2025-05-03 01:07:23 +03:00
Qjuh
2ebb5cbd53 fix: regression in allowedMentions when replying (#10866)
* fix: regression in allowedMentions

* fix: lint

* fix: jsdoc

* tests: added a manual regression test

* fix: lint

* fix: tests

* fix: tests

* fix: typo

* fix: typings test

* chore: bumo zlib-sync to not crash on mac
2025-05-02 12:48:57 +02:00
Vlad Frangu
096cd92b87 chore(discord.js): release discord.js@14.19.2 2025-04-28 23:44:30 +03:00
Qjuh
37ef57b880 fix(WebSocketManager): always emit shardDisconnect on unresumable close (#10826) 2025-04-28 23:43:50 +03:00
Danial Raza
e3c247e423 types(GuildSoundboardSoundEditOptions): add missing reason (#10863) 2025-04-28 17:20:59 +01:00
Danial Raza
5f3fc170fb fix(SoundboardSound): wrong emoji comparison in equals (#10861) 2025-04-28 08:46:03 +03:00
Qjuh
20fade2a87 fix: allowedMentions, container, media item toJSON() for components v2 (#10852)
* fix: allowedMentions for components v2

* refactor: passing allowed_mentions

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

* fix: missing UnfurledMediaItem#toJSON()

* fix: find interactive component in container

* fix: recursive flatMap

* fix: lint

* refactor: top-level function

* fix: jsdoc

* fix: jsdoc
2025-04-27 17:23:12 +00:00
n1ck_pro
e827644b5a fix(Guild): cache soundboard sounds when patching (#10857) 2025-04-27 00:11:40 +01:00
Danial Raza
62815928ab fix(GuildSoundboardSoundManager): value "undefined" is not snowflake (#10854) 2025-04-26 17:06:52 +03:00
Vlad Frangu
7fb6630c02 chore(ci): backport actions updates (#10853) 2025-04-26 17:06:03 +03:00
Vlad Frangu
737b80b5f2 chore(discord.js): release discord.js@14.19.1 2025-04-26 04:17:25 +03:00
Vlad Frangu
481ccd228b fix: add in withComponents to Webhook 2025-04-26 04:16:58 +03:00
Vlad Frangu
a3fff7b8be chore(discord.js): release discord.js@14.19.0 2025-04-26 03:58:29 +03:00
Vlad Frangu
8cdbe23766 fix: set with_components when sending components through webhooks 2025-04-26 03:57:04 +03:00
Vlad Frangu
d920933dc5 fix(GuildAuditLogEntry): fix some incorrect types and runtime logic (#10849)
* fix(GuildAuditLogEntry): fix some incorrect types and runtime logic

* chore: bite me
2025-04-26 00:54:17 +01:00
Danial Raza
2d817df3b5 feat: soundboard missing things (#10850) 2025-04-26 00:49:05 +01:00
Vlad Frangu
1605a2c289 fix: spread out section components next to accessory 2025-04-26 02:37:33 +03:00
Vlad Frangu
464ea2ab30 chore(core): release @discordjs/core@2.1.0 2025-04-26 01:11:36 +03:00
Vlad Frangu
0d1d54a537 chore(ws): release @discordjs/ws@2.0.2 2025-04-26 01:09:37 +03:00
Vlad Frangu
dd8bb397a8 chore(deps): bump discord-api-types round at least 2 2025-04-26 01:08:54 +03:00
Vlad Frangu
61d3d6d4ae chore: bump builders and ws 2025-04-26 00:59:43 +03:00
Vlad Frangu
512b0c67b9 chore(rest): release @discordjs/rest@2.5.0 2025-04-26 00:07:10 +03:00
Vlad Frangu
532c3842bc fix: correctly extend CachedManager in GuildSoundboardSoundManager 2025-04-25 23:52:21 +03:00
Qjuh
edace17a13 feat: components v2 in v14 (#10781)
Co-authored-by: Naiyar <137700126+imnaiyar@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
Co-authored-by: Timo <mail@geniustimo.de>
2025-04-25 23:43:09 +03:00
Danial Raza
d3154cf8f1 feat: add soundboard in v14 (#10843) 2025-04-25 23:37:03 +03:00
René
45552faf02 types: make Client.on() compatible with esnext.disposable in TS5.6+ (#10773) 2025-02-24 11:39:22 +02:00
Danial Raza
ebfd52695e fix(MessagePayload): preserve existing flags when editing (#10766)
* fix(MessagePayload): preserve existing flags when editing

* refactor: sync with #10765
2025-02-21 15:21:16 +00:00
Vlad Frangu
595bded8a5 chore(discord.js): release discord.js@14.18.0 2025-02-11 01:05:37 +02:00
Vlad Frangu
c74c632cdb build: bump @discordjs/ws to 1.2.1 2025-02-11 01:04:37 +02:00
Vlad Frangu
fc003050de build: bump @discordjs/builders to 1.10.1 2025-02-11 00:57:15 +02:00
Vlad Frangu
8702978057 chore(rest): release @discordjs/rest@2.4.3 2025-02-11 00:54:31 +02:00
Jiralite
c2b18d6d8b build: bump undici to 6.21.1 2025-02-08 15:39:26 +00:00
Jiralite
519aa3abe8 build: bump discord-api-types to 0.37.119 2025-02-07 21:45:33 +00:00
Naiyar
89c076c89e feat: message forwards (#10733)
* feat: message forwards

* fix: spelling

* feat: add guildId option for forward

* refactor: type

* refactor: do not use ID suffix for resolvables

* Update TextBasedChannel.js

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-02-07 21:42:30 +00:00
Jiralite
f224a07381 build: modify origin/main to origin/v14 2025-02-06 00:01:27 +00:00
Syed Waheed
8e1e1be0c2 fix(Guild): type error with permissionOverwrites (#10527)
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Almeida <github@almeidx.dev>
Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
2025-02-04 20:56:10 +02:00
Vlad Frangu
193a5e9e20 types: fix recurrence rule types (#10694)
* types: fix recurrence rule types

* fix: endAt not endsAt

* types: remove fields that cannot be set by the client

* chore: cleanup JS lands too

* chore: missed you

* chore: bite me

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-02-04 14:06:46 +00:00
ŊʂƓ PRIYANSHU
73c6bc2c36 chore: Add contributors and last commit badges (#10428)
* chore: add new fancy badges

* chore: add util

* style: remove extra space

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-29 15:06:09 +00:00
Naiyar
b7f1ebc334 fix: incorrect relative path (#10734) 2025-01-29 14:51:11 +00:00
Jiralite
92aea94411 style: prettier 2025-01-29 09:48:17 +00:00
Jiralite
41dee5177d feat: Incident Actions (#10727)
* feat: initial commit

* feat: add guild helper

* docs: `guild` is required

* docs(IncidentActions): move to guild

* fix: `incidents_data` is nullable

* fix: method typo

* fix: default to `null`

* fix: use `new Date()`

* docs: note that it is not received over the gateway

* refactor: use transformer

* chore: resolve TODO

* chore: typo

Co-authored-by: Danial Raza <danialrazafb@gmail.com>

* chore: suggestions

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

* chore: consistency

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

---------

Co-authored-by: Danial Raza <danialrazafb@gmail.com>
Co-authored-by: Almeida <github@almeidx.dev>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-29 09:43:27 +00:00
Jiralite
bbde371324 build: bump discord-api-types to 0.37.118 2025-01-29 09:35:44 +00:00
Jiralite
66b971899a docs: Use link tags to render links on the documentation (#10731)
* docs: use link tags

* docs(DateResolvable): update link

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-29 09:29:59 +00:00
Qjuh
43235d43fe feat(website): type parameters links, builtin doc links, default values (#10515)
* feat(website): links to type parameters, builtin doc links in api.json

* feat(website): show default values for params and props in excerpt

* fix: link in jsdoc

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-01-28 13:32:29 +00:00
Amgelo563
31df3d21cd docs(Message): improve message snapshots description (#10709)
* docs(Message): improve message snapshots description

* docs(Message): remove snapshots single entry callout

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-01-25 19:49:28 +00:00
Almeida
2663d76709 refactor: use throw instead of Promise.reject (#10712)
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Renegade334 <Renegade334@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-24 09:41:17 +00:00
Danial Raza
44a1e85847 types(ThreadOnlyChannel): remove incorrect messages property (#10708)
* types(ThreadOnlyChannel): remove incorrect `messages` property

Co-authored-by: TÆMBØ <TAEMBO@users.noreply.github.com>

* test: t e s t s

* test: revamp tests

---------

Co-authored-by: TÆMBØ <TAEMBO@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-01-18 07:45:39 +00:00
Danial Raza
d2e1924fa6 types: add undefined to flags for exactOptionalPropertyTypes (#10707) 2025-01-18 07:44:24 +00:00
Naiyar
68dd260dee types: Allow only ephemeral for defer reply (#10696)
* fix(types): remove unusable flags from InteractionDeferReplyOptions

* fix: include flags in WebhookMessageEditOptions

* chore: update jsdoc

* fix: wrong order

* chore: specify the flag

* chore: extend MessageEditOptions

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-01-18 07:43:13 +00:00
Naiyar
5e66f85f55 feat(PartialGroupDMChannel): add missing properties (#10502)
* fix(PartialGroupDMChannel): add missing ownerId property

* refactor: make ownerID nullable

* feat: add last_message_id & last_pin_timestamp prop

* feat: add component collector methods

* fix: handle null case

Co-authored-by: Vlad Frangu <me@vladfrangu.dev>

---------

Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-14 09:28:10 +00:00
Almeida
46060419a9 refactor: remove data resolver exports (#10701)
refactor!: remove data resolver exports

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-13 10:35:40 +00:00
Digital
7c1b73cc69 fix(PresenceUpdate): correctly add user regardless of their properties (#10672)
* fix(PresenceUpdate): correctly add user regardless of their properties

* refactor(PresenceUpdate): reflect partials

* refactor(PresenceUpdate): prettier

* refactor(PresenceUpdate): add import

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-13 10:34:19 +00:00
Naiyar
95db597fc8 refactor(IntegrationApplication): move common properties to Application (#10627)
* refactor(IntegrationApplication): move common properties to Application

* fix: remove prop from ClientApplication
2025-01-13 10:33:56 +00:00
Almeida
0047a49b73 types: remove createComponent and createComponentBuilder (#10687)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-13 10:31:58 +00:00
Naiyar
32dff01f29 fix(InteractionResponses): mark replied true for followUps (#10688)
fix: mark replied true for followUps

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2025-01-12 22:29:58 +00:00
Danial Raza
efa50fc3fa feat(Subscription): add renewalSkuIds (#10662) 2025-01-09 19:03:50 +00:00
Qjuh
aa61c20ffd feat(website): include reexported members in docs (#10518)
* feat(website): add re-exported members to docs site

* refactor(scripts): rewrite sourceURL for externals

* feat(website): add external badge

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-09 19:02:28 +00:00
Jiralite
d48136bee1 chore(discord.js): release discord.js@14.17.3 2025-01-08 00:33:54 +00:00
Jiralite
46bf8f0146 fix(Message): Ensure channel is defined for clean content (#10681)
fix(Message): ensure channel is defined for clean content

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-01-04 17:23:55 +00:00
Danial Raza
7280d4e82e fix: use resolve() for PermissionOverwrites (#10686)
* fix: use `resolve()` for `PermissionOverwrites`

* fix: typo

Co-authored-by: René <contact.9a5d6388@renegade334.me.uk>

---------

Co-authored-by: René <contact.9a5d6388@renegade334.me.uk>
2025-01-04 17:09:05 +00:00
Jiralite
bd2914cc98 chore(discord.js): release discord.js@14.17.2 2025-01-02 00:27:09 +00:00
Jiralite
77804cfd55 fix(InteractionResponses): check correct property for deprecation
Resolves #10676.
2025-01-02 00:07:51 +00:00
Vlad Frangu
8fea3ed978 chore(discord.js): release discord.js@14.17.1 2025-01-02 01:48:25 +02:00
Vlad Frangu
05c63cd9a1 chore(rest): release @discordjs/rest@2.4.2 2025-01-02 01:45:37 +02:00
Jiralite
8d69b24b5c fix: correct guild member banner URL 2025-01-01 23:44:32 +00:00
Vlad Frangu
9baee4b2ce chore(discord.js): release discord.js@14.17.0 2025-01-02 00:29:07 +02:00
Vlad Frangu
c986a99104 chore(core): release @discordjs/core@2.0.1 2025-01-02 00:25:35 +02:00
Vlad Frangu
2b9e4cf9d0 chore(ws): release @discordjs/ws@2.0.1 2025-01-02 00:22:09 +02:00
Vlad Frangu
1af2f4ed0e chore: point ws to ^1.2.0 2025-01-02 00:09:25 +02:00
Vlad Frangu
3fbfe9f1ae chore: deps update 2025-01-01 23:43:16 +02:00
Vlad Frangu
b901ff7c4c chore: bump builders, formatters and unpin ws 2025-01-01 23:38:43 +02:00
Vlad Frangu
5f8915f6d1 chore(rest): release @discordjs/rest@2.4.1 2025-01-01 23:38:38 +02:00
Jiralite
ff42d7af72 fix(InteractionResponses): do not use in if a string is passed 2024-12-24 18:20:02 +00:00
Jiralite
0fdbabea98 build: bump discord-api-types to 0.37.114 2024-12-24 12:06:51 +00:00
Jiralite
e9944b3d2d build: bump discord-api-types to 0.37.113 2024-12-22 20:58:53 +00:00
Jiralite
2b9833cd36 Revert "feat(ClientApplication): add webhook events (#10588)"
This reverts commit 7b2a2e3a15.
2024-12-19 00:14:41 +00:00
Naiyar
7b2a2e3a15 feat(ClientApplication): add webhook events (#10588)
* feat(ClientApplication): add webhook events

* refactor: update enum names and add external types

* docs(APITypes): reorder

* chore: requested changes

* chore: requested changes

* docs: remove redundancy

* Update ClientApplication.js

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Almeida <github@almeidx.dev>
2024-12-19 00:12:47 +00:00
Naiyar
6087088579 fix: use Message#interactionMetadata (#10654) 2024-12-19 00:10:31 +00:00
Jiralite
622acbcbf0 feat(InteractionResponses): support with_response query parameter (#10636)
feat(InteractionResponses): support with_response query parameter

Co-authored-by: Ryan Munro <monbrey@gmail.com>
2024-12-18 18:58:22 +00:00
Danial Raza
b2754d4a0e fix(InteractionResponses): properly resolve message flags (#10661) 2024-12-18 15:02:35 +00:00
Jiralite
53cbb0e36d test: Remove unused test (#10638)
test: remove unused test

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-12-05 23:23:02 +00:00
Jiralite
7ce6f2fc8a refactor(FetchApplicationCommandOptions): Use Locale over LocaleString (#10625)
refactor(FetchApplicationCommandOptions): prefer `Locale`
2024-12-05 22:05:23 +00:00
Vlad Frangu
76042f0538 docs: correct discord-api-types URLs (#10622)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-12-05 22:03:31 +00:00
Danial Raza
dedaa5d657 refactor: use cache.get() for snowflakes, resolve() otherwise (#10626)
* refactor: use `cache.get()` for snowflakes, `resolve()` otherwise

* fix: requested changes

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

* chore: remove unnecessary `?? null`

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-12-05 22:01:13 +00:00
Jiralite
ed00a10e1f build: Bump discord-api-types to 0.37.109 (#10619)
build: bump discord-api-types
2024-12-05 21:48:14 +00:00
Naiyar
ae1deac2bf feat(ClientApplication): add webhook events (#10588)
* feat(ClientApplication): add webhook events

* refactor: update enum names and add external types

* docs(APITypes): reorder

* chore: requested changes

* chore: requested changes

* docs: remove redundancy

* Update ClientApplication.js

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: Almeida <github@almeidx.dev>
2024-12-05 20:30:44 +00:00
Jiralite
a367e2c8c9 feat(EntitlementManager): Support get entitlement (#10606)
* feat: support get entitlement

* docs: add return type

Co-authored-by: Danial Raza <danialrazafb@gmail.com>

* fix: property typo

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

* fix: property typo

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

---------

Co-authored-by: Danial Raza <danialrazafb@gmail.com>
Co-authored-by: Almeida <github@almeidx.dev>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-12-05 15:58:19 +00:00
Jiralite
7678f1176a fix(ThreadChannel): Make ownerId always present (#10618)
fix: thread owner id is always present

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-12-05 15:57:06 +00:00
Danial Raza
4cca33d9b0 feat: add subscriptions (#10541)
* feat: add subscriptions

* types: fix fetch options types

* fix: correct properties in patch method

* chore: requested changes

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

* fix: correct export syntax

* chore(Entitlement): mark `ends_at` as nullable`

* types(FetchSubscriptionOptions): add missing `cache` option

* Revert "types(FetchSubscriptionOptions): add missing `cache` option"

This reverts commit ba472bdc599e1860754e59fce4806610f06ac682.

* chore(Entitlement): mark `startsTimestamp` as nullable

* fix: requested changes

* docs(SubscriptionManager): correct return type

---------

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-12-02 08:33:52 +00:00
Jiralite
388783d7dd docs: Typos (#10628)
chore: typos

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-12-02 08:31:15 +00:00
Jiralite
bda31284bf feat: Emit reaction type on gateway events (#10598)
feat: emit reaction type

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-11-18 12:16:35 +00:00
Jiralite
76968b4bc1 fix(MessageReaction): Address undefined burst properties (#10597)
* fix(MessageReaction): `undefined` burst properties

* refactor: simpler burst colour check

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

---------

Co-authored-by: Almeida <github@almeidx.dev>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-11-18 11:47:51 +00:00
Jiralite
34343c6afa feat: Voice Channel Effect Send (#10318)
* feat: Voice Channel Send Effects (#9288)

* feat: add soundboard fields

* chore: address TODO

* docs: volume is a closed interval

* types: use `GatewayVoiceChannelEffectSendDispatchData`

* refactor: prefer getting from cache

* fix: correctly access cache

Co-authored-by: Danial Raza <danialrazafb@gmail.com>

---------

Co-authored-by: Danial Raza <danialrazafb@gmail.com>
2024-11-15 13:51:07 +00:00
Jiralite
56c9396b71 fix(ThreadChannel): Address parameter type on fetchOwner() (#10592)
fix(ThreadChannel): address parameter type on fetchOwner()
2024-11-13 16:51:16 +00:00
Naiyar
21c283f964 fix(InteractionResponses): throw error on deleting response of unacknowledged interaction (#10587)
fix: error on deleting response of non-acknowledged interaction

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-11-11 15:55:19 +00:00
Danial Raza
13471fa1b7 types: add missing Caches managers (#10540)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-11-05 09:36:51 +00:00
Jiralite
b1ded63e42 feat(GuildMember): Banners (#10384)
* feat: initial support for guild member banners

* feat: serialise in `toJSON()`

* feat: serialise in `toJSON()`

* docs: lowercase i

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-11-05 09:35:59 +00:00
Souji
565fc0192a docs: add note about idempotence to role add/remove routes (#10586)
* chore(docs): Add note about idempotence to role add/remove routes

* chore: remove trailing spaces
2024-11-05 09:29:46 +00:00
Jiralite
33533b7284 refactor: remove extra traversing (#10580)
* refactor: remove extra traversion

* refactor(GuildScheduledEventManager): address fetch
2024-10-25 11:13:49 +01:00
Jiralite
be38f57926 refactor(InteractionResponses): Deprecate ephemeral response option (#10574)
* refactor(InteractionResponses): deprecate `ephemeral` response option

* refactor: add runtime deprecations

* docs: fix reference

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

* types: add `MessageFlagsResolvable`

---------

Co-authored-by: Almeida <github@almeidx.dev>
2024-10-25 09:15:54 +01:00
almeidx
f79ba52c7a docs(Client): fix incorrect managers descriptions
Co-authored-by: Luna <84203950+Wolvinny@users.noreply.github.com>
2024-10-12 19:30:51 +01:00
Jiralite
72e0c99454 refactor: Deprecate reason parameter on adding and removing thread members (#10551)
* refactor: deprecate `reason` on thread member add and remove

* chore: address TSLint errors

* refactor: use function
2024-10-12 00:57:14 +01:00
Jiralite
3d06c9d872 refactor: Deprecate fetching user flags (#10550)
* refactor: deprecate fetching user flags

* docs: fix reference

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

* refactor: use function

* refactor: name approach

---------

Co-authored-by: Almeida <github@almeidx.dev>
2024-10-12 00:52:09 +01:00
Eejit
831aafa733 fix(GuildScheduledEvent): handle null recurrence_rule (#10543)
* fix(GuildScheduledEvent): handle null recurrence_rule

* refactor: consistency

* feat: implement suggested logic change

* fix: correct data.recurrence_rule check

---------

Co-authored-by: Almeida <github@almeidx.dev>
2024-10-11 09:03:48 +01:00
Amgelo563
5faf074c14 types: remove newMessage partial on messageUpdate event typing (#10526)
* types: remove newMessage partial on messageUpdate event typing

* types: omit partial group DM for newMessage on messageUpdate

* types: omit partial group DM for oldMessage on messageUpdate

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-10-09 12:47:29 +01:00
Superchupu
297e959f48 docs(discord.js): remove utf-8-validate (#10531) 2024-10-09 12:46:05 +01:00
Moebits
1fc87a9698 feat: Add ApplicationEmoji to EmojiResolvable and MessageReaction#emoji (#10477)
* types: add ApplicationEmoji to EmojiResolvable

* typings: add ApplicationEmoji to MessageReaction#emoji

* removed ApplicationEmoji from MessageReaction

* update BaseGuildEmojiManager

* chore: lint error

* feat: add ApplicationEmoji to MessageReaction#emoji getter

* refactor: check application emojis first

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-10-09 12:42:48 +01:00
Qjuh
366f7174d0 chore: unpin discord-api-types (#10524)
* chore: unpin discord-api-types

* chore: bump discord-api-types
2024-10-01 19:00:33 +01:00
Almeida
97c3237a70 feat: recurring scheduled events (#10447)
* feat: recurring scheduled events

* fix: nullable on patch

* docs: remove unnecessary parenthesis

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

---------

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
2024-09-30 11:27:54 +01:00
TÆMBØ
c12217829b feat: message forwarding (#10464)
* feat: message forwarding

* fix: redundant usage

* feat: add additional snapshot fields

* refactor: use collection to store snapshots

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-09-30 11:27:51 +01:00
Vlad Frangu
0873f9a4c3 chore(discord.js): release discord.js@14.16.3 (#10522) 2024-09-29 11:20:02 +00:00
Ryan Munro
6c77fee41b fix(BaseInteraction): add missing props (#10517)
* fix(AutocompleteInteraction): add missing authorizingIntegrationOwners

* fix(AutocompleteInteraction): add missing context

* fix(AutocompleteInteraction): types

* fix: move to BaseInteraction

* fix: remove props from CommandInteraction

* Update packages/discord.js/typings/index.d.ts

Co-authored-by: Danial Raza <danialrazafb@gmail.com>

---------

Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
Co-authored-by: Danial Raza <danialrazafb@gmail.com>
2024-09-23 14:13:14 +00:00
Danial Raza
cda8d88ad5 build: bump discord-api-types to 0.37.100 (#10488)
* build: bump discord-api-types to 0.37.100

* build: fix lockfile

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2024-09-17 09:15:00 +00:00
TÆMBØ
665bf1486a types(MessageEditOptions): Omit poll (#10509)
fix: creating poll from message edit

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2024-09-17 08:18:08 +00:00
Qjuh
99136d6be8 fix(website): nullable parameters on events (#10510) 2024-09-15 19:27:43 +00:00
ckohen
896dc8b21e chore: update cliff configs (#10471)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-09-15 17:58:21 +00:00
Qjuh
651f2d036a feat: show default values in docs (#10465) 2024-09-15 19:49:31 +02:00
Qjuh
2adee06b6e fix: GuildChannel#guildId not being patched to undefined (#10505)
* fix: `GuildChannel#guildId` not being patched to `undefined`

* fix: guildId to guild_id check
2024-09-14 17:14:03 +00:00
Almeida
495bc60345 fix: docs search (#10501) 2024-09-12 23:24:07 +02:00
Vlad Frangu
d9d578391a chore(discord.js): release discord.js@14.16.2 (#10500) 2024-09-12 11:18:05 +03:00
Ryan Munro
3c74aa2049 fix(ApplicationCommand): incorrect comparison in equals method (#10497) 2024-09-11 07:40:54 +00:00
Danial Raza
799fa54fa4 docs: update discord documentation links (#10484) 2024-09-10 19:23:53 +00:00
Denis Cristea
8a74f144ac chore: pin builders in discord.js (#10490) 2024-09-06 13:12:19 +00:00
Vlad Frangu
dea68400a3 fix: type guard for sendable text-based channels (#10482)
* fix: type-guard for sendable text-based channels

* chore: suggested change

* Update packages/discord.js/typings/index.test-d.ts

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

* fix: make isSendable strictly check for `.send`

* fix: tests

---------

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
2024-09-06 07:16:38 +00:00
Danial Raza
c13f18e90e docs(Message): mark interaction as deprecated (#10481)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-09-04 22:22:10 +00:00
Qjuh
aff772c7aa types: export GroupDM helper type (#10478)
* types: export GroupDM helper type

* refactor: rename type

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-09-04 22:16:54 +00:00
Danial Raza
4594896b54 docs(ApplicationEmojiManager): fix fetch example (#10480)
* docs(ApplicationEmojiManager): fix fetch example

* docs: requested changes
2024-09-03 22:20:01 +00:00
Vlad Frangu
a11ff75631 chore(discord.js): release discord.js@14.16.1 (#10476) 2024-09-03 00:24:53 +03:00
Vlad Frangu
9257a09abb fix(Message): reacting returning undefined (#10475) 2024-09-03 00:20:16 +03:00
space
4810f7c863 fix(Transformers): pass client to recursive call (#10474) 2024-09-02 21:12:28 +00:00
Vlad Frangu
18ce10a9af chore: bump major releases to node 20 2024-09-02 22:26:25 +03:00
Vlad Frangu
ed1c1737df chore: everyone goes to node 18+ 2024-09-02 22:26:25 +03:00
247 changed files with 11737 additions and 1571 deletions

2
.github/CODEOWNERS vendored
View File

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

View File

@@ -1,4 +1,4 @@
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
# https://docs.github.com/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
name: Cleanup caches
on:
pull_request:

View File

@@ -103,7 +103,15 @@ jobs:
if: ${{ env.REF_TYPE == 'tag' && (!inputs.ref || inputs.ref == 'main') }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CF_D1_DOCS_API_KEY: ${{ secrets.CF_D1_DOCS_API_KEY }}
CF_D1_DOCS_ID: ${{ secrets.CF_D1_DOCS_ID }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
CF_R2_DOCS_BUCKET_URL: ${{ secrets.CF_R2_DOCS_BUCKET_URL }}
uses: ./packages/actions/src/uploadDocumentation
with:
package: ${{ steps.extract-tag.outputs.package }}
@@ -113,7 +121,15 @@ jobs:
if: ${{ env.REF_TYPE == 'tag' && inputs.ref && inputs.ref != 'main' }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CF_D1_DOCS_API_KEY: ${{ secrets.CF_D1_DOCS_API_KEY }}
CF_D1_DOCS_ID: ${{ secrets.CF_D1_DOCS_ID }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
CF_R2_DOCS_BUCKET_URL: ${{ secrets.CF_R2_DOCS_BUCKET_URL }}
uses: ./main/packages/actions/src/uploadDocumentation
with:
package: ${{ steps.extract-tag.outputs.package }}
@@ -123,6 +139,10 @@ jobs:
if: ${{ env.REF_TYPE == 'tag' && (!inputs.ref || inputs.ref == 'main') }}
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
uses: ./packages/actions/src/uploadSplitDocumentation
with:
package: ${{ steps.extract-tag.outputs.package }}
@@ -132,6 +152,10 @@ jobs:
if: ${{ env.REF_TYPE == 'tag' && inputs.ref && inputs.ref != 'main' }}
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
uses: ./main/packages/actions/src/uploadSplitDocumentation
with:
package: ${{ steps.extract-tag.outputs.package }}
@@ -155,26 +179,50 @@ jobs:
if: ${{ env.REF_TYPE == 'branch' && (!inputs.ref || inputs.ref == 'main') }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CF_D1_DOCS_API_KEY: ${{ secrets.CF_D1_DOCS_API_KEY }}
CF_D1_DOCS_ID: ${{ secrets.CF_D1_DOCS_ID }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
CF_R2_DOCS_BUCKET_URL: ${{ secrets.CF_R2_DOCS_BUCKET_URL }}
uses: ./packages/actions/src/uploadDocumentation
- name: Upload documentation to database
if: ${{ env.REF_TYPE == 'branch' && inputs.ref && inputs.ref != 'main' }}
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CF_D1_DOCS_API_KEY: ${{ secrets.CF_D1_DOCS_API_KEY }}
CF_D1_DOCS_ID: ${{ secrets.CF_D1_DOCS_ID }}
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
CF_R2_DOCS_BUCKET_URL: ${{ secrets.CF_R2_DOCS_BUCKET_URL }}
uses: ./main/packages/actions/src/uploadDocumentation
- name: Upload split documentation to blob storage
if: ${{ env.REF_TYPE == 'branch' && (!inputs.ref || inputs.ref == 'main') }}
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
uses: ./packages/actions/src/uploadSplitDocumentation
- name: Upload split documentation to blob storage
if: ${{ env.REF_TYPE == 'branch' && inputs.ref && inputs.ref != 'main' }}
env:
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
CF_R2_DOCS_URL: ${{ secrets.CF_R2_DOCS_URL }}
CF_R2_DOCS_ACCESS_KEY_ID: ${{ secrets.CF_R2_DOCS_ACCESS_KEY_ID }}
CF_R2_DOCS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_DOCS_SECRET_ACCESS_KEY }}
CF_R2_DOCS_BUCKET: ${{ secrets.CF_R2_DOCS_BUCKET }}
uses: ./main/packages/actions/src/uploadSplitDocumentation
- name: Move docs to correct directory

View File

@@ -9,7 +9,9 @@
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="npm downloads" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Tests status" /></a>
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2" alt="Code coverage" /></a>
<a href="https://github.com/discordjs/discord.js/commits/main"><img src="https://img.shields.io/github/last-commit/discordjs/discord.js.svg?logo=github&logoColor=ffffff" alt="Last commit." /></a>
<a href="https://github.com/discordjs/discord.js/graphs/contributors"><img src="https://img.shields.io/github/contributors/discordjs/discord.js.svg?maxAge=3600&logo=github&logoColor=fff&color=00c7be" alt="contributors" /></a>
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2" alt="Code coverage" /></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>

View File

@@ -29,6 +29,7 @@ export async function ParameterNode({
{description ? <Badges node={parameter} /> : null}
{parameter.name}
{parameter.isOptional ? '?' : ''}: <ExcerptNode node={parameter.typeExcerpt} version={version} />
{parameter.defaultValue ? ` = ${parameter.defaultValue}` : ''}
</span>
{description && parameter.description?.length ? (
<div className="mt-4 pl-4">

View File

@@ -51,7 +51,13 @@ export async function PropertyNode({
<LinkIcon aria-hidden size={16} />
</Link>
{property.displayName}
{property.isOptional ? '?' : ''} : <ExcerptNode node={property.typeExcerpt} version={version} />
{property.isOptional ? '?' : ''} : <ExcerptNode node={property.typeExcerpt} version={version} />{' '}
{property.summary?.defaultValueBlock.length
? `= ${property.summary.defaultValueBlock.reduce(
(acc: string, def: { kind: string; text: string }) => `${acc}${def.text}`,
'',
)}`
: ''}
</span>
</h3>

View File

@@ -20,7 +20,7 @@ export async function fetchDependencies({
return Object.entries<string>(parsedDependencies)
.filter(([key]) => key.startsWith('@discordjs/') && !key.includes('api-extractor'))
.map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${value.replaceAll('.', '-')}`);
.map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${sanitizeVersion(value)}`);
} catch {
return [];
}
@@ -36,8 +36,12 @@ export async function fetchDependencies({
return Object.entries<string>(parsedDependencies)
.filter(([key]) => key.startsWith('@discordjs/') && !key.includes('api-extractor'))
.map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${value.replaceAll('.', '-')}`);
.map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${sanitizeVersion(value)}`);
} catch {
return [];
}
}
function sanitizeVersion(version: string) {
return version.replaceAll('.', '-').replace(/^[\^~]/, '');
}

View File

@@ -6,19 +6,19 @@
"private": true,
"scripts": {
"build": "turbo run build --concurrency=4",
"build:affected": "turbo run build --filter=...[origin/main] --concurrency=4",
"build:affected": "turbo run build --filter=...[origin/v14] --concurrency=4",
"build:apps": "turbo run build:local --filter=...{apps/*} --concurrency=4",
"build:apps:affected": "turbo run build:local --filter=...{apps/*}[origin/main] --concurrency=4",
"build:apps:affected": "turbo run build:local --filter=...{apps/*}[origin/v14] --concurrency=4",
"test": "turbo run test --concurrency=4",
"test:affected": "turbo run test --filter=...[origin/main] --concurrency=4",
"test:affected": "turbo run test --filter=...[origin/v14] --concurrency=4",
"lint": "turbo run lint --concurrency=4",
"lint:affected": "turbo run lint --filter=...[origin/main] --concurrency=4",
"lint:affected": "turbo run lint --filter=...[origin/v14] --concurrency=4",
"format": "turbo run format --concurrency=4",
"format:affected": "turbo run format --filter=...[origin/main] --concurrency=4",
"format:affected": "turbo run format --filter=...[origin/v14] --concurrency=4",
"fmt": "turbo run format --concurrency=4",
"fmt:affected": "turbo run format --filter=...[origin/main] --concurrency=4",
"fmt:affected": "turbo run format --filter=...[origin/v14] --concurrency=4",
"docs": "turbo run docs --concurrency=4",
"docs:affected": "turbo run docs --filter=...[origin/main] --concurrency=4",
"docs:affected": "turbo run docs --filter=...[origin/v14] --concurrency=4",
"prepare": "is-ci || husky",
"update": "pnpm --recursive update --interactive",
"update:latest": "pnpm --recursive update --interactive --latest",

View File

@@ -41,28 +41,32 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/core": "^1.11.1",
"@actions/glob": "^0.5.0",
"@aws-sdk/client-s3": "^3.787.0",
"@discordjs/scripts": "workspace:^",
"@vercel/blob": "^0.23.4",
"@vercel/blob": "^0.27.3",
"@vercel/postgres": "^0.9.0",
"cloudflare": "^4.2.0",
"meilisearch": "^0.38.0",
"p-limit": "^6.1.0",
"tslib": "^2.6.3",
"undici": "6.21.1"
"p-limit": "^6.2.0",
"p-queue": "^8.1.0",
"tslib": "^2.8.1",
"undici": "7.8.0"
},
"devDependencies": {
"@types/node": "^18.19.45",
"@vitest/coverage-v8": "^2.0.5",
"@types/node": "^22.14.0",
"@vitest/coverage-v8": "^3.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-neon": "^0.1.62",
"eslint-formatter-pretty": "^6.0.1",
"prettier": "^3.3.3",
"tsup": "^8.2.4",
"turbo": "^2.0.14",
"typescript": "~5.5.4",
"vitest": "^2.0.5"
"prettier": "^3.5.3",
"terser": "^5.37.0",
"tsup": "^8.4.0",
"turbo": "^2.5.0",
"typescript": "~5.8.3",
"vitest": "^3.1.1"
},
"engines": {
"node": ">=18"

View File

@@ -9,7 +9,7 @@ runs:
with:
swap-size-gb: 10
- uses: pnpm/action-setup@v4.0.0
- uses: pnpm/action-setup@v4.1.0
name: Install pnpm
with:
run_install: false

View File

@@ -1,13 +1,26 @@
/* eslint-disable @typescript-eslint/no-loop-func */
import { readFile } from 'node:fs/promises';
import process from 'node:process';
import { getInput, setFailed } from '@actions/core';
import { create } from '@actions/glob';
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { put } from '@vercel/blob';
import { createPool } from '@vercel/postgres';
import Cloudflare from 'cloudflare';
import pLimit from 'p-limit';
if (!process.env.DATABASE_URL) {
setFailed('DATABASE_URL is not set');
if (
!process.env.DATABASE_URL ||
!process.env.CF_R2_DOCS_URL ||
!process.env.CF_R2_DOCS_ACCESS_KEY_ID ||
!process.env.CF_R2_DOCS_SECRET_ACCESS_KEY ||
!process.env.CF_R2_DOCS_BUCKET ||
!process.env.CF_R2_DOCS_BUCKET_URL ||
!process.env.CF_D1_DOCS_API_KEY ||
!process.env.CF_D1_DOCS_ID ||
!process.env.CF_ACCOUNT_ID
) {
setFailed('Missing environment variables');
}
const pkg = getInput('package') || '*';
@@ -17,6 +30,21 @@ const pool = createPool({
connectionString: process.env.DATABASE_URL,
});
const S3 = new S3Client({
region: 'auto',
endpoint: process.env.CF_R2_DOCS_URL!,
credentials: {
accessKeyId: process.env.CF_R2_DOCS_ACCESS_KEY_ID!,
secretAccessKey: process.env.CF_R2_DOCS_SECRET_ACCESS_KEY!,
},
requestChecksumCalculation: 'WHEN_REQUIRED',
responseChecksumValidation: 'WHEN_REQUIRED',
});
const client = new Cloudflare({
apiToken: process.env.CF_D1_DOCS_API_KEY,
});
const limit = pLimit(10);
const promises = [];
@@ -26,12 +54,14 @@ for await (const file of globber.globGenerator()) {
const data = await readFile(file, 'utf8');
try {
promises.push(
// eslint-disable-next-line @typescript-eslint/no-loop-func
limit(async () => {
console.log(`Uploading ${file} with ${version}...`);
const json = JSON.parse(data);
const name = json.name ?? json.n;
const { url } = await put(`${name.replace('@discordjs/', '')}/${version}.json`, data, {
const key = `${name.replace('@discordjs/', '')}/${version}.json`;
const { url } = await put(key, data, {
access: 'public',
addRandomSuffix: false,
});
@@ -39,6 +69,19 @@ for await (const file of globber.globGenerator()) {
'@discordjs/',
'',
)}, ${version}, ${url}) on conflict (name, version) do update set url = EXCLUDED.url`;
await S3.send(
new PutObjectCommand({
Bucket: process.env.CF_R2_DOCS_BUCKET,
Key: key,
Body: data,
}),
);
await client.d1.database.raw(process.env.CF_D1_DOCS_ID!, {
account_id: process.env.CF_ACCOUNT_ID!,
sql: `insert into documentation (name, version, url) values (?, ?, ?) on conflict (name, version) do update set url = excluded.url;`,
params: [name.replace('@discordjs/', ''), version, process.env.CF_R2_DOCS_BUCKET_URL + '/' + key],
});
}),
);
} catch (error) {

View File

@@ -1,32 +1,82 @@
/* eslint-disable @typescript-eslint/no-loop-func */
import { readFile } from 'node:fs/promises';
import { basename, dirname, relative, sep } from 'node:path';
import { cwd } from 'node:process';
import { getInput } from '@actions/core';
import process from 'node:process';
import { setTimeout as sleep } from 'node:timers/promises';
import { setFailed, getInput } from '@actions/core';
import { create } from '@actions/glob';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { put } from '@vercel/blob';
import pLimit from 'p-limit';
import PQueue from 'p-queue';
if (
!process.env.CF_R2_DOCS_URL ||
!process.env.CF_R2_DOCS_ACCESS_KEY_ID ||
!process.env.CF_R2_DOCS_SECRET_ACCESS_KEY ||
!process.env.CF_R2_DOCS_BUCKET
) {
setFailed('Missing environment variables');
}
const pkg = getInput('package') || '*';
const version = getInput('version') || 'main';
const limit = pLimit(10);
const queue = new PQueue({ concurrency: 10, interval: 60_000, intervalCap: 1_000 });
const promises = [];
const failedUploads: string[] = [];
const S3 = new S3Client({
region: 'auto',
endpoint: process.env.CF_R2_DOCS_URL!,
credentials: {
accessKeyId: process.env.CF_R2_DOCS_ACCESS_KEY_ID!,
secretAccessKey: process.env.CF_R2_DOCS_SECRET_ACCESS_KEY!,
},
requestChecksumCalculation: 'WHEN_REQUIRED',
responseChecksumValidation: 'WHEN_REQUIRED',
});
const globber = await create(`packages/${pkg}/docs/${pkg}/split/*.api.json`);
console.log('Glob: ', await globber.glob());
for await (const file of globber.globGenerator()) {
const data = await readFile(file, 'utf8');
const pkgName = dirname(relative(cwd(), file)).split(sep)[1];
const pkgName = dirname(relative(process.cwd(), file)).split(sep)[1];
try {
promises.push(
// eslint-disable-next-line @typescript-eslint/no-loop-func
limit(async () => {
queue.add(async () => {
console.log(`Uploading ${file} with ${version} from ${pkgName}...`);
const name = basename(file).replace('main.', '');
await put(`rewrite/${pkgName}/${version}.${name}`, data, {
access: 'public',
addRandomSuffix: false,
});
async function upload(retries = 0) {
try {
await put(`rewrite/${pkgName}/${version}.${name}`, data, {
access: 'public',
addRandomSuffix: false,
});
await S3.send(
new PutObjectCommand({
Bucket: process.env.CF_R2_DOCS_BUCKET,
Key: `${pkgName}/${version}.${name}`,
Body: data,
}),
);
} catch (error) {
if (retries > 3) {
console.error(`Could not upload ${file} after 3 retries`, error);
failedUploads.push(name);
return;
}
if (typeof error === 'object' && error && 'retryAfter' in error && typeof error.retryAfter === 'number') {
await sleep(error.retryAfter * 1_000);
return upload(retries + 1);
} else {
console.error(`Could not upload ${file}`, error);
failedUploads.push(name);
}
}
}
await upload();
}),
);
} catch (error) {
@@ -36,6 +86,9 @@ for await (const file of globber.globGenerator()) {
try {
await Promise.all(promises);
if (failedUploads.length) {
setFailed(`Failed to upload ${failedUploads.length} files: ${failedUploads.join(', ')}`);
}
} catch (error) {
console.log(error);
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "https://json.schemastore.org/tsconfig.json",
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"skipLibCheck": true
},
"include": ["__tests__/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -14,6 +14,7 @@ import type { IExcerptTokenRange } from './Excerpt.js';
* @public
*/
export interface IApiParameterOptions {
defaultValue: string | undefined;
isOptional: boolean;
isRest: boolean;
parameterName: string;
@@ -124,6 +125,7 @@ export function ApiParameterListMixin<TBaseClass extends IApiItemConstructor>(
isOptional: Boolean(parameterOptions.isOptional),
isRest: Boolean(parameterOptions.isRest),
parent: this,
defaultValue: parameterOptions.defaultValue,
});
this[_parameters].push(parameter);
@@ -171,6 +173,7 @@ export function ApiParameterListMixin<TBaseClass extends IApiItemConstructor>(
parameterTypeTokenRange: parameter.parameterTypeExcerpt.tokenRange,
isOptional: parameter.isOptional,
isRest: parameter.isRest,
defaultValue: parameter.defaultValue,
});
}

View File

@@ -41,6 +41,7 @@ const MinifyJSONMapping = {
constraintTokenRange: 'ctr',
dependencies: 'dp',
defaultTypeTokenRange: 'dtr',
defaultValue: 'dv',
docComment: 'd',
endIndex: 'en',
excerptTokens: 'ex',

View File

@@ -262,6 +262,7 @@ function mapParam(
startIndex: 1 + index + paramTokens.slice(0, index).reduce((akk, num) => akk + num, 0),
endIndex: 1 + index + paramTokens.slice(0, index + 1).reduce((akk, num) => akk + num, 0),
},
defaultValue: param.default,
};
}

View File

@@ -12,6 +12,7 @@ import type { Excerpt } from '../mixins/Excerpt.js';
* @public
*/
export interface IParameterOptions {
defaultValue: string | undefined;
isOptional: boolean;
isRest: boolean;
name: string;
@@ -56,6 +57,11 @@ export class Parameter {
*/
public isRest: boolean;
/**
* The default value for this parameter if optional
*/
public defaultValue: string | undefined;
private readonly _parent: ApiParameterListMixin;
public constructor(options: IParameterOptions) {
@@ -64,6 +70,7 @@ export class Parameter {
this.isOptional = options.isOptional;
this.isRest = options.isRest;
this._parent = options.parent;
this.defaultValue = options.defaultValue;
}
/**

View File

@@ -114,7 +114,7 @@ interface DocgenEventJson {
}
interface DocgenParamJson {
default?: string;
default?: boolean | number | string;
description: string;
name: string;
nullable?: boolean;
@@ -155,7 +155,7 @@ interface DocgenMethodJson {
interface DocgenPropertyJson {
abstract?: boolean;
access?: DocgenAccess;
default?: string;
default?: boolean | number | string;
deprecated?: DocgenDeprecated;
description: string;
meta: DocgenMetaJson;
@@ -843,6 +843,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(
nodesToCapture,
functionDeclaration.parameters,
jsDoc?.params,
);
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
@@ -1043,6 +1044,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(
nodesToCapture,
methodDeclaration.parameters,
jsDoc?.params,
);
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
@@ -1137,7 +1139,11 @@ export class ApiModelGenerator {
methodSignature.typeParameters,
);
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, methodSignature.parameters);
const parameters: IApiParameterOptions[] = this._captureParameters(
nodesToCapture,
methodSignature.parameters,
jsDoc?.params,
);
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
@@ -1264,7 +1270,7 @@ export class ApiModelGenerator {
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString(
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}\n${
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}${jsDoc.default === undefined ? '' : ` (default: ${this._escapeSpecialChars(jsDoc.default)})`}\n${
'see' in jsDoc ? jsDoc.see.map((see) => ` * @see ${see}\n`).join('') : ''
}${'readonly' in jsDoc && jsDoc.readonly ? ' * @readonly\n' : ''}${
'deprecated' in jsDoc && jsDoc.deprecated
@@ -1342,7 +1348,7 @@ export class ApiModelGenerator {
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString(
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}\n${
`/**\n * ${this._fixLinkTags(jsDoc.description) ?? ''}${jsDoc.default === undefined ? '' : `\n * @defaultValue ${this._escapeSpecialChars(jsDoc.default)}`}\n${
'see' in jsDoc ? jsDoc.see.map((see) => ` * @see ${see}\n`).join('') : ''
}${'readonly' in jsDoc && jsDoc.readonly ? ' * @readonly\n' : ''}${
'deprecated' in jsDoc && jsDoc.deprecated
@@ -1510,15 +1516,17 @@ export class ApiModelGenerator {
const excerptTokens: IExcerptToken[] = [
{
kind: ExcerptTokenKind.Content,
text: `on('${name}', (${
jsDoc.params?.length ? `${jsDoc.params[0]?.name}${jsDoc.params[0]?.nullable ? '?' : ''}: ` : ') => {})'
text: `public on(eventName: '${name}', listener: (${
jsDoc.params?.length
? `${jsDoc.params[0]?.name}${jsDoc.params[0]?.optional ? '?' : ''}: `
: ') => void): this;'
}`,
},
];
const parameters: IApiParameterOptions[] = [];
for (let index = 0; index < (jsDoc.params?.length ?? 0) - 1; index++) {
const parameter = jsDoc.params![index]!;
const newTokens = this._mapVarType(parameter.type);
const newTokens = this._mapVarType(parameter.type, parameter.nullable);
parameters.push({
parameterName: parameter.name,
parameterTypeTokenRange: {
@@ -1527,6 +1535,7 @@ export class ApiModelGenerator {
},
isOptional: Boolean(parameter.optional),
isRest: parameter.name.startsWith('...'),
defaultValue: parameter.default?.toString(),
});
excerptTokens.push(...newTokens);
excerptTokens.push({
@@ -1537,7 +1546,7 @@ export class ApiModelGenerator {
if (jsDoc.params?.length) {
const parameter = jsDoc.params![jsDoc.params.length - 1]!;
const newTokens = this._mapVarType(parameter.type);
const newTokens = this._mapVarType(parameter.type, parameter.nullable);
parameters.push({
parameterName: parameter.name,
parameterTypeTokenRange: {
@@ -1546,11 +1555,12 @@ export class ApiModelGenerator {
},
isOptional: Boolean(parameter.optional),
isRest: parameter.name.startsWith('...'),
defaultValue: parameter.default?.toString(),
});
excerptTokens.push(...newTokens);
excerptTokens.push({
kind: ExcerptTokenKind.Content,
text: `) => {})`,
text: `) => void): this;`,
});
}
@@ -1638,6 +1648,7 @@ export class ApiModelGenerator {
private _captureParameters(
nodesToCapture: IExcerptBuilderNodeToCapture[],
parameterNodes: ts.NodeArray<ts.ParameterDeclaration>,
jsDoc?: DocgenParamJson[] | undefined,
): IApiParameterOptions[] {
const parameters: IApiParameterOptions[] = [];
for (const parameter of parameterNodes) {
@@ -1648,6 +1659,9 @@ export class ApiModelGenerator {
parameterTypeTokenRange,
isOptional: this._collector.typeChecker.isOptionalParameter(parameter),
isRest: Boolean(parameter.dotDotDotToken),
defaultValue:
parameter.initializer?.getText() ??
jsDoc?.find((param) => param.name === parameter.name.getText().trim())?.default?.toString(),
});
}
@@ -1746,6 +1760,14 @@ export class ApiModelGenerator {
return sourceLocation;
}
private _escapeSpecialChars(input: boolean | number | string) {
if (typeof input !== 'string') {
return input;
}
return input.replaceAll(/(?<char>[@{}])/g, '\\$<char>');
}
private _fixLinkTags(input?: string): string | undefined {
return input
?.replaceAll(linkRegEx, (_match, _p1, _p2, _p3, _p4, _p5, _offset, _string, groups) => {
@@ -1765,7 +1787,7 @@ export class ApiModelGenerator {
.replaceAll('* ', '\n * * ');
}
private _mapVarType(typey: DocgenVarTypeJson): IExcerptToken[] {
private _mapVarType(typey: DocgenVarTypeJson, nullable?: boolean): IExcerptToken[] {
const mapper = Array.isArray(typey) ? typey : (typey.types ?? []);
const lookup: { [K in ts.SyntaxKind]?: string } = {
[ts.SyntaxKind.ClassDeclaration]: 'class',
@@ -1808,7 +1830,22 @@ export class ApiModelGenerator {
{ kind: ExcerptTokenKind.Content, text: symbol ?? '' },
];
}, []);
return index === 0 ? result : [{ kind: ExcerptTokenKind.Content, text: ' | ' }, ...result];
return index === 0
? mapper.length === 1 && (nullable || ('nullable' in typey && typey.nullable))
? [
...result,
{ kind: ExcerptTokenKind.Content, text: ' | ' },
{ kind: ExcerptTokenKind.Reference, text: 'null' },
]
: result
: index === mapper.length - 1 && (nullable || ('nullable' in typey && typey.nullable))
? [
{ kind: ExcerptTokenKind.Content, text: ' | ' },
...result,
{ kind: ExcerptTokenKind.Content, text: ' | ' },
{ kind: ExcerptTokenKind.Reference, text: 'null' },
]
: [{ kind: ExcerptTokenKind.Content, text: ' | ' }, ...result];
})
.filter((excerpt) => excerpt.text.length);
}
@@ -1823,7 +1860,7 @@ export class ApiModelGenerator {
isOptional: Boolean(prop.nullable),
isReadonly: Boolean(prop.readonly),
docComment: this._tsDocParser.parseString(
`/**\n * ${this._fixLinkTags(prop.description) ?? ''}\n${
`/**\n * ${this._fixLinkTags(prop.description) ?? ''}\n${prop.default ? ` * @defaultValue ${this._escapeSpecialChars(prop.default)}\n` : ''}${
prop.see?.map((see) => ` * @see ${see}\n`).join('') ?? ''
}${prop.readonly ? ' * @readonly\n' : ''} */`,
).docComment,
@@ -1835,7 +1872,7 @@ export class ApiModelGenerator {
}${prop.name} :`,
},
...mappedVarType,
{ kind: ExcerptTokenKind.Content, text: ';' },
{ kind: ExcerptTokenKind.Content, text: `${prop.default ? ` = ${prop.default}` : ''};` },
],
propertyTypeTokenRange: { startIndex: 1, endIndex: 1 + mappedVarType.length },
releaseTag: prop.access === 'private' ? ReleaseTag.Internal : ReleaseTag.Public,
@@ -1858,6 +1895,7 @@ export class ApiModelGenerator {
startIndex: 1 + index + paramTokens.slice(0, index).reduce((akk, num) => akk + num, 0),
endIndex: 1 + index + paramTokens.slice(0, index + 1).reduce((akk, num) => akk + num, 0),
},
defaultValue: param.default?.toString(),
};
}
@@ -1882,7 +1920,7 @@ export class ApiModelGenerator {
excerptTokens.push(...newTokens);
excerptTokens.push({
kind: ExcerptTokenKind.Content,
text: `, ${method.params![index + 1]!.name}${
text: `${method.params![index]!.default ? ` = ${method.params![index]!.default}` : ''}, ${method.params![index + 1]!.name}${
method.params![index + 1]!.nullable || method.params![index + 1]!.optional ? '?' : ''
}: `,
});
@@ -1892,7 +1930,10 @@ export class ApiModelGenerator {
const newTokens = this._mapVarType(method.params[method.params.length - 1]!.type);
paramTokens.push(newTokens.length);
excerptTokens.push(...newTokens);
excerptTokens.push({ kind: ExcerptTokenKind.Content, text: `): ` });
excerptTokens.push({
kind: ExcerptTokenKind.Content,
text: `${method.params![method.params.length - 1]!.default ? ` = ${method.params![method.params.length - 1]!.default}` : ''}): `,
});
}
const returnTokens = this._mapVarType(method.returns?.[0] ?? []);

View File

@@ -9,7 +9,8 @@
<a href="https://www.npmjs.com/package/@discordjs/brokers"><img src="https://img.shields.io/npm/v/@discordjs/brokers.svg?maxAge=3600" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/@discordjs/brokers"><img src="https://img.shields.io/npm/dt/@discordjs/brokers.svg?maxAge=3600" alt="npm downloads" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=brokers" alt="Code coverage" /></a>
<a href="https://github.com/discordjs/discord.js/commits/main/packages/brokers"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fbrokers"></a>
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=brokers" alt="Code coverage" /></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
@@ -23,7 +24,7 @@
## Installation
**Node.js 18 or newer is required.**
**Node.js 20 or newer is required.**
```sh
npm install @discordjs/brokers

View File

@@ -30,8 +30,10 @@ body = """
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
{% if commit.breaking %}\
{% for breakingChange in commit.footers %}\
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
{% for footer in commit.footers %}\
{% if footer.breaking %}\
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
{% endif %}\
{% endfor %}\
{% endif %}\
{% endfor %}

View File

@@ -89,7 +89,7 @@
"vitest": "^2.0.5"
},
"engines": {
"node": ">=18"
"node": ">=20"
},
"publishConfig": {
"access": "public",

View File

@@ -2,6 +2,40 @@
All notable changes to this project will be documented in this file.
# [@discordjs/builders@1.13.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.12.2...@discordjs/builders@1.13.0) - (2025-10-24)
## Features
- V1 builders file uploads support (#11196) ([1417c49](https://github.com/discordjs/discord.js/commit/1417c498a40b843d772ecf88dfff5f87a1665042))
## Testing
- Fix type error ([f780c6a](https://github.com/discordjs/discord.js/commit/f780c6a5500f7ea5c7a1ea7cd6720f6159d9d36e))
# [@discordjs/builders@1.12.2](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.12.1...@discordjs/builders@1.12.2) - (2025-10-09)
## Bug Fixes
- **Assertions:** Literal default values ([43362c9](https://github.com/discordjs/discord.js/commit/43362c93525f98d72b894eb0fc6b358d30ec45b9))
# [@discordjs/builders@1.12.1](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.12.0...@discordjs/builders@1.12.1) - (2025-10-08)
## Bug Fixes
- **builders:** Text display component support for modals (#11155) ([99b8436](https://github.com/discordjs/discord.js/commit/99b8436117bc12654278337abc4a23f5bdf4ba46))
# [@discordjs/builders@1.12.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.11.3...@discordjs/builders@1.12.0) - (2025-10-08)
## Features
- **builders:** Modal select menus in builders v1 (#11138) ([ac683b9](https://github.com/discordjs/discord.js/commit/ac683b9d040635de8514c80a9d433d9c6d63701b))
# [@discordjs/builders@1.11.3](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.11.2...@discordjs/builders@1.11.3) - (2025-08-10)
## Bug Fixes
- **contextMenuCommands:** Remove regular expression validation (#10996) ([4906aae](https://github.com/discordjs/discord.js/commit/4906aaea4c0e6e868fa658d3359026eb662fbcb8))
# [@discordjs/builders@1.11.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.10.1...@discordjs/builders@1.11.0) - (2025-04-25)
## Features

View File

@@ -9,7 +9,8 @@
<a href="https://www.npmjs.com/package/@discordjs/builders"><img src="https://img.shields.io/npm/v/@discordjs/builders.svg?maxAge=3600" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/@discordjs/builders"><img src="https://img.shields.io/npm/dt/@discordjs/builders.svg?maxAge=3600" alt="npm downloads" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=builders" alt="Code coverage" /></a>
<a href="https://github.com/discordjs/discord.js/commits/main/packages/builders"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fbuilders"></a>
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=builders" alt="Code coverage" /></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>

View File

@@ -0,0 +1,46 @@
import type { APIFileUploadComponent } from 'discord-api-types/v10';
import { ComponentType } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { FileUploadBuilder } from '../../src/components/fileUpload/FileUpload.js';
describe('File Upload Components', () => {
test('Valid builder does not throw.', () => {
expect(() => new FileUploadBuilder().setCustomId('file_upload').toJSON()).not.toThrowError();
expect(() => new FileUploadBuilder().setCustomId('file_upload').setId(5).toJSON()).not.toThrowError();
expect(() =>
new FileUploadBuilder().setCustomId('file_upload').setMaxValues(5).setMinValues(2).toJSON(),
).not.toThrowError();
expect(() => new FileUploadBuilder().setCustomId('file_upload').setRequired(false).toJSON()).not.toThrowError();
});
test('Invalid builder does throw.', () => {
expect(() => new FileUploadBuilder().toJSON()).toThrowError();
expect(() => new FileUploadBuilder().setCustomId('file_upload').setId(-3).toJSON()).toThrowError();
expect(() => new FileUploadBuilder().setMaxValues(5).setMinValues(2).setId(10).toJSON()).toThrowError();
expect(() => new FileUploadBuilder().setCustomId('file_upload').setMaxValues(500).toJSON()).toThrowError();
expect(() =>
new FileUploadBuilder().setCustomId('file_upload').setMinValues(500).setMaxValues(501).toJSON(),
).toThrowError();
expect(() => new FileUploadBuilder().setRequired(false).toJSON()).toThrowError();
});
test('API data equals toJSON().', () => {
const fileUploadData = {
type: ComponentType.FileUpload,
custom_id: 'file_upload',
min_values: 4,
max_values: 9,
required: false,
} satisfies APIFileUploadComponent;
expect(new FileUploadBuilder(fileUploadData).toJSON()).toEqual(fileUploadData);
expect(
new FileUploadBuilder().setCustomId('file_upload').setMinValues(4).setMaxValues(9).setRequired(false).toJSON(),
).toEqual(fileUploadData);
});
});

View File

@@ -100,11 +100,11 @@ describe('Text Input Components', () => {
.setPlaceholder('hello')
.setStyle(TextInputStyle.Paragraph)
.toJSON();
}).toThrowError();
}).not.toThrowError();
});
test('GIVEN valid input THEN valid JSON outputs are given', () => {
const textInputData: APITextInputComponent = {
const textInputData = {
type: ComponentType.TextInput,
label: 'label',
custom_id: 'custom id',
@@ -114,7 +114,7 @@ describe('Text Input Components', () => {
value: 'value',
required: false,
style: TextInputStyle.Paragraph,
};
} satisfies APITextInputComponent;
expect(new TextInputBuilder(textInputData).toJSON()).toEqual(textInputData);
expect(

View File

@@ -1,8 +1,8 @@
import { type APIContainerComponent, ComponentType, SeparatorSpacingSize } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { ButtonBuilder } from '../../../dist/index.mjs';
import { ActionRowBuilder } from '../../../src/components/ActionRow.js';
import { createComponentBuilder } from '../../../src/components/Components.js';
import { ButtonBuilder } from '../../../src/components/button/Button.js';
import { ContainerBuilder } from '../../../src/components/v2/Container.js';
import { FileBuilder } from '../../../src/components/v2/File.js';
import { MediaGalleryBuilder } from '../../../src/components/v2/MediaGallery.js';

View File

@@ -16,8 +16,8 @@ describe('Context Menu Commands', () => {
// Too short of a name
expect(() => ContextMenuCommandAssertions.validateName('')).toThrowError();
// Invalid characters used
expect(() => ContextMenuCommandAssertions.validateName('ABC123$%^&')).toThrowError();
// This should be fine, even with trailing and leading spaces (API trims it).
expect(() => ContextMenuCommandAssertions.validateName(' 🩵 ABC 123 $%^& ')).not.toThrowError();
// Too long of a name
expect(() =>
@@ -60,8 +60,6 @@ describe('Context Menu Commands', () => {
});
test('GIVEN invalid name THEN throw error', () => {
expect(() => getBuilder().setName('$$$')).toThrowError();
expect(() => getBuilder().setName(' ')).toThrowError();
});

View File

@@ -30,8 +30,10 @@ body = """
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
{% if commit.breaking %}\
{% for breakingChange in commit.footers %}\
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
{% for footer in commit.footers %}\
{% if footer.breaking %}\
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
{% endif %}\
{% endfor %}\
{% endif %}\
{% endfor %}

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/builders",
"version": "1.11.1",
"version": "1.13.0",
"description": "A set of builders that you can use when creating your bot",
"scripts": {
"test": "vitest run",
@@ -68,7 +68,7 @@
"@discordjs/formatters": "workspace:^",
"@discordjs/util": "workspace:^",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "^0.38.1",
"discord-api-types": "^0.38.32",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3"

View File

@@ -5,6 +5,7 @@ import type {
APIBaseComponent,
ComponentType,
APIMessageComponent,
APIModalComponent,
} from 'discord-api-types/v10';
import { idValidator } from './Assertions';
@@ -14,7 +15,8 @@ import { idValidator } from './Assertions';
export type AnyAPIActionRowComponent =
| APIActionRowComponent<APIComponentInActionRow>
| APIComponentInActionRow
| APIMessageComponent;
| APIMessageComponent
| APIModalComponent;
/**
* The base component builder that contains common symbols for all sorts of components.

View File

@@ -8,6 +8,8 @@ import {
} from './ActionRow.js';
import { ComponentBuilder } from './Component.js';
import { ButtonBuilder } from './button/Button.js';
import { FileUploadBuilder } from './fileUpload/FileUpload.js';
import { LabelBuilder } from './label/Label.js';
import { ChannelSelectMenuBuilder } from './selectMenu/ChannelSelectMenu.js';
import { MentionableSelectMenuBuilder } from './selectMenu/MentionableSelectMenu.js';
import { RoleSelectMenuBuilder } from './selectMenu/RoleSelectMenu.js';
@@ -100,6 +102,14 @@ export interface MappedComponentTypes {
* The media gallery component type is associated with a {@link MediaGalleryBuilder}.
*/
[ComponentType.MediaGallery]: MediaGalleryBuilder;
/**
* The label component type is associated with a {@link LabelBuilder}.
*/
[ComponentType.Label]: LabelBuilder;
/**
* The file upload component type is associated with a {@link FileUploadBuilder}.
*/
[ComponentType.FileUpload]: FileUploadBuilder;
}
/**
@@ -161,6 +171,10 @@ export function createComponentBuilder(
return new ThumbnailBuilder(data);
case ComponentType.MediaGallery:
return new MediaGalleryBuilder(data);
case ComponentType.Label:
return new LabelBuilder(data);
case ComponentType.FileUpload:
return new FileUploadBuilder(data);
default:
// @ts-expect-error This case can still occur if we get a newer unsupported component type
throw new Error(`Cannot properly serialize component type: ${data.type}`);

View File

@@ -0,0 +1,12 @@
import { s } from '@sapphire/shapeshift';
import { ComponentType } from 'discord-api-types/v10';
import { customIdValidator, idValidator } from '../Assertions.js';
export const fileUploadPredicate = s.object({
type: s.literal(ComponentType.FileUpload),
id: idValidator.optional(),
custom_id: customIdValidator,
min_values: s.number().greaterThanOrEqual(0).lessThanOrEqual(10).optional(),
max_values: s.number().greaterThanOrEqual(1).lessThanOrEqual(10).optional(),
required: s.boolean().optional(),
});

View File

@@ -0,0 +1,99 @@
import { type APIFileUploadComponent, ComponentType } from 'discord-api-types/v10';
import { ComponentBuilder } from '../Component.js';
import { fileUploadPredicate } from './Assertions.js';
/**
* A builder that creates API-compatible JSON data for file uploads.
*/
export class FileUploadBuilder extends ComponentBuilder<APIFileUploadComponent> {
/**
* Creates a new file upload.
*
* @param data - The API data to create this file upload with
* @example
* Creating a file upload from an API data object:
* ```ts
* const fileUpload = new FileUploadBuilder({
* custom_id: "file_upload",
* min_values: 2,
* max_values: 5,
* });
* ```
* @example
* Creating a file upload using setters and API data:
* ```ts
* const fileUpload = new FileUploadBuilder({
* custom_id: "file_upload",
* min_values: 2,
* max_values: 5,
* }).setRequired();
* ```
*/
public constructor(data: Partial<APIFileUploadComponent> = {}) {
super({ type: ComponentType.FileUpload, ...data });
}
/**
* Sets the custom id for this file upload.
*
* @param customId - The custom id to use
*/
public setCustomId(customId: string) {
this.data.custom_id = customId;
return this;
}
/**
* Sets the minimum number of file uploads required.
*
* @param minValues - The minimum values that must be uploaded
*/
public setMinValues(minValues: number) {
this.data.min_values = minValues;
return this;
}
/**
* Clears the minimum values.
*/
public clearMinValues() {
this.data.min_values = undefined;
return this;
}
/**
* Sets the maximum number of file uploads required.
*
* @param maxValues - The maximum values that can be uploaded
*/
public setMaxValues(maxValues: number) {
this.data.max_values = maxValues;
return this;
}
/**
* Clears the maximum values.
*/
public clearMaxValues() {
this.data.max_values = undefined;
return this;
}
/**
* Sets whether this file upload is required.
*
* @param required - Whether this file upload is required
*/
public setRequired(required = true) {
this.data.required = required;
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIFileUploadComponent {
fileUploadPredicate.parse(this.data);
return this.data as APIFileUploadComponent;
}
}

View File

@@ -0,0 +1,31 @@
import { s } from '@sapphire/shapeshift';
import { ComponentType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { idValidator } from '../Assertions.js';
import { fileUploadPredicate } from '../fileUpload/Assertions.js';
import {
selectMenuChannelPredicate,
selectMenuMentionablePredicate,
selectMenuRolePredicate,
selectMenuStringPredicate,
selectMenuUserPredicate,
} from '../selectMenu/Assertions.js';
import { textInputPredicate } from '../textInput/Assertions.js';
export const labelPredicate = s
.object({
id: idValidator.optional(),
type: s.literal(ComponentType.Label),
label: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(45),
description: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100).optional(),
component: s.union([
textInputPredicate,
selectMenuUserPredicate,
selectMenuRolePredicate,
selectMenuMentionablePredicate,
selectMenuChannelPredicate,
selectMenuStringPredicate,
fileUploadPredicate,
]),
})
.setValidationEnabled(isValidationEnabled);

View File

@@ -0,0 +1,213 @@
import type {
APIChannelSelectComponent,
APIFileUploadComponent,
APILabelComponent,
APIMentionableSelectComponent,
APIRoleSelectComponent,
APIStringSelectComponent,
APITextInputComponent,
APIUserSelectComponent,
} from 'discord-api-types/v10';
import { ComponentType } from 'discord-api-types/v10';
import { ComponentBuilder } from '../Component.js';
import { createComponentBuilder, resolveBuilder } from '../Components.js';
import { FileUploadBuilder } from '../fileUpload/FileUpload.js';
import { ChannelSelectMenuBuilder } from '../selectMenu/ChannelSelectMenu.js';
import { MentionableSelectMenuBuilder } from '../selectMenu/MentionableSelectMenu.js';
import { RoleSelectMenuBuilder } from '../selectMenu/RoleSelectMenu.js';
import { StringSelectMenuBuilder } from '../selectMenu/StringSelectMenu.js';
import { UserSelectMenuBuilder } from '../selectMenu/UserSelectMenu.js';
import { TextInputBuilder } from '../textInput/TextInput.js';
import { labelPredicate } from './Assertions.js';
export interface LabelBuilderData extends Partial<Omit<APILabelComponent, 'component'>> {
component?:
| ChannelSelectMenuBuilder
| FileUploadBuilder
| MentionableSelectMenuBuilder
| RoleSelectMenuBuilder
| StringSelectMenuBuilder
| TextInputBuilder
| UserSelectMenuBuilder;
}
/**
* A builder that creates API-compatible JSON data for labels.
*/
export class LabelBuilder extends ComponentBuilder<LabelBuilderData> {
/**
* @internal
*/
public override readonly data: LabelBuilderData;
/**
* Creates a new label.
*
* @param data - The API data to create this label with
* @example
* Creating a label from an API data object:
* ```ts
* const label = new LabelBuilder({
* label: "label",
* component,
* });
* ```
* @example
* Creating a label using setters and API data:
* ```ts
* const label = new LabelBuilder({
* label: 'label',
* component,
* }).setLabel('new text');
* ```
*/
public constructor(data: Partial<APILabelComponent> = {}) {
super({ type: ComponentType.Label });
const { component, ...rest } = data;
this.data = {
...rest,
component: component ? createComponentBuilder(component) : undefined,
type: ComponentType.Label,
};
}
/**
* Sets the label for this label.
*
* @param label - The label to use
*/
public setLabel(label: string) {
this.data.label = label;
return this;
}
/**
* Sets the description for this label.
*
* @param description - The description to use
*/
public setDescription(description: string) {
this.data.description = description;
return this;
}
/**
* Clears the description for this label.
*/
public clearDescription() {
this.data.description = undefined;
return this;
}
/**
* Sets a string select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setStringSelectMenuComponent(
input:
| APIStringSelectComponent
| StringSelectMenuBuilder
| ((builder: StringSelectMenuBuilder) => StringSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, StringSelectMenuBuilder);
return this;
}
/**
* Sets a user select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setUserSelectMenuComponent(
input: APIUserSelectComponent | UserSelectMenuBuilder | ((builder: UserSelectMenuBuilder) => UserSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, UserSelectMenuBuilder);
return this;
}
/**
* Sets a role select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setRoleSelectMenuComponent(
input: APIRoleSelectComponent | RoleSelectMenuBuilder | ((builder: RoleSelectMenuBuilder) => RoleSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, RoleSelectMenuBuilder);
return this;
}
/**
* Sets a mentionable select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setMentionableSelectMenuComponent(
input:
| APIMentionableSelectComponent
| MentionableSelectMenuBuilder
| ((builder: MentionableSelectMenuBuilder) => MentionableSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, MentionableSelectMenuBuilder);
return this;
}
/**
* Sets a channel select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setChannelSelectMenuComponent(
input:
| APIChannelSelectComponent
| ChannelSelectMenuBuilder
| ((builder: ChannelSelectMenuBuilder) => ChannelSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, ChannelSelectMenuBuilder);
return this;
}
/**
* Sets a text input component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setTextInputComponent(
input: APITextInputComponent | TextInputBuilder | ((builder: TextInputBuilder) => TextInputBuilder),
): this {
this.data.component = resolveBuilder(input, TextInputBuilder);
return this;
}
/**
* Sets a file upload component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setFileUploadComponent(
input: APIFileUploadComponent | FileUploadBuilder | ((builder: FileUploadBuilder) => FileUploadBuilder),
): this {
this.data.component = resolveBuilder(input, FileUploadBuilder);
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public override toJSON(): APILabelComponent {
const { component, ...rest } = this.data;
const data = {
...rest,
// The label predicate validates the component.
component: component?.toJSON(),
};
labelPredicate.parse(data);
return data as APILabelComponent;
}
}

View File

@@ -0,0 +1,92 @@
import { Result, s } from '@sapphire/shapeshift';
import { ChannelType, ComponentType, SelectMenuDefaultValueType } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { customIdValidator, emojiValidator, idValidator } from '../Assertions.js';
import { labelValidator } from '../textInput/Assertions.js';
const selectMenuBasePredicate = s.object({
id: idValidator.optional(),
placeholder: s.string().lengthLessThanOrEqual(150).optional(),
min_values: s.number().greaterThanOrEqual(0).lessThanOrEqual(25).optional(),
max_values: s.number().greaterThanOrEqual(0).lessThanOrEqual(25).optional(),
custom_id: customIdValidator,
disabled: s.boolean().optional(),
});
export const selectMenuChannelPredicate = selectMenuBasePredicate
.extend({
type: s.literal(ComponentType.ChannelSelect),
channel_types: s.nativeEnum(ChannelType).array().optional(),
default_values: s
.object({ id: s.string(), type: s.literal(SelectMenuDefaultValueType.Channel) })
.array()
.lengthLessThanOrEqual(25)
.optional(),
})
.setValidationEnabled(isValidationEnabled);
export const selectMenuMentionablePredicate = selectMenuBasePredicate
.extend({
type: s.literal(ComponentType.MentionableSelect),
default_values: s
.object({
id: s.string(),
type: s.union([s.literal(SelectMenuDefaultValueType.Role), s.literal(SelectMenuDefaultValueType.User)]),
})
.array()
.lengthLessThanOrEqual(25)
.optional(),
})
.setValidationEnabled(isValidationEnabled);
export const selectMenuRolePredicate = selectMenuBasePredicate
.extend({
type: s.literal(ComponentType.RoleSelect),
default_values: s
.object({ id: s.string(), type: s.literal(SelectMenuDefaultValueType.Role) })
.array()
.lengthLessThanOrEqual(25)
.optional(),
})
.setValidationEnabled(isValidationEnabled);
export const selectMenuUserPredicate = selectMenuBasePredicate
.extend({
type: s.literal(ComponentType.UserSelect),
default_values: s
.object({ id: s.string(), type: s.literal(SelectMenuDefaultValueType.User) })
.array()
.lengthLessThanOrEqual(25)
.optional(),
})
.setValidationEnabled(isValidationEnabled);
export const selectMenuStringOptionPredicate = s
.object({
label: labelValidator,
value: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100),
description: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100).optional(),
emoji: emojiValidator.optional(),
default: s.boolean().optional(),
})
.setValidationEnabled(isValidationEnabled);
export const selectMenuStringPredicate = selectMenuBasePredicate
.extend({
type: s.literal(ComponentType.StringSelect),
options: selectMenuStringOptionPredicate.array().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(25),
})
.reshape((value) => {
if (value.min_values !== undefined && value.options.length < value.min_values) {
return Result.err(new RangeError(`The number of options must be greater than or equal to min_values`));
}
if (value.min_values !== undefined && value.max_values !== undefined && value.min_values > value.max_values) {
return Result.err(
new RangeError(`The maximum amount of options must be greater than or equal to the minimum amount of options`),
);
}
return Result.ok(value);
})
.setValidationEnabled(isValidationEnabled);

View File

@@ -1,6 +1,7 @@
import type { APISelectMenuComponent } from 'discord-api-types/v10';
import { customIdValidator, disabledValidator, minMaxValidator, placeholderValidator } from '../Assertions.js';
import { ComponentBuilder } from '../Component.js';
import { requiredValidator } from '../textInput/Assertions.js';
/**
* The base select menu builder that contains common symbols for select menu builders.
@@ -31,9 +32,9 @@ export abstract class BaseSelectMenuBuilder<
}
/**
* Sets the maximum values that must be selected in the select menu.
* Sets the maximum values that can be selected in the select menu.
*
* @param maxValues - The maximum values that must be selected
* @param maxValues - The maximum values that can be selected
*/
public setMaxValues(maxValues: number) {
this.data.max_values = minMaxValidator.parse(maxValues);
@@ -60,6 +61,17 @@ export abstract class BaseSelectMenuBuilder<
return this;
}
/**
* Sets whether this select menu is required.
*
* @remarks Only for use in modals.
* @param required - Whether this select menu is required
*/
public setRequired(required = true) {
this.data.required = requiredValidator.parse(required);
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/

View File

@@ -1,9 +1,9 @@
import { s } from '@sapphire/shapeshift';
import { TextInputStyle } from 'discord-api-types/v10';
import { ComponentType, TextInputStyle } from 'discord-api-types/v10';
import { isValidationEnabled } from '../../util/validation.js';
import { customIdValidator } from '../Assertions.js';
import { customIdValidator, idValidator } from '../Assertions.js';
export const textInputStyleValidator = s.nativeEnum(TextInputStyle);
export const textInputStyleValidator = s.nativeEnum(TextInputStyle).setValidationEnabled(isValidationEnabled);
export const minLengthValidator = s
.number()
.int()
@@ -16,7 +16,7 @@ export const maxLengthValidator = s
.greaterThanOrEqual(1)
.lessThanOrEqual(4_000)
.setValidationEnabled(isValidationEnabled);
export const requiredValidator = s.boolean();
export const requiredValidator = s.boolean().setValidationEnabled(isValidationEnabled);
export const valueValidator = s.string().lengthLessThanOrEqual(4_000).setValidationEnabled(isValidationEnabled);
export const placeholderValidator = s.string().lengthLessThanOrEqual(100).setValidationEnabled(isValidationEnabled);
export const labelValidator = s
@@ -25,8 +25,21 @@ export const labelValidator = s
.lengthLessThanOrEqual(45)
.setValidationEnabled(isValidationEnabled);
export function validateRequiredParameters(customId?: string, style?: TextInputStyle, label?: string) {
export const textInputPredicate = s
.object({
type: s.literal(ComponentType.TextInput),
custom_id: customIdValidator,
style: textInputStyleValidator,
id: idValidator.optional(),
min_length: minLengthValidator.optional(),
max_length: maxLengthValidator.optional(),
placeholder: placeholderValidator.optional(),
value: valueValidator.optional(),
required: requiredValidator.optional(),
})
.setValidationEnabled(isValidationEnabled);
export function validateRequiredParameters(customId?: string, style?: TextInputStyle) {
customIdValidator.parse(customId);
textInputStyleValidator.parse(style);
labelValidator.parse(label);
}

View File

@@ -30,7 +30,7 @@ export class TextInputBuilder
* ```ts
* const textInput = new TextInputBuilder({
* custom_id: 'a cool text input',
* label: 'Type something',
* placeholder: 'Type something',
* style: TextInputStyle.Short,
* });
* ```
@@ -38,7 +38,7 @@ export class TextInputBuilder
* Creating a text input using setters and API data:
* ```ts
* const textInput = new TextInputBuilder({
* label: 'Type something else',
* placeholder: 'Type something else',
* })
* .setCustomId('woah')
* .setStyle(TextInputStyle.Paragraph);
@@ -62,6 +62,7 @@ export class TextInputBuilder
* Sets the label for this text input.
*
* @param label - The label to use
* @deprecated Use a label builder to create a label (and optionally a description) instead.
*/
public setLabel(label: string) {
this.data.label = labelValidator.parse(label);
@@ -132,7 +133,7 @@ export class TextInputBuilder
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APITextInputComponent {
validateRequiredParameters(this.data.custom_id, this.data.style, this.data.label);
validateRequiredParameters(this.data.custom_id, this.data.style);
return {
...this.data,

View File

@@ -57,7 +57,7 @@ export function assertReturnOfBuilder<ReturnType extends MediaGalleryItemBuilder
input: unknown,
ExpectedInstanceOf: new () => ReturnType,
): asserts input is ReturnType {
s.instance(ExpectedInstanceOf).parse(input);
s.instance(ExpectedInstanceOf).setValidationEnabled(isValidationEnabled).parse(input);
}
export function validateComponentArray<
@@ -67,5 +67,6 @@ export function validateComponentArray<
.array()
.lengthGreaterThanOrEqual(min)
.lengthLessThanOrEqual(max)
.setValidationEnabled(isValidationEnabled)
.parse(input);
}

View File

@@ -19,7 +19,7 @@ import type { AnyComponentBuilder, MessageActionRowComponentBuilder } from '../A
import { ActionRowBuilder } from '../ActionRow.js';
import { ComponentBuilder } from '../Component.js';
import { createComponentBuilder, resolveBuilder } from '../Components.js';
import { containerColorPredicate, spoilerPredicate, validateComponentArray } from './Assertions.js';
import { containerColorPredicate, spoilerPredicate } from './Assertions.js';
import { FileBuilder } from './File.js';
import { SeparatorBuilder } from './Separator.js';
import { TextDisplayBuilder } from './TextDisplay.js';
@@ -231,7 +231,6 @@ export class ContainerBuilder extends ComponentBuilder<APIContainerComponent> {
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIContainerComponent {
validateComponentArray(this.components, 1, 10);
return {
...this.data,
components: this.components.map((component) => component.toJSON()),

View File

@@ -11,7 +11,7 @@ export class ThumbnailBuilder extends ComponentBuilder<APIThumbnailComponent> {
* @example
* Creating a thumbnail from an API data object:
* ```ts
* const thumbnaik = new ThumbnailBuilder({
* const thumbnail = new ThumbnailBuilder({
* description: 'some text',
* media: {
* url: 'https://cdn.discordapp.com/embed/avatars/4.png',

View File

@@ -34,6 +34,12 @@ export {
export * from './components/selectMenu/StringSelectMenuOption.js';
export * from './components/selectMenu/UserSelectMenu.js';
export * from './components/fileUpload/FileUpload.js';
export * as FileUploadAssertions from './components/fileUpload/Assertions.js';
export * from './components/label/Label.js';
export * as LabelAssertions from './components/label/Assertions.js';
export * as ComponentsV2Assertions from './components/v2/Assertions.js';
export * from './components/v2/Container.js';
export * from './components/v2/File.js';

View File

@@ -7,8 +7,7 @@ const namePredicate = s
.string()
.lengthGreaterThanOrEqual(1)
.lengthLessThanOrEqual(32)
// eslint-disable-next-line prefer-named-capture-group
.regex(/^( *[\p{P}\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}]+ *)+$/u)
.regex(/\S/)
.setValidationEnabled(isValidationEnabled);
const typePredicate = s
.union([s.literal(ApplicationCommandType.User), s.literal(ApplicationCommandType.Message)])

View File

@@ -1,6 +1,8 @@
import { s } from '@sapphire/shapeshift';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { LabelBuilder } from '../../components/label/Label.js';
import { TextDisplayBuilder } from '../../components/v2/TextDisplay.js';
import { isValidationEnabled } from '../../util/validation.js';
export const titleValidator = s
@@ -9,7 +11,7 @@ export const titleValidator = s
.lengthLessThanOrEqual(45)
.setValidationEnabled(isValidationEnabled);
export const componentsValidator = s
.instance(ActionRowBuilder)
.union([s.instance(ActionRowBuilder), s.instance(LabelBuilder), s.instance(TextDisplayBuilder)])
.array()
.lengthGreaterThanOrEqual(1)
.setValidationEnabled(isValidationEnabled);
@@ -17,7 +19,7 @@ export const componentsValidator = s
export function validateRequiredParameters(
customId?: string,
title?: string,
components?: ActionRowBuilder<ModalActionRowComponentBuilder>[],
components?: (ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder | TextDisplayBuilder)[],
) {
customIdValidator.parse(customId);
titleValidator.parse(title);

View File

@@ -2,13 +2,20 @@
import type { JSONEncodable } from '@discordjs/util';
import type {
APITextInputComponent,
APIActionRowComponent,
APIComponentInModalActionRow,
APILabelComponent,
APIModalInteractionResponseCallbackData,
APITextDisplayComponent,
} from 'discord-api-types/v10';
import { ComponentType } from 'discord-api-types/v10';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { createComponentBuilder } from '../../components/Components.js';
import { createComponentBuilder, resolveBuilder } from '../../components/Components.js';
import { LabelBuilder } from '../../components/label/Label.js';
import { TextInputBuilder } from '../../components/textInput/TextInput.js';
import { TextDisplayBuilder } from '../../components/v2/TextDisplay.js';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { titleValidator, validateRequiredParameters } from './Assertions.js';
@@ -24,7 +31,8 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
/**
* The components within this modal.
*/
public readonly components: ActionRowBuilder<ModalActionRowComponentBuilder>[] = [];
public readonly components: (ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder | TextDisplayBuilder)[] =
[];
/**
* Creates a new modal from API data.
@@ -33,8 +41,10 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
*/
public constructor({ components, ...data }: Partial<APIModalInteractionResponseCallbackData> = {}) {
this.data = { ...data };
this.components = (components?.map((component) => createComponentBuilder(component)) ??
[]) as ActionRowBuilder<ModalActionRowComponentBuilder>[];
this.components = (components?.map((component) => createComponentBuilder(component)) ?? []) as (
| ActionRowBuilder<ModalActionRowComponentBuilder>
| LabelBuilder
)[];
}
/**
@@ -61,28 +71,182 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
* Adds components to this modal.
*
* @param components - The components to add
* @deprecated Use {@link ModalBuilder.addLabelComponents} or {@link ModalBuilder.addTextDisplayComponents} instead
*/
public addComponents(
...components: RestOrArray<
ActionRowBuilder<ModalActionRowComponentBuilder> | APIActionRowComponent<APIComponentInModalActionRow>
| ActionRowBuilder<ModalActionRowComponentBuilder>
| APIActionRowComponent<APIComponentInModalActionRow>
| APILabelComponent
| APITextDisplayComponent
| APITextInputComponent
| LabelBuilder
| TextDisplayBuilder
| TextInputBuilder
>
) {
this.components.push(
...normalizeArray(components).map((component) =>
component instanceof ActionRowBuilder
? component
: new ActionRowBuilder<ModalActionRowComponentBuilder>(component),
),
...normalizeArray(components).map((component, idx) => {
if (
component instanceof ActionRowBuilder ||
component instanceof LabelBuilder ||
component instanceof TextDisplayBuilder
) {
return component;
}
// Deprecated support
if (component instanceof TextInputBuilder) {
return new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(component);
}
if ('type' in component) {
if (component.type === ComponentType.ActionRow) {
return new ActionRowBuilder<ModalActionRowComponentBuilder>(component);
}
if (component.type === ComponentType.Label) {
return new LabelBuilder(component);
}
if (component.type === ComponentType.TextDisplay) {
return new TextDisplayBuilder(component);
}
// Deprecated, should go in a label component
if (component.type === ComponentType.TextInput) {
return new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
new TextInputBuilder(component),
);
}
}
throw new TypeError(`Invalid component passed in ModalBuilder.addComponents at index ${idx}!`);
}),
);
return this;
}
/**
* Adds label components to this modal.
*
* @param components - The components to add
*/
public addLabelComponents(
...components: RestOrArray<APILabelComponent | LabelBuilder | ((builder: LabelBuilder) => LabelBuilder)>
) {
const normalized = normalizeArray(components);
const resolved = normalized.map((label) => resolveBuilder(label, LabelBuilder));
this.components.push(...resolved);
return this;
}
/**
* Adds text display components to this modal.
*
* @param components - The components to add
*/
public addTextDisplayComponents(
...components: RestOrArray<
APITextDisplayComponent | TextDisplayBuilder | ((builder: TextDisplayBuilder) => TextDisplayBuilder)
>
) {
const normalized = normalizeArray(components);
const resolved = normalized.map((row) => resolveBuilder(row, TextDisplayBuilder));
this.components.push(...resolved);
return this;
}
/**
* Adds action rows to this modal.
*
* @param components - The components to add
* @deprecated Use {@link ModalBuilder.addLabelComponents} instead
*/
public addActionRowComponents(
...components: RestOrArray<
| ActionRowBuilder<ModalActionRowComponentBuilder>
| APIActionRowComponent<APIComponentInModalActionRow>
| ((
builder: ActionRowBuilder<ModalActionRowComponentBuilder>,
) => ActionRowBuilder<ModalActionRowComponentBuilder>)
>
) {
const normalized = normalizeArray(components);
const resolved = normalized.map((row) => resolveBuilder(row, ActionRowBuilder<ModalActionRowComponentBuilder>));
this.components.push(...resolved);
return this;
}
/**
* Sets the labels for this modal.
*
* @param components - The components to set
*/
public setLabelComponents(
...components: RestOrArray<APILabelComponent | LabelBuilder | ((builder: LabelBuilder) => LabelBuilder)>
) {
const normalized = normalizeArray(components);
this.spliceLabelComponents(0, this.components.length, ...normalized);
return this;
}
/**
* Removes, replaces, or inserts labels for this modal.
*
* @remarks
* This method behaves similarly
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
* The maximum amount of labels that can be added is 5.
*
* It's useful for modifying and adjusting order of the already-existing labels of a modal.
* @example
* Remove the first label:
* ```ts
* modal.spliceLabelComponents(0, 1);
* ```
* @example
* Remove the first n labels:
* ```ts
* const n = 4;
* modal.spliceLabelComponents(0, n);
* ```
* @example
* Remove the last label:
* ```ts
* modal.spliceLabelComponents(-1, 1);
* ```
* @param index - The index to start at
* @param deleteCount - The number of labels to remove
* @param labels - The replacing label objects
*/
public spliceLabelComponents(
index: number,
deleteCount: number,
...labels: (APILabelComponent | LabelBuilder | ((builder: LabelBuilder) => LabelBuilder))[]
): this {
const resolved = labels.map((label) => resolveBuilder(label, LabelBuilder));
this.components.splice(index, deleteCount, ...resolved);
return this;
}
/**
* Sets components for this modal.
*
* @param components - The components to set
* @deprecated Use {@link ModalBuilder.setLabelComponents} instead
*/
public setComponents(...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder>>) {
public setComponents(
...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder | TextDisplayBuilder>
) {
this.components.splice(0, this.components.length, ...normalizeArray(components));
return this;
}

View File

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

View File

@@ -9,7 +9,8 @@
<a href="https://www.npmjs.com/package/@discordjs/collection"><img src="https://img.shields.io/npm/v/@discordjs/collection.svg?maxAge=3600" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/@discordjs/collection"><img src="https://img.shields.io/npm/dt/@discordjs/collection.svg?maxAge=3600" alt="npm downloads" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=collection" alt="Code coverage" /></a>
<a href="https://github.com/discordjs/discord.js/commits/main/packages/collection"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fcollection"></a>
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=collection" alt="Code coverage" /></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>

View File

@@ -30,8 +30,10 @@ body = """
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
{% if commit.breaking %}\
{% for breakingChange in commit.footers %}\
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
{% for footer in commit.footers %}\
{% if footer.breaking %}\
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
{% endif %}\
{% endfor %}\
{% endif %}\
{% endfor %}

View File

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

View File

@@ -9,7 +9,8 @@
<a href="https://www.npmjs.com/package/@discordjs/core"><img src="https://img.shields.io/npm/v/@discordjs/core.svg?maxAge=3600" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/@discordjs/core"><img src="https://img.shields.io/npm/dt/@discordjs/core.svg?maxAge=3600" alt="npm downloads" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=core" alt="Code coverage" /></a>
<a href="https://github.com/discordjs/discord.js/commits/main/packages/core"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fcore"></a>
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=core" alt="Code coverage" /></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
@@ -23,7 +24,7 @@
## Installation
**Node.js 18 or newer is required.**
**Node.js 20 or newer is required.**
```sh
npm install @discordjs/core

View File

@@ -30,8 +30,10 @@ body = """
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
{% if commit.breaking %}\
{% for breakingChange in commit.footers %}\
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
{% for footer in commit.footers %}\
{% if footer.breaking %}\
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
{% endif %}\
{% endfor %}\
{% endif %}\
{% endfor %}

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@discordjs/core",
"version": "2.0.0",
"version": "2.3.0",
"description": "A thinly abstracted wrapper around the rest API, and gateway.",
"scripts": {
"test": "vitest run",
@@ -70,7 +70,7 @@
"@discordjs/ws": "workspace:^",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.37.119"
"discord-api-types": "^0.38.32"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
@@ -90,7 +90,7 @@
"vitest": "^2.0.5"
},
"engines": {
"node": ">=18"
"node": ">=20"
},
"publishConfig": {
"access": "public",

View File

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

View File

@@ -1,10 +1,9 @@
/* eslint-disable jsdoc/check-param-names */
import { makeURLSearchParams, type RawFile, type REST, type RequestData } from '@discordjs/rest';
import { makeURLSearchParams, type RawFile, type RequestData, type REST } from '@discordjs/rest';
import {
Routes,
type RESTPostAPIChannelWebhookJSONBody,
type RESTPostAPIChannelWebhookResult,
type APIThreadChannel,
type RESTDeleteAPIChannelResult,
type RESTGetAPIChannelInvitesResult,
type RESTGetAPIChannelMessageReactionUsersQuery,
@@ -17,8 +16,8 @@ import {
type RESTGetAPIChannelThreadsArchivedQuery,
type RESTGetAPIChannelUsersThreadsArchivedResult,
type RESTGetAPIChannelWebhooksResult,
type RESTPatchAPIChannelMessageJSONBody,
type RESTPatchAPIChannelJSONBody,
type RESTPatchAPIChannelMessageJSONBody,
type RESTPatchAPIChannelMessageResult,
type RESTPatchAPIChannelResult,
type RESTPostAPIChannelFollowersResult,
@@ -27,12 +26,14 @@ import {
type RESTPostAPIChannelMessageCrosspostResult,
type RESTPostAPIChannelMessageJSONBody,
type RESTPostAPIChannelMessageResult,
type RESTPutAPIChannelPermissionJSONBody,
type Snowflake,
type RESTPostAPIChannelThreadsJSONBody,
type RESTPostAPIChannelThreadsResult,
type APIThreadChannel,
type RESTPostAPIChannelWebhookJSONBody,
type RESTPostAPIChannelWebhookResult,
type RESTPostAPIGuildForumThreadsJSONBody,
type RESTPutAPIChannelPermissionJSONBody,
type RESTPutAPIChannelRecipientJSONBody,
type Snowflake,
} from 'discord-api-types/v10';
export interface StartForumThreadOptions extends RESTPostAPIGuildForumThreadsJSONBody {
@@ -45,7 +46,7 @@ export class ChannelsAPI {
/**
* Sends a message in a channel
*
* @see {@link https://discord.com/developers/docs/resources/channel#create-message}
* @see {@link https://discord.com/developers/docs/resources/message#create-message}
* @param channelId - The id of the channel to send the message in
* @param body - The data for sending the message
* @param options - The options for sending the message
@@ -65,7 +66,7 @@ export class ChannelsAPI {
/**
* Edits a message
*
* @see {@link https://discord.com/developers/docs/resources/channel#edit-message}
* @see {@link https://discord.com/developers/docs/resources/message#edit-message}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to edit
* @param body - The data for editing the message
@@ -87,7 +88,7 @@ export class ChannelsAPI {
/**
* Fetches the reactions for a message
*
* @see {@link https://discord.com/developers/docs/resources/channel#get-reactions}
* @see {@link https://discord.com/developers/docs/resources/message#get-reactions}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to get the reactions for
* @param emoji - The emoji to get the reactions for
@@ -110,7 +111,7 @@ export class ChannelsAPI {
/**
* Deletes a reaction for the current user
*
* @see {@link https://discord.com/developers/docs/resources/channel#delete-own-reaction}
* @see {@link https://discord.com/developers/docs/resources/message#delete-own-reaction}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to delete the reaction for
* @param emoji - The emoji to delete the reaction for
@@ -130,7 +131,7 @@ export class ChannelsAPI {
/**
* Deletes a reaction for a user
*
* @see {@link https://discord.com/developers/docs/resources/channel#delete-user-reaction}
* @see {@link https://discord.com/developers/docs/resources/message#delete-user-reaction}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to delete the reaction for
* @param emoji - The emoji to delete the reaction for
@@ -152,7 +153,7 @@ export class ChannelsAPI {
/**
* Deletes all reactions for a message
*
* @see {@link https://discord.com/developers/docs/resources/channel#delete-all-reactions}
* @see {@link https://discord.com/developers/docs/resources/message#delete-all-reactions}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to delete the reactions for
* @param options - The options for deleting the reactions
@@ -168,7 +169,7 @@ export class ChannelsAPI {
/**
* Deletes all reactions of an emoji for a message
*
* @see {@link https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji}
* @see {@link https://discord.com/developers/docs/resources/message#delete-all-reactions-for-emoji}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to delete the reactions for
* @param emoji - The emoji to delete the reactions for
@@ -186,7 +187,7 @@ export class ChannelsAPI {
/**
* Adds a reaction to a message
*
* @see {@link https://discord.com/developers/docs/resources/channel#create-reaction}
* @see {@link https://discord.com/developers/docs/resources/message#create-reaction}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to add the reaction to
* @param emoji - The emoji to add the reaction with
@@ -223,9 +224,13 @@ export class ChannelsAPI {
public async edit(
channelId: Snowflake,
body: RESTPatchAPIChannelJSONBody,
{ signal }: Pick<RequestData, 'signal'> = {},
{ signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {},
) {
return this.rest.patch(Routes.channel(channelId), { body, signal }) as Promise<RESTPatchAPIChannelResult>;
return this.rest.patch(Routes.channel(channelId), {
reason,
body,
signal,
}) as Promise<RESTPatchAPIChannelResult>;
}
/**
@@ -235,14 +240,14 @@ export class ChannelsAPI {
* @param channelId - The id of the channel to delete
* @param options - The options for deleting the channel
*/
public async delete(channelId: Snowflake, { signal }: Pick<RequestData, 'signal'> = {}) {
return this.rest.delete(Routes.channel(channelId), { signal }) as Promise<RESTDeleteAPIChannelResult>;
public async delete(channelId: Snowflake, { signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {}) {
return this.rest.delete(Routes.channel(channelId), { signal, reason }) as Promise<RESTDeleteAPIChannelResult>;
}
/**
* Fetches the messages of a channel
*
* @see {@link https://discord.com/developers/docs/resources/channel#get-channel-messages}
* @see {@link https://discord.com/developers/docs/resources/message#get-channel-messages}
* @param channelId - The id of the channel to fetch messages from
* @param query - The query options for fetching messages
* @param options - The options for fetching the messages
@@ -299,7 +304,7 @@ export class ChannelsAPI {
/**
* Deletes a message
*
* @see {@link https://discord.com/developers/docs/resources/channel#delete-message}
* @see {@link https://discord.com/developers/docs/resources/message#delete-message}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to delete
* @param options - The options for deleting the message
@@ -315,7 +320,7 @@ export class ChannelsAPI {
/**
* Bulk deletes messages
*
* @see {@link https://discord.com/developers/docs/resources/channel#bulk-delete-messages}
* @see {@link https://discord.com/developers/docs/resources/message#bulk-delete-messages}
* @param channelId - The id of the channel the messages are in
* @param messageIds - The ids of the messages to delete
* @param options - The options for deleting the messages
@@ -331,7 +336,7 @@ export class ChannelsAPI {
/**
* Fetches a message
*
* @see {@link https://discord.com/developers/docs/resources/channel#get-channel-message}
* @see {@link https://discord.com/developers/docs/resources/message#get-channel-message}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to fetch
* @param options - The options for fetching the message
@@ -345,7 +350,7 @@ export class ChannelsAPI {
/**
* Crossposts a message
*
* @see {@link https://discord.com/developers/docs/resources/channel#crosspost-message}
* @see {@link https://discord.com/developers/docs/resources/message#crosspost-message}
* @param channelId - The id of the channel the message is in
* @param messageId - The id of the message to crosspost
* @param options - The options for crossposting the message
@@ -441,18 +446,19 @@ export class ChannelsAPI {
channelId: Snowflake,
body: RESTPostAPIChannelThreadsJSONBody,
messageId?: Snowflake,
{ signal }: Pick<RequestData, 'signal'> = {},
{ signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {},
) {
return this.rest.post(Routes.threads(channelId, messageId), {
body,
signal,
reason,
}) as Promise<RESTPostAPIChannelThreadsResult>;
}
/**
* Creates a new forum post
*
* @see {@link https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel}
* @see {@link https://discord.com/developers/docs/resources/channel#start-thread-in-forum-or-media-channel}
* @param channelId - The id of the forum channel to start the thread in
* @param body - The data for starting the thread
* @param options - The options for starting the thread
@@ -460,7 +466,7 @@ export class ChannelsAPI {
public async createForumThread(
channelId: Snowflake,
{ message, ...optionsBody }: StartForumThreadOptions,
{ signal }: Pick<RequestData, 'signal'> = {},
{ signal, reason }: Pick<RequestData, 'reason' | 'signal'> = {},
) {
const { files, ...messageBody } = message;
@@ -469,7 +475,12 @@ export class ChannelsAPI {
message: messageBody,
};
return this.rest.post(Routes.threads(channelId), { files, body, signal }) as Promise<APIThreadChannel>;
return this.rest.post(Routes.threads(channelId), {
files,
body,
reason,
signal,
}) as Promise<APIThreadChannel>;
}
/**
@@ -583,4 +594,43 @@ export class ChannelsAPI {
signal,
});
}
/**
* Adds a recipient to a group DM channel
*
* @see {@link https://discord.com/developers/docs/resources/channel#group-dm-add-recipient}
* @param channelId - The id of the channel to add the recipient to
* @param userId - The id of the user to add as a recipient
* @param body - The data for adding the recipient
* @param options - The options for adding the recipient
*/
public async addGroupDMRecipient(
channelId: Snowflake,
userId: Snowflake,
body: RESTPutAPIChannelRecipientJSONBody,
{ signal }: Pick<RequestData, 'signal'> = {},
) {
await this.rest.put(Routes.channelRecipient(channelId, userId), {
body,
signal,
});
}
/**
* Removes a recipient from a group DM channel
*
* @see {@link https://discord.com/developers/docs/resources/channel#group-dm-remove-recipient}
* @param channelId - The id of the channel to remove the recipient from
* @param userId - The id of the user to remove as a recipient
* @param options - The options for removing the recipient
*/
public async removeGroupDMRecipient(
channelId: Snowflake,
userId: Snowflake,
{ signal }: Pick<RequestData, 'signal'> = {},
) {
await this.rest.delete(Routes.channelRecipient(channelId, userId), {
signal,
});
}
}

View File

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

View File

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

View File

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

View File

@@ -258,7 +258,7 @@ export class InteractionsAPI {
* @param interactionId - The id of the interaction
* @param interactionToken - The token of the interaction
* @param options - The options for sending the premium required response
* @deprecated Sending a premium-style button is the new Discord behaviour.
* @deprecated Sending a premium-style button is the new Discord behavior.
*/
public async sendPremiumRequired(
interactionId: Snowflake,

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,7 @@
<p>
<a href="https://discord.gg/djs"><img src="https://img.shields.io/discord/222078108977594368?color=5865F2&logo=discord&logoColor=white" alt="Discord server" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
<a href="https://github.com/discordjs/discord.js/commits/main/packages/create-discord-bot"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fcreate-discord-bot"></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>

View File

@@ -30,8 +30,10 @@ body = """
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
{% if commit.breaking %}\
{% for breakingChange in commit.footers %}\
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
{% for footer in commit.footers %}\
{% if footer.breaking %}\
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
{% endif %}\
{% endfor %}\
{% endif %}\
{% endfor %}

View File

@@ -83,7 +83,7 @@
"no-void": "error",
"no-warning-comments": "warn",
"prefer-promise-reject-errors": "error",
"require-await": "warn",
"require-await": "off",
"wrap-iife": "error",
"yoda": "error",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://json.schemastore.org/lintstagedrc.schema.json",
"*": "prettier --ignore-unknown --write",
"{src/**,test/**,typings/**,scripts/**}.{mjs,js,ts}": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint --ext mjs,js,ts --fix"
"{src/**,typings/**,scripts/**}.{mjs,js,ts}": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint --ext mjs,js,ts --fix"
}

View File

@@ -2,6 +2,350 @@
All notable changes to this project will be documented in this file.
# [14.24.2](https://github.com/discordjs/discord.js/compare/14.24.1...14.24.2) - (2025-10-30)
## Bug Fixes
- **GuildMember:** JoinedAt possibly being NaN ([fb2b728](https://github.com/discordjs/discord.js/commit/fb2b7281e019de9dbd1eb307d9a2ed655c165187))
# [14.24.1](https://github.com/discordjs/discord.js/compare/14.24.0...14.24.1) - (2025-10-28)
## Bug Fixes
- **Message:** Check if in voice based channel for `pinnable` (#11215) ([c2c8cce](https://github.com/discordjs/discord.js/commit/c2c8cce1d77d7afb9da3b0c6a1ee5787e922ec3c))
## Documentation
- **GuildMemberFlagsBitField:** Remove duplicate word ([abb84ce](https://github.com/discordjs/discord.js/commit/abb84ce88f7b9586740855085bb5abc6f0a6282c))
## Typings
- **FileUploadModalData:** Correct fields (#11209) ([d317ca1](https://github.com/discordjs/discord.js/commit/d317ca1053734d6fed651e1e8600750e4d8d16d4))
- **LabelModalData:** Singular `ModalData` (#11207) ([072fbb2](https://github.com/discordjs/discord.js/commit/072fbb228a096e8cfb2a1f55c6170f68bc84345d))
- **FileUploadComponentData:** `boolean` ([548c254](https://github.com/discordjs/discord.js/commit/548c25488a832f8aa274e7834ac57ad9c3e23890))
# [14.24.0](https://github.com/discordjs/discord.js/compare/14.23.2...14.24.0) - (2025-10-24)
## Bug Fixes
- **Message:** Update `pinnable` to check for migrated guilds (#11189) ([ee988e3](https://github.com/discordjs/discord.js/commit/ee988e3e75d39e91a98a572e72a5981e0ef87dbc))
## Features
- Handle file upload component for v14 (#11179) ([104ad75](https://github.com/discordjs/discord.js/commit/104ad754f36933276f3acfd4164f7f19d50dfe2e))
# [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)
## Bug Fixes
- Regression in allowedMentions when replying (#10866) ([2ebb5cb](https://github.com/discordjs/discord.js/commit/2ebb5cbd53d869a52cba4549e7acc417963741cd))
# [14.19.2](https://github.com/discordjs/discord.js/compare/14.19.1...14.19.2) - (2025-04-28)
## Bug Fixes
- **WebSocketManager:** Always emit shardDisconnect on unresumable close (#10826) ([37ef57b](https://github.com/discordjs/discord.js/commit/37ef57b88079db2f87036dfd9d57af9a2e5d1b9f))
- **SoundboardSound:** Wrong emoji comparison in `equals` (#10861) ([5f3fc17](https://github.com/discordjs/discord.js/commit/5f3fc170fbfd49fa4f117901578c309f00f65d0b))
- AllowedMentions, container, media item `toJSON()` for components v2 (#10852) ([20fade2](https://github.com/discordjs/discord.js/commit/20fade2a875695aa677e32b983320e746fd4d69c))
- **Guild:** Cache soundboard sounds when patching (#10857) ([e827644](https://github.com/discordjs/discord.js/commit/e827644b5aecf05f7551e4e31a620894cc7aa71f))
- **GuildSoundboardSoundManager:** Value "undefined" is not snowflake (#10854) ([6281592](https://github.com/discordjs/discord.js/commit/62815928aba0baa96b3422a4a7f1c4321c123dc7))
## Typings
- **GuildSoundboardSoundEditOptions:** Add missing `reason` (#10863) ([e3c247e](https://github.com/discordjs/discord.js/commit/e3c247e423c91b9d9e1d40d0521b6eddf0bc53c9))
# [14.19.1](https://github.com/discordjs/discord.js/compare/14.19.0...14.19.1) - (2025-04-26)
## Bug Fixes
- Add in `withComponents` to Webhook ([481ccd2](https://github.com/discordjs/discord.js/commit/481ccd228bc240e32ac552475f8427a8042e1add))
# [14.19.0](https://github.com/discordjs/discord.js/compare/14.18.0...14.19.0) - (2025-04-26)
## Bug Fixes
- Set `with_components` when sending components through webhooks ([8cdbe23](https://github.com/discordjs/discord.js/commit/8cdbe23766c6e5fe1e0acc040120e839511fea2c))
- **GuildAuditLogEntry:** Fix some incorrect types and runtime logic (#10849) ([d920933](https://github.com/discordjs/discord.js/commit/d920933dc5b3c518754f526a9864582fc2c92a43))
- Spread out section components next to accessory ([1605a2c](https://github.com/discordjs/discord.js/commit/1605a2c2894c4bb834c604f13a5a91cdbffac3a8))
- Correctly extend CachedManager in GuildSoundboardSoundManager ([532c384](https://github.com/discordjs/discord.js/commit/532c3842bc293c965dd9fee846257c9e0bbb450a))
- **MessagePayload:** Preserve existing flags when editing (#10766) ([ebfd526](https://github.com/discordjs/discord.js/commit/ebfd52695e205bccda3ae6f4ec39d4e5e8891ab0))
## Features
- Soundboard missing things (#10850) ([2d817df](https://github.com/discordjs/discord.js/commit/2d817df3b5894da84a1990cb4e0cfded8a925e75))
- Components v2 in v14 (#10781) ([edace17](https://github.com/discordjs/discord.js/commit/edace17a131f857547163a3acf4bb6fec0c1e415))
- Add soundboard in v14 (#10843) ([d3154cf](https://github.com/discordjs/discord.js/commit/d3154cf8f1eb027b5b4921d4048a32f464a3cd85))
## Typings
- Make `Client.on()` compatible with esnext.disposable in TS5.6+ (#10773) ([45552fa](https://github.com/discordjs/discord.js/commit/45552faf02c67b5079f34567c0214203cd927d2e))
# [14.18.0](https://github.com/discordjs/discord.js/compare/14.17.3...14.18.0) - (2025-02-10)
## Bug Fixes
- **Guild:** Type error with permissionOverwrites (#10527) ([8e1e1be](https://github.com/discordjs/discord.js/commit/8e1e1be0c23a0a063a6b530ac8cee30cf7629644))
- Incorrect relative path (#10734) ([b7f1ebc](https://github.com/discordjs/discord.js/commit/b7f1ebc334e110be3208c476b61b82a69386fd84))
- **PresenceUpdate:** Correctly add user regardless of their properties (#10672) ([7c1b73c](https://github.com/discordjs/discord.js/commit/7c1b73cc697fd3b85011bdb2c098ca3a3f863b1f))
- **InteractionResponses:** Mark replied true for followUps (#10688) ([32dff01](https://github.com/discordjs/discord.js/commit/32dff01f291271bde3cfb354964ed140a6fa82d7))
## Documentation
- Use link tags to render links on the documentation (#10731) ([66b9718](https://github.com/discordjs/discord.js/commit/66b971899ab702240642e3ae2d189fd9e7efc701))
- **Message:** Improve message snapshots description (#10709) ([31df3d2](https://github.com/discordjs/discord.js/commit/31df3d21cdc53400672924bc7c5dc7fd3053630b))
## Features
- Message forwards (#10733) ([89c076c](https://github.com/discordjs/discord.js/commit/89c076c89e90e8f5912786e8899ced9e8eea6003))
- Incident Actions (#10727) ([41dee51](https://github.com/discordjs/discord.js/commit/41dee5177d9cb15f667e60a34619882222bf249c))
- **website:** Type parameters links, builtin doc links, default values (#10515) ([43235d4](https://github.com/discordjs/discord.js/commit/43235d43fe76e26805c52dcff13519652bcb6a4a))
- **PartialGroupDMChannel:** Add missing properties (#10502) ([5e66f85](https://github.com/discordjs/discord.js/commit/5e66f85f55724a583921252b035eb2097345fec8))
- **Subscription:** Add `renewalSkuIds` (#10662) ([efa50fc](https://github.com/discordjs/discord.js/commit/efa50fc3fa463b09bde11c1640daa2abb8c22686))
- **website:** Include reexported members in docs (#10518) ([aa61c20](https://github.com/discordjs/discord.js/commit/aa61c20ffdac3f3a0dca224f9e48e614309ecb2e))
## Refactor
- Use throw instead of Promise.reject (#10712) ([2663d76](https://github.com/discordjs/discord.js/commit/2663d767099f2e14a23f9cbfb868f279ffb253d1))
- Remove data resolver exports (#10701) ([4606041](https://github.com/discordjs/discord.js/commit/46060419a9593dc5132ba6f13b58d0c18613679b))
- **IntegrationApplication:** Move common properties to Application (#10627) ([95db597](https://github.com/discordjs/discord.js/commit/95db597fc844e7951b07cfb5741e27086ac7451a))
## Styling
- Prettier ([92aea94](https://github.com/discordjs/discord.js/commit/92aea944119638b12c03be0f627f20fe5fe5145e))
## Typings
- Fix recurrence rule types (#10694) ([193a5e9](https://github.com/discordjs/discord.js/commit/193a5e9e20fc4832592b2a3b6f142752121f43d5))
- **ThreadOnlyChannel:** Remove incorrect `messages` property (#10708) ([44a1e85](https://github.com/discordjs/discord.js/commit/44a1e858473a51809cb1e6114d6a659fe28587f0))
- Add `undefined` to `flags` for `exactOptionalPropertyTypes` (#10707) ([d2e1924](https://github.com/discordjs/discord.js/commit/d2e1924fa6a06120879a1158d501a899db3d6d96))
- Allow only ephemeral for defer reply (#10696) ([68dd260](https://github.com/discordjs/discord.js/commit/68dd260dee1a7b0bbd4fcdff1b39283ea8dcedec))
- Remove createComponent and createComponentBuilder (#10687) ([0047a49](https://github.com/discordjs/discord.js/commit/0047a49b7395acf0936702f233e7fb89e9f352fe))
# [14.17.3](https://github.com/discordjs/discord.js/compare/14.17.2...14.17.3) - (2025-01-08)
## Bug Fixes
- **Message:** Ensure channel is defined for clean content (#10681) ([46bf8f0](https://github.com/discordjs/discord.js/commit/46bf8f0146b67d7c480a3512ade1edbfb16e7a26))
- Use `resolve()` for `PermissionOverwrites` (#10686) ([7280d4e](https://github.com/discordjs/discord.js/commit/7280d4e82eb47ce7cb3964057d7d56a62179cf18))
# [14.17.2](https://github.com/discordjs/discord.js/compare/14.17.1...14.17.2) - (2025-01-02)
## Bug Fixes
- **InteractionResponses:** Check correct property for deprecation ([77804cf](https://github.com/discordjs/discord.js/commit/77804cfd559691d9b8c85aec8c494cd6c14c4ea7))
# [14.17.0](https://github.com/discordjs/discord.js/compare/14.16.3...14.17.0) - (2025-01-01)
## Bug Fixes
- **InteractionResponses:** Do not use `in` if a string is passed ([ff42d7a](https://github.com/discordjs/discord.js/commit/ff42d7af72e940ae72c61d2c5164ae68f2708b96))
- Use Message#interactionMetadata (#10654) ([6087088](https://github.com/discordjs/discord.js/commit/60870885790eb1857ed4c2969c9c404e356a1299))
- **InteractionResponses:** Properly resolve message flags (#10661) ([b2754d4](https://github.com/discordjs/discord.js/commit/b2754d4a0ec250ae84057d0f07c078376f54829c))
- **ThreadChannel:** Make `ownerId` always present (#10618) ([7678f11](https://github.com/discordjs/discord.js/commit/7678f1176a645878261361faef0429f9cf7f4810))
- **MessageReaction:** Address `undefined` burst properties (#10597) ([76968b4](https://github.com/discordjs/discord.js/commit/76968b4bc14b8a66825f9140d130b1e04c11855a))
- **ThreadChannel:** Address parameter type on fetchOwner() (#10592) ([56c9396](https://github.com/discordjs/discord.js/commit/56c9396b717d4dec2410ca13938ce238ec21215d))
- **InteractionResponses:** Throw error on deleting response of unacknowledged interaction (#10587) ([21c283f](https://github.com/discordjs/discord.js/commit/21c283f964ab9e331db53cc0c21ca64980372488))
- **GuildScheduledEvent:** Handle null recurrence_rule (#10543) ([831aafa](https://github.com/discordjs/discord.js/commit/831aafa733e8eea55534c4c39b87775d2e2f56c4))
## Documentation
- Correct discord-api-types URLs (#10622) ([76042f0](https://github.com/discordjs/discord.js/commit/76042f05386edcbadc5ad4ded22e8b15c7b6f8ec))
- Typos (#10628) ([388783d](https://github.com/discordjs/discord.js/commit/388783d7dd718aae519801b90aa781d07b7fb64e))
- Add note about idempotence to role add/remove routes (#10586) ([565fc01](https://github.com/discordjs/discord.js/commit/565fc0192a5ed2642ff1bd615c59678b5c3cd24b))
- **Client:** Fix incorrect managers descriptions ([f79ba52](https://github.com/discordjs/discord.js/commit/f79ba52c7a1334d987e9873a8a411e92d5140116))
- **discord.js:** Remove `utf-8-validate` (#10531) ([297e959](https://github.com/discordjs/discord.js/commit/297e959f48abbfd3af58cc29cdcef139d3579821))
## Features
- **ClientApplication:** Add webhook events (#10588) ([7b2a2e3](https://github.com/discordjs/discord.js/commit/7b2a2e3a154afd69ff892da615ea75c46730f226))
- **InteractionResponses:** Support `with_response` query parameter (#10636) ([622acbc](https://github.com/discordjs/discord.js/commit/622acbcbf02c3b8e0eae4296964c3e745e19378d))
- **ClientApplication:** Add webhook events (#10588) ([ae1deac](https://github.com/discordjs/discord.js/commit/ae1deac2bf37aecda4c044bf5c28d03930bd763b))
- **EntitlementManager:** Support get entitlement (#10606) ([a367e2c](https://github.com/discordjs/discord.js/commit/a367e2c8c99ab3bfb83cdbfb65e7a5020b50b7f7))
- Add subscriptions (#10541) ([4cca33d](https://github.com/discordjs/discord.js/commit/4cca33d9b0759294c9a2dfec39d80a24a2cc1595))
- Emit reaction type on gateway events (#10598) ([bda3128](https://github.com/discordjs/discord.js/commit/bda31284bf46515747e002e86ea35d0b6910e269))
- Voice Channel Effect Send (#10318) ([34343c6](https://github.com/discordjs/discord.js/commit/34343c6afae65205d3b17b60fdd202d0937d6a46))
- **GuildMember:** Banners (#10384) ([b1ded63](https://github.com/discordjs/discord.js/commit/b1ded63e42e7349f535df4680509b9393dd8f288))
- Add ApplicationEmoji to EmojiResolvable and MessageReaction#emoji (#10477) ([1fc87a9](https://github.com/discordjs/discord.js/commit/1fc87a96987fe69722502d7574500926a4e0bfde))
- Recurring scheduled events (#10447) ([97c3237](https://github.com/discordjs/discord.js/commit/97c3237a70027f71bb3f046357a55bb730daca14))
- Message forwarding (#10464) ([c122178](https://github.com/discordjs/discord.js/commit/c12217829b46f7a60266f65af4af19cdbfcd7906))
## Refactor
- **FetchApplicationCommandOptions:** Use `Locale` over `LocaleString` (#10625) ([7ce6f2f](https://github.com/discordjs/discord.js/commit/7ce6f2fc8a8756532d71a542186d10a0aa951471))
- Use `cache.get()` for snowflakes, `resolve()` otherwise (#10626) ([dedaa5d](https://github.com/discordjs/discord.js/commit/dedaa5d657f15491910ec05102ce72affc822b97))
- Remove extra traversing (#10580) ([33533b7](https://github.com/discordjs/discord.js/commit/33533b72849d9741dae8c979734b45abbf3657a7))
- **InteractionResponses:** Deprecate ephemeral response option (#10574) ([be38f57](https://github.com/discordjs/discord.js/commit/be38f5792602ed1a79a9638aa8e629e7ad6bdd0d))
- Deprecate `reason` parameter on adding and removing thread members (#10551) ([72e0c99](https://github.com/discordjs/discord.js/commit/72e0c994547f2a9c99b320870e14d7f1643f3851))
- Deprecate fetching user flags (#10550) ([3d06c9d](https://github.com/discordjs/discord.js/commit/3d06c9d872b2e79356f1239f7d0eb0577a4bcedf))
## Testing
- Remove unused test (#10638) ([53cbb0e](https://github.com/discordjs/discord.js/commit/53cbb0e36d4ab191cbc15a022d752da14c2e0ace))
## Typings
- Add missing `Caches` managers (#10540) ([13471fa](https://github.com/discordjs/discord.js/commit/13471fa1b7c44b236db9fe9b1a64dacd41b14b76))
- Remove newMessage partial on messageUpdate event typing (#10526) ([5faf074](https://github.com/discordjs/discord.js/commit/5faf074c145044f0edefafab97fd07a8dfb8bc30))
# [14.16.3](https://github.com/discordjs/discord.js/compare/14.16.2...14.16.3) - (2024-09-29)
## Bug Fixes
- **BaseInteraction:** Add missing props (#10517) ([6c77fee](https://github.com/discordjs/discord.js/commit/6c77fee41b1aabc243bff623debd157a4c7fad6a)) by @monbrey
- `GuildChannel#guildId` not being patched to `undefined` (#10505) ([2adee06](https://github.com/discordjs/discord.js/commit/2adee06b6e92b7854ebb1c2bfd04940aab68dd10)) by @Qjuh
## Typings
- **MessageEditOptions:** Omit `poll` (#10509) ([665bf14](https://github.com/discordjs/discord.js/commit/665bf1486aec62e9528f5f7b5a6910ae6b5a6c9c)) by @TAEMBO
# [14.16.2](https://github.com/discordjs/discord.js/compare/14.16.1...14.16.2) - (2024-09-12)
## Bug Fixes
- **ApplicationCommand:** Incorrect comparison in equals method (#10497) ([3c74aa2](https://github.com/discordjs/discord.js/commit/3c74aa204909323ff6d05991438bee2c583e838b)) by @monbrey
- Type guard for sendable text-based channels (#10482) ([dea6840](https://github.com/discordjs/discord.js/commit/dea68400a38edb90b8b4242d64be14968943130d)) by @vladfrangu
## Documentation
- Update discord documentation links (#10484) ([799fa54](https://github.com/discordjs/discord.js/commit/799fa54fa4434144855be2f7a0bbac6ff8ce9d0b)) by @sdanialraza
- **Message:** Mark `interaction` as deprecated (#10481) ([c13f18e](https://github.com/discordjs/discord.js/commit/c13f18e90eb6eb315397c095e948993856428757)) by @sdanialraza
- **ApplicationEmojiManager:** Fix fetch example (#10480) ([4594896](https://github.com/discordjs/discord.js/commit/4594896b5404c6a34e07544951c59ff8f3657184)) by @sdanialraza
## Typings
- Export GroupDM helper type (#10478) ([aff772c](https://github.com/discordjs/discord.js/commit/aff772c7aa3b3de58780a94588d1f3576a434f32)) by @Qjuh
# [14.16.1](https://github.com/discordjs/discord.js/compare/14.16.0...14.16.1) - (2024-09-02)
## Bug Fixes
- **Message:** Reacting returning undefined (#10475) ([9257a09](https://github.com/discordjs/discord.js/commit/9257a09abbf80558ed2d5d209a2f6bd2a4b3d799)) by @vladfrangu
- **Transformers:** Pass client to recursive call (#10474) ([4810f7c](https://github.com/discordjs/discord.js/commit/4810f7c8637dacf77d0442bd84e0d579e1f1d3bd)) by @SpaceEEC
# [14.16.0](https://github.com/discordjs/discord.js/compare/14.15.3...14.16.0) - (2024-09-01)
## Bug Fixes

View File

@@ -9,7 +9,8 @@
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="npm downloads" /></a>
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Tests status" /></a>
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2" alt="Code coverage" /></a>
<a href="https://github.com/discordjs/discord.js/commits/main/packages/discord.js"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fdiscord.js"></a>
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2" alt="Code coverage" /></a>
</p>
<p>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
@@ -29,7 +30,7 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
## Installation
**Node.js 16.11.0 or newer is required.**
**Node.js 18 or newer is required.**
```sh
npm install discord.js
@@ -42,7 +43,6 @@ bun add discord.js
- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for WebSocket data compression and inflation (`npm install zlib-sync`)
- [bufferutil](https://www.npmjs.com/package/bufferutil) for a much faster WebSocket connection (`npm install bufferutil`)
- [utf-8-validate](https://www.npmjs.com/package/utf-8-validate) in combination with `bufferutil` for much faster WebSocket processing (`npm install utf-8-validate`)
- [@discordjs/voice](https://www.npmjs.com/package/@discordjs/voice) for interacting with the Discord Voice API (`npm install @discordjs/voice`)
## Example usage

View File

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

View File

@@ -30,8 +30,10 @@ body = """
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
{% if commit.breaking %}\
{% for breakingChange in commit.footers %}\
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
{% for footer in commit.footers %}\
{% if footer.breaking %}\
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
{% endif %}\
{% endfor %}\
{% endif %}\
{% endfor %}
@@ -67,8 +69,7 @@ commit_parsers = [
{ body = ".*security", group = "Security"},
]
filter_commits = true
tag_pattern = "[0-9]*"
skip_tags = "v[0-9]*|@discordjs*"
tag_pattern = "^[0-9]+"
ignore_tags = ""
topo_order = false
sort_commits = "newest"

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "discord.js",
"version": "14.16.0",
"version": "14.24.2",
"description": "A powerful library for interacting with the Discord API",
"scripts": {
"test": "pnpm run docs:test && pnpm run test:typescript",
@@ -36,7 +36,8 @@
},
"files": [
"src",
"typings"
"typings/*.d.ts",
"typings/*.d.mts"
],
"contributors": [
"Crawl <icrawltogo@gmail.com>",
@@ -70,13 +71,14 @@
"@discordjs/formatters": "workspace:^",
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"@discordjs/ws": "1.1.1",
"@discordjs/ws": "^1.2.3",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "^0.38.1",
"discord-api-types": "^0.38.32",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.21.1"
"undici": "6.21.3"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",
@@ -98,7 +100,7 @@
"typescript": "~5.5.4"
},
"engines": {
"node": ">=16.11.0"
"node": ">=18"
},
"publishConfig": {
"provenance": true

View File

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

View File

@@ -18,6 +18,7 @@ const ClientPresence = require('../structures/ClientPresence');
const GuildPreview = require('../structures/GuildPreview');
const GuildTemplate = require('../structures/GuildTemplate');
const Invite = require('../structures/Invite');
const { SoundboardSound } = require('../structures/SoundboardSound');
const { Sticker } = require('../structures/Sticker');
const StickerPack = require('../structures/StickerPack');
const VoiceRegion = require('../structures/VoiceRegion');
@@ -107,20 +108,20 @@ class Client extends BaseClient {
: null;
/**
* All of the {@link User} objects that have been cached at any point, mapped by their ids
* The user manager of this client
* @type {UserManager}
*/
this.users = new UserManager(this);
/**
* All of the guilds the client is currently handling, mapped by their ids -
* A manager of all the guilds the client is currently handling -
* as long as sharding isn't being used, this will be *every* guild the bot is a member of
* @type {GuildManager}
*/
this.guilds = new GuildManager(this);
/**
* All of the {@link BaseChannel}s that the client is currently handling, mapped by their ids -
* All of the {@link BaseChannel}s that the client is currently handling -
* as long as sharding isn't being used, this will be *every* channel in *every* guild the bot
* is a member of. Note that DM channels will not be initially cached, and thus not be present
* in the Manager without their explicit fetching or use.
@@ -174,7 +175,7 @@ class Client extends BaseClient {
}
/**
* All custom emojis that the client has access to, mapped by their ids
* A manager of all the custom emojis that the client has access to
* @type {BaseGuildEmojiManager}
* @readonly
*/
@@ -276,7 +277,6 @@ class Client extends BaseClient {
const code = resolveInviteCode(invite);
const query = makeURLSearchParams({
with_counts: true,
with_expiration: true,
guild_scheduled_event_id: options?.guildScheduledEventId,
});
const data = await this.rest.get(Routes.invite(code), { query });
@@ -390,6 +390,19 @@ class Client extends BaseClient {
return this.fetchStickerPacks();
}
/**
* Obtains the list of default soundboard sounds.
* @returns {Promise<Collection<string, SoundboardSound>>}
* @example
* client.fetchDefaultSoundboardSounds()
* .then(sounds => console.log(`Available soundboard sounds are: ${sounds.map(sound => sound.name).join(', ')}`))
* .catch(console.error);
*/
async fetchDefaultSoundboardSounds() {
const data = await this.rest.get(Routes.soundboardDefaultSounds());
return new Collection(data.map(sound => [sound.sound_id, new SoundboardSound(this, sound)]));
}
/**
* Obtains a guild preview from Discord, available for all guilds the bot is in and all Discoverable guilds.
* @param {GuildResolvable} guild The guild to fetch the preview for
@@ -509,7 +522,7 @@ class Client extends BaseClient {
}
/**
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
* Calls {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script
* with the client as `this`.
* @param {string} script Script to eval
* @returns {*}
@@ -591,7 +604,7 @@ module.exports = Client;
*/
/**
* A {@link https://developer.twitter.com/en/docs/twitter-ids Twitter snowflake},
* A {@link https://docs.x.com/resources/fundamentals/x-ids Twitter snowflake},
* except the epoch is 2015-01-01T00:00:00.000Z.
*
* If we have a snowflake '266241948824764416' we can represent it as binary:
@@ -625,6 +638,11 @@ module.exports = Client;
* @see {@link https://discord.js.org/docs/packages/rest/stable/ImageURLOptions:Interface}
*/
/**
* @external EmojiURLOptions
* @see {@link https://discord.js.org/docs/packages/rest/stable/EmojiURLOptions:TypeAlias}
*/
/**
* @external BaseImageURLOptions
* @see {@link https://discord.js.org/docs/packages/rest/stable/BaseImageURLOptions:Interface}

View File

@@ -1,6 +1,8 @@
'use strict';
const Partials = require('../../util/Partials');
const { Poll } = require('../../structures/Poll.js');
const { PollAnswer } = require('../../structures/PollAnswer.js');
const Partials = require('../../util/Partials.js');
/*
@@ -63,6 +65,23 @@ class GenericAction {
);
}
getPoll(data, message, channel) {
const includePollPartial = this.client.options.partials.includes(Partials.Poll);
const includePollAnswerPartial = this.client.options.partials.includes(Partials.PollAnswer);
if (message.partial && (!includePollPartial || !includePollAnswerPartial)) return null;
if (!message.poll && includePollPartial) {
message.poll = new Poll(this.client, data, message, channel);
}
if (message.poll && !message.poll.answers.has(data.answer_id) && includePollAnswerPartial) {
const pollAnswer = new PollAnswer(this.client, data, message.poll);
message.poll.answers.set(data.answer_id, pollAnswer);
}
return message.poll;
}
getReaction(data, message, user) {
const id = data.emoji.id ?? decodeURIComponent(data.emoji.name);
return this.getPayload(
@@ -111,6 +130,14 @@ class GenericAction {
getThreadMember(id, manager) {
return this.getPayload({ user_id: id }, manager, id, Partials.ThreadMember, false);
}
getSoundboardSound(data, guild) {
return this.getPayload(data, guild.soundboardSounds, data.sound_id, Partials.SoundboardSound);
}
spreadInjectedData(data) {
return Object.fromEntries(Object.getOwnPropertySymbols(data).map(symbol => [symbol, data[symbol]]));
}
}
module.exports = GenericAction;

View File

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

View File

@@ -0,0 +1,29 @@
'use strict';
const Action = require('./Action.js');
const Events = require('../../util/Events.js');
class GuildSoundboardSoundDeleteAction extends Action {
handle(data) {
const guild = this.client.guilds.cache.get(data.guild_id);
if (!guild) return {};
const soundboardSound = this.getSoundboardSound(data, guild);
if (soundboardSound) {
guild.soundboardSounds.cache.delete(soundboardSound.soundId);
/**
* Emitted whenever a soundboard sound is deleted in a guild.
* @event Client#guildSoundboardSoundDelete
* @param {SoundboardSound} soundboardSound The soundboard sound that was deleted
*/
this.client.emit(Events.GuildSoundboardSoundDelete, soundboardSound);
}
return { soundboardSound };
}
}
module.exports = GuildSoundboardSoundDeleteAction;

View File

@@ -9,6 +9,7 @@ const ChatInputCommandInteraction = require('../../structures/ChatInputCommandIn
const MentionableSelectMenuInteraction = require('../../structures/MentionableSelectMenuInteraction');
const MessageContextMenuCommandInteraction = require('../../structures/MessageContextMenuCommandInteraction');
const ModalSubmitInteraction = require('../../structures/ModalSubmitInteraction');
const PrimaryEntryPointCommandInteraction = require('../../structures/PrimaryEntryPointCommandInteraction');
const RoleSelectMenuInteraction = require('../../structures/RoleSelectMenuInteraction');
const StringSelectMenuInteraction = require('../../structures/StringSelectMenuInteraction');
const UserContextMenuCommandInteraction = require('../../structures/UserContextMenuCommandInteraction');
@@ -38,6 +39,9 @@ class InteractionCreateAction extends Action {
if (channel && !channel.isTextBased()) return;
InteractionClass = MessageContextMenuCommandInteraction;
break;
case ApplicationCommandType.PrimaryEntryPoint:
InteractionClass = PrimaryEntryPointCommandInteraction;
break;
default:
client.emit(
Events.Debug,

View File

@@ -6,7 +6,11 @@ const Events = require('../../util/Events');
class MessageCreateAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id, author: data.author });
const channel = this.getChannel({
id: data.channel_id,
author: data.author,
...('guild_id' in data && { guild_id: data.guild_id }),
});
if (channel) {
if (!channel.isTextBased()) return {};

View File

@@ -6,7 +6,7 @@ const Events = require('../../util/Events');
class MessageDeleteAction extends Action {
handle(data) {
const client = this.client;
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
let message;
if (channel) {
if (!channel.isTextBased()) return {};

View File

@@ -5,17 +5,24 @@ const Events = require('../../util/Events');
class MessagePollVoteAddAction extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel?.isTextBased()) return false;
const message = this.getMessage(data, channel);
if (!message) return false;
const { poll } = message;
const poll = this.getPoll(data, message, channel);
if (!poll) return false;
const answer = poll?.answers.get(data.answer_id);
const answer = poll.answers.get(data.answer_id);
if (!answer) return false;
const user = this.getUser(data);
if (user) {
answer.voters._add(user);
}
answer.voteCount++;
/**

View File

@@ -5,18 +5,23 @@ const Events = require('../../util/Events');
class MessagePollVoteRemoveAction extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel?.isTextBased()) return false;
const message = this.getMessage(data, channel);
if (!message) return false;
const { poll } = message;
const poll = this.getPoll(data, message, channel);
if (!poll) return false;
const answer = poll?.answers.get(data.answer_id);
const answer = poll.answers.get(data.answer_id);
if (!answer) return false;
answer.voteCount--;
answer.voters.cache.delete(data.user_id);
if (answer.voteCount > 0) {
answer.voteCount--;
}
/**
* Emitted whenever a user removes their vote in a poll.

View File

@@ -23,7 +23,13 @@ class MessageReactionAdd extends Action {
if (!user) return false;
// Verify channel
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id, user_id: data.user_id });
const channel = this.getChannel({
id: data.channel_id,
...('guild_id' in data && { guild_id: data.guild_id }),
user_id: data.user_id,
...this.spreadInjectedData(data),
});
if (!channel?.isTextBased()) return false;
// Verify message
@@ -45,6 +51,7 @@ class MessageReactionAdd extends Action {
/**
* Provides additional information about altered reaction
* @typedef {Object} MessageReactionEventDetails
* @property {ReactionType} type The type of the reaction
* @property {boolean} burst Determines whether a super reaction was used
*/
/**
@@ -54,7 +61,7 @@ class MessageReactionAdd extends Action {
* @param {User} user The user that applied the guild or reaction emoji
* @param {MessageReactionEventDetails} details Details of adding the reaction
*/
this.client.emit(Events.MessageReactionAdd, reaction, user, { burst: data.burst });
this.client.emit(Events.MessageReactionAdd, reaction, user, { type: data.type, burst: data.burst });
return { message, reaction, user };
}

View File

@@ -19,7 +19,11 @@ class MessageReactionRemove extends Action {
if (!user) return false;
// Verify channel
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id, user_id: data.user_id });
const channel = this.getChannel({
id: data.channel_id,
...('guild_id' in data && { guild_id: data.guild_id }),
user_id: data.user_id,
});
if (!channel?.isTextBased()) return false;
// Verify message
@@ -37,7 +41,7 @@ class MessageReactionRemove extends Action {
* @param {User} user The user whose emoji or reaction emoji was removed
* @param {MessageReactionEventDetails} details Details of removing the reaction
*/
this.client.emit(Events.MessageReactionRemove, reaction, user, { burst: data.burst });
this.client.emit(Events.MessageReactionRemove, reaction, user, { type: data.type, burst: data.burst });
return { message, reaction, user };
}

View File

@@ -6,7 +6,7 @@ const Events = require('../../util/Events');
class MessageReactionRemoveAll extends Action {
handle(data) {
// Verify channel
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel?.isTextBased()) return false;
// Verify message

View File

@@ -5,7 +5,7 @@ const Events = require('../../util/Events');
class MessageReactionRemoveEmoji extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel?.isTextBased()) return false;
const message = this.getMessage(data, channel);

View File

@@ -4,7 +4,7 @@ const Action = require('./Action');
class MessageUpdateAction extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (channel) {
if (!channel.isTextBased()) return {};

View File

@@ -2,11 +2,14 @@
const Action = require('./Action');
const Events = require('../../util/Events');
const Partials = require('../../util/Partials');
class PresenceUpdateAction extends Action {
handle(data) {
let user = this.client.users.cache.get(data.user.id);
if (!user && data.user.username) user = this.client.users._add(data.user);
if (!user && ('username' in data.user || this.client.options.partials.includes(Partials.User))) {
user = this.client.users._add(data.user);
}
if (!user) return;
if (data.user.username) {

View File

@@ -13,7 +13,7 @@ class ThreadListSyncAction extends Action {
if (data.channel_ids) {
for (const id of data.channel_ids) {
const channel = client.channels.resolve(id);
const channel = client.channels.cache.get(id);
if (channel) this.removeStale(channel);
}
} else {

View File

@@ -6,7 +6,7 @@ const Events = require('../../util/Events');
class TypingStart extends Action {
handle(data) {
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
if (!channel) return;
if (!channel.isTextBased()) {

View File

@@ -19,6 +19,7 @@ const Status = require('../../util/Status');
const WebSocketShardEvents = require('../../util/WebSocketShardEvents');
let zlib;
let deprecationEmitted = false;
try {
zlib = require('zlib-sync');
@@ -36,10 +37,13 @@ const BeforeReadyWhitelist = [
const WaitingForGuildEvents = [GatewayDispatchEvents.GuildCreate, GatewayDispatchEvents.GuildDelete];
const UNRESUMABLE_CLOSE_CODES = [
CloseCodes.Normal,
GatewayCloseCodes.AlreadyAuthenticated,
GatewayCloseCodes.InvalidSeq,
const UNRECOVERABLE_CLOSE_CODES = [
GatewayCloseCodes.AuthenticationFailed,
GatewayCloseCodes.InvalidShard,
GatewayCloseCodes.ShardingRequired,
GatewayCloseCodes.InvalidAPIVersion,
GatewayCloseCodes.InvalidIntents,
GatewayCloseCodes.DisallowedIntents,
];
const reasonIsDeprecated = 'the reason property is deprecated, use the code property to determine the reason';
@@ -242,7 +246,7 @@ class WebSocketManager extends EventEmitter {
this._ws.on(WSWebSocketShardEvents.Closed, ({ code, shardId }) => {
const shard = this.shards.get(shardId);
shard.emit(WebSocketShardEvents.Close, { code, reason: reasonIsDeprecated, wasClean: true });
if (UNRESUMABLE_CLOSE_CODES.includes(code) && this.destroyed) {
if (UNRECOVERABLE_CLOSE_CODES.includes(code)) {
shard.status = Status.Disconnected;
/**
* Emitted when a shard's WebSocket disconnects and will no longer reconnect.
@@ -251,7 +255,7 @@ class WebSocketManager extends EventEmitter {
* @param {number} id The shard id that disconnected
*/
this.client.emit(Events.ShardDisconnect, { code, reason: reasonIsDeprecated, wasClean: true }, shardId);
this.debug([`Shard not resumable: ${code} (${GatewayCloseCodes[code] ?? CloseCodes[code]})`], shardId);
this.debug([`Shard not recoverable: ${code} (${GatewayCloseCodes[code] ?? CloseCodes[code]})`], shardId);
return;
}
@@ -376,6 +380,22 @@ class WebSocketManager extends EventEmitter {
/**
* Emitted when the client becomes ready to start working.
* @event Client#ready
* @deprecated Use {@link Client#event:clientReady} instead.
* @param {Client} client The client
*/
if (this.client.emit('ready', this.client) && !deprecationEmitted) {
deprecationEmitted = true;
process.emitWarning(
// eslint-disable-next-line max-len
'The ready event has been renamed to clientReady to distinguish it from the gateway READY event and will only emit under that name in v15. Please use clientReady instead.',
'DeprecationWarning',
);
}
/**
* Emitted when the client becomes ready to start working.
* @event Client#clientReady
* @param {Client} client The client
*/
this.client.emit(Events.ClientReady, this.client);

View File

@@ -0,0 +1,24 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const Events = require('../../../util/Events.js');
module.exports = (client, { d: data }) => {
const guild = client.guilds.cache.get(data.guild_id);
if (!guild) return;
const soundboardSounds = new Collection();
for (const soundboardSound of data.soundboard_sounds) {
soundboardSounds.set(soundboardSound.sound_id, guild.soundboardSounds._add(soundboardSound));
}
/**
* Emitted whenever multiple guild soundboard sounds are updated.
* @event Client#guildSoundboardSoundsUpdate
* @param {Collection<Snowflake, SoundboardSound>} soundboardSounds The updated soundboard sounds
* @param {Guild} guild The guild that the soundboard sounds are from
*/
client.emit(Events.GuildSoundboardSoundsUpdate, soundboardSounds, guild);
};

View File

@@ -0,0 +1,18 @@
'use strict';
const Events = require('../../../util/Events.js');
module.exports = (client, { d: data }) => {
const guild = client.guilds.cache.get(data.guild_id);
if (!guild) return;
const soundboardSound = guild.soundboardSounds._add(data);
/**
* Emitted whenever a guild soundboard sound is created.
* @event Client#guildSoundboardSoundCreate
* @param {SoundboardSound} soundboardSound The created guild soundboard sound
*/
client.emit(Events.GuildSoundboardSoundCreate, soundboardSound);
};

View File

@@ -0,0 +1,5 @@
'use strict';
module.exports = (client, { d: data }) => {
client.actions.GuildSoundboardSoundDelete.handle(data);
};

View File

@@ -0,0 +1,20 @@
'use strict';
const Events = require('../../../util/Events.js');
module.exports = (client, { d: data }) => {
const guild = client.guilds.cache.get(data.guild_id);
if (!guild) return;
const oldGuildSoundboardSound = guild.soundboardSounds.cache.get(data.sound_id)?._clone() ?? null;
const newGuildSoundboardSound = guild.soundboardSounds._add(data);
/**
* Emitted whenever a guild soundboard sound is updated.
* @event Client#guildSoundboardSoundUpdate
* @param {?SoundboardSound} oldGuildSoundboardSound The guild soundboard sound before the update
* @param {SoundboardSound} newGuildSoundboardSound The guild soundboard sound after the update
*/
client.emit(Events.GuildSoundboardSoundUpdate, oldGuildSoundboardSound, newGuildSoundboardSound);
};

View File

@@ -0,0 +1,24 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const Events = require('../../../util/Events.js');
module.exports = (client, { d: data }) => {
const guild = client.guilds.cache.get(data.guild_id);
if (!guild) return;
const soundboardSounds = new Collection();
for (const soundboardSound of data.soundboard_sounds) {
soundboardSounds.set(soundboardSound.sound_id, guild.soundboardSounds._add(soundboardSound));
}
/**
* Emitted whenever soundboard sounds are received (all soundboard sounds come from the same guild).
* @event Client#soundboardSounds
* @param {Collection<Snowflake, SoundboardSound>} soundboardSounds The sounds received
* @param {Guild} guild The guild that the soundboard sounds are from
*/
client.emit(Events.SoundboardSounds, soundboardSounds, guild);
};

View File

@@ -0,0 +1,14 @@
'use strict';
const Events = require('../../../util/Events');
module.exports = (client, { d: data }) => {
const subscription = client.application.subscriptions._add(data);
/**
* Emitted whenever a subscription is created.
* @event Client#subscriptionCreate
* @param {Subscription} subscription The subscription that was created
*/
client.emit(Events.SubscriptionCreate, subscription);
};

View File

@@ -0,0 +1,16 @@
'use strict';
const Events = require('../../../util/Events');
module.exports = (client, { d: data }) => {
const subscription = client.application.subscriptions._add(data, false);
client.application.subscriptions.cache.delete(subscription.id);
/**
* Emitted whenever a subscription is deleted.
* @event Client#subscriptionDelete
* @param {Subscription} subscription The subscription that was deleted
*/
client.emit(Events.SubscriptionDelete, subscription);
};

View File

@@ -0,0 +1,16 @@
'use strict';
const Events = require('../../../util/Events');
module.exports = (client, { d: data }) => {
const oldSubscription = client.application.subscriptions.cache.get(data.id)?._clone() ?? null;
const newSubscription = client.application.subscriptions._add(data);
/**
* Emitted whenever a subscription is updated - i.e. when a user's subscription renews.
* @event Client#subscriptionUpdate
* @param {?Subscription} oldSubscription The subscription before the update
* @param {Subscription} newSubscription The subscription after the update
*/
client.emit(Events.SubscriptionUpdate, oldSubscription, newSubscription);
};

View File

@@ -0,0 +1,16 @@
'use strict';
const VoiceChannelEffect = require('../../../structures/VoiceChannelEffect');
const Events = require('../../../util/Events');
module.exports = (client, { d: data }) => {
const guild = client.guilds.cache.get(data.guild_id);
if (!guild) return;
/**
* Emitted when someone sends an effect, such as an emoji reaction, in a voice channel the client is connected to.
* @event Client#voiceChannelEffectSend
* @param {VoiceChannelEffect} voiceChannelEffect The sent voice channel effect
*/
client.emit(Events.VoiceChannelEffectSend, new VoiceChannelEffect(data, guild));
};

View File

@@ -32,6 +32,10 @@ const handlers = Object.fromEntries([
['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')],
['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')],
['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')],
['GUILD_SOUNDBOARD_SOUNDS_UPDATE', require('./GUILD_SOUNDBOARD_SOUNDS_UPDATE')],
['GUILD_SOUNDBOARD_SOUND_CREATE', require('./GUILD_SOUNDBOARD_SOUND_CREATE')],
['GUILD_SOUNDBOARD_SOUND_DELETE', require('./GUILD_SOUNDBOARD_SOUND_DELETE')],
['GUILD_SOUNDBOARD_SOUND_UPDATE', require('./GUILD_SOUNDBOARD_SOUND_UPDATE')],
['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')],
['GUILD_UPDATE', require('./GUILD_UPDATE')],
['INTERACTION_CREATE', require('./INTERACTION_CREATE')],
@@ -50,9 +54,13 @@ const handlers = Object.fromEntries([
['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')],
['READY', require('./READY')],
['RESUMED', require('./RESUMED')],
['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS')],
['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')],
['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')],
['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')],
['SUBSCRIPTION_CREATE', require('./SUBSCRIPTION_CREATE')],
['SUBSCRIPTION_DELETE', require('./SUBSCRIPTION_DELETE')],
['SUBSCRIPTION_UPDATE', require('./SUBSCRIPTION_UPDATE')],
['THREAD_CREATE', require('./THREAD_CREATE')],
['THREAD_DELETE', require('./THREAD_DELETE')],
['THREAD_LIST_SYNC', require('./THREAD_LIST_SYNC')],
@@ -61,6 +69,7 @@ const handlers = Object.fromEntries([
['THREAD_UPDATE', require('./THREAD_UPDATE')],
['TYPING_START', require('./TYPING_START')],
['USER_UPDATE', require('./USER_UPDATE')],
['VOICE_CHANNEL_EFFECT_SEND', require('./VOICE_CHANNEL_EFFECT_SEND')],
['VOICE_SERVER_UPDATE', require('./VOICE_SERVER_UPDATE')],
['VOICE_STATE_UPDATE', require('./VOICE_STATE_UPDATE')],
['WEBHOOKS_UPDATE', require('./WEBHOOKS_UPDATE')],

Some files were not shown because too many files have changed in this diff Show More