Compare commits

..

109 Commits

Author SHA1 Message Date
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
181 changed files with 4737 additions and 803 deletions

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

@@ -18,8 +18,9 @@ export async function Badges({ node }: { readonly node: any }) {
const isAbstract = node.isAbstract;
const isReadonly = node.isReadonly;
const isOptional = node.isOptional;
const isExternal = node.isExternal;
const isAny = isDeprecated || isProtected || isStatic || isAbstract || isReadonly || isOptional;
const isAny = isDeprecated || isProtected || isStatic || isAbstract || isReadonly || isOptional || isExternal;
return isAny ? (
<div className="mb-1 flex gap-3">
@@ -33,6 +34,7 @@ export async function Badges({ node }: { readonly node: any }) {
{isAbstract ? <Badge className="bg-cyan-500/20 text-cyan-500">abstract</Badge> : null}
{isReadonly ? <Badge className="bg-purple-500/20 text-purple-500">readonly</Badge> : null}
{isOptional ? <Badge className="bg-cyan-500/20 text-cyan-500">optional</Badge> : null}
{isExternal ? <Badge className="bg-purple-500/20 text-purple-500">external</Badge> : null}
</div>
) : null;
}

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

@@ -49,7 +49,7 @@
"meilisearch": "^0.38.0",
"p-limit": "^6.1.0",
"tslib": "^2.6.3",
"undici": "6.19.8"
"undici": "6.21.1"
},
"devDependencies": {
"@types/node": "^18.19.45",

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 ? ` (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 ? `\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>

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

@@ -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

@@ -166,7 +166,7 @@ describe('Context Menu Commands', () => {
});
describe('integration types', () => {
test('GIVEN a builder with valid integraton types THEN does not throw an error', () => {
test('GIVEN a builder with valid integration types THEN does not throw an error', () => {
expect(() =>
getBuilder().setIntegrationTypes([
ApplicationIntegrationType.GuildInstall,

View File

@@ -565,7 +565,7 @@ describe('Slash Commands', () => {
});
describe('integration types', () => {
test('GIVEN a builder with valid integraton types THEN does not throw an error', () => {
test('GIVEN a builder with valid integration types THEN does not throw an error', () => {
expect(() =>
getBuilder().setIntegrationTypes([
ApplicationIntegrationType.GuildInstall,

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

@@ -68,7 +68,7 @@
"@discordjs/formatters": "workspace:^",
"@discordjs/util": "workspace:^",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "0.37.97",
"discord-api-types": "^0.37.119",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3"

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

@@ -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>

View File

@@ -1,5 +1,6 @@
{
"extends": "../../api-extractor.json",
"bundledPackages": ["discord-api-types"],
"docModel": {
"projectFolderUrl": "https://github.com/discordjs/discord.js/tree/main/packages/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.0.1",
"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.97"
"discord-api-types": "^0.37.119"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",

View File

@@ -45,7 +45,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 +65,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 +87,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 +110,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 +130,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 +152,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 +168,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 +186,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
@@ -242,7 +242,7 @@ export class ChannelsAPI {
/**
* 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 +299,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 +315,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 +331,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 +345,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
@@ -452,7 +452,7 @@ export class ChannelsAPI {
/**
* 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

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

@@ -196,7 +196,6 @@ export class Client extends AsyncEventEmitter<MappedEvents> {
this.api = new API(rest);
this.gateway.on(WebSocketShardEvents.Dispatch, (dispatch, shardId) => {
// @ts-expect-error event props can't be resolved properly, but they are correct
this.emit(dispatch.t, this.toEventProps(dispatch.d, shardId));
});
}

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

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

@@ -1,6 +1,14 @@
{
"extends": "../../api-extractor.json",
"mainEntryPointFilePath": "<projectFolder>/typings/index.d.ts",
"bundledPackages": [
"discord-api-types",
"@discordjs/builders",
"@discordjs/formatters",
"@discordjs/rest",
"@discordjs/util",
"@discordjs/ws"
],
"docModel": {
"projectFolderUrl": "https://github.com/discordjs/discord.js/tree/main/packages/discord.js"
}

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.18.0",
"description": "A powerful library for interacting with the Discord API",
"scripts": {
"test": "pnpm run docs:test && pnpm run test:typescript",
@@ -65,18 +65,19 @@
"homepage": "https://discord.js.org",
"funding": "https://github.com/discordjs/discord.js?sponsor",
"dependencies": {
"@discordjs/builders": "workspace:^",
"@discordjs/builders": "^1.11.0",
"@discordjs/collection": "1.5.3",
"@discordjs/formatters": "workspace:^",
"@discordjs/formatters": "^0.6.0",
"@discordjs/rest": "workspace:^",
"@discordjs/util": "workspace:^",
"@discordjs/ws": "1.1.1",
"@discordjs/ws": "^1.2.1",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "0.37.97",
"discord-api-types": "^0.38.1",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.19.8"
"undici": "6.21.1"
},
"devDependencies": {
"@discordjs/api-extractor": "workspace:^",

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
*/
@@ -390,6 +391,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

View File

@@ -111,6 +111,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

@@ -43,6 +43,7 @@ class ActionsManager {
this.register(require('./GuildScheduledEventUpdate'));
this.register(require('./GuildScheduledEventUserAdd'));
this.register(require('./GuildScheduledEventUserRemove'));
this.register(require('./GuildSoundboardSoundDelete.js'));
this.register(require('./GuildStickerCreate'));
this.register(require('./GuildStickerDelete'));
this.register(require('./GuildStickerUpdate'));

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

@@ -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,7 +5,7 @@ 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);

View File

@@ -5,7 +5,7 @@ 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);

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

@@ -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.js')],
['GUILD_SOUNDBOARD_SOUND_CREATE', require('./GUILD_SOUNDBOARD_SOUND_CREATE.js')],
['GUILD_SOUNDBOARD_SOUND_DELETE', require('./GUILD_SOUNDBOARD_SOUND_DELETE.js')],
['GUILD_SOUNDBOARD_SOUND_UPDATE', require('./GUILD_SOUNDBOARD_SOUND_UPDATE.js')],
['GUILD_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.js')],
['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')],

View File

@@ -106,6 +106,7 @@
* @property {'GuildChannelUnowned'} GuildChannelUnowned
* @property {'GuildOwned'} GuildOwned
* @property {'GuildMembersTimeout'} GuildMembersTimeout
* @property {'GuildSoundboardSoundsTimeout'} GuildSoundboardSoundsTimeout
* @property {'GuildUncachedMe'} GuildUncachedMe
* @property {'ChannelNotCached'} ChannelNotCached
* @property {'StageChannelResolve'} StageChannelResolve
@@ -131,6 +132,8 @@
* @property {'MissingManageEmojisAndStickersPermission'} MissingManageEmojisAndStickersPermission
* <warn>This property is deprecated. Use `MissingManageGuildExpressionsPermission` instead.</warn>
*
* @property {'NotGuildSoundboardSound'} NotGuildSoundboardSound
* @property {'NotGuildSticker'} NotGuildSticker
* @property {'ReactionResolveUser'} ReactionResolveUser
@@ -266,6 +269,7 @@ const keys = [
'GuildChannelUnowned',
'GuildOwned',
'GuildMembersTimeout',
'GuildSoundboardSoundsTimeout',
'GuildUncachedMe',
'ChannelNotCached',
'StageChannelResolve',
@@ -290,6 +294,7 @@ const keys = [
'MissingManageGuildExpressionsPermission',
'MissingManageEmojisAndStickersPermission',
'NotGuildSoundboardSound',
'NotGuildSticker',
'ReactionResolveUser',

View File

@@ -91,11 +91,13 @@ const Messages = {
[DjsErrorCodes.GuildChannelUnowned]: "The fetched channel does not belong to this manager's guild.",
[DjsErrorCodes.GuildOwned]: 'Guild is owned by the client.',
[DjsErrorCodes.GuildMembersTimeout]: "Members didn't arrive in time.",
[DjsErrorCodes.GuildSoundboardSoundsTimeout]: "Soundboard sounds didn't arrive in time.",
[DjsErrorCodes.GuildUncachedMe]: 'The client user as a member of this guild is uncached.',
[DjsErrorCodes.ChannelNotCached]: 'Could not find the channel where this message came from in the cache!',
[DjsErrorCodes.StageChannelResolve]: 'Could not resolve channel to a stage channel.',
[DjsErrorCodes.GuildScheduledEventResolve]: 'Could not resolve the guild scheduled event.',
[DjsErrorCodes.FetchOwnerId]: type => `Couldn't resolve the ${type} ownerId to fetch the ${type} member.`,
[DjsErrorCodes.FetchOwnerId]: type =>
`Couldn't resolve the ${type} ownerId to fetch the ${type} ${type === 'group DM' ? 'owner' : 'member'}.`,
[DjsErrorCodes.InvalidType]: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
[DjsErrorCodes.InvalidElement]: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`,
@@ -117,6 +119,8 @@ const Messages = {
[DjsErrorCodes.MissingManageEmojisAndStickersPermission]: guild =>
`Client must have Manage Emojis and Stickers permission in guild ${guild} to see emoji authors.`,
[DjsErrorCodes.NotGuildSoundboardSound]: action =>
`Soundboard sound is a default (non-guild) soundboard sound and can't be ${action}.`,
[DjsErrorCodes.NotGuildSticker]: 'Sticker is a standard (non-guild) sticker and has no author.',
[DjsErrorCodes.ReactionResolveUser]: "Couldn't resolve the user id to remove from the reaction.",

View File

@@ -29,7 +29,6 @@ exports.ChannelFlagsBitField = require('./util/ChannelFlagsBitField');
exports.Collection = require('@discordjs/collection').Collection;
exports.Constants = require('./util/Constants');
exports.Colors = require('./util/Colors');
__exportStar(require('./util/DataResolver.js'), exports);
exports.Events = require('./util/Events');
exports.Formatters = require('./util/Formatters');
exports.GuildMemberFlagsBitField = require('./util/GuildMemberFlagsBitField').GuildMemberFlagsBitField;
@@ -76,6 +75,7 @@ exports.GuildMemberManager = require('./managers/GuildMemberManager');
exports.GuildMemberRoleManager = require('./managers/GuildMemberRoleManager');
exports.GuildMessageManager = require('./managers/GuildMessageManager');
exports.GuildScheduledEventManager = require('./managers/GuildScheduledEventManager');
exports.GuildSoundboardSoundManager = require('./managers/GuildSoundboardSoundManager.js').GuildSoundboardSoundManager;
exports.GuildStickerManager = require('./managers/GuildStickerManager');
exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager');
exports.MessageManager = require('./managers/MessageManager');
@@ -85,6 +85,7 @@ exports.ReactionManager = require('./managers/ReactionManager');
exports.ReactionUserManager = require('./managers/ReactionUserManager');
exports.RoleManager = require('./managers/RoleManager');
exports.StageInstanceManager = require('./managers/StageInstanceManager');
exports.SubscriptionManager = require('./managers/SubscriptionManager').SubscriptionManager;
exports.ThreadManager = require('./managers/ThreadManager');
exports.ThreadMemberManager = require('./managers/ThreadMemberManager');
exports.UserManager = require('./managers/UserManager');
@@ -123,12 +124,14 @@ exports.CommandInteraction = require('./structures/CommandInteraction');
exports.Collector = require('./structures/interfaces/Collector');
exports.CommandInteractionOptionResolver = require('./structures/CommandInteractionOptionResolver');
exports.Component = require('./structures/Component');
exports.ContainerComponent = require('./structures/ContainerComponent');
exports.ContextMenuCommandInteraction = require('./structures/ContextMenuCommandInteraction');
exports.DMChannel = require('./structures/DMChannel');
exports.Embed = require('./structures/Embed');
exports.EmbedBuilder = require('./structures/EmbedBuilder');
exports.Emoji = require('./structures/Emoji').Emoji;
exports.Entitlement = require('./structures/Entitlement').Entitlement;
exports.FileComponent = require('./structures/FileComponent');
exports.ForumChannel = require('./structures/ForumChannel');
exports.Guild = require('./structures/Guild').Guild;
exports.GuildAuditLogs = require('./structures/GuildAuditLogs');
@@ -146,6 +149,9 @@ exports.GuildScheduledEvent = require('./structures/GuildScheduledEvent').GuildS
exports.GuildTemplate = require('./structures/GuildTemplate');
exports.Integration = require('./structures/Integration');
exports.IntegrationApplication = require('./structures/IntegrationApplication');
exports.InteractionCallback = require('./structures/InteractionCallback');
exports.InteractionCallbackResource = require('./structures/InteractionCallbackResource');
exports.InteractionCallbackResponse = require('./structures/InteractionCallbackResponse');
exports.BaseInteraction = require('./structures/BaseInteraction');
exports.InteractionCollector = require('./structures/InteractionCollector');
exports.InteractionResponse = require('./structures/InteractionResponse');
@@ -158,6 +164,8 @@ exports.Attachment = require('./structures/Attachment');
exports.AttachmentBuilder = require('./structures/AttachmentBuilder');
exports.ModalBuilder = require('./structures/ModalBuilder');
exports.MediaChannel = require('./structures/MediaChannel');
exports.MediaGalleryComponent = require('./structures/MediaGalleryComponent');
exports.MediaGalleryItem = require('./structures/MediaGalleryItem');
exports.MessageCollector = require('./structures/MessageCollector');
exports.MessageComponentInteraction = require('./structures/MessageComponentInteraction');
exports.MessageContextMenuCommandInteraction = require('./structures/MessageContextMenuCommandInteraction');
@@ -177,6 +185,7 @@ exports.ReactionCollector = require('./structures/ReactionCollector');
exports.ReactionEmoji = require('./structures/ReactionEmoji');
exports.RichPresenceAssets = require('./structures/Presence').RichPresenceAssets;
exports.Role = require('./structures/Role').Role;
exports.SectionComponent = require('./structures/SectionComponent');
exports.SelectMenuBuilder = require('./structures/SelectMenuBuilder');
exports.ChannelSelectMenuBuilder = require('./structures/ChannelSelectMenuBuilder');
exports.MentionableSelectMenuBuilder = require('./structures/MentionableSelectMenuBuilder');
@@ -198,23 +207,30 @@ exports.RoleSelectMenuInteraction = require('./structures/RoleSelectMenuInteract
exports.StringSelectMenuInteraction = require('./structures/StringSelectMenuInteraction');
exports.UserSelectMenuInteraction = require('./structures/UserSelectMenuInteraction');
exports.SelectMenuOptionBuilder = require('./structures/SelectMenuOptionBuilder');
exports.SeparatorComponent = require('./structures/SeparatorComponent');
exports.SKU = require('./structures/SKU').SKU;
exports.SoundboardSound = require('./structures/SoundboardSound.js').SoundboardSound;
exports.StringSelectMenuOptionBuilder = require('./structures/StringSelectMenuOptionBuilder');
exports.StageChannel = require('./structures/StageChannel');
exports.StageInstance = require('./structures/StageInstance').StageInstance;
exports.Subscription = require('./structures/Subscription').Subscription;
exports.Sticker = require('./structures/Sticker').Sticker;
exports.StickerPack = require('./structures/StickerPack');
exports.Team = require('./structures/Team');
exports.TeamMember = require('./structures/TeamMember');
exports.TextChannel = require('./structures/TextChannel');
exports.TextDisplayComponent = require('./structures/TextDisplayComponent');
exports.TextInputBuilder = require('./structures/TextInputBuilder');
exports.TextInputComponent = require('./structures/TextInputComponent');
exports.ThreadChannel = require('./structures/ThreadChannel');
exports.ThreadMember = require('./structures/ThreadMember');
exports.ThreadOnlyChannel = require('./structures/ThreadOnlyChannel');
exports.ThumbnailComponent = require('./structures/ThumbnailComponent');
exports.Typing = require('./structures/Typing');
exports.UnfurledMediaItem = require('./structures/UnfurledMediaItem');
exports.User = require('./structures/User');
exports.UserContextMenuCommandInteraction = require('./structures/UserContextMenuCommandInteraction');
exports.VoiceChannelEffect = require('./structures/VoiceChannelEffect');
exports.VoiceChannel = require('./structures/VoiceChannel');
exports.VoiceRegion = require('./structures/VoiceRegion');
exports.VoiceState = require('./structures/VoiceState');

View File

@@ -82,7 +82,7 @@ class ApplicationCommandManager extends CachedManager {
* Options used to fetch Application Commands from Discord
* @typedef {BaseFetchOptions} FetchApplicationCommandOptions
* @property {Snowflake} [guildId] The guild's id to fetch commands for, for when the guild is not cached
* @property {LocaleString} [locale] The locale to use when fetching this command
* @property {Locale} [locale] The locale to use when fetching this command
* @property {boolean} [withLocalizations] Whether to fetch all localization data
*/

View File

@@ -65,12 +65,12 @@ class ApplicationEmojiManager extends CachedManager {
* @returns {Promise<ApplicationEmoji|Collection<Snowflake, ApplicationEmoji>>}
* @example
* // Fetch all emojis from the application
* message.application.emojis.fetch()
* application.emojis.fetch()
* .then(emojis => console.log(`There are ${emojis.size} emojis.`))
* .catch(console.error);
* @example
* // Fetch a single emoji
* message.application.emojis.fetch('222078108977594368')
* application.emojis.fetch('222078108977594368')
* .then(emoji => console.log(`The emoji name is: ${emoji.name}`))
* .catch(console.error);
*/

View File

@@ -1,6 +1,7 @@
'use strict';
const CachedManager = require('./CachedManager');
const ApplicationEmoji = require('../structures/ApplicationEmoji');
const GuildEmoji = require('../structures/GuildEmoji');
const ReactionEmoji = require('../structures/ReactionEmoji');
const { parseEmoji } = require('../util/Util');
@@ -25,7 +26,8 @@ class BaseGuildEmojiManager extends CachedManager {
* * A Snowflake
* * A GuildEmoji object
* * A ReactionEmoji object
* @typedef {Snowflake|GuildEmoji|ReactionEmoji} EmojiResolvable
* * An ApplicationEmoji object
* @typedef {Snowflake|GuildEmoji|ReactionEmoji|ApplicationEmoji} EmojiResolvable
*/
/**
@@ -34,7 +36,8 @@ class BaseGuildEmojiManager extends CachedManager {
* @returns {?GuildEmoji}
*/
resolve(emoji) {
if (emoji instanceof ReactionEmoji) return super.resolve(emoji.id);
if (emoji instanceof ReactionEmoji) return this.cache.get(emoji.id) ?? null;
if (emoji instanceof ApplicationEmoji) return this.cache.get(emoji.id) ?? null;
return super.resolve(emoji);
}
@@ -45,6 +48,7 @@ class BaseGuildEmojiManager extends CachedManager {
*/
resolveId(emoji) {
if (emoji instanceof ReactionEmoji) return emoji.id;
if (emoji instanceof ApplicationEmoji) return emoji.id;
return super.resolveId(emoji);
}
@@ -65,6 +69,7 @@ class BaseGuildEmojiManager extends CachedManager {
const emojiResolvable = this.resolve(emoji);
if (emojiResolvable) return emojiResolvable.identifier;
if (emoji instanceof ReactionEmoji) return emoji.identifier;
if (emoji instanceof ApplicationEmoji) return emoji.identifier;
if (typeof emoji === 'string') {
const res = parseEmoji(emoji);
if (res?.name.length) {

View File

@@ -37,6 +37,12 @@ class EntitlementManager extends CachedManager {
* @typedef {SKU|Snowflake} SKUResolvable
*/
/**
* Options used to fetch an entitlement
* @typedef {BaseFetchOptions} FetchEntitlementOptions
* @property {EntitlementResolvable} entitlement The entitlement to fetch
*/
/**
* Options used to fetch entitlements
* @typedef {Object} FetchEntitlementsOptions
@@ -45,6 +51,7 @@ class EntitlementManager extends CachedManager {
* @property {UserResolvable} [user] The user to fetch entitlements for
* @property {SKUResolvable[]} [skus] The SKUs to fetch entitlements for
* @property {boolean} [excludeEnded] Whether to exclude ended entitlements
* @property {boolean} [excludeDeleted] Whether to exclude deleted entitlements
* @property {boolean} [cache=true] Whether to cache the fetched entitlements
* @property {Snowflake} [before] Consider only entitlements before this entitlement id
* @property {Snowflake} [after] Consider only entitlements after this entitlement id
@@ -53,21 +60,49 @@ class EntitlementManager extends CachedManager {
/**
* Fetches entitlements for this application
* @param {FetchEntitlementsOptions} [options={}] Options for fetching the entitlements
* @returns {Promise<Collection<Snowflake, Entitlement>>}
* @param {EntitlementResolvable|FetchEntitlementOptions|FetchEntitlementsOptions} [options]
* Options for fetching the entitlements
* @returns {Promise<Entitlement|Collection<Snowflake, Entitlement>>}
*/
async fetch({ limit, guild, user, skus, excludeEnded, cache = true, before, after } = {}) {
async fetch(options) {
if (!options) return this._fetchMany(options);
const { entitlement, cache, force } = options;
const resolvedEntitlement = this.resolveId(entitlement ?? options);
if (resolvedEntitlement) {
return this._fetchSingle({ entitlement: resolvedEntitlement, cache, force });
}
return this._fetchMany(options);
}
async _fetchSingle({ entitlement, cache, force = false }) {
if (!force) {
const existing = this.cache.get(entitlement);
if (existing) {
return existing;
}
}
const data = await this.client.rest.get(Routes.entitlement(this.client.application.id, entitlement));
return this._add(data, cache);
}
async _fetchMany({ limit, guild, user, skus, excludeEnded, excludeDeleted, cache, before, after } = {}) {
const query = makeURLSearchParams({
limit,
guild_id: guild && this.client.guilds.resolveId(guild),
user_id: user && this.client.users.resolveId(user),
sku_ids: skus?.map(sku => resolveSKUId(sku)).join(','),
exclude_ended: excludeEnded,
exclude_deleted: excludeDeleted,
before,
after,
});
const entitlements = await this.client.rest.get(Routes.entitlements(this.client.application.id), { query });
return entitlements.reduce(
(coll, entitlement) => coll.set(entitlement.id, this._add(entitlement, cache)),
new Collection(),

View File

@@ -97,14 +97,14 @@ class GuildBanManager extends CachedManager {
* .then(console.log)
* .catch(console.error);
*/
fetch(options) {
async fetch(options) {
if (!options) return this._fetchMany();
const { user, cache, force, limit, before, after } = options;
const resolvedUser = this.client.users.resolveId(user ?? options);
if (resolvedUser) return this._fetchSingle({ user: resolvedUser, cache, force });
if (!before && !after && !limit && cache === undefined) {
return Promise.reject(new DiscordjsError(ErrorCodes.FetchBanResolveId));
throw new DiscordjsError(ErrorCodes.FetchBanResolveId);
}
return this._fetchMany(options);
@@ -175,7 +175,7 @@ class GuildBanManager extends CachedManager {
reason: options.reason,
});
if (user instanceof GuildMember) return user;
const _user = this.client.users.resolve(id);
const _user = this.client.users.cache.get(id);
if (_user) {
return this.guild.members.resolve(_user) ?? _user;
}

View File

@@ -84,7 +84,7 @@ class GuildChannelManager extends CachedManager {
* @returns {?(GuildChannel|ThreadChannel)}
*/
resolve(channel) {
if (channel instanceof ThreadChannel) return super.resolve(channel.id);
if (channel instanceof ThreadChannel) return this.cache.get(channel.id) ?? null;
return super.resolve(channel);
}
@@ -287,7 +287,7 @@ class GuildChannelManager extends CachedManager {
const resolvedChannel = this.resolve(channel);
if (!resolvedChannel) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'channel', 'GuildChannelResolvable');
const parent = options.parent && this.client.channels.resolveId(options.parent);
const parentId = options.parent && this.client.channels.resolveId(options.parent);
if (options.position !== undefined) {
await this.setPosition(resolvedChannel, options.position, { position: options.position, reason: options.reason });
@@ -298,8 +298,8 @@ class GuildChannelManager extends CachedManager {
);
if (options.lockPermissions) {
if (parent) {
const newParent = this.guild.channels.resolve(parent);
if (parentId) {
const newParent = this.cache.get(parentId);
if (newParent?.type === ChannelType.GuildCategory) {
permission_overwrites = newParent.permissionOverwrites.cache.map(overwrite =>
PermissionOverwrites.resolve(overwrite, this.guild),
@@ -322,7 +322,7 @@ class GuildChannelManager extends CachedManager {
user_limit: options.userLimit,
rtc_region: options.rtcRegion,
video_quality_mode: options.videoQualityMode,
parent_id: parent,
parent_id: parentId,
lock_permissions: options.lockPermissions,
rate_limit_per_user: options.rateLimitPerUser,
default_auto_archive_duration: options.defaultAutoArchiveDuration,

View File

@@ -39,14 +39,14 @@ class GuildEmojiRoleManager extends DataManager {
* @param {RoleResolvable|RoleResolvable[]|Collection<Snowflake, Role>} roleOrRoles The role or roles to add
* @returns {Promise<GuildEmoji>}
*/
add(roleOrRoles) {
async add(roleOrRoles) {
if (!Array.isArray(roleOrRoles) && !(roleOrRoles instanceof Collection)) roleOrRoles = [roleOrRoles];
const resolvedRoles = [];
for (const role of roleOrRoles.values()) {
const resolvedRole = this.guild.roles.resolveId(role);
if (!resolvedRole) {
return Promise.reject(new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role));
throw new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role);
}
resolvedRoles.push(resolvedRole);
}
@@ -60,14 +60,14 @@ class GuildEmojiRoleManager extends DataManager {
* @param {RoleResolvable|RoleResolvable[]|Collection<Snowflake, Role>} roleOrRoles The role or roles to remove
* @returns {Promise<GuildEmoji>}
*/
remove(roleOrRoles) {
async remove(roleOrRoles) {
if (!Array.isArray(roleOrRoles) && !(roleOrRoles instanceof Collection)) roleOrRoles = [roleOrRoles];
const resolvedRoleIds = [];
for (const role of roleOrRoles.values()) {
const roleId = this.guild.roles.resolveId(role);
if (!roleId) {
return Promise.reject(new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role));
throw new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role);
}
resolvedRoleIds.push(roleId);
}

View File

@@ -121,22 +121,22 @@ class GuildInviteManager extends CachedManager {
* .then(console.log)
* .catch(console.error);
*/
fetch(options) {
async fetch(options) {
if (!options) return this._fetchMany();
if (typeof options === 'string') {
const code = resolveInviteCode(options);
if (!code) return Promise.reject(new DiscordjsError(ErrorCodes.InviteResolveCode));
if (!code) throw new DiscordjsError(ErrorCodes.InviteResolveCode);
return this._fetchSingle({ code, cache: true });
}
if (!options.code) {
if (options.channelId) {
const id = this.guild.channels.resolveId(options.channelId);
if (!id) return Promise.reject(new DiscordjsError(ErrorCodes.GuildChannelResolve));
if (!id) throw new DiscordjsError(ErrorCodes.GuildChannelResolve);
return this._fetchChannelMany(id, options.cache);
}
if ('cache' in options) return this._fetchMany(options.cache);
return Promise.reject(new DiscordjsError(ErrorCodes.InviteResolveCode));
throw new DiscordjsError(ErrorCodes.InviteResolveCode);
}
return this._fetchSingle({
...options,

View File

@@ -4,8 +4,9 @@ const process = require('node:process');
const { setTimeout, clearTimeout } = require('node:timers');
const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { Routes, RouteBases } = require('discord-api-types/v10');
const { GatewayOpcodes, Routes, RouteBases } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager');
const { ErrorCodes, DiscordjsError } = require('../errors/index.js');
const ShardClientUtil = require('../sharding/ShardClientUtil');
const { Guild } = require('../structures/Guild');
const GuildChannel = require('../structures/GuildChannel');
@@ -18,6 +19,7 @@ const { resolveImage } = require('../util/DataResolver');
const Events = require('../util/Events');
const PermissionsBitField = require('../util/PermissionsBitField');
const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField');
const { _transformAPIIncidentsData } = require('../util/Transformers.js');
const { resolveColor } = require('../util/Util');
let cacheWarningEmitted = false;
@@ -281,6 +283,112 @@ class GuildManager extends CachedManager {
return data.reduce((coll, guild) => coll.set(guild.id, new OAuth2Guild(this.client, guild)), new Collection());
}
/**
* @typedef {Object} FetchSoundboardSoundsOptions
* @param {Snowflake[]} guildIds The ids of the guilds to fetch soundboard sounds for
* @param {number} [time=10_000] The timeout for receipt of the soundboard sounds
*/
/**
* Fetches soundboard sounds for the specified guilds.
* @param {FetchSoundboardSoundsOptions} options The options for fetching soundboard sounds
* @returns {Promise<Collection<Snowflake, Collection<Snowflake, SoundboardSound>>>}
* @example
* // Fetch soundboard sounds for multiple guilds
* const soundboardSounds = await client.guilds.fetchSoundboardSounds({
* guildIds: ['123456789012345678', '987654321098765432'],
* })
*
* console.log(soundboardSounds.get('123456789012345678'));
*/
async fetchSoundboardSounds({ guildIds, time = 10_000 }) {
const shardCount = this.client.options.shardCount;
const shardIds = new Map();
for (const guildId of guildIds) {
const shardId = ShardClientUtil.shardIdForGuildId(guildId, shardCount);
const group = shardIds.get(shardId);
if (group) group.push(guildId);
else shardIds.set(shardId, [guildId]);
}
for (const [shardId, shardGuildIds] of shardIds) {
this.client.ws.shards.get(shardId).send({
op: GatewayOpcodes.RequestSoundboardSounds,
d: {
guild_ids: shardGuildIds,
},
});
}
return new Promise((resolve, reject) => {
const remainingGuildIds = new Set(guildIds);
const fetchedSoundboardSounds = new Collection();
const handler = (soundboardSounds, guild) => {
timeout.refresh();
if (!remainingGuildIds.has(guild.id)) return;
fetchedSoundboardSounds.set(guild.id, soundboardSounds);
remainingGuildIds.delete(guild.id);
if (remainingGuildIds.size === 0) {
clearTimeout(timeout);
this.client.removeListener(Events.SoundboardSounds, handler);
this.client.decrementMaxListeners();
resolve(fetchedSoundboardSounds);
}
};
const timeout = setTimeout(() => {
this.client.removeListener(Events.SoundboardSounds, handler);
this.client.decrementMaxListeners();
reject(new DiscordjsError(ErrorCodes.GuildSoundboardSoundsTimeout));
}, time).unref();
this.client.incrementMaxListeners();
this.client.on(Events.SoundboardSounds, handler);
});
}
/**
* Options used to set incident actions. Supplying `null` to any option will disable the action.
* @typedef {Object} IncidentActionsEditOptions
* @property {?DateResolvable} [invitesDisabledUntil] When invites should be enabled again
* @property {?DateResolvable} [dmsDisabledUntil] When direct messages should be enabled again
*/
/**
* Sets the incident actions for a guild.
* @param {GuildResolvable} guild The guild
* @param {IncidentActionsEditOptions} incidentActions The incident actions to set
* @returns {Promise<IncidentActions>}
*/
async setIncidentActions(guild, { invitesDisabledUntil, dmsDisabledUntil }) {
const guildId = this.resolveId(guild);
const data = await this.client.rest.put(Routes.guildIncidentActions(guildId), {
body: {
invites_disabled_until: invitesDisabledUntil && new Date(invitesDisabledUntil).toISOString(),
dms_disabled_until: dmsDisabledUntil && new Date(dmsDisabledUntil).toISOString(),
},
});
const parsedData = _transformAPIIncidentsData(data);
const resolvedGuild = this.resolve(guild);
if (resolvedGuild) {
resolvedGuild.incidentsData = parsedData;
}
return parsedData;
}
/**
* Returns a URL for the PNG widget of a guild.
* @param {GuildResolvable} guild The guild of the widget image

View File

@@ -54,8 +54,8 @@ class GuildMemberManager extends CachedManager {
resolve(member) {
const memberResolvable = super.resolve(member);
if (memberResolvable) return memberResolvable;
const userResolvable = this.client.users.resolveId(member);
if (userResolvable) return super.resolve(userResolvable);
const userId = this.client.users.resolveId(member);
if (userId) return this.cache.get(userId) ?? null;
return null;
}
@@ -67,8 +67,8 @@ class GuildMemberManager extends CachedManager {
resolveId(member) {
const memberResolvable = super.resolveId(member);
if (memberResolvable) return memberResolvable;
const userResolvable = this.client.users.resolveId(member);
return this.cache.has(userResolvable) ? userResolvable : null;
const userId = this.client.users.resolveId(member);
return this.cache.has(userId) ? userId : null;
}
/**
@@ -144,7 +144,7 @@ class GuildMemberManager extends CachedManager {
*/
get me() {
return (
this.resolve(this.client.user.id) ??
this.cache.get(this.client.user.id) ??
(this.client.options.partials.includes(Partials.GuildMember)
? this._add({ user: { id: this.client.user.id } }, true)
: null)
@@ -223,7 +223,7 @@ class GuildMemberManager extends CachedManager {
return this._add(data, cache);
}
_fetchMany({
async _fetchMany({
limit = 0,
withPresences: presences,
users,
@@ -231,7 +231,7 @@ class GuildMemberManager extends CachedManager {
time = 120e3,
nonce = DiscordSnowflake.generate().toString(),
} = {}) {
if (nonce.length > 32) return Promise.reject(new DiscordjsRangeError(ErrorCodes.MemberFetchNonceLength));
if (nonce.length > 32) throw new DiscordjsRangeError(ErrorCodes.MemberFetchNonceLength);
return new Promise((resolve, reject) => {
if (!query && !users) query = '';
@@ -461,7 +461,7 @@ class GuildMemberManager extends CachedManager {
*/
async kick(user, reason) {
const id = this.client.users.resolveId(user);
if (!id) return Promise.reject(new DiscordjsTypeError(ErrorCodes.InvalidType, 'user', 'UserResolvable'));
if (!id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'user', 'UserResolvable');
await this.client.rest.delete(Routes.guildMember(this.guild.id, id), { reason });
@@ -535,7 +535,7 @@ class GuildMemberManager extends CachedManager {
*/
async addRole(options) {
const { user, role, reason } = options;
const userId = this.guild.members.resolveId(user);
const userId = this.resolveId(user);
const roleId = this.guild.roles.resolveId(role);
await this.client.rest.put(Routes.guildMemberRole(this.guild.id, userId, roleId), { reason });
@@ -549,7 +549,7 @@ class GuildMemberManager extends CachedManager {
*/
async removeRole(options) {
const { user, role, reason } = options;
const userId = this.guild.members.resolveId(user);
const userId = this.resolveId(user);
const roleId = this.guild.roles.resolveId(role);
await this.client.rest.delete(Routes.guildMemberRole(this.guild.id, userId, roleId), { reason });

View File

@@ -101,6 +101,8 @@ class GuildMemberRoleManager extends DataManager {
/**
* Adds a role (or multiple roles) to the member.
*
* <info>Uses the idempotent PUT route for singular roles, otherwise PATCHes the underlying guild member</info>
* @param {RoleResolvable|RoleResolvable[]|Collection<Snowflake, Role>} roleOrRoles The role or roles to add
* @param {string} [reason] Reason for adding the role(s)
* @returns {Promise<GuildMember>}
@@ -138,6 +140,8 @@ class GuildMemberRoleManager extends DataManager {
/**
* Removes a role (or multiple roles) from the member.
*
* <info>Uses the idempotent DELETE route for singular roles, otherwise PATCHes the underlying guild member</info>
* @param {RoleResolvable|RoleResolvable[]|Collection<Snowflake, Role>} roleOrRoles The role or roles to remove
* @param {string} [reason] Reason for removing the role(s)
* @returns {Promise<GuildMember>}

View File

@@ -7,6 +7,7 @@ const CachedManager = require('./CachedManager');
const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../errors');
const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent');
const { resolveImage } = require('../util/DataResolver');
const { _transformGuildScheduledEventRecurrenceRule } = require('../util/Transformers');
/**
* Manages API methods for GuildScheduledEvents and stores their cache.
@@ -36,6 +37,18 @@ class GuildScheduledEventManager extends CachedManager {
* @typedef {Snowflake|GuildScheduledEvent} GuildScheduledEventResolvable
*/
/**
* Options for setting a recurrence rule for a guild scheduled event.
* @typedef {Object} GuildScheduledEventRecurrenceRuleOptions
* @property {DateResolvable} startAt The time the recurrence rule interval starts at
* @property {GuildScheduledEventRecurrenceRuleFrequency} frequency How often the event occurs
* @property {number} interval The spacing between the events
* @property {?GuildScheduledEventRecurrenceRuleWeekday[]} byWeekday The days within a week to recur on
* @property {?GuildScheduledEventRecurrenceRuleNWeekday[]} byNWeekday The days within a week to recur on
* @property {?GuildScheduledEventRecurrenceRuleMonth[]} byMonth The months to recur on
* @property {?number[]} byMonthDay The days within a month to recur on
*/
/**
* Options used to create a guild scheduled event.
* @typedef {Object} GuildScheduledEventCreateOptions
@@ -54,6 +67,8 @@ class GuildScheduledEventManager extends CachedManager {
* <warn>This is required if `entityType` is {@link GuildScheduledEventEntityType.External}</warn>
* @property {?(BufferResolvable|Base64Resolvable)} [image] The cover image of the guild scheduled event
* @property {string} [reason] The reason for creating the guild scheduled event
* @property {GuildScheduledEventRecurrenceRuleOptions} [recurrenceRule]
* The recurrence rule of the guild scheduled event
*/
/**
@@ -81,6 +96,7 @@ class GuildScheduledEventManager extends CachedManager {
entityMetadata,
reason,
image,
recurrenceRule,
} = options;
let entity_metadata, channel_id;
@@ -104,6 +120,7 @@ class GuildScheduledEventManager extends CachedManager {
entity_type: entityType,
entity_metadata,
image: image && (await resolveImage(image)),
recurrence_rule: recurrenceRule && _transformGuildScheduledEventRecurrenceRule(recurrenceRule),
},
reason,
});
@@ -153,10 +170,7 @@ class GuildScheduledEventManager extends CachedManager {
return data.reduce(
(coll, rawGuildScheduledEventData) =>
coll.set(
rawGuildScheduledEventData.id,
this.guild.scheduledEvents._add(rawGuildScheduledEventData, options.cache),
),
coll.set(rawGuildScheduledEventData.id, this._add(rawGuildScheduledEventData, options.cache)),
new Collection(),
);
}
@@ -178,6 +192,8 @@ class GuildScheduledEventManager extends CachedManager {
* {@link GuildScheduledEventEntityType.External}</warn>
* @property {?(BufferResolvable|Base64Resolvable)} [image] The cover image of the guild scheduled event
* @property {string} [reason] The reason for editing the guild scheduled event
* @property {?GuildScheduledEventRecurrenceRuleOptions} [recurrenceRule]
* The recurrence rule of the guild scheduled event
*/
/**
@@ -203,6 +219,7 @@ class GuildScheduledEventManager extends CachedManager {
entityMetadata,
reason,
image,
recurrenceRule,
} = options;
let entity_metadata;
@@ -224,6 +241,7 @@ class GuildScheduledEventManager extends CachedManager {
status,
image: image && (await resolveImage(image)),
entity_metadata,
recurrence_rule: recurrenceRule && _transformGuildScheduledEventRecurrenceRule(recurrenceRule),
},
reason,
});

View File

@@ -0,0 +1,192 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { lazy } = require('@discordjs/util');
const { Routes } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager.js');
const { DiscordjsTypeError, ErrorCodes } = require('../errors/index.js');
const { SoundboardSound } = require('../structures/SoundboardSound.js');
const { resolveBase64, resolveFile } = require('../util/DataResolver.js');
const fileTypeMime = lazy(() => require('magic-bytes.js').filetypemime);
/**
* Manages API methods for Soundboard Sounds and stores their cache.
* @extends {CachedManager}
*/
class GuildSoundboardSoundManager extends CachedManager {
constructor(guild, iterable) {
super(guild.client, SoundboardSound, iterable);
/**
* The guild this manager belongs to
* @type {Guild}
*/
this.guild = guild;
}
/**
* The cache of Soundboard Sounds
* @type {Collection<Snowflake, SoundboardSound>}
* @name GuildSoundboardSoundManager#cache
*/
_add(data, cache) {
return super._add(data, cache, { extras: [this.guild], id: data.sound_id });
}
/**
* Data that resolves to give a SoundboardSound object. This can be:
* * A SoundboardSound object
* * A Snowflake
* @typedef {SoundboardSound|Snowflake} SoundboardSoundResolvable
*/
/**
* Resolves a SoundboardSoundResolvable to a SoundboardSound object.
* @method resolve
* @memberof GuildSoundboardSoundManager
* @instance
* @param {SoundboardSoundResolvable} soundboardSound The SoundboardSound resolvable to identify
* @returns {?SoundboardSound}
*/
/**
* Resolves a {@link SoundboardSoundResolvable} to a {@link SoundboardSound} id.
* @param {SoundboardSoundResolvable} soundboardSound The soundboard sound resolvable to resolve
* @returns {?Snowflake}
*/
resolveId(soundboardSound) {
if (soundboardSound instanceof this.holds) return soundboardSound.soundId;
if (typeof soundboardSound === 'string') return soundboardSound;
return null;
}
/**
* Options used to create a soundboard sound in a guild.
* @typedef {Object} GuildSoundboardSoundCreateOptions
* @property {BufferResolvable|Stream} file The file for the soundboard sound
* @property {string} name The name for the soundboard sound
* @property {string} [contentType] The content type for the soundboard sound file
* @property {number} [volume] The volume (a double) for the soundboard sound, from 0 (inclusive) to 1. Defaults to 1
* @property {Snowflake} [emojiId] The emoji id for the soundboard sound
* @property {string} [emojiName] The emoji name for the soundboard sound
* @property {string} [reason] The reason for creating the soundboard sound
*/
/**
* Creates a new guild soundboard sound.
* @param {GuildSoundboardSoundCreateOptions} options Options for creating a guild soundboard sound
* @returns {Promise<SoundboardSound>} The created soundboard sound
* @example
* // Create a new soundboard sound from a file on your computer
* guild.soundboardSounds.create({ file: './sound.mp3', name: 'sound' })
* .then(sound => console.log(`Created new soundboard sound with name ${sound.name}!`))
* .catch(console.error);
*/
async create({ contentType, emojiId, emojiName, file, name, reason, volume }) {
const resolvedFile = await resolveFile(file);
const resolvedContentType = contentType ?? resolvedFile.contentType ?? fileTypeMime()(resolvedFile.data)[0];
const sound = resolveBase64(resolvedFile.data, resolvedContentType);
const body = { emoji_id: emojiId, emoji_name: emojiName, name, sound, volume };
const soundboardSound = await this.client.rest.post(Routes.guildSoundboardSounds(this.guild.id), {
body,
reason,
});
return this._add(soundboardSound);
}
/**
* Data for editing a soundboard sound.
* @typedef {Object} GuildSoundboardSoundEditOptions
* @property {string} [name] The name of the soundboard sound
* @property {?number} [volume] The volume of the soundboard sound, from 0 to 1
* @property {?Snowflake} [emojiId] The emoji id of the soundboard sound
* @property {?string} [emojiName] The emoji name of the soundboard sound
* @property {string} [reason] The reason for editing the soundboard sound
*/
/**
* Edits a soundboard sound.
* @param {SoundboardSoundResolvable} soundboardSound The soundboard sound to edit
* @param {GuildSoundboardSoundEditOptions} [options={}] The new data for the soundboard sound
* @returns {Promise<SoundboardSound>}
*/
async edit(soundboardSound, options = {}) {
const soundId = this.resolveId(soundboardSound);
if (!soundId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'soundboardSound', 'SoundboardSoundResolvable');
const { emojiId, emojiName, name, reason, volume } = options;
const body = { emoji_id: emojiId, emoji_name: emojiName, name, volume };
const data = await this.client.rest.patch(Routes.guildSoundboardSound(this.guild.id, soundId), {
body,
reason,
});
const existing = this.cache.get(soundId);
if (existing) {
const clone = existing._clone();
clone._patch(data);
return clone;
}
return this._add(data);
}
/**
* Deletes a soundboard sound.
* @param {SoundboardSoundResolvable} soundboardSound The soundboard sound to delete
* @param {string} [reason] Reason for deleting this soundboard sound
* @returns {Promise<void>}
*/
async delete(soundboardSound, reason) {
const soundId = this.resolveId(soundboardSound);
if (!soundId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'soundboardSound', 'SoundboardSoundResolvable');
await this.client.rest.delete(Routes.guildSoundboardSound(this.guild.id, soundId), { reason });
}
/**
* Obtains one or more soundboard sounds from Discord, or the soundboard sound cache if they're already available.
* @param {Snowflake} [id] The soundboard sound's id
* @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<SoundboardSound|Collection<Snowflake, SoundboardSound>>}
* @example
* // Fetch all soundboard sounds from the guild
* guild.soundboardSounds.fetch()
* .then(sounds => console.log(`There are ${sounds.size} soundboard sounds.`))
* .catch(console.error);
* @example
* // Fetch a single soundboard sound
* guild.soundboardSounds.fetch('222078108977594368')
* .then(sound => console.log(`The soundboard sound name is: ${sound.name}`))
* .catch(console.error);
*/
async fetch(id, { cache = true, force = false } = {}) {
if (id) {
if (!force) {
const existing = this.cache.get(id);
if (existing) return existing;
}
const sound = await this.client.rest.get(Routes.guildSoundboardSound(this.guild.id, id));
return this._add(sound, cache);
}
const data = await this.client.rest.get(Routes.guildSoundboardSounds(this.guild.id));
return new Collection(data.map(sound => [sound.sound_id, this._add(sound, cache)]));
}
}
exports.GuildSoundboardSoundManager = GuildSoundboardSoundManager;

View File

@@ -62,15 +62,13 @@ class PermissionOverwriteManager extends CachedManager {
* },
* ], 'Needed to change permissions');
*/
set(overwrites, reason) {
async set(overwrites, reason) {
if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) {
return Promise.reject(
new DiscordjsTypeError(
ErrorCodes.InvalidType,
'overwrites',
'Array or Collection of Permission Overwrites',
true,
),
throw new DiscordjsTypeError(
ErrorCodes.InvalidType,
'overwrites',
'Array or Collection of Permission Overwrites',
true,
);
}
return this.channel.edit({ permissionOverwrites: overwrites, reason });

View File

@@ -38,8 +38,8 @@ class PresenceManager extends CachedManager {
resolve(presence) {
const presenceResolvable = super.resolve(presence);
if (presenceResolvable) return presenceResolvable;
const UserResolvable = this.client.users.resolveId(presence);
return super.resolve(UserResolvable);
const userId = this.client.users.resolveId(presence);
return super.cache.get(userId) ?? null;
}
/**
@@ -50,8 +50,8 @@ class PresenceManager extends CachedManager {
resolveId(presence) {
const presenceResolvable = super.resolveId(presence);
if (presenceResolvable) return presenceResolvable;
const userResolvable = this.client.users.resolveId(presence);
return this.cache.has(userResolvable) ? userResolvable : null;
const userId = this.client.users.resolveId(presence);
return this.cache.has(userId) ? userId : null;
}
}

View File

@@ -0,0 +1,81 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager');
const { DiscordjsTypeError, ErrorCodes } = require('../errors/index');
const { Subscription } = require('../structures/Subscription');
const { resolveSKUId } = require('../util/Util');
/**
* Manages API methods for subscriptions and stores their cache.
* @extends {CachedManager}
*/
class SubscriptionManager extends CachedManager {
constructor(client, iterable) {
super(client, Subscription, iterable);
}
/**
* The cache of this manager
* @type {Collection<Snowflake, Subscription>}
* @name SubscriptionManager#cache
*/
/**
* Options used to fetch a subscription
* @typedef {BaseFetchOptions} FetchSubscriptionOptions
* @property {SKUResolvable} sku The SKU to fetch the subscription for
* @property {Snowflake} subscriptionId The id of the subscription to fetch
*/
/**
* Options used to fetch subscriptions
* @typedef {Object} FetchSubscriptionsOptions
* @property {Snowflake} [after] Consider only subscriptions after this subscription id
* @property {Snowflake} [before] Consider only subscriptions before this subscription id
* @property {number} [limit] The maximum number of subscriptions to fetch
* @property {SKUResolvable} sku The SKU to fetch subscriptions for
* @property {UserResolvable} user The user to fetch entitlements for
* <warn>If both `before` and `after` are provided, only `before` is respected</warn>
*/
/**
* Fetches subscriptions for this application
* @param {FetchSubscriptionOptions|FetchSubscriptionsOptions} [options={}] Options for fetching the subscriptions
* @returns {Promise<Subscription|Collection<Snowflake, Subscription>>}
*/
async fetch(options = {}) {
if (typeof options !== 'object') throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'options', 'object', true);
const { after, before, cache, limit, sku, subscriptionId, user } = options;
const skuId = resolveSKUId(sku);
if (!skuId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'sku', 'SKUResolvable');
if (subscriptionId) {
const subscription = await this.client.rest.get(Routes.skuSubscription(skuId, subscriptionId));
return this._add(subscription, cache);
}
const query = makeURLSearchParams({
limit,
user_id: this.client.users.resolveId(user) ?? undefined,
sku_id: skuId,
before,
after,
});
const subscriptions = await this.client.rest.get(Routes.skuSubscriptions(skuId), { query });
return subscriptions.reduce(
(coll, subscription) => coll.set(subscription.id, this._add(subscription, cache)),
new Collection(),
);
}
}
exports.SubscriptionManager = SubscriptionManager;

View File

@@ -97,7 +97,7 @@ class ThreadManager extends CachedManager {
* Data that can be resolved to a Date object. This can be:
* * A Date object
* * A number representing a timestamp
* * An [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) string
* * An {@link https://en.wikipedia.org/wiki/ISO_8601 ISO 8601} string
* @typedef {Date|number|string} DateResolvable
*/

View File

@@ -1,11 +1,15 @@
'use strict';
const process = require('node:process');
const { Collection } = require('@discordjs/collection');
const { makeURLSearchParams } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v10');
const CachedManager = require('./CachedManager');
const { DiscordjsTypeError, ErrorCodes } = require('../errors');
const ThreadMember = require('../structures/ThreadMember');
const { emitDeprecationWarningForRemoveThreadMember } = require('../util/Util');
let deprecationEmittedForAdd = false;
/**
* Manages API methods for GuildMembers and stores their cache.
@@ -53,7 +57,7 @@ class ThreadMemberManager extends CachedManager {
* @readonly
*/
get me() {
return this.resolve(this.client.user.id);
return this.cache.get(this.client.user.id) ?? null;
}
/**
@@ -71,8 +75,8 @@ class ThreadMemberManager extends CachedManager {
resolve(member) {
const memberResolvable = super.resolve(member);
if (memberResolvable) return memberResolvable;
const userResolvable = this.client.users.resolveId(member);
if (userResolvable) return super.resolve(userResolvable);
const userId = this.client.users.resolveId(member);
if (userId) return super.cache.get(userId) ?? null;
return null;
}
@@ -92,9 +96,20 @@ class ThreadMemberManager extends CachedManager {
* Adds a member to the thread.
* @param {UserResolvable|'@me'} member The member to add
* @param {string} [reason] The reason for adding this member
* <warn>This parameter is **deprecated**. Reasons cannot be used.</warn>
* @returns {Promise<Snowflake>}
*/
async add(member, reason) {
if (reason !== undefined && !deprecationEmittedForAdd) {
process.emitWarning(
// eslint-disable-next-line max-len
'The reason parameter of ThreadMemberManager#add() is deprecated as Discord does not parse them. It will be removed in the next major version.',
'DeprecationWarning',
);
deprecationEmittedForAdd = true;
}
const id = member === '@me' ? member : this.client.users.resolveId(member);
if (!id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'member', 'UserResolvable');
await this.client.rest.put(Routes.threadMembers(this.thread.id, id), { reason });
@@ -105,9 +120,14 @@ class ThreadMemberManager extends CachedManager {
* Remove a user from the thread.
* @param {UserResolvable|'@me'} member The member to remove
* @param {string} [reason] The reason for removing this member from the thread
* <warn>This parameter is **deprecated**. Reasons cannot be used.</warn>
* @returns {Promise<Snowflake>}
*/
async remove(member, reason) {
if (reason !== undefined) {
emitDeprecationWarningForRemoveThreadMember(this.constructor.name);
}
const id = member === '@me' ? member : this.client.users.resolveId(member);
if (!id) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'member', 'UserResolvable');
await this.client.rest.delete(Routes.threadMembers(this.thread.id, id), { reason });

View File

@@ -7,6 +7,7 @@ const { GuildMember } = require('../structures/GuildMember');
const { Message } = require('../structures/Message');
const ThreadMember = require('../structures/ThreadMember');
const User = require('../structures/User');
const { emitDeprecationWarningForUserFetchFlags } = require('../util/Util');
/**
* Manages API methods for users and stores their cache.
@@ -100,8 +101,11 @@ class UserManager extends CachedManager {
* @param {UserResolvable} user The UserResolvable to identify
* @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<UserFlagsBitField>}
* @deprecated <warn>This method is deprecated and will be removed in the next major version.
* Flags may still be retrieved via {@link UserManager#fetch}.</warn>
*/
async fetchFlags(user, options) {
emitDeprecationWarningForUserFetchFlags(this.constructor.name);
return (await this.fetch(user, options)).flags;
}

View File

@@ -120,7 +120,7 @@ class Shard extends EventEmitter {
* before resolving (`-1` or `Infinity` for no wait)
* @returns {Promise<ChildProcess>}
*/
spawn(timeout = 30_000) {
async spawn(timeout = 30_000) {
if (this.process) throw new DiscordjsError(ErrorCodes.ShardingProcessExists, this.id);
if (this.worker) throw new DiscordjsError(ErrorCodes.ShardingWorkerExists, this.id);
@@ -161,7 +161,7 @@ class Shard extends EventEmitter {
*/
this.emit(ShardEvents.Spawn, child);
if (timeout === -1 || timeout === Infinity) return Promise.resolve(child);
if (timeout === -1 || timeout === Infinity) return child;
return new Promise((resolve, reject) => {
const cleanup = () => {
clearTimeout(spawnTimeoutTimer);
@@ -260,10 +260,10 @@ class Shard extends EventEmitter {
* .then(count => console.log(`${count} guilds in shard ${shard.id}`))
* .catch(console.error);
*/
fetchClientValue(prop) {
async fetchClientValue(prop) {
// Shard is dead (maybe respawning), don't cache anything and error immediately
if (!this.process && !this.worker) {
return Promise.reject(new DiscordjsError(ErrorCodes.ShardingNoChildExists, this.id));
throw new DiscordjsError(ErrorCodes.ShardingNoChildExists, this.id);
}
// Cached promise from previous call
@@ -302,13 +302,13 @@ class Shard extends EventEmitter {
* @param {*} [context] The context for the eval
* @returns {Promise<*>} Result of the script execution
*/
eval(script, context) {
async eval(script, context) {
// Stringify the script if it's a Function
const _eval = typeof script === 'function' ? `(${script})(this, ${JSON.stringify(context)})` : script;
// Shard is dead (maybe respawning), don't cache anything and error immediately
if (!this.process && !this.worker) {
return Promise.reject(new DiscordjsError(ErrorCodes.ShardingNoChildExists, this.id));
throw new DiscordjsError(ErrorCodes.ShardingNoChildExists, this.id);
}
// Cached promise from previous call

View File

@@ -8,7 +8,7 @@ const { makeError, makePlainError } = require('../util/Util');
/**
* Helper class for sharded clients spawned as a child process/worker, such as from a {@link ShardingManager}.
* Utilises IPC to send and receive data to/from the master process and other shards.
* Utilizes IPC to send and receive data to/from the master process and other shards.
*/
class ShardClientUtil {
constructor(client, mode) {
@@ -225,7 +225,8 @@ class ShardClientUtil {
* Emitted when the client encounters an error.
* <warn>Errors thrown within this event do not have a catch handler, it is
* recommended to not use async functions as `error` event handlers. See the
* [Node.js docs](https://nodejs.org/api/events.html#capture-rejections-of-promises) for details.</warn>
* {@link https://nodejs.org/api/events.html#capture-rejections-of-promises Node.js documentation}
* for details.)</warn>
* @event Client#error
* @param {Error} error The error encountered
*/

View File

@@ -14,7 +14,7 @@ const { fetchRecommendedShardCount } = require('../util/Util');
* This is a utility class that makes multi-process sharding of a bot an easy and painless experience.
* It works by spawning a self-contained {@link ChildProcess} or {@link Worker} for each individual shard, each
* containing its own instance of your bot's {@link Client}. They all have a line of communication with the master
* process, and there are several useful methods that utilise it in order to simplify tasks that are normally difficult
* process, and there are several useful methods that utilize it in order to simplify tasks that are normally difficult
* with sharding. It can spawn a specific number of shards or the amount that Discord suggests for the bot, and takes a
* path to your main bot script to launch for each one.
* @extends {EventEmitter}
@@ -256,9 +256,9 @@ class ShardingManager extends EventEmitter {
* @param {BroadcastEvalOptions} [options={}] The options for the broadcast
* @returns {Promise<*|Array<*>>} Results of the script execution
*/
broadcastEval(script, options = {}) {
async broadcastEval(script, options = {}) {
if (typeof script !== 'function') {
return Promise.reject(new DiscordjsTypeError(ErrorCodes.ShardingInvalidEvalBroadcast));
throw new DiscordjsTypeError(ErrorCodes.ShardingInvalidEvalBroadcast);
}
return this._performOnShards('eval', [`(${script})(this, ${JSON.stringify(options.context)})`], options.shard);
}
@@ -285,16 +285,16 @@ class ShardingManager extends EventEmitter {
* @returns {Promise<*|Array<*>>} Results of the method execution
* @private
*/
_performOnShards(method, args, shard) {
if (this.shards.size === 0) return Promise.reject(new DiscordjsError(ErrorCodes.ShardingNoShards));
async _performOnShards(method, args, shard) {
if (this.shards.size === 0) throw new DiscordjsError(ErrorCodes.ShardingNoShards);
if (typeof shard === 'number') {
if (this.shards.has(shard)) return this.shards.get(shard)[method](...args);
return Promise.reject(new DiscordjsError(ErrorCodes.ShardingShardNotFound, shard));
throw new DiscordjsError(ErrorCodes.ShardingShardNotFound, shard);
}
if (this.shards.size !== this.shardList.length) {
return Promise.reject(new DiscordjsError(ErrorCodes.ShardingInProcess));
throw new DiscordjsError(ErrorCodes.ShardingInProcess);
}
const promises = [];

View File

@@ -418,7 +418,7 @@ class ApplicationCommand extends Base {
command.descriptionLocalizations ?? command.description_localizations ?? {},
this.descriptionLocalizations ?? {},
) ||
!isEqual(command.integrationTypes ?? command.integration_types ?? [], this.integrationTypes ?? {}) ||
!isEqual(command.integrationTypes ?? command.integration_types ?? [], this.integrationTypes ?? []) ||
!isEqual(command.contexts ?? [], this.contexts ?? [])
) {
return false;

View File

@@ -155,6 +155,14 @@ class BaseChannel extends Base {
return 'availableTags' in this;
}
/**
* Indicates whether this channel is sendable.
* @returns {boolean}
*/
isSendable() {
return 'send' in this;
}
toJSON(...props) {
return super.toJSON({ createdTimestamp: true }, ...props);
}

View File

@@ -107,6 +107,21 @@ class BaseInteraction extends Base {
(coll, entitlement) => coll.set(entitlement.id, this.client.application.entitlements._add(entitlement)),
new Collection(),
);
/* eslint-disable max-len */
/**
* Mapping of installation contexts that the interaction was authorized for the related user or guild ids
* @type {APIAuthorizingIntegrationOwnersMap}
* @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object}
*/
this.authorizingIntegrationOwners = data.authorizing_integration_owners;
/* eslint-enable max-len */
/**
* Context where the interaction was triggered from
* @type {?InteractionContextType}
*/
this.context = data.context ?? null;
}
/**

View File

@@ -9,6 +9,7 @@ const Application = require('./interfaces/Application');
const ApplicationCommandManager = require('../managers/ApplicationCommandManager');
const ApplicationEmojiManager = require('../managers/ApplicationEmojiManager');
const { EntitlementManager } = require('../managers/EntitlementManager');
const { SubscriptionManager } = require('../managers/SubscriptionManager');
const ApplicationFlagsBitField = require('../util/ApplicationFlagsBitField');
const { resolveImage } = require('../util/DataResolver');
const PermissionsBitField = require('../util/PermissionsBitField');
@@ -44,6 +45,12 @@ class ClientApplication extends Application {
* @type {EntitlementManager}
*/
this.entitlements = new EntitlementManager(this.client);
/**
* The subscription manager for this application
* @type {SubscriptionManager}
*/
this.subscriptions = new SubscriptionManager(this.client);
}
_patch(data) {
@@ -166,26 +173,6 @@ class ClientApplication extends Application {
this.guildId ??= null;
}
if ('cover_image' in data) {
/**
* The hash of the application's cover image
* @type {?string}
*/
this.cover = data.cover_image;
} else {
this.cover ??= null;
}
if ('rpc_origins' in data) {
/**
* The application's RPC origins, if enabled
* @type {string[]}
*/
this.rpcOrigins = data.rpc_origins;
} else {
this.rpcOrigins ??= [];
}
if ('bot_require_code_grant' in data) {
/**
* If this application's bot requires a code grant when using the OAuth2 flow
@@ -236,6 +223,36 @@ class ClientApplication extends Application {
this.roleConnectionsVerificationURL ??= null;
}
if ('event_webhooks_url' in data) {
/**
* This application's URL to receive event webhooks
* @type {?string}
*/
this.eventWebhooksURL = data.event_webhooks_url;
} else {
this.eventWebhooksURL ??= null;
}
if ('event_webhooks_status' in data) {
/**
* This application's event webhooks status
* @type {?ApplicationWebhookEventStatus}
*/
this.eventWebhooksStatus = data.event_webhooks_status;
} else {
this.eventWebhooksStatus ??= null;
}
if ('event_webhooks_types' in data) {
/**
* List of event webhooks types this application subscribes to
* @type {?ApplicationWebhookEventType[]}
*/
this.eventWebhooksTypes = data.event_webhooks_types;
} else {
this.eventWebhooksTypes ??= null;
}
/**
* The owner of this OAuth application
* @type {?(User|Team)}
@@ -277,6 +294,10 @@ class ClientApplication extends Application {
* @property {?(BufferResolvable|Base64Resolvable)} [icon] The application's icon
* @property {?(BufferResolvable|Base64Resolvable)} [coverImage] The application's cover image
* @property {string} [interactionsEndpointURL] The application's interaction endpoint URL
* @property {string} [eventWebhooksURL] The application's event webhooks URL
* @property {ApplicationWebhookEventStatus.Enabled|ApplicationWebhookEventStatus.Disabled} [eventWebhooksStatus]
* The application's event webhooks status.
* @property {ApplicationWebhookEventType[]} [eventWebhooksTypes] The application's event webhooks types
* @property {string[]} [tags] The application's tags
*/
@@ -294,6 +315,9 @@ class ClientApplication extends Application {
icon,
coverImage,
interactionsEndpointURL,
eventWebhooksURL,
eventWebhooksStatus,
eventWebhooksTypes,
tags,
} = {}) {
const data = await this.client.rest.patch(Routes.currentApplication(), {
@@ -306,6 +330,9 @@ class ClientApplication extends Application {
icon: icon && (await resolveImage(icon)),
cover_image: coverImage && (await resolveImage(coverImage)),
interactions_endpoint_url: interactionsEndpointURL,
event_webhooks_url: eventWebhooksURL,
event_webhooks_status: eventWebhooksStatus,
event_webhooks_types: eventWebhooksTypes,
tags,
},
});

View File

@@ -45,21 +45,6 @@ class CommandInteraction extends BaseInteraction {
*/
this.commandGuildId = data.data.guild_id ?? null;
/* eslint-disable max-len */
/**
* Mapping of installation contexts that the interaction was authorized for the related user or guild ids
* @type {APIAuthorizingIntegrationOwnersMap}
* @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-authorizing-integration-owners-object}
*/
this.authorizingIntegrationOwners = data.authorizing_integration_owners;
/* eslint-enable max-len */
/**
* Context where the interaction was triggered from
* @type {?InteractionContextType}
*/
this.context = data.context ?? null;
/**
* Whether the reply to this interaction has been deferred
* @type {boolean}

View File

@@ -14,6 +14,15 @@ class Component {
this.data = data;
}
/**
* The id of this component
* @type {number}
* @readonly
*/
get id() {
return this.data.id;
}
/**
* The type of the component
* @type {ComponentType}

View File

@@ -0,0 +1,60 @@
'use strict';
const Component = require('./Component');
const { createComponent } = require('../util/Components');
/**
* Represents a container component
* @extends {Component}
*/
class ContainerComponent extends Component {
constructor({ components, ...data }) {
super(data);
/**
* The components in this container
* @type {Component[]}
* @readonly
*/
this.components = components.map(component => createComponent(component));
}
/**
* The accent color of this container
* @type {?number}
* @readonly
*/
get accentColor() {
return this.data.accent_color ?? null;
}
/**
* The hex accent color of this container
* @type {?string}
* @readonly
*/
get hexAccentColor() {
return typeof this.data.accent_color === 'number'
? `#${this.data.accent_color.toString(16).padStart(6, '0')}`
: (this.data.accent_color ?? null);
}
/**
* Whether this container is spoilered
* @type {boolean}
* @readonly
*/
get spoiler() {
return this.data.spoiler ?? false;
}
/**
* Returns the API-compatible JSON for this component
* @returns {APIContainerComponent}
*/
toJSON() {
return { ...this.data, components: this.components.map(component => component.toJSON()) };
}
}
module.exports = ContainerComponent;

View File

@@ -73,10 +73,9 @@ class Entitlement extends Base {
if ('starts_at' in data) {
/**
* The timestamp at which this entitlement is valid
* <info>This is only `null` for test entitlements</info>
* @type {?number}
*/
this.startsTimestamp = Date.parse(data.starts_at);
this.startsTimestamp = data.starts_at ? Date.parse(data.starts_at) : null;
} else {
this.startsTimestamp ??= null;
}
@@ -84,10 +83,9 @@ class Entitlement extends Base {
if ('ends_at' in data) {
/**
* The timestamp at which this entitlement is no longer valid
* <info>This is only `null` for test entitlements</info>
* @type {?number}
*/
this.endsTimestamp = Date.parse(data.ends_at);
this.endsTimestamp = data.ends_at ? Date.parse(data.ends_at) : null;
} else {
this.endsTimestamp ??= null;
}
@@ -114,7 +112,6 @@ class Entitlement extends Base {
/**
* The start date at which this entitlement is valid
* <info>This is only `null` for test entitlements</info>
* @type {?Date}
*/
get startsAt() {
@@ -123,7 +120,6 @@ class Entitlement extends Base {
/**
* The end date at which this entitlement is no longer valid
* <info>This is only `null` for test entitlements</info>
* @type {?Date}
*/
get endsAt() {

View File

@@ -0,0 +1,40 @@
'use strict';
const Component = require('./Component');
const UnfurledMediaItem = require('./UnfurledMediaItem');
/**
* Represents a file component
* @extends {Component}
*/
class FileComponent extends Component {
constructor({ file, ...data }) {
super(data);
/**
* The media associated with this file
* @type {UnfurledMediaItem}
* @readonly
*/
this.file = new UnfurledMediaItem(file);
}
/**
* Whether this thumbnail is spoilered
* @type {boolean}
* @readonly
*/
get spoiler() {
return this.data.spoiler ?? false;
}
/**
* Returns the API-compatible JSON for this component
* @returns {APIFileComponent}
*/
toJSON() {
return { ...this.data, file: this.file.toJSON() };
}
}
module.exports = FileComponent;

View File

@@ -21,6 +21,7 @@ const GuildEmojiManager = require('../managers/GuildEmojiManager');
const GuildInviteManager = require('../managers/GuildInviteManager');
const GuildMemberManager = require('../managers/GuildMemberManager');
const GuildScheduledEventManager = require('../managers/GuildScheduledEventManager');
const { GuildSoundboardSoundManager } = require('../managers/GuildSoundboardSoundManager');
const GuildStickerManager = require('../managers/GuildStickerManager');
const PresenceManager = require('../managers/PresenceManager');
const RoleManager = require('../managers/RoleManager');
@@ -29,6 +30,7 @@ const VoiceStateManager = require('../managers/VoiceStateManager');
const { resolveImage } = require('../util/DataResolver');
const Status = require('../util/Status');
const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField');
const { _transformAPIIncidentsData } = require('../util/Transformers.js');
const { discordSort, getSortableGroupTypes, resolvePartialEmoji } = require('../util/Util');
/**
@@ -107,6 +109,12 @@ class Guild extends AnonymousGuild {
*/
this.autoModerationRules = new AutoModerationRuleManager(this);
/**
* A manager of the soundboard sounds of this guild.
* @type {GuildSoundboardSoundManager}
*/
this.soundboardSounds = new GuildSoundboardSoundManager(this);
if (!data) return;
if (data.unavailable) {
/**
@@ -470,6 +478,27 @@ class Guild extends AnonymousGuild {
stickers: data.stickers,
});
}
if ('incidents_data' in data) {
/**
* Incident actions of a guild.
* @typedef {Object} IncidentActions
* @property {?Date} invitesDisabledUntil When invites would be enabled again
* @property {?Date} dmsDisabledUntil When direct messages would be enabled again
* @property {?Date} dmSpamDetectedAt When direct message spam was detected
* @property {?Date} raidDetectedAt When a raid was detected
*/
/**
* The incidents data of this guild.
* <info>You will need to fetch the guild using {@link BaseGuild#fetch} if you want to receive
* this property.</info>
* @type {?IncidentActions}
*/
this.incidentsData = data.incidents_data && _transformAPIIncidentsData(data.incidents_data);
} else {
this.incidentsData ??= null;
}
}
/**
@@ -1365,6 +1394,15 @@ class Guild extends AnonymousGuild {
return this.edit({ features });
}
/**
* Sets the incident actions for a guild.
* @param {IncidentActionsEditOptions} incidentActions The incident actions to set
* @returns {Promise<IncidentActions>}
*/
async setIncidentActions(incidentActions) {
return this.client.guilds.setIncidentActions(this.id, incidentActions);
}
/**
* Whether this guild equals another guild. It compares all properties, so for most operations
* it is advisable to just compare `guild.id === guild2.id` as it is much faster and is often

View File

@@ -53,10 +53,11 @@ const Targets = {
* * An application command
* * An auto moderation rule
* * A guild onboarding prompt
* * A soundboard sound
* * An object with an id key if target was deleted or fake entity
* * An object where the keys represent either the new value or the old value
* @typedef {?(Object|Guild|BaseChannel|User|Role|Invite|Webhook|GuildEmoji|Message|Integration|StageInstance|Sticker|
* GuildScheduledEvent|ApplicationCommand|AutoModerationRule|GuildOnboardingPrompt)} AuditLogEntryTarget
* GuildScheduledEvent|ApplicationCommand|AutoModerationRule|GuildOnboardingPrompt|SoundboardSound)} AuditLogEntryTarget
*/
/**
@@ -367,6 +368,8 @@ class GuildAuditLogsEntry {
: changesReduce(this.changes, { id: data.target_id });
} else if (targetType === Targets.GuildOnboarding) {
this.target = changesReduce(this.changes, { id: data.target_id });
} else if (targetType === Targets.SoundboardSound) {
this.target = guild.soundboardSounds.cache.get(data.target_id) ?? { id: data.target_id };
} else if (data.target_id) {
this.target = guild[`${targetType.toLowerCase()}s`]?.cache.get(data.target_id) ?? { id: data.target_id };
}

View File

@@ -265,8 +265,8 @@ class GuildChannel extends BaseChannel {
* Locks in the permission overwrites from the parent channel.
* @returns {Promise<GuildChannel>}
*/
lockPermissions() {
if (!this.parent) return Promise.reject(new DiscordjsError(ErrorCodes.GuildChannelOrphan));
async lockPermissions() {
if (!this.parent) throw new DiscordjsError(ErrorCodes.GuildChannelOrphan);
const permissionOverwrites = this.parent.permissionOverwrites.cache.map(overwrite => overwrite.toJSON());
return this.edit({ permissionOverwrites });
}

View File

@@ -84,6 +84,17 @@ class GuildMember extends Base {
} else if (typeof this.avatar !== 'string') {
this.avatar = null;
}
if ('banner' in data) {
/**
* The guild member's banner hash.
* @type {?string}
*/
this.banner = data.banner;
} else {
this.banner ??= null;
}
if ('joined_at' in data) this.joinedTimestamp = Date.parse(data.joined_at);
if ('premium_since' in data) {
this.premiumSinceTimestamp = data.premium_since ? Date.parse(data.premium_since) : null;
@@ -155,6 +166,15 @@ class GuildMember extends Base {
return this.avatar && this.client.rest.cdn.guildMemberAvatar(this.guild.id, this.id, this.avatar, options);
}
/**
* A link to the member's banner.
* @param {ImageURLOptions} [options={}] Options for the banner URL
* @returns {?string}
*/
bannerURL(options = {}) {
return this.banner && this.client.rest.cdn.guildMemberBanner(this.guild.id, this.id, this.banner, options);
}
/**
* A link to the member's guild avatar if they have one.
* Otherwise, a link to their {@link User#displayAvatarURL} will be returned.
@@ -165,6 +185,16 @@ class GuildMember extends Base {
return this.avatarURL(options) ?? this.user.displayAvatarURL(options);
}
/**
* A link to the member's guild banner if they have one.
* Otherwise, a link to their {@link User#bannerURL} will be returned.
* @param {ImageURLOptions} [options={}] Options for the image URL
* @returns {?string}
*/
displayBannerURL(options) {
return this.bannerURL(options) ?? this.user.bannerURL(options);
}
/**
* The time this member joined the guild
* @type {?Date}
@@ -198,7 +228,7 @@ class GuildMember extends Base {
* @readonly
*/
get presence() {
return this.guild.presences.resolve(this.id);
return this.guild.presences.cache.get(this.id) ?? null;
}
/**
@@ -464,6 +494,7 @@ class GuildMember extends Base {
this.joinedTimestamp === member.joinedTimestamp &&
this.nickname === member.nickname &&
this.avatar === member.avatar &&
this.banner === member.banner &&
this.pending === member.pending &&
this.communicationDisabledUntilTimestamp === member.communicationDisabledUntilTimestamp &&
this.flags.bitfield === member.flags.bitfield &&
@@ -491,7 +522,9 @@ class GuildMember extends Base {
roles: true,
});
json.avatarURL = this.avatarURL();
json.bannerURL = this.bannerURL();
json.displayAvatarURL = this.displayAvatarURL();
json.displayBannerURL = this.displayBannerURL();
return json;
}
}

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