From 93b84ae7a6bf2b5521daa8d38998db3fa1eb8aff Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sat, 19 Oct 2024 01:53:56 +0200 Subject: [PATCH 01/22] refactor!: fix several issues with /ws incorporation (#10556) BREAKING CHANGE: `Client#ping` is nullable now --- packages/discord.js/src/client/Client.js | 21 +++-- packages/discord.js/src/errors/ErrorCodes.js | 81 -------------------- packages/discord.js/typings/index.d.ts | 2 +- 3 files changed, 11 insertions(+), 93 deletions(-) diff --git a/packages/discord.js/src/client/Client.js b/packages/discord.js/src/client/Client.js index cec6ef95ab89..9db2739c3ce3 100644 --- a/packages/discord.js/src/client/Client.js +++ b/packages/discord.js/src/client/Client.js @@ -105,14 +105,6 @@ class Client extends BaseClient { */ this.actions = new ActionsManager(this); - /** - * Shard helpers for the client (only if the process was spawned from a {@link ShardingManager}) - * @type {?ShardClientUtil} - */ - this.shard = process.env.SHARDING_MANAGER - ? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE) - : null; - /** * The user manager of this client * @type {UserManager} @@ -170,6 +162,14 @@ class Client extends BaseClient { */ this.ws = new WebSocketManager(wsOptions); + /** + * Shard helpers for the client (only if the process was spawned from a {@link ShardingManager}) + * @type {?ShardClientUtil} + */ + this.shard = process.env.SHARDING_MANAGER + ? ShardClientUtil.singleton(this, process.env.SHARDING_MANAGER_MODE) + : null; + /** * The voice manager of the client * @type {ClientVoiceManager} @@ -414,12 +414,11 @@ class Client extends BaseClient { /** * The average ping of all WebSocketShards - * @type {number} + * @type {?number} * @readonly */ get ping() { - const sum = this.pings.reduce((a, b) => a + b, 0); - return sum / this.pings.size; + return this.pings.size ? this.pings.reduce((a, b) => a + b, 0) / this.pings.size : null; } /** diff --git a/packages/discord.js/src/errors/ErrorCodes.js b/packages/discord.js/src/errors/ErrorCodes.js index c1552392aa90..f23b3232b442 100644 --- a/packages/discord.js/src/errors/ErrorCodes.js +++ b/packages/discord.js/src/errors/ErrorCodes.js @@ -12,25 +12,8 @@ * @property {'TokenMissing'} TokenMissing * @property {'ApplicationCommandPermissionsTokenMissing'} ApplicationCommandPermissionsTokenMissing - * @property {'WSCloseRequested'} WSCloseRequested - * This property is deprecated. - * @property {'WSConnectionExists'} WSConnectionExists - * This property is deprecated. - * @property {'WSNotOpen'} WSNotOpen - * This property is deprecated. - * @property {'ManagerDestroyed'} ManagerDestroyed - * This property is deprecated. - * @property {'BitFieldInvalid'} BitFieldInvalid - * @property {'ShardingInvalid'} ShardingInvalid - * This property is deprecated. - * @property {'ShardingRequired'} ShardingRequired - * This property is deprecated. - * @property {'InvalidIntents'} InvalidIntents - * This property is deprecated. - * @property {'DisallowedIntents'} DisallowedIntents - * This property is deprecated. * @property {'ShardingNoShards'} ShardingNoShards * @property {'ShardingInProcess'} ShardingInProcess * @property {'ShardingInvalidEvalBroadcast'} ShardingInvalidEvalBroadcast @@ -49,30 +32,10 @@ * @property {'InviteOptionsMissingChannel'} InviteOptionsMissingChannel - * @property {'ButtonLabel'} ButtonLabel - * This property is deprecated. - * @property {'ButtonURL'} ButtonURL - * This property is deprecated. - * @property {'ButtonCustomId'} ButtonCustomId - * This property is deprecated. - - * @property {'SelectMenuCustomId'} SelectMenuCustomId - * This property is deprecated. - * @property {'SelectMenuPlaceholder'} SelectMenuPlaceholder - * This property is deprecated. - * @property {'SelectOptionLabel'} SelectOptionLabel - * This property is deprecated. - * @property {'SelectOptionValue'} SelectOptionValue - * This property is deprecated. - * @property {'SelectOptionDescription'} SelectOptionDescription - * This property is deprecated. - * @property {'InteractionCollectorError'} InteractionCollectorError * @property {'FileNotFound'} FileNotFound - * @property {'UserBannerNotFetched'} UserBannerNotFetched - * This property is deprecated. * @property {'UserNoDMChannel'} UserNoDMChannel * @property {'VoiceNotStageChannel'} VoiceNotStageChannel @@ -82,19 +45,11 @@ * @property {'ReqResourceType'} ReqResourceType - * @property {'ImageFormat'} ImageFormat - * This property is deprecated. - * @property {'ImageSize'} ImageSize - * This property is deprecated. - * @property {'MessageBulkDeleteType'} MessageBulkDeleteType * @property {'MessageContentType'} MessageContentType * @property {'MessageNonceRequired'} MessageNonceRequired * @property {'MessageNonceType'} MessageNonceType - * @property {'SplitMaxLen'} SplitMaxLen - * This property is deprecated. - * @property {'BanResolveId'} BanResolveId * @property {'FetchBanResolveId'} FetchBanResolveId @@ -128,16 +83,11 @@ * @property {'EmojiType'} EmojiType * @property {'EmojiManaged'} EmojiManaged * @property {'MissingManageGuildExpressionsPermission'} MissingManageGuildExpressionsPermission - * @property {'MissingManageEmojisAndStickersPermission'} MissingManageEmojisAndStickersPermission - * This property is deprecated. Use `MissingManageGuildExpressionsPermission` instead. * * @property {'NotGuildSticker'} NotGuildSticker * @property {'ReactionResolveUser'} ReactionResolveUser - * @property {'VanityURL'} VanityURL - * This property is deprecated. - * @property {'InviteResolveCode'} InviteResolveCode * @property {'InviteNotFound'} InviteNotFound @@ -152,8 +102,6 @@ * @property {'InteractionAlreadyReplied'} InteractionAlreadyReplied * @property {'InteractionNotReplied'} InteractionNotReplied - * @property {'InteractionEphemeralReplied'} InteractionEphemeralReplied - * This property is deprecated. * @property {'CommandInteractionOptionNotFound'} CommandInteractionOptionNotFound * @property {'CommandInteractionOptionType'} CommandInteractionOptionType @@ -192,17 +140,8 @@ const keys = [ 'TokenMissing', 'ApplicationCommandPermissionsTokenMissing', - 'WSCloseRequested', - 'WSConnectionExists', - 'WSNotOpen', - 'ManagerDestroyed', - 'BitFieldInvalid', - 'ShardingInvalid', - 'ShardingRequired', - 'InvalidIntents', - 'DisallowedIntents', 'ShardingNoShards', 'ShardingInProcess', 'ShardingInvalidEvalBroadcast', @@ -221,21 +160,10 @@ const keys = [ 'InviteOptionsMissingChannel', - 'ButtonLabel', - 'ButtonURL', - 'ButtonCustomId', - - 'SelectMenuCustomId', - 'SelectMenuPlaceholder', - 'SelectOptionLabel', - 'SelectOptionValue', - 'SelectOptionDescription', - 'InteractionCollectorError', 'FileNotFound', - 'UserBannerNotFetched', 'UserNoDMChannel', 'VoiceNotStageChannel', @@ -245,16 +173,11 @@ const keys = [ 'ReqResourceType', - 'ImageFormat', - 'ImageSize', - 'MessageBulkDeleteType', 'MessageContentType', 'MessageNonceRequired', 'MessageNonceType', - 'SplitMaxLen', - 'BanResolveId', 'FetchBanResolveId', @@ -288,14 +211,11 @@ const keys = [ 'EmojiType', 'EmojiManaged', 'MissingManageGuildExpressionsPermission', - 'MissingManageEmojisAndStickersPermission', 'NotGuildSticker', 'ReactionResolveUser', - 'VanityURL', - 'InviteResolveCode', 'InviteNotFound', @@ -310,7 +230,6 @@ const keys = [ 'InteractionAlreadyReplied', 'InteractionNotReplied', - 'InteractionEphemeralReplied', 'CommandInteractionOptionNotFound', 'CommandInteractionOptionType', diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 2ac75da47a11..ad512d8ed58b 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -985,7 +985,7 @@ export class Client extends BaseClient { public guilds: GuildManager; public lastPingTimestamp: number; public options: Omit & { intents: IntentsBitField }; - public get ping(): number; + public get ping(): number | null; public get readyAt(): If; public readyTimestamp: If; public sweepers: Sweepers; From 3540c3176caf41c0ee12ac03fb15d1769f146a1c Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sat, 19 Oct 2024 02:04:01 +0200 Subject: [PATCH 02/22] 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> --- apps/website/src/components/ParameterNode.tsx | 1 + apps/website/src/components/PropertyNode.tsx | 8 +- .../src/mixins/ApiParameterListMixin.ts | 3 + .../src/model/ApiPackage.ts | 1 + .../src/model/Deserializer.ts | 1 + .../src/model/Parameter.ts | 7 ++ .../src/generators/ApiModelGenerator.ts | 30 +++-- packages/discord.js/src/util/Options.js | 4 +- packages/discord.js/typings/index.d.ts | 2 +- .../scripts/src/builtinDocumentationLinks.ts | 109 ++++++++++++++++++ .../scripts/src/generateSplitDocumentation.ts | 74 ++++++++++-- 11 files changed, 219 insertions(+), 21 deletions(-) create mode 100644 packages/scripts/src/builtinDocumentationLinks.ts diff --git a/apps/website/src/components/ParameterNode.tsx b/apps/website/src/components/ParameterNode.tsx index 02f35ec29b83..030e11820e33 100644 --- a/apps/website/src/components/ParameterNode.tsx +++ b/apps/website/src/components/ParameterNode.tsx @@ -29,6 +29,7 @@ export async function ParameterNode({ {description ? : null} {parameter.name} {parameter.isOptional ? '?' : ''}: + {parameter.defaultValue ? ` = ${parameter.defaultValue}` : ''} {description && parameter.description?.length ? (
diff --git a/apps/website/src/components/PropertyNode.tsx b/apps/website/src/components/PropertyNode.tsx index 2bbc31cae0dd..231eacc541c0 100644 --- a/apps/website/src/components/PropertyNode.tsx +++ b/apps/website/src/components/PropertyNode.tsx @@ -51,7 +51,13 @@ export async function PropertyNode({ {property.displayName} - {property.isOptional ? '?' : ''} : + {property.isOptional ? '?' : ''} : {' '} + {property.summary?.defaultValueBlock.length + ? `= ${property.summary.defaultValueBlock.reduce( + (acc: string, def: { kind: string; text: string }) => `${acc}${def.text}`, + '', + )}` + : ''} diff --git a/packages/api-extractor-model/src/mixins/ApiParameterListMixin.ts b/packages/api-extractor-model/src/mixins/ApiParameterListMixin.ts index 5df2f2d4962b..cb4c895eab95 100644 --- a/packages/api-extractor-model/src/mixins/ApiParameterListMixin.ts +++ b/packages/api-extractor-model/src/mixins/ApiParameterListMixin.ts @@ -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( isOptional: Boolean(parameterOptions.isOptional), isRest: Boolean(parameterOptions.isRest), parent: this, + defaultValue: parameterOptions.defaultValue, }); this[_parameters].push(parameter); @@ -171,6 +173,7 @@ export function ApiParameterListMixin( parameterTypeTokenRange: parameter.parameterTypeExcerpt.tokenRange, isOptional: parameter.isOptional, isRest: parameter.isRest, + defaultValue: parameter.defaultValue, }); } diff --git a/packages/api-extractor-model/src/model/ApiPackage.ts b/packages/api-extractor-model/src/model/ApiPackage.ts index fee2211a6770..89f8b4372ca1 100644 --- a/packages/api-extractor-model/src/model/ApiPackage.ts +++ b/packages/api-extractor-model/src/model/ApiPackage.ts @@ -41,6 +41,7 @@ const MinifyJSONMapping = { constraintTokenRange: 'ctr', dependencies: 'dp', defaultTypeTokenRange: 'dtr', + defaultValue: 'dv', docComment: 'd', endIndex: 'en', excerptTokens: 'ex', diff --git a/packages/api-extractor-model/src/model/Deserializer.ts b/packages/api-extractor-model/src/model/Deserializer.ts index fc60ed1c8920..454912626b39 100644 --- a/packages/api-extractor-model/src/model/Deserializer.ts +++ b/packages/api-extractor-model/src/model/Deserializer.ts @@ -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, }; } diff --git a/packages/api-extractor-model/src/model/Parameter.ts b/packages/api-extractor-model/src/model/Parameter.ts index c0d47f0a6432..158ac5222986 100644 --- a/packages/api-extractor-model/src/model/Parameter.ts +++ b/packages/api-extractor-model/src/model/Parameter.ts @@ -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; } /** diff --git a/packages/api-extractor/src/generators/ApiModelGenerator.ts b/packages/api-extractor/src/generators/ApiModelGenerator.ts index c55268815004..19c46fa2b44c 100644 --- a/packages/api-extractor/src/generators/ApiModelGenerator.ts +++ b/packages/api-extractor/src/generators/ApiModelGenerator.ts @@ -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); @@ -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) ?? ''}${jsDoc.default ? ` (default: ${this._escapeSpecialChars(jsDoc.default)})` : ''}\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 @@ -1529,6 +1535,7 @@ export class ApiModelGenerator { }, isOptional: Boolean(parameter.optional), isRest: parameter.name.startsWith('...'), + defaultValue: parameter.default?.toString(), }); excerptTokens.push(...newTokens); excerptTokens.push({ @@ -1548,6 +1555,7 @@ export class ApiModelGenerator { }, isOptional: Boolean(parameter.optional), isRest: parameter.name.startsWith('...'), + defaultValue: parameter.default?.toString(), }); excerptTokens.push(...newTokens); excerptTokens.push({ @@ -1640,6 +1648,7 @@ export class ApiModelGenerator { private _captureParameters( nodesToCapture: IExcerptBuilderNodeToCapture[], parameterNodes: ts.NodeArray, + jsDoc?: DocgenParamJson[] | undefined, ): IApiParameterOptions[] { const parameters: IApiParameterOptions[] = []; for (const parameter of parameterNodes) { @@ -1650,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(), }); } @@ -1753,7 +1765,7 @@ export class ApiModelGenerator { return input; } - return input.replaceAll(/(?[{}])/g, '\\$'); + return input.replaceAll(/(?[@{}])/g, '\\$'); } private _fixLinkTags(input?: string): string | undefined { @@ -1848,7 +1860,7 @@ export class ApiModelGenerator { isOptional: Boolean(prop.nullable), isReadonly: Boolean(prop.readonly), docComment: this._tsDocParser.parseString( - `/**\n * ${this._fixLinkTags(prop.description) ?? ''}${prop.default ? ` (default: ${this._escapeSpecialChars(prop.default)})` : ''}\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, @@ -1860,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, @@ -1883,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(), }; } @@ -1907,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 ? '?' : '' }: `, }); @@ -1917,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] ?? []); diff --git a/packages/discord.js/src/util/Options.js b/packages/discord.js/src/util/Options.js index 14af76ea692c..aebc63af4d48 100644 --- a/packages/discord.js/src/util/Options.js +++ b/packages/discord.js/src/util/Options.js @@ -26,7 +26,7 @@ const { version } = require('../../package.json'); * @property {MessageMentionOptions} [allowedMentions] The default value for {@link BaseMessageOptions#allowedMentions} * @property {Partials[]} [partials] Structures allowed to be partial. This means events can be emitted even when * they're missing all the data for a particular structure. See the "Partial Structures" topic on the - * [guide](https://discordjs.guide/popular-topics/partials.html) for some + * {@link https://discordjs.guide/popular-topics/partials.html guide} for some * important usage information, as partials require you to put checks in place when handling data. * @property {boolean} [failIfNotExists=true] The default value for {@link MessageReplyOptions#failIfNotExists} * @property {PresenceData} [presence] Presence data to use upon login @@ -37,7 +37,7 @@ const { version } = require('../../package.json'); * @property {WebSocketManagerOptions} [ws] Options for the WebSocketManager * @property {RESTOptions} [rest] Options for the REST manager * @property {Function} [jsonTransformer] A function used to transform outgoing json data - * @property {boolean} [enforceNonce=false] The default value for {@link MessageReplyOptions#enforceNonce} + * @property {boolean} [enforceNonce=false] The default value for {@link MessageCreateOptions#enforceNonce} */ /** diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index ad512d8ed58b..8a603abefb17 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -5731,7 +5731,7 @@ export type GuildAuditLogsResolvable = AuditLogEvent | null; export type GuildAuditLogsTargetType = GuildAuditLogsTypes[keyof GuildAuditLogsTypes][0] | 'All' | 'Unknown'; export type GuildAuditLogsTargets = { - [key in GuildAuditLogsTargetType]: GuildAuditLogsTargetType; + [Key in GuildAuditLogsTargetType]: GuildAuditLogsTargetType; }; export type GuildBanResolvable = GuildBan | UserResolvable; diff --git a/packages/scripts/src/builtinDocumentationLinks.ts b/packages/scripts/src/builtinDocumentationLinks.ts new file mode 100644 index 000000000000..1cafb2dbfc42 --- /dev/null +++ b/packages/scripts/src/builtinDocumentationLinks.ts @@ -0,0 +1,109 @@ +export const BuiltinDocumentationLinks = { + // Built-in types + bigint: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/BigInt', + boolean: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean', + null: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/null', + number: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number', + string: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String', + symbol: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol', + undefined: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined', + + // Built-in classes + AbortSignal: 'https://developer.mozilla.org/docs/Web/API/AbortSignal', + Agent: 'https://undici.nodejs.org/#/docs/api/Agent', + Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array', + ArrayBuffer: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer', + AsyncGenerator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator', + AsyncIterable: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols', + AsyncIterableIterator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols', + Buffer: 'https://nodejs.org/api/buffer.html#class-buffer', + ChildProcess: 'https://nodejs.org/api/child_process.html#class-childprocess', + Date: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date', + Dispatcher: 'https://undici.nodejs.org/#/docs/api/Dispatcher', + Error: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error', + Function: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function', + Generator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Generator', + IncomingMessage: 'https://nodejs.org/api/http.html#class-httpincomingmessage', + Iterable: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols', + IterableIterator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols', + Iterator: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Iterator', + Map: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map', + MessagePort: 'https://nodejs.org/api/worker_threads.html#class-messageport', + Promise: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise', + RangeError: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RangeError', + Readable: 'https://nodejs.org/api/stream.html#class-streamreadable', + ReadableStream: 'https://developer.mozilla.org/docs/Web/API/ReadableStream', + RegExp: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp', + Response: 'https://developer.mozilla.org/docs/Web/API/Response', + ServerResponse: 'https://nodejs.org/api/http.html#class-httpserverresponse', + Set: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set', + Stream: 'https://nodejs.org/api/stream.html#stream', + SymbolConstructor: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Symbol', + TypeError: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError', + URL: 'https://developer.mozilla.org/docs/Web/API/URL', + URLSearchParams: 'https://developer.mozilla.org/docs/Web/API/URLSearchParams', + WeakMap: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakMap', + WeakRef: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakRef', + WeakSet: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WeakSet', + WebSocket: 'https://developer.mozilla.org/docs/Web/API/WebSocket', + Worker: 'https://nodejs.org/api/worker_threads.html#class-worker', + 'NodeJS.Timeout': 'https://nodejs.org/api/timers.html#class-timeout', + + // Typed arrays + BigInt64Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array', + BigUint64Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array', + Float32Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float32Array', + Float64Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float64Array', + Int16Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int16Array', + Int32Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int32Array', + Int8Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Int8Array', + Uint16Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array', + Uint32Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array', + Uint8Array: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array', + Uint8ClampedArray: 'https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray', + + // TypeScript types + any: 'https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any', + keyof: 'https://www.typescriptlang.org/docs/handbook/2/keyof-types.html', + never: 'https://www.typescriptlang.org/docs/handbook/2/functions.html#never', + object: 'https://www.typescriptlang.org/docs/handbook/2/functions.html#object', + ReadonlyArray: 'https://www.typescriptlang.org/docs/handbook/2/objects.html#the-readonlyarray-type', + ReadonlyMap: + 'https://github.com/microsoft/TypeScript/blob/1416053b9e85ca2344a7a6aa10456d633ea1cd65/src/lib/es2015.collection.d.ts#L38-L43', + ReadonlySet: + 'https://github.com/microsoft/TypeScript/blob/1416053b9e85ca2344a7a6aa10456d633ea1cd65/src/lib/es2015.collection.d.ts#L104-L108', + unknown: 'https://www.typescriptlang.org/docs/handbook/2/functions.html#unknown', + this: 'https://www.typescriptlang.org/docs/handbook/2/classes.html#this-types', + typeof: 'https://www.typescriptlang.org/docs/handbook/2/typeof-types.html', + void: 'https://www.typescriptlang.org/docs/handbook/2/functions.html#void', + + // TypeScript utility types + Awaited: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#awaitedtype', + Partial: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype', + Required: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype', + Readonly: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype', + Record: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type', + Pick: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys', + Omit: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys', + Exclude: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers', + Extract: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union', + NonNullable: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#nonnullabletype', + Parameters: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#parameterstype', + ConstructorParameters: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#constructorparameterstype', + ReturnType: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype', + InstanceType: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#instancetypetype', + ThisParameterType: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#thisparametertypetype', + OmitThisParameter: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#omitthisparametertype', + ThisType: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#thistypetype', + Uppercase: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#uppercasestringtype', + Lowercase: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#lowercasestringtype', + Capitalize: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#capitalizestringtype', + Uncapitalize: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#uncapitalizestringtype', + + // External Libraries + AsyncEventEmitter: 'https://github.com/vladfrangu/async_event_emitter', + AsyncQueue: 'https://www.sapphirejs.dev/docs/Documentation/api-utilities/classes/sapphire_async_queue.AsyncQueue', + Redis: 'https://redis.github.io/ioredis/classes/Redis.html', + 'prism.opus.Encoder': 'https://amishshah.github.io/prism-media/opus.Encoder.html', + 'prism.VolumeTransformer': 'https://amishshah.github.io/prism-media/core.VolumeTransformer.html', +} as const; diff --git a/packages/scripts/src/generateSplitDocumentation.ts b/packages/scripts/src/generateSplitDocumentation.ts index 6c523a34ef47..ee83a2b6f2b3 100644 --- a/packages/scripts/src/generateSplitDocumentation.ts +++ b/packages/scripts/src/generateSplitDocumentation.ts @@ -18,10 +18,10 @@ import type { ApiProperty, ApiPropertySignature, ApiTypeAlias, - ApiTypeParameterListMixin, ApiVariable, } from '@discordjs/api-extractor-model'; import { + ApiTypeParameterListMixin, Excerpt, Meaning, ApiAbstractMixin, @@ -49,6 +49,7 @@ import type { DocComment, } from '@microsoft/tsdoc'; import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js'; +import { BuiltinDocumentationLinks } from './builtinDocumentationLinks.js'; import { PACKAGES, fetchVersionDocs, fetchVersions } from './shared.js'; function resolvePackageName(packageName: string) { @@ -225,7 +226,7 @@ function resolveItemURI(item: ApiItemLike): string { : `${item.parent.displayName}:${item.parent.kind}#${item.displayName}`; } -function itemExcerptText(excerpt: Excerpt, apiPackage: ApiPackage) { +function itemExcerptText(excerpt: Excerpt, apiPackage: ApiPackage, parent?: ApiTypeParameterListMixin) { const DISCORD_API_TYPES_VERSION = 'v10'; const DISCORD_API_TYPES_DOCS_URL = `https://discord-api-types.dev/api/discord-api-types-${DISCORD_API_TYPES_VERSION}`; @@ -286,6 +287,27 @@ function itemExcerptText(excerpt: Excerpt, apiPackage: ApiPackage) { }; } + if (token.text in BuiltinDocumentationLinks) { + return { + text: token.text, + href: BuiltinDocumentationLinks[token.text as keyof typeof BuiltinDocumentationLinks], + }; + } + + if (parent?.typeParameters.some((type) => type.name === token.text)) { + const [packageName, parentItem] = parent.canonicalReference.toString().split('!'); + return { + text: token.text, + resolvedItem: { + kind: 'TypeParameter', + displayName: token.text, + containerKey: `${parent.containerKey}|${token.text}`, + uri: `${parentItem}#${token.text}`, + packageName: packageName?.replace('@discordjs/', ''), + }, + }; + } + return { text: token.text, }; @@ -346,9 +368,19 @@ function itemTsDoc(item: DocNode, apiItem: ApiItem) { const resolved = resolveCanonicalReference(codeDestination, apiItem.getAssociatedPackage()); if (!foundItem && !resolved) { + const itemName = codeDestination.memberReferences[0]?.memberIdentifier?.identifier; + + if (itemName && itemName in BuiltinDocumentationLinks) { + return { + kind: DocNodeKind.LinkTag, + text: itemName, + uri: BuiltinDocumentationLinks[itemName as keyof typeof BuiltinDocumentationLinks], + }; + } + return { kind: DocNodeKind.LinkTag, - text: codeDestination.memberReferences[0]?.memberIdentifier?.identifier ?? null, + text: itemName ?? null, }; } @@ -534,6 +566,7 @@ function resolveParameters(item: ApiDocumentedItem & ApiParameterListMixin) { isOptional: param.isOptional, isRest: param.isRest, parameterTypeExcerpt: param.parameterTypeExcerpt, + defaultValue: param.defaultValue, }; }); } @@ -553,9 +586,9 @@ function itemTypeParameters(item: ApiTypeParameterListMixin) { return item.typeParameters.map((typeParam) => ({ name: typeParam.name, - constraintsExcerpt: itemExcerptText(typeParam.constraintExcerpt, item.getAssociatedPackage()!), + constraintsExcerpt: itemExcerptText(typeParam.constraintExcerpt, item.getAssociatedPackage()!, item), isOptional: typeParam.isOptional, - defaultExcerpt: itemExcerptText(typeParam.defaultTypeExcerpt, item.getAssociatedPackage()!), + defaultExcerpt: itemExcerptText(typeParam.defaultTypeExcerpt, item.getAssociatedPackage()!, item), description: typeParam.tsdocTypeParamBlock ? itemTsDoc(typeParam.tsdocTypeParamBlock.content, item) : null, })); } @@ -572,9 +605,14 @@ function itemParameters(item: ApiDocumentedItem & ApiParameterListMixin) { return params.map((param) => ({ name: param.isRest ? `...${param.name}` : param.name, - typeExcerpt: itemExcerptText(param.parameterTypeExcerpt, item.getAssociatedPackage()!), + typeExcerpt: itemExcerptText( + param.parameterTypeExcerpt, + item.getAssociatedPackage()!, + item.getHierarchy().find(ApiTypeParameterListMixin.isBaseClassOf), + ), isOptional: param.isOptional, description: param.description ? itemTsDoc(param.description, item) : null, + defaultValue: param.defaultValue, })); } @@ -622,7 +660,11 @@ function itemProperty(item: ApiItemContainerMixin) { return { ...itemInfo(property.item), inheritedFrom: property.inherited ? resolveItemURI(property.inherited) : null, - typeExcerpt: itemExcerptText(property.item.propertyTypeExcerpt, property.item.getAssociatedPackage()!), + typeExcerpt: itemExcerptText( + property.item.propertyTypeExcerpt, + property.item.getAssociatedPackage()!, + property.item.getHierarchy().find(ApiTypeParameterListMixin.isBaseClassOf), + ), summary: hasSummary ? itemTsDoc(property.item.tsdocComment!, property.item) : null, }; }); @@ -658,7 +700,11 @@ function itemMethod(item: ApiItemContainerMixin) { ...itemInfo(method.item), overloadIndex: method.item.overloadIndex, parametersString: parametersString(method.item), - returnTypeExcerpt: itemExcerptText(method.item.returnTypeExcerpt, method.item.getAssociatedPackage()!), + returnTypeExcerpt: itemExcerptText( + method.item.returnTypeExcerpt, + method.item.getAssociatedPackage()!, + method.item.getHierarchy().find(ApiTypeParameterListMixin.isBaseClassOf), + ), inheritedFrom: method.inherited ? resolveItemURI(method.inherited) : null, typeParameters: itemTypeParameters(method.item), parameters: itemParameters(method.item), @@ -754,7 +800,11 @@ export function itemHierarchyText({ return excerpts.map((excerpt) => { return { type, - excerpts: itemExcerptText(excerpt, item.getAssociatedPackage()!), + excerpts: itemExcerptText( + excerpt, + item.getAssociatedPackage()!, + item.getHierarchy().find(ApiTypeParameterListMixin.isBaseClassOf), + ), }; }); } @@ -848,7 +898,11 @@ function itemTypeAlias(item: ApiTypeAlias) { ...itemInfo(item), typeParameters: itemTypeParameters(item), unionMembers: itemUnion(item).map((member) => - itemExcerptText(new Excerpt(member, { startIndex: 0, endIndex: member.length }), item.getAssociatedPackage()!), + itemExcerptText( + new Excerpt(member, { startIndex: 0, endIndex: member.length }), + item.getAssociatedPackage()!, + item.getHierarchy().find(ApiTypeParameterListMixin.isBaseClassOf), + ), ), }; } From 6cbe2487bc91b61e0a65df879a8065ab48f7843b Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Sat, 19 Oct 2024 21:34:51 +0200 Subject: [PATCH 03/22] fix: missing tsdocConfig in api.json preventing index generation (#10565) --- packages/api-extractor-model/src/model/ApiPackage.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/api-extractor-model/src/model/ApiPackage.ts b/packages/api-extractor-model/src/model/ApiPackage.ts index 89f8b4372ca1..e55b843b9b5f 100644 --- a/packages/api-extractor-model/src/model/ApiPackage.ts +++ b/packages/api-extractor-model/src/model/ApiPackage.ts @@ -294,7 +294,11 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration(); - if (versionToDeserialize >= ApiJsonSchemaVersion.V_1004 && 'tsdocConfig' in jsonObject.metadata) { + if ( + versionToDeserialize >= ApiJsonSchemaVersion.V_1004 && + 'tsdocConfig' in jsonObject.metadata && + '$schema' in jsonObject.metadata.tsdocConfig + ) { const tsdocConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromObject(jsonObject.metadata.tsdocConfig); if (tsdocConfigFile.hasErrors) { throw new Error(`Error loading ${apiJsonFilename}:\n` + tsdocConfigFile.getErrorSummary()); From 48a9c665dedf9f5085a7992df3a87de3bca2301a Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Tue, 22 Oct 2024 07:10:30 +0100 Subject: [PATCH 04/22] refactor(InteractionResponses)!: Remove `ephemeral` response option (#10564) BREAKING CHANGE: MessagePayload#isInteraction no longer serves a purpose and has been removed. BREAKING CHANGE: InteractionDeferReplyOptions no longer accepts ephemeral. Use flags instead. BREAKING CHANGE: InteractionReplyOptions no longer accepts ephemeral. Use flags instead. --- .../src/structures/MessagePayload.js | 21 +---------------- .../interfaces/InteractionResponses.js | 23 +++++++++---------- .../src/util/MessageFlagsBitField.js | 9 ++++++++ packages/discord.js/typings/index.d.ts | 11 +++++---- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/packages/discord.js/src/structures/MessagePayload.js b/packages/discord.js/src/structures/MessagePayload.js index dad8ffedb026..7dc567924ba3 100644 --- a/packages/discord.js/src/structures/MessagePayload.js +++ b/packages/discord.js/src/structures/MessagePayload.js @@ -1,17 +1,14 @@ 'use strict'; const { Buffer } = require('node:buffer'); -const { lazy, isJSONEncodable } = require('@discordjs/util'); +const { isJSONEncodable } = require('@discordjs/util'); const { DiscordSnowflake } = require('@sapphire/snowflake'); -const { MessageFlags } = require('discord-api-types/v10'); const ActionRowBuilder = require('./ActionRowBuilder'); const { DiscordjsError, DiscordjsRangeError, ErrorCodes } = require('../errors'); const { resolveFile } = require('../util/DataResolver'); const MessageFlagsBitField = require('../util/MessageFlagsBitField'); const { basename, verifyString, resolvePartialEmoji } = require('../util/Util'); -const getBaseInteraction = lazy(() => require('./BaseInteraction')); - /** * Represents a message to be sent to the API. */ @@ -88,17 +85,6 @@ class MessagePayload { return this.target instanceof MessageManager; } - /** - * Whether or not the target is an {@link BaseInteraction} or an {@link InteractionWebhook} - * @type {boolean} - * @readonly - */ - get isInteraction() { - const BaseInteraction = getBaseInteraction(); - const InteractionWebhook = require('./InteractionWebhook'); - return this.target instanceof BaseInteraction || this.target instanceof InteractionWebhook; - } - /** * Makes the content of this message. * @returns {?string} @@ -120,7 +106,6 @@ class MessagePayload { */ resolveBody() { if (this.body) return this; - const isInteraction = this.isInteraction; const isWebhook = this.isWebhook; const content = this.makeContent(); @@ -175,10 +160,6 @@ class MessagePayload { : this.target.flags?.bitfield; } - if (isInteraction && this.options.ephemeral) { - flags |= MessageFlags.Ephemeral; - } - let allowedMentions = this.options.allowedMentions === undefined ? this.target.client.options.allowedMentions diff --git a/packages/discord.js/src/structures/interfaces/InteractionResponses.js b/packages/discord.js/src/structures/interfaces/InteractionResponses.js index 5c8900530f61..7b1cfb1429f0 100644 --- a/packages/discord.js/src/structures/interfaces/InteractionResponses.js +++ b/packages/discord.js/src/structures/interfaces/InteractionResponses.js @@ -3,7 +3,6 @@ const { isJSONEncodable } = require('@discordjs/util'); const { InteractionResponseType, MessageFlags, Routes, InteractionType } = require('discord-api-types/v10'); const { DiscordjsError, ErrorCodes } = require('../../errors'); -const MessageFlagsBitField = require('../../util/MessageFlagsBitField'); const InteractionCollector = require('../InteractionCollector'); const InteractionResponse = require('../InteractionResponse'); const MessagePayload = require('../MessagePayload'); @@ -23,7 +22,8 @@ class InteractionResponses { /** * Options for deferring the reply to an {@link BaseInteraction}. * @typedef {Object} InteractionDeferReplyOptions - * @property {boolean} [ephemeral] Whether the reply should be ephemeral + * @property {MessageFlagsResolvable} [flags] Flags for the reply. + * Only `MessageFlags.Ephemeral` can be set. * @property {boolean} [fetchReply] Whether to fetch the reply */ @@ -37,9 +37,8 @@ class InteractionResponses { * Options for a reply to a {@link BaseInteraction}. * @typedef {BaseMessageOptionsWithPoll} InteractionReplyOptions * @property {boolean} [tts=false] Whether the message should be spoken aloud - * @property {boolean} [ephemeral] Whether the reply should be ephemeral * @property {boolean} [fetchReply] Whether to fetch the reply - * @property {MessageFlags} [flags] Which flags to set for the message. + * @property {MessageFlagsResolvable} [flags] Which flags to set for the message. * Only `MessageFlags.Ephemeral`, `MessageFlags.SuppressEmbeds`, and `MessageFlags.SuppressNotifications` * can be set. */ @@ -61,24 +60,25 @@ class InteractionResponses { * .catch(console.error) * @example * // Defer to send an ephemeral reply later - * interaction.deferReply({ ephemeral: true }) + * interaction.deferReply({ flags: MessageFlags.Ephemeral }) * .then(console.log) * .catch(console.error); */ async deferReply(options = {}) { if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied); - this.ephemeral = options.ephemeral ?? false; + await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.DeferredChannelMessageWithSource, data: { - flags: options.ephemeral ? MessageFlags.Ephemeral : undefined, + flags: options.flags, }, }, auth: false, }); - this.deferred = true; + this.deferred = true; + this.ephemeral = Boolean(options.flags & MessageFlags.Ephemeral); return options.fetchReply ? this.fetchReply() : new InteractionResponse(this); } @@ -96,7 +96,7 @@ class InteractionResponses { * // Create an ephemeral reply with an embed * const embed = new EmbedBuilder().setDescription('Pong!'); * - * interaction.reply({ embeds: [embed], ephemeral: true }) + * interaction.reply({ embeds: [embed], flags: MessageFlags.Ephemeral }) * .then(() => console.log('Reply sent.')) * .catch(console.error); */ @@ -109,8 +109,6 @@ class InteractionResponses { const { body: data, files } = await messagePayload.resolveBody().resolveFiles(); - this.ephemeral = new MessageFlagsBitField(data.flags).has(MessageFlags.Ephemeral); - await this.client.rest.post(Routes.interactionCallback(this.id, this.token), { body: { type: InteractionResponseType.ChannelMessageWithSource, @@ -119,8 +117,9 @@ class InteractionResponses { files, auth: false, }); - this.replied = true; + this.ephemeral = Boolean(options.flags & MessageFlags.Ephemeral); + this.replied = true; return options.fetchReply ? this.fetchReply() : new InteractionResponse(this); } diff --git a/packages/discord.js/src/util/MessageFlagsBitField.js b/packages/discord.js/src/util/MessageFlagsBitField.js index 71f1fd6c5c08..e6764b2fd791 100644 --- a/packages/discord.js/src/util/MessageFlagsBitField.js +++ b/packages/discord.js/src/util/MessageFlagsBitField.js @@ -23,6 +23,15 @@ class MessageFlagsBitField extends BitField { * @param {BitFieldResolvable} [bits=0] Bit(s) to read from */ +/** + * Data that can be resolved to give a message flags bit field. This can be: + * * A string (see {@link MessageFlagsBitField.Flags}) + * * A message flag + * * An instance of {@link MessageFlagsBitField} + * * An array of `MessageFlagsResolvable` + * @typedef {string|number|MessageFlagsBitField|MessageFlagsResolvable[]} MessageFlagsResolvable + */ + /** * Bitfield of the packed bits * @type {number} diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 8a603abefb17..edc0c29965a9 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -2398,7 +2398,6 @@ export class MessagePayload { public get isWebhook(): boolean; public get isMessage(): boolean; public get isMessageManager(): boolean; - public get isInteraction(): boolean; public files: RawFile[] | null; public options: MessagePayloadOption; public target: MessageTarget; @@ -6066,15 +6065,19 @@ export interface InteractionCollectorOptions< } export interface InteractionDeferReplyOptions { - ephemeral?: boolean; + flags?: BitFieldResolvable< + Extract, + MessageFlags.Ephemeral | MessageFlags.SuppressEmbeds | MessageFlags.SuppressNotifications + >; fetchReply?: boolean; } -export interface InteractionDeferUpdateOptions extends Omit {} +export interface InteractionDeferUpdateOptions { + fetchReply?: boolean; +} export interface InteractionReplyOptions extends BaseMessageOptionsWithPoll { tts?: boolean; - ephemeral?: boolean; fetchReply?: boolean; flags?: BitFieldResolvable< Extract, From b932b64d94f23a03ee65ed54ff0f2666ced7a773 Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:39:47 +0100 Subject: [PATCH 05/22] refactor: remove extra traversing (#10580) * refactor: remove extra traversion * refactor(GuildScheduledEventManager): address fetch --- packages/discord.js/src/managers/GuildChannelManager.js | 2 +- packages/discord.js/src/managers/GuildMemberManager.js | 4 ++-- .../discord.js/src/managers/GuildScheduledEventManager.js | 5 +---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/discord.js/src/managers/GuildChannelManager.js b/packages/discord.js/src/managers/GuildChannelManager.js index b981801b601f..4e3753b7198f 100644 --- a/packages/discord.js/src/managers/GuildChannelManager.js +++ b/packages/discord.js/src/managers/GuildChannelManager.js @@ -299,7 +299,7 @@ class GuildChannelManager extends CachedManager { if (options.lockPermissions) { if (parent) { - const newParent = this.guild.channels.resolve(parent); + const newParent = this.resolve(parent); if (newParent?.type === ChannelType.GuildCategory) { permission_overwrites = newParent.permissionOverwrites.cache.map(overwrite => PermissionOverwrites.resolve(overwrite, this.guild), diff --git a/packages/discord.js/src/managers/GuildMemberManager.js b/packages/discord.js/src/managers/GuildMemberManager.js index fb471328447f..d65cb1c5dd0c 100644 --- a/packages/discord.js/src/managers/GuildMemberManager.js +++ b/packages/discord.js/src/managers/GuildMemberManager.js @@ -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 }); diff --git a/packages/discord.js/src/managers/GuildScheduledEventManager.js b/packages/discord.js/src/managers/GuildScheduledEventManager.js index 383875d70002..02e14c06259b 100644 --- a/packages/discord.js/src/managers/GuildScheduledEventManager.js +++ b/packages/discord.js/src/managers/GuildScheduledEventManager.js @@ -173,10 +173,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(), ); } From ed78e45706ba7a6bfa9f868580ab21eb1c23f58c Mon Sep 17 00:00:00 2001 From: Pablo <79182286+Yareaj@users.noreply.github.com> Date: Sun, 27 Oct 2024 01:15:28 -0500 Subject: [PATCH 06/22] build: bump discord-api-types version (#10575) * chore: bump discord-api-types version * fix: delete extra file --- packages/builders/package.json | 2 +- packages/core/package.json | 2 +- packages/discord.js/package.json | 2 +- packages/formatters/package.json | 2 +- packages/next/package.json | 2 +- packages/rest/package.json | 2 +- packages/voice/package.json | 2 +- packages/ws/package.json | 2 +- pnpm-lock.yaml | 242 +++++++++++++++++++------------ 9 files changed, 158 insertions(+), 100 deletions(-) diff --git a/packages/builders/package.json b/packages/builders/package.json index f678a01e0ab7..0f0548de4eb8 100644 --- a/packages/builders/package.json +++ b/packages/builders/package.json @@ -66,7 +66,7 @@ "funding": "https://github.com/discordjs/discord.js?sponsor", "dependencies": { "@discordjs/util": "workspace:^", - "discord-api-types": "^0.37.101", + "discord-api-types": "^0.37.103", "ts-mixer": "^6.0.4", "tslib": "^2.6.3", "zod": "^3.23.8", diff --git a/packages/core/package.json b/packages/core/package.json index b4e74637898e..e6d7e5eabc88 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -70,7 +70,7 @@ "@discordjs/ws": "workspace:^", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", - "discord-api-types": "^0.37.101" + "discord-api-types": "^0.37.103" }, "devDependencies": { "@discordjs/api-extractor": "workspace:^", diff --git a/packages/discord.js/package.json b/packages/discord.js/package.json index e64d85856d0b..6a3e69fcbccf 100644 --- a/packages/discord.js/package.json +++ b/packages/discord.js/package.json @@ -72,7 +72,7 @@ "@discordjs/util": "workspace:^", "@discordjs/ws": "workspace:^", "@sapphire/snowflake": "3.5.3", - "discord-api-types": "^0.37.101", + "discord-api-types": "^0.37.103", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "tslib": "^2.6.3", diff --git a/packages/formatters/package.json b/packages/formatters/package.json index 5e5f348e5db6..b895f26e618e 100644 --- a/packages/formatters/package.json +++ b/packages/formatters/package.json @@ -55,7 +55,7 @@ "homepage": "https://discord.js.org", "funding": "https://github.com/discordjs/discord.js?sponsor", "dependencies": { - "discord-api-types": "^0.37.101" + "discord-api-types": "^0.37.103" }, "devDependencies": { "@discordjs/api-extractor": "workspace:^", diff --git a/packages/next/package.json b/packages/next/package.json index 2ac8450050f9..a8a01bc6ce78 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -72,7 +72,7 @@ "@discordjs/rest": "workspace:^", "@discordjs/util": "workspace:^", "@discordjs/ws": "workspace:^", - "discord-api-types": "^0.37.101" + "discord-api-types": "^0.37.103" }, "devDependencies": { "@discordjs/api-extractor": "workspace:^", diff --git a/packages/rest/package.json b/packages/rest/package.json index 4288337d3280..425a3afcb246 100644 --- a/packages/rest/package.json +++ b/packages/rest/package.json @@ -88,7 +88,7 @@ "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", - "discord-api-types": "^0.37.101", + "discord-api-types": "^0.37.103", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.19.8" diff --git a/packages/voice/package.json b/packages/voice/package.json index f33cee6492be..3a8c22360e26 100644 --- a/packages/voice/package.json +++ b/packages/voice/package.json @@ -64,7 +64,7 @@ "funding": "https://github.com/discordjs/discord.js?sponsor", "dependencies": { "@types/ws": "^8.5.12", - "discord-api-types": "^0.37.101", + "discord-api-types": "^0.37.103", "prism-media": "^1.3.5", "tslib": "^2.6.3", "ws": "^8.18.0" diff --git a/packages/ws/package.json b/packages/ws/package.json index d49a1006d9b2..0612473945c0 100644 --- a/packages/ws/package.json +++ b/packages/ws/package.json @@ -79,7 +79,7 @@ "@sapphire/async-queue": "^1.5.3", "@types/ws": "^8.5.12", "@vladfrangu/async_event_emitter": "^2.4.6", - "discord-api-types": "^0.37.101", + "discord-api-types": "^0.37.103", "tslib": "^2.6.3", "ws": "^8.18.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ff2a3ee1619..988f6e6f35df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -674,8 +674,8 @@ importers: specifier: workspace:^ version: link:../util discord-api-types: - specifier: ^0.37.101 - version: 0.37.101 + specifier: ^0.37.103 + version: 0.37.103 ts-mixer: specifier: ^6.0.4 version: 6.0.4 @@ -801,8 +801,8 @@ importers: specifier: ^2.4.6 version: 2.4.6 discord-api-types: - specifier: ^0.37.101 - version: 0.37.101 + specifier: ^0.37.103 + version: 0.37.103 devDependencies: '@discordjs/api-extractor': specifier: workspace:^ @@ -938,8 +938,8 @@ importers: specifier: 3.5.3 version: 3.5.3 discord-api-types: - specifier: ^0.37.101 - version: 0.37.101 + specifier: ^0.37.103 + version: 0.37.103 fast-deep-equal: specifier: 3.1.3 version: 3.1.3 @@ -1057,8 +1057,8 @@ importers: packages/formatters: dependencies: discord-api-types: - specifier: ^0.37.101 - version: 0.37.101 + specifier: ^0.37.103 + version: 0.37.103 devDependencies: '@discordjs/api-extractor': specifier: workspace:^ @@ -1130,8 +1130,8 @@ importers: specifier: workspace:^ version: link:../ws discord-api-types: - specifier: ^0.37.101 - version: 0.37.101 + specifier: ^0.37.103 + version: 0.37.103 devDependencies: '@discordjs/api-extractor': specifier: workspace:^ @@ -1304,8 +1304,8 @@ importers: specifier: ^2.4.6 version: 2.4.6 discord-api-types: - specifier: ^0.37.101 - version: 0.37.101 + specifier: ^0.37.103 + version: 0.37.103 magic-bytes.js: specifier: ^1.10.0 version: 1.10.0 @@ -1601,8 +1601,8 @@ importers: specifier: ^8.5.12 version: 8.5.12 discord-api-types: - specifier: ^0.37.101 - version: 0.37.101 + specifier: ^0.37.103 + version: 0.37.103 prism-media: specifier: ^1.3.5 version: 1.3.5(@discordjs/opus@0.9.0(encoding@0.1.13)) @@ -1689,8 +1689,8 @@ importers: specifier: ^2.4.6 version: 2.4.6 discord-api-types: - specifier: ^0.37.101 - version: 0.37.101 + specifier: ^0.37.103 + version: 0.37.103 tslib: specifier: ^2.6.3 version: 2.6.3 @@ -2589,16 +2589,16 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@definitelytyped/header-parser@0.2.12': - resolution: {integrity: sha512-UYtSXiLMhzRFKh7xHMkgiWsscgHxIndmjetaptZMMS0EOvfhUTuEM68GpjiCtz5shXw22Vacs1vDTAkKGDhNmg==} + '@definitelytyped/header-parser@0.2.13': + resolution: {integrity: sha512-m7YEtGhwAjmQyJQFQ7q8+hTGTiC/WrdRATvw8fyTwgW+RiWUt8MAeehuFj4txnCYXDcLO0ozuW5gNrLoYR4Ubg==} engines: {node: '>=18.18.0'} '@definitelytyped/typescript-versions@0.1.4': resolution: {integrity: sha512-4Rz5kCpyxofwXCtBQaNfmWYXZcH0sMJxpbIgJzS+PAxgFCAa9W+2Jil7rrkpzsjx9E7+zOPukbXBXjyXohcyuQ==} engines: {node: '>=18.18.0'} - '@definitelytyped/utils@0.1.7': - resolution: {integrity: sha512-t58AeNg6+mvyMnBHyPC6JQqWMW0Iwyb+vlpBz4V0d0iDY9H8gGCnLFg9vtN1nC+JXfTXBlf9efu9unMUeaPCiA==} + '@definitelytyped/utils@0.1.8': + resolution: {integrity: sha512-4JINx4Rttha29f50PBsJo48xZXx/He5yaIWJRwVarhYAN947+S84YciHl+AIhQNRPAFkg8+5qFngEGtKxQDWXA==} engines: {node: '>=18.18.0'} '@discordjs/builders@1.9.0': @@ -5680,6 +5680,9 @@ packages: '@types/node@18.19.45': resolution: {integrity: sha512-VZxPKNNhjKmaC1SUYowuXSRSMGyQGmQjvvA1xE4QZ0xce2kLtEhPDS+kqpCPBZYgqblCLQ2DAjSzmgCM5auvhA==} + '@types/node@18.19.59': + resolution: {integrity: sha512-vizm2EqwV/7Zay+A6J3tGl9Lhr7CjZe2HmWS988sefiEmsyP9CeXEleho6i4hJk/8UtZAo0bWN4QPZZr83RxvQ==} + '@types/node@20.16.1': resolution: {integrity: sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==} @@ -6577,8 +6580,8 @@ packages: aws-sign2@0.7.0: resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - aws4@1.13.1: - resolution: {integrity: sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA==} + aws4@1.13.2: + resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} axe-core@4.10.0: resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} @@ -6590,8 +6593,8 @@ packages: axobject-query@4.0.0: resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} - b4a@1.6.6: - resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} babel-code-frame@6.26.0: resolution: {integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==} @@ -6650,8 +6653,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.4.2: - resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} + bare-events@2.5.0: + resolution: {integrity: sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==} base64-js@0.0.8: resolution: {integrity: sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==} @@ -7470,6 +7473,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} engines: {node: '>=0.10.0'} @@ -7612,8 +7624,8 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - discord-api-types@0.37.101: - resolution: {integrity: sha512-2wizd94t7G3A8U5Phr3AiuL4gSvhqistDwWnlk1VLTit8BI1jWUncFqFQNdPbHqS3661+Nx/iEyIwtVjPuBP3w==} + discord-api-types@0.37.103: + resolution: {integrity: sha512-r+qitxXKe2l6KFw5odPdZSSqdEou+7eNC7BfbZ7mny5Me/K06wCTeKUMVeH/YsI9+4QQudskeQ307kr/7ppQ1A==} discord-api-types@0.37.97: resolution: {integrity: sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==} @@ -8020,6 +8032,27 @@ packages: peerDependencies: eslint: '>=8.0.0' + eslint-module-utils@2.12.0: + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + eslint-module-utils@2.8.1: resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} @@ -8185,6 +8218,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@10.1.0: @@ -8610,6 +8644,9 @@ packages: get-tsconfig@4.7.6: resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-uri@6.0.3: resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} engines: {node: '>= 14'} @@ -12263,6 +12300,7 @@ packages: stream-connect@1.0.2: resolution: {integrity: sha512-68Kl+79cE0RGKemKkhxTSg8+6AGrqBt+cbZAXevg2iJ6Y3zX4JhA/sZeGzLpxW9cXhmqAcE7KnJCisUmIUfnFQ==} engines: {node: '>=0.10.0'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. stream-to-array@2.3.0: resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==} @@ -12278,8 +12316,8 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - streamx@2.19.0: - resolution: {integrity: sha512-5z6CNR4gtkPbwlxyEqoDGDmWIzoNJqCBt4Eac1ICP9YaIT08ct712cFj0u1rx4F8luAuL+3Qc+RFIdI4OX00kg==} + streamx@2.20.1: + resolution: {integrity: sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==} string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} @@ -12558,8 +12596,8 @@ packages: resolution: {integrity: sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==} engines: {node: '>=4.0.0'} - text-decoder@1.1.1: - resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} + text-decoder@1.2.1: + resolution: {integrity: sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==} text-extensions@2.4.0: resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} @@ -13815,7 +13853,7 @@ snapshots: '@babel/core': 7.25.2 '@babel/helper-compilation-targets': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - debug: 4.3.6 + debug: 4.3.7 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -14803,18 +14841,18 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@definitelytyped/header-parser@0.2.12': + '@definitelytyped/header-parser@0.2.13': dependencies: '@definitelytyped/typescript-versions': 0.1.4 - '@definitelytyped/utils': 0.1.7 - semver: 7.5.4 + '@definitelytyped/utils': 0.1.8 + semver: 7.6.3 '@definitelytyped/typescript-versions@0.1.4': {} - '@definitelytyped/utils@0.1.7': + '@definitelytyped/utils@0.1.8': dependencies: '@qiwi/npm-registry-client': 8.9.1 - '@types/node': 18.19.45 + '@types/node': 18.19.59 cachedir: 2.4.0 charm: 1.0.2 minimatch: 9.0.5 @@ -14924,7 +14962,7 @@ snapshots: '@esbuild-plugins/node-resolve@0.1.4(esbuild@0.18.20)': dependencies: '@types/resolve': 1.20.6 - debug: 4.3.6 + debug: 4.3.7 esbuild: 0.18.20 escape-string-regexp: 4.0.0 resolve: 1.22.8 @@ -15656,7 +15694,7 @@ snapshots: nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.5.4 + semver: 7.6.3 tar: 6.2.1 transitivePeerDependencies: - encoding @@ -15940,14 +15978,14 @@ snapshots: ini: 4.1.3 nopt: 7.2.1 proc-log: 4.2.0 - semver: 7.5.4 + semver: 7.6.3 walk-up-path: 3.0.1 transitivePeerDependencies: - bluebird '@npmcli/fs@3.1.1': dependencies: - semver: 7.5.4 + semver: 7.6.3 '@npmcli/git@5.0.8': dependencies: @@ -15958,7 +15996,7 @@ snapshots: proc-log: 4.2.0 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.5.4 + semver: 7.6.3 which: 4.0.0 transitivePeerDependencies: - bluebird @@ -15980,7 +16018,7 @@ snapshots: json-parse-even-better-errors: 3.0.2 normalize-package-data: 6.0.2 proc-log: 4.2.0 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - bluebird @@ -16146,7 +16184,7 @@ snapshots: '@opentelemetry/propagator-b3': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/propagator-jaeger': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) - semver: 7.5.4 + semver: 7.6.3 '@opentelemetry/semantic-conventions@1.15.2': {} @@ -16192,7 +16230,7 @@ snapshots: request: 2.88.2 retry: 0.12.0 safe-buffer: 5.2.1 - semver: 7.5.4 + semver: 7.6.3 slide: 1.1.6 ssri: 8.0.1 optionalDependencies: @@ -19050,6 +19088,10 @@ snapshots: dependencies: undici-types: 5.26.5 + '@types/node@18.19.59': + dependencies: + undici-types: 5.26.5 + '@types/node@20.16.1': dependencies: undici-types: 6.19.8 @@ -19283,10 +19325,10 @@ snapshots: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.6 + debug: 4.3.7 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 + semver: 7.6.3 tsutils: 3.21.0(typescript@5.5.4) optionalDependencies: typescript: 5.5.4 @@ -19297,7 +19339,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.11.0 '@typescript-eslint/visitor-keys': 7.11.0 - debug: 4.3.6 + debug: 4.3.7 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -19348,7 +19390,7 @@ snapshots: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.4) eslint: 8.57.0 eslint-scope: 5.1.1 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -20042,7 +20084,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.6 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -20351,7 +20393,7 @@ snapshots: aws-sign2@0.7.0: {} - aws4@1.13.1: {} + aws4@1.13.2: {} axe-core@4.10.0: {} @@ -20363,7 +20405,7 @@ snapshots: dependencies: dequal: 2.0.3 - b4a@1.6.6: {} + b4a@1.6.7: {} babel-code-frame@6.26.0: dependencies: @@ -20468,7 +20510,7 @@ snapshots: balanced-match@1.0.2: {} - bare-events@2.4.2: + bare-events@2.5.0: optional: true base64-js@0.0.8: {} @@ -21083,7 +21125,7 @@ snapshots: handlebars: 4.7.8 json-stringify-safe: 5.0.1 meow: 12.1.1 - semver: 7.5.4 + semver: 7.6.3 split2: 4.2.0 conventional-changelog@5.1.0: @@ -21332,6 +21374,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.7: + dependencies: + ms: 2.1.3 + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 @@ -21462,7 +21508,7 @@ snapshots: dependencies: path-type: 4.0.0 - discord-api-types@0.37.101: {} + discord-api-types@0.37.103: {} discord-api-types@0.37.97: {} @@ -21515,7 +21561,7 @@ snapshots: dts-critic@3.3.11(typescript@5.5.4): dependencies: - '@definitelytyped/header-parser': 0.2.12 + '@definitelytyped/header-parser': 0.2.13 command-exists: 1.2.9 rimraf: 3.0.2 semver: 6.3.1 @@ -21525,9 +21571,9 @@ snapshots: dtslint@4.2.1(typescript@5.5.4): dependencies: - '@definitelytyped/header-parser': 0.2.12 + '@definitelytyped/header-parser': 0.2.13 '@definitelytyped/typescript-versions': 0.1.4 - '@definitelytyped/utils': 0.1.7 + '@definitelytyped/utils': 0.1.8 dts-critic: 3.3.11(typescript@5.5.4) fs-extra: 6.0.1 json-stable-stringify: 1.1.1 @@ -21773,14 +21819,14 @@ snapshots: esbuild-register@3.6.0(esbuild@0.18.20): dependencies: - debug: 4.3.6 + debug: 4.3.7 esbuild: 0.18.20 transitivePeerDependencies: - supports-color esbuild-register@3.6.0(esbuild@0.21.5): dependencies: - debug: 4.3.6 + debug: 4.3.7 esbuild: 0.21.5 transitivePeerDependencies: - supports-color @@ -22026,7 +22072,7 @@ snapshots: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-i@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-import: eslint-plugin-i@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 @@ -22059,7 +22105,7 @@ snapshots: - bluebird - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-i@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -22070,6 +22116,16 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.5.4) + eslint: 8.57.0 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-i@2.29.1)(eslint@8.57.0) + transitivePeerDependencies: + - supports-color + eslint-plugin-astro@0.33.1(eslint@8.57.0): dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) @@ -22098,15 +22154,15 @@ snapshots: eslint-plugin-i@2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: - debug: 4.3.6 + debug: 4.3.7 doctrine: 3.0.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-i@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - get-tsconfig: 4.7.6 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + get-tsconfig: 4.8.1 is-glob: 4.0.3 minimatch: 3.1.2 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-typescript @@ -22903,6 +22959,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-tsconfig@4.8.1: + dependencies: + resolve-pkg-maps: 1.0.0 + get-uri@6.0.3: dependencies: basic-ftp: 5.0.5 @@ -22965,7 +23025,7 @@ snapshots: git-semver-tags@7.0.1: dependencies: meow: 12.1.1 - semver: 7.5.4 + semver: 7.6.3 github-slugger@2.0.0: {} @@ -23367,7 +23427,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.6 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -23387,7 +23447,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.6 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -23789,7 +23849,7 @@ snapshots: '@babel/parser': 7.25.4 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -23801,7 +23861,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.6 + debug: 4.3.7 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -24178,7 +24238,7 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -25629,7 +25689,7 @@ snapshots: micromark@2.11.4: dependencies: - debug: 4.3.6 + debug: 4.3.7 parse-entities: 2.0.0 transitivePeerDependencies: - supports-color @@ -25637,7 +25697,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.6 + debug: 4.3.7 decode-named-character-reference: 1.0.2 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -25659,7 +25719,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.6 + debug: 4.3.7 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -26002,13 +26062,13 @@ snapshots: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.15.1 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-license: 3.0.4 normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} @@ -26017,7 +26077,7 @@ snapshots: npm-install-checks@6.3.0: dependencies: - semver: 7.5.4 + semver: 7.6.3 npm-normalize-package-bin@3.0.1: {} @@ -26032,13 +26092,13 @@ snapshots: dependencies: hosted-git-info: 7.0.2 proc-log: 4.2.0 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-name: 5.0.1 npm-package-arg@8.1.5: dependencies: hosted-git-info: 4.1.0 - semver: 7.5.4 + semver: 7.6.3 validate-npm-package-name: 3.0.0 npm-pick-manifest@9.1.0: @@ -26046,7 +26106,7 @@ snapshots: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 11.0.3 - semver: 7.5.4 + semver: 7.6.3 npm-registry-fetch@14.0.5: dependencies: @@ -27302,7 +27362,7 @@ snapshots: request@2.88.2: dependencies: aws-sign2: 0.7.0 - aws4: 1.13.1 + aws4: 1.13.2 caseless: 0.12.0 combined-stream: 1.0.8 extend: 3.0.2 @@ -27677,7 +27737,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.6 + debug: 4.3.7 socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -27845,13 +27905,13 @@ snapshots: streamsearch@1.1.0: {} - streamx@2.19.0: + streamx@2.20.1: dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 - text-decoder: 1.1.1 + text-decoder: 1.2.1 optionalDependencies: - bare-events: 2.4.2 + bare-events: 2.5.0 string-argv@0.3.2: {} @@ -28117,9 +28177,9 @@ snapshots: tar-stream@3.1.7: dependencies: - b4a: 1.6.6 + b4a: 1.6.7 fast-fifo: 1.3.2 - streamx: 2.19.0 + streamx: 2.20.1 tar@4.4.18: dependencies: @@ -28197,9 +28257,7 @@ snapshots: array-back: 2.0.0 typical: 2.6.1 - text-decoder@1.1.1: - dependencies: - b4a: 1.6.6 + text-decoder@1.2.1: {} text-extensions@2.4.0: {} From a9f629b0d3e0b876b4ce90779834276523b7b1d8 Mon Sep 17 00:00:00 2001 From: Danial Raza Date: Mon, 4 Nov 2024 11:03:13 +0100 Subject: [PATCH 07/22] feat: add soundboard (#10536) * feat: add soundboard * chore: disable `jsdoc/check-param-names` rule * fix: export `SoundboardSoundsAPI` --- packages/core/src/api/channel.ts | 21 +++++ packages/core/src/api/guild.ts | 95 +++++++++++++++++++++++ packages/core/src/api/index.ts | 5 ++ packages/core/src/api/soundboardSounds.ts | 20 +++++ packages/core/src/client.ts | 8 ++ 5 files changed, 149 insertions(+) create mode 100644 packages/core/src/api/soundboardSounds.ts diff --git a/packages/core/src/api/channel.ts b/packages/core/src/api/channel.ts index fe8aa20c68b9..a651c4ff430c 100644 --- a/packages/core/src/api/channel.ts +++ b/packages/core/src/api/channel.ts @@ -33,6 +33,8 @@ import { type RESTPostAPIChannelThreadsResult, type APIThreadChannel, type RESTPostAPIGuildForumThreadsJSONBody, + type RESTPostAPISoundboardSendSoundJSONBody, + type RESTPostAPISendSoundboardSoundResult, } from 'discord-api-types/v10'; export interface StartForumThreadOptions extends RESTPostAPIGuildForumThreadsJSONBody { @@ -583,4 +585,23 @@ export class ChannelsAPI { signal, }); } + + /** + * Sends a soundboard sound in a channel + * + * @see {@link https://discord.com/developers/docs/resources/soundboard#send-soundboard-sound} + * @param channelId - The id of the channel to send the soundboard sound in + * @param body - The data for sending the soundboard sound + * @param options - The options for sending the soundboard sound + */ + public async sendSoundboardSound( + channelId: Snowflake, + body: RESTPostAPISoundboardSendSoundJSONBody, + { signal }: Pick = {}, + ) { + return this.rest.post(Routes.sendSoundboardSound(channelId), { + body, + signal, + }) as Promise; + } } diff --git a/packages/core/src/api/guild.ts b/packages/core/src/api/guild.ts index 9cb9f8fdeb03..698b6df36958 100644 --- a/packages/core/src/api/guild.ts +++ b/packages/core/src/api/guild.ts @@ -100,6 +100,12 @@ import { type RESTPutAPIGuildOnboardingJSONBody, type RESTPutAPIGuildOnboardingResult, type RESTPutAPIGuildTemplateSyncResult, + type RESTGetAPIGuildSoundboardSoundResult, + type RESTGetAPIGuildSoundboardSoundsResult, + type RESTPatchAPIGuildSoundboardSoundJSONBody, + type RESTPatchAPIGuildSoundboardSoundResult, + type RESTPostAPIGuildSoundboardSoundJSONBody, + type RESTPostAPIGuildSoundboardSoundResult, type Snowflake, } from 'discord-api-types/v10'; import { VoiceAPI } from './voice'; @@ -1356,4 +1362,93 @@ export class GuildsAPI { signal, }) as Promise; } + + /** + * Fetches all the soundboard sounds for a guild + * + * @see {@link https://discord.com/developers/docs/resources/soundboard#list-guild-soundboard-sounds} + * @param guildId - The id of the guild to fetch the soundboard sounds for + * @param options - The options for fetching the soundboard sounds + */ + public async getSoundboardSounds(guildId: Snowflake, { signal }: Pick = {}) { + return this.rest.get(Routes.guildSoundboardSounds(guildId), { + signal, + }) as Promise; + } + + /** + * Fetches a soundboard sound for a guild + * + * @see {@link https://discord.com/developers/docs/resources/soundboard#get-guild-soundboard-sound} + * @param guildId - The id of the guild to fetch the soundboard sound for + * @param soundId - The id of the soundboard sound to fetch + * @param options - The options for fetching the soundboard sound + */ + public async getSoundboardSound( + guildId: Snowflake, + soundId: Snowflake, + { signal }: Pick = {}, + ) { + return this.rest.get(Routes.guildSoundboardSound(guildId, soundId), { + signal, + }) as Promise; + } + + /** + * Creates a new soundboard sound for a guild + * + * @see {@link https://discord.com/developers/docs/resources/soundboard#create-guild-soundboard-sound} + * @param guildId - The id of the guild to create the soundboard sound for + * @param body - The data for creating the soundboard sound + * @param options - The options for creating the soundboard sound + */ + public async createSoundboardSound( + guildId: Snowflake, + body: RESTPostAPIGuildSoundboardSoundJSONBody, + { reason, signal }: Pick = {}, + ) { + return this.rest.post(Routes.guildSoundboardSounds(guildId), { + body, + reason, + signal, + }) as Promise; + } + + /** + * Edits a soundboard sound for a guild + * + * @see {@link https://discord.com/developers/docs/resources/soundboard#modify-guild-soundboard-sound} + * @param guildId - The id of the guild to edit the soundboard sound for + * @param soundId - The id of the soundboard sound to edit + * @param body - The data for editing the soundboard sound + * @param options - The options for editing the soundboard sound + */ + public async editSoundboardSound( + guildId: Snowflake, + soundId: Snowflake, + body: RESTPatchAPIGuildSoundboardSoundJSONBody, + { reason, signal }: Pick = {}, + ) { + return this.rest.patch(Routes.guildSoundboardSound(guildId, soundId), { + body, + reason, + signal, + }) as Promise; + } + + /** + * Deletes a soundboard sound for a guild + * + * @see {@link https://discord.com/developers/docs/resources/soundboard#delete-guild-soundboard-sound} + * @param guildId - The id of the guild to delete the soundboard sound for + * @param soundId - The id of the soundboard sound to delete + * @param options - The options for deleting the soundboard sound + */ + public async deleteSoundboardSound( + guildId: Snowflake, + soundId: Snowflake, + { reason, signal }: Pick = {}, + ) { + await this.rest.delete(Routes.guildSoundboardSound(guildId, soundId), { reason, signal }); + } } diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts index a44fc53a8d7b..d9fbe6b1e93b 100644 --- a/packages/core/src/api/index.ts +++ b/packages/core/src/api/index.ts @@ -9,6 +9,7 @@ import { MonetizationAPI } from './monetization.js'; import { OAuth2API } from './oauth2.js'; import { PollAPI } from './poll.js'; import { RoleConnectionsAPI } from './roleConnections.js'; +import { SoundboardSoundsAPI } from './soundboardSounds.js'; import { StageInstancesAPI } from './stageInstances.js'; import { StickersAPI } from './sticker.js'; import { ThreadsAPI } from './thread.js'; @@ -26,6 +27,7 @@ export * from './monetization.js'; export * from './oauth2.js'; export * from './poll.js'; export * from './roleConnections.js'; +export * from './soundboardSounds.js'; export * from './stageInstances.js'; export * from './sticker.js'; export * from './thread.js'; @@ -54,6 +56,8 @@ export class API { public readonly roleConnections: RoleConnectionsAPI; + public readonly soundboardSounds: SoundboardSoundsAPI; + public readonly stageInstances: StageInstancesAPI; public readonly stickers: StickersAPI; @@ -76,6 +80,7 @@ export class API { this.oauth2 = new OAuth2API(rest); this.poll = new PollAPI(rest); this.roleConnections = new RoleConnectionsAPI(rest); + this.soundboardSounds = new SoundboardSoundsAPI(rest); this.stageInstances = new StageInstancesAPI(rest); this.stickers = new StickersAPI(rest); this.threads = new ThreadsAPI(rest); diff --git a/packages/core/src/api/soundboardSounds.ts b/packages/core/src/api/soundboardSounds.ts new file mode 100644 index 000000000000..b474fd1e7a6f --- /dev/null +++ b/packages/core/src/api/soundboardSounds.ts @@ -0,0 +1,20 @@ +/* eslint-disable jsdoc/check-param-names */ + +import type { RequestData, REST } from '@discordjs/rest'; +import { Routes, type RESTGetAPISoundboardDefaultSoundsResult } from 'discord-api-types/v10'; + +export class SoundboardSoundsAPI { + public constructor(private readonly rest: REST) {} + + /** + * Fetches all the soundboard default sounds. + * + * @see {@link https://discord.com/developers/docs/resources/soundboard#list-default-soundboard-sounds} + * @param options - The options for fetching the soundboard default sounds. + */ + public async getSoundboardDefaultSounds({ signal }: Pick = {}) { + return this.rest.get(Routes.soundboardDefaultSounds(), { + signal, + }) as Promise; + } +} diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 90eea1e2d8e9..23b1110c0ad1 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -38,6 +38,10 @@ import { type GatewayGuildScheduledEventUpdateDispatchData, type GatewayGuildScheduledEventUserAddDispatchData, type GatewayGuildScheduledEventUserRemoveDispatchData, + type GatewayGuildSoundboardSoundCreateDispatch, + type GatewayGuildSoundboardSoundDeleteDispatch, + type GatewayGuildSoundboardSoundUpdateDispatch, + type GatewayGuildSoundboardSoundsUpdateDispatch, type GatewayGuildStickersUpdateDispatchData, type GatewayGuildUpdateDispatchData, type GatewayIntegrationCreateDispatchData, @@ -131,6 +135,10 @@ export interface MappedEvents { [GatewayDispatchEvents.GuildScheduledEventUserRemove]: [ ToEventProps, ]; + [GatewayDispatchEvents.GuildSoundboardSoundCreate]: [ToEventProps]; + [GatewayDispatchEvents.GuildSoundboardSoundDelete]: [ToEventProps]; + [GatewayDispatchEvents.GuildSoundboardSoundUpdate]: [ToEventProps]; + [GatewayDispatchEvents.GuildSoundboardSoundsUpdate]: [ToEventProps]; [GatewayDispatchEvents.GuildStickersUpdate]: [ToEventProps]; [GatewayDispatchEvents.GuildUpdate]: [ToEventProps]; [GatewayDispatchEvents.IntegrationCreate]: [ToEventProps]; From ef2a6879d3921b895f99332baf7fd4ffa606db45 Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:43:34 +0000 Subject: [PATCH 08/22] 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> --- .../discord.js/src/structures/GuildMember.js | 33 +++++++++++++++++++ packages/discord.js/typings/index.d.ts | 3 ++ 2 files changed, 36 insertions(+) diff --git a/packages/discord.js/src/structures/GuildMember.js b/packages/discord.js/src/structures/GuildMember.js index db71c66b5473..b03a1bc4d6ec 100644 --- a/packages/discord.js/src/structures/GuildMember.js +++ b/packages/discord.js/src/structures/GuildMember.js @@ -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} @@ -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; } } diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index edc0c29965a9..bd3cb855c498 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -1634,6 +1634,7 @@ export class GuildMember extends Base { private constructor(client: Client, data: RawGuildMemberData, guild: Guild); private _roles: Snowflake[]; public avatar: string | null; + public banner: string | null; public get bannable(): boolean; public get dmChannel(): DMChannel | null; public get displayColor(): number; @@ -1660,6 +1661,7 @@ export class GuildMember extends Base { public user: User; public get voice(): VoiceState; public avatarURL(options?: ImageURLOptions): string | null; + public bannerURL(options?: ImageURLOptions): string | null; public ban(options?: BanOptions): Promise; public disableCommunicationUntil(timeout: DateResolvable | null, reason?: string): Promise; public timeout(timeout: number | null, reason?: string): Promise; @@ -1667,6 +1669,7 @@ export class GuildMember extends Base { public createDM(force?: boolean): Promise; public deleteDM(): Promise; public displayAvatarURL(options?: ImageURLOptions): string; + public displayBannerURL(options?: ImageURLOptions): string | null; public edit(options: GuildMemberEditOptions): Promise; public isCommunicationDisabled(): this is GuildMember & { communicationDisabledUntilTimestamp: number; From 1fd662629d24521426c581cd89025e17820ec3e9 Mon Sep 17 00:00:00 2001 From: Danial Raza Date: Mon, 4 Nov 2024 11:48:41 +0100 Subject: [PATCH 09/22] feat: add subscriptions (#10486) * feat: add subscriptions * docs: requested changes Co-authored-by: Almeida --------- Co-authored-by: Almeida Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- packages/core/src/api/monetization.ts | 51 ++++++++++++++++++++++++--- packages/core/src/client.ts | 6 ++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/packages/core/src/api/monetization.ts b/packages/core/src/api/monetization.ts index 736b8930a1e5..c4cd8dade6b6 100644 --- a/packages/core/src/api/monetization.ts +++ b/packages/core/src/api/monetization.ts @@ -6,6 +6,9 @@ import { type RESTGetAPIEntitlementsQuery, type RESTGetAPIEntitlementsResult, type RESTGetAPISKUsResult, + type RESTGetAPISKUSubscriptionResult, + type RESTGetAPISKUSubscriptionsQuery, + type RESTGetAPISKUSubscriptionsResult, type RESTPostAPIEntitlementJSONBody, type RESTPostAPIEntitlementResult, type Snowflake, @@ -17,17 +20,55 @@ export class MonetizationAPI { /** * Fetches the SKUs for an application. * - * @see {@link https://discord.com/developers/docs/monetization/skus#list-skus} + * @see {@link https://discord.com/developers/docs/resources/sku#list-skus} + * @param applicationId - The application id to fetch SKUs for * @param options - The options for fetching the SKUs. */ public async getSKUs(applicationId: Snowflake, { signal }: Pick = {}) { return this.rest.get(Routes.skus(applicationId), { signal }) as Promise; } + /** + * Fetches subscriptions for an SKU. + * + * @see {@link https://discord.com/developers/docs/resources/subscription#list-sku-subscriptions} + * @param skuId - The SKU id to fetch subscriptions for + * @param query - The query options for fetching subscriptions + * @param options - The options for fetching subscriptions + */ + public async getSKUSubscriptions( + skuId: Snowflake, + query: RESTGetAPISKUSubscriptionsQuery, + { signal }: Pick = {}, + ) { + return this.rest.get(Routes.skuSubscriptions(skuId), { + signal, + query: makeURLSearchParams(query), + }) as Promise; + } + + /** + * Fetches a subscription for an SKU. + * + * @see {@link https://discord.com/developers/docs/resources/subscription#get-sku-subscription} + * @param skuId - The SKU id to fetch subscription for + * @param subscriptionId - The subscription id to fetch + * @param options - The options for fetching the subscription + */ + public async getSKUSubscription( + skuId: Snowflake, + subscriptionId: Snowflake, + { signal }: Pick = {}, + ) { + return this.rest.get(Routes.skuSubscription(skuId, subscriptionId), { + signal, + }) as Promise; + } + /** * Fetches the entitlements for an application. * - * @see {@link https://discord.com/developers/docs/monetization/entitlements#list-entitlements} + * @see {@link https://discord.com/developers/docs/resources/entitlement#list-entitlements} * @param applicationId - The application id to fetch entitlements for * @param query - The query options for fetching entitlements * @param options - The options for fetching entitlements @@ -46,7 +87,7 @@ export class MonetizationAPI { /** * Creates a test entitlement for an application's SKU. * - * @see {@link https://discord.com/developers/docs/monetization/entitlements#create-test-entitlement} + * @see {@link https://discord.com/developers/docs/resources/entitlement#create-test-entitlement} * @param applicationId - The application id to create the entitlement for * @param body - The data for creating the entitlement * @param options - The options for creating the entitlement @@ -65,7 +106,7 @@ export class MonetizationAPI { /** * Deletes a test entitlement for an application's SKU. * - * @see {@link https://discord.com/developers/docs/monetization/entitlements#delete-test-entitlement} + * @see {@link https://discord.com/developers/docs/resources/entitlement#delete-test-entitlement} * @param applicationId - The application id to delete the entitlement for * @param entitlementId - The entitlement id to delete * @param options - The options for deleting the entitlement @@ -81,7 +122,7 @@ export class MonetizationAPI { /** * Marks a given entitlement for the user as consumed. Only available for One-Time Purchase consumable SKUs. * - * @see {@link https://discord.com/developers/docs/monetization/entitlements#consume-an-entitlement} + * @see {@link https://discord.com/developers/docs/resources/entitlement#consume-an-entitlement} * @param applicationId - The application id to consume the entitlement for * @param entitlementId - The entitlement id to consume * @param options - The options for consuming the entitlement diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 23b1110c0ad1..5bb1c96b7322 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -66,6 +66,9 @@ import { type GatewayStageInstanceCreateDispatchData, type GatewayStageInstanceDeleteDispatchData, type GatewayStageInstanceUpdateDispatchData, + type GatewaySubscriptionCreateDispatchData, + type GatewaySubscriptionDeleteDispatchData, + type GatewaySubscriptionUpdateDispatchData, type GatewayThreadCreateDispatchData, type GatewayThreadDeleteDispatchData, type GatewayThreadListSyncDispatchData, @@ -163,6 +166,9 @@ export interface MappedEvents { [GatewayDispatchEvents.StageInstanceCreate]: [ToEventProps]; [GatewayDispatchEvents.StageInstanceDelete]: [ToEventProps]; [GatewayDispatchEvents.StageInstanceUpdate]: [ToEventProps]; + [GatewayDispatchEvents.SubscriptionCreate]: [ToEventProps]; + [GatewayDispatchEvents.SubscriptionDelete]: [ToEventProps]; + [GatewayDispatchEvents.SubscriptionUpdate]: [ToEventProps]; [GatewayDispatchEvents.ThreadCreate]: [ToEventProps]; [GatewayDispatchEvents.ThreadDelete]: [ToEventProps]; [GatewayDispatchEvents.ThreadListSync]: [ToEventProps]; From f02bdc3be37bbcae97991df0693531b93da447d4 Mon Sep 17 00:00:00 2001 From: Souji Date: Tue, 5 Nov 2024 10:29:29 +0100 Subject: [PATCH 10/22] 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 --- packages/discord.js/src/managers/GuildMemberRoleManager.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/discord.js/src/managers/GuildMemberRoleManager.js b/packages/discord.js/src/managers/GuildMemberRoleManager.js index e5302683d0f9..7d19bf778b64 100644 --- a/packages/discord.js/src/managers/GuildMemberRoleManager.js +++ b/packages/discord.js/src/managers/GuildMemberRoleManager.js @@ -101,6 +101,8 @@ class GuildMemberRoleManager extends DataManager { /** * Adds a role (or multiple roles) to the member. + * + * Uses the idempotent PUT route for singular roles, otherwise PATCHes the underlying guild member * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to add * @param {string} [reason] Reason for adding the role(s) * @returns {Promise} @@ -138,6 +140,8 @@ class GuildMemberRoleManager extends DataManager { /** * Removes a role (or multiple roles) from the member. + * + * Uses the idempotent DELETE route for singular roles, otherwise PATCHes the underlying guild member * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to remove * @param {string} [reason] Reason for removing the role(s) * @returns {Promise} From 939e3644e1e1dabe5d3074682c522cc9bc5c7148 Mon Sep 17 00:00:00 2001 From: Danial Raza Date: Tue, 5 Nov 2024 10:36:41 +0100 Subject: [PATCH 11/22] types: add missing `Caches` managers (#10540) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- packages/discord.js/typings/index.d.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index bd3cb855c498..0661f25eadad 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -4961,18 +4961,20 @@ export type BitFieldResolvable]; - GuildEmojiManager: [manager: typeof GuildEmojiManager, holds: typeof GuildEmoji]; // TODO: ChannelManager: [manager: typeof ChannelManager, holds: typeof Channel]; - // TODO: GuildChannelManager: [manager: typeof GuildChannelManager, holds: typeof GuildChannel]; - // TODO: GuildManager: [manager: typeof GuildManager, holds: typeof Guild]; - GuildMemberManager: [manager: typeof GuildMemberManager, holds: typeof GuildMember]; + DMMessageManager: [manager: typeof MessageManager, holds: typeof Message]; + EntitlementManager: [manager: typeof EntitlementManager, holds: typeof Entitlement]; GuildBanManager: [manager: typeof GuildBanManager, holds: typeof GuildBan]; + // TODO: GuildChannelManager: [manager: typeof GuildChannelManager, holds: typeof GuildChannel]; + GuildEmojiManager: [manager: typeof GuildEmojiManager, holds: typeof GuildEmoji]; GuildForumThreadManager: [manager: typeof GuildForumThreadManager, holds: typeof ThreadChannel]; GuildInviteManager: [manager: typeof GuildInviteManager, holds: typeof Invite]; + // TODO: GuildManager: [manager: typeof GuildManager, holds: typeof Guild]; + GuildMemberManager: [manager: typeof GuildMemberManager, holds: typeof GuildMember]; GuildMessageManager: [manager: typeof GuildMessageManager, holds: typeof Message]; GuildScheduledEventManager: [manager: typeof GuildScheduledEventManager, holds: typeof GuildScheduledEvent]; GuildStickerManager: [manager: typeof GuildStickerManager, holds: typeof Sticker]; From 1184b38d3e43193036e17a3c4d6aed95546ba73c Mon Sep 17 00:00:00 2001 From: Naiyar <137700126+imnaiyar@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:30:44 +0530 Subject: [PATCH 12/22] refactor(ThreadManager)!: match parent ID when fetching a single thread (#10557) BREAKING CHANGE: `ThreadManager#fetch` now throws when the provided thread ID doesn't belong to the current channel --- packages/discord.js/src/errors/ErrorCodes.js | 2 ++ packages/discord.js/src/errors/Messages.js | 1 + packages/discord.js/src/managers/ThreadManager.js | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/discord.js/src/errors/ErrorCodes.js b/packages/discord.js/src/errors/ErrorCodes.js index f23b3232b442..b160068e7669 100644 --- a/packages/discord.js/src/errors/ErrorCodes.js +++ b/packages/discord.js/src/errors/ErrorCodes.js @@ -73,6 +73,7 @@ * @property {'MessageThreadParent'} MessageThreadParent * @property {'MessageExistingThread'} MessageExistingThread * @property {'ThreadInvitableType'} ThreadInvitableType + * @property {'NotAThreadOfParent'} NotAThreadOfParent * @property {'WebhookMessage'} WebhookMessage * @property {'WebhookTokenUnavailable'} WebhookTokenUnavailable @@ -201,6 +202,7 @@ const keys = [ 'MessageThreadParent', 'MessageExistingThread', 'ThreadInvitableType', + 'NotAThreadOfParent', 'WebhookMessage', 'WebhookTokenUnavailable', diff --git a/packages/discord.js/src/errors/Messages.js b/packages/discord.js/src/errors/Messages.js index 2ee1ab3405e4..133c4d0cee50 100644 --- a/packages/discord.js/src/errors/Messages.js +++ b/packages/discord.js/src/errors/Messages.js @@ -78,6 +78,7 @@ const Messages = { [DjsErrorCodes.MessageThreadParent]: 'The message was not sent in a guild text or announcement channel', [DjsErrorCodes.MessageExistingThread]: 'The message already has a thread', [DjsErrorCodes.ThreadInvitableType]: type => `Invitable cannot be edited on ${type}`, + [DjsErrorCodes.NotAThreadOfParent]: 'Provided ThreadChannelResolvable is not a thread of the parent channel.', [DjsErrorCodes.WebhookMessage]: 'The message was not sent by a webhook.', [DjsErrorCodes.WebhookTokenUnavailable]: 'This action requires a webhook token, but none is available.', diff --git a/packages/discord.js/src/managers/ThreadManager.js b/packages/discord.js/src/managers/ThreadManager.js index a8bf2a67e83c..afb75a1e0b7a 100644 --- a/packages/discord.js/src/managers/ThreadManager.js +++ b/packages/discord.js/src/managers/ThreadManager.js @@ -83,10 +83,15 @@ class ThreadManager extends CachedManager { * .then(channel => console.log(channel.name)) * .catch(console.error); */ - fetch(options, { cache, force } = {}) { + async fetch(options, { cache, force } = {}) { if (!options) return this.fetchActive(cache); const channel = this.client.channels.resolveId(options); - if (channel) return this.client.channels.fetch(channel, { cache, force }); + if (channel) { + const threadChannel = await this.client.channels.fetch(channel, { cache, force }); + if (threadChannel.parentId !== this.channel.id) throw new DiscordjsTypeError(ErrorCodes.NotAThreadOfParent); + return threadChannel; + } + if (options.archived) { return this.fetchArchived(options.archived, cache); } From c34a57b79826989634a2109ded3fc2c8372c5698 Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Tue, 5 Nov 2024 22:19:48 +0000 Subject: [PATCH 13/22] fix(ThreadChannel): Address parameter type on `fetchOwner()` (#10579) * fix(ThreadChannel): address parameter on owner helper method * docs: fix description Co-authored-by: Almeida --------- Co-authored-by: Almeida Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- packages/discord.js/src/structures/ThreadChannel.js | 10 ++++++++-- packages/discord.js/typings/index.d.ts | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/discord.js/src/structures/ThreadChannel.js b/packages/discord.js/src/structures/ThreadChannel.js index 129fa3ae3ed1..d3b92dd80592 100644 --- a/packages/discord.js/src/structures/ThreadChannel.js +++ b/packages/discord.js/src/structures/ThreadChannel.js @@ -287,11 +287,17 @@ class ThreadChannel extends BaseChannel { return this.parent?.permissionsFor(memberOrRole, checkAdmin) ?? null; } + /** + * Options used to fetch a thread owner. + * @typedef {BaseFetchOptions} FetchThreadOwnerOptions + * @property {boolean} [withMember] Whether to also return the guild member associated with this thread member + */ + /** * Fetches the owner of this thread. If the thread member object isn't needed, * use {@link ThreadChannel#ownerId} instead. - * @param {BaseFetchOptions} [options] The options for fetching the member - * @returns {Promise} + * @param {FetchThreadOwnerOptions} [options] Options for fetching the owner + * @returns {Promise} */ async fetchOwner(options) { if (!this.ownerId) { diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 0661f25eadad..afb73b20a91b 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -3318,7 +3318,7 @@ export class ThreadChannel extends BaseCha memberOrRole: GuildMemberResolvable | RoleResolvable, checkAdmin?: boolean, ): Readonly | null; - public fetchOwner(options?: BaseFetchOptions): Promise; + public fetchOwner(options?: FetchThreadOwnerOptions): Promise; public fetchStarterMessage(options?: BaseFetchOptions): Promise | null>; public setArchived(archived?: boolean, reason?: string): Promise; public setAutoArchiveDuration(autoArchiveDuration: ThreadAutoArchiveDuration, reason?: string): Promise; @@ -5565,6 +5565,10 @@ export interface FetchThreadMemberOptions extends BaseFetchOptions { withMember?: boolean; } +export interface FetchThreadOwnerOptions extends BaseFetchOptions { + withMember?: boolean; +} + export interface FetchThreadMembersWithGuildMemberDataOptions { withMember: true; after?: Snowflake; From ea042458a3f378f1cb83545b3291f3552092bf85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= <9092381+Renegade334@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:03:26 +0000 Subject: [PATCH 14/22] perf(collection): optimisations (#10552) * perf: `merge()`: deduplicate boolean checks * perf: `toSorted()`: remove redundant closure * perf: `last[Key]()`: order of operations - do not perform iterable-to-array until required - test ! before < * perf: `{at,keyAt}()`: manually iterate to target * perf: `first[Key]()`: avoid `Array.from()` * perf: `map()`: avoid `Array.from()` * perf: `random[Key]()`: avoid `Array.from()` * test: `.{at,keyAt}()` indices * perf: `last[Key]()`: use `.at()`/`.keyAt()` for single element * perf: `first[Key]()`: use iterable-to-array if returning all * perf: `random[Key]()`: use `{at,keyAt}()` for single value - skip iterable-to-array for returning single value - short-circuit if amount or collection size is zero * perf: `random[Key]()`: use Durstenfeld shuffle * refactor: `{key,keyAt}()`: reorder index check --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../collection/__tests__/collection.test.ts | 20 ++- packages/collection/src/collection.ts | 142 ++++++++++++------ 2 files changed, 115 insertions(+), 47 deletions(-) diff --git a/packages/collection/__tests__/collection.test.ts b/packages/collection/__tests__/collection.test.ts index 0ea401fd44e2..ea8f918bdce6 100644 --- a/packages/collection/__tests__/collection.test.ts +++ b/packages/collection/__tests__/collection.test.ts @@ -70,12 +70,20 @@ describe('at() tests', () => { expect(coll.at(0)).toStrictEqual(1); }); + test('positive non-integer index', () => { + expect(coll.at(1.5)).toStrictEqual(2); + }); + test('negative index', () => { expect(coll.at(-1)).toStrictEqual(3); }); + test('negative non-integer index', () => { + expect(coll.at(-2.5)).toStrictEqual(2); + }); + test('invalid positive index', () => { - expect(coll.at(4)).toBeUndefined(); + expect(coll.at(3)).toBeUndefined(); }); test('invalid negative index', () => { @@ -432,12 +440,20 @@ describe('keyAt() tests', () => { expect(coll.keyAt(0)).toStrictEqual('a'); }); + test('positive non-integer index', () => { + expect(coll.keyAt(1.5)).toStrictEqual('b'); + }); + test('negative index', () => { expect(coll.keyAt(-1)).toStrictEqual('c'); }); + test('negative non-integer index', () => { + expect(coll.keyAt(-2.5)).toStrictEqual('b'); + }); + test('invalid positive index', () => { - expect(coll.keyAt(4)).toBeUndefined(); + expect(coll.keyAt(3)).toBeUndefined(); }); test('invalid negative index', () => { diff --git a/packages/collection/src/collection.ts b/packages/collection/src/collection.ts index 19fc01da9b11..5fa11cb1ebb9 100644 --- a/packages/collection/src/collection.ts +++ b/packages/collection/src/collection.ts @@ -85,9 +85,16 @@ export class Collection extends Map { public first(amount?: number): Value | Value[] | undefined { if (amount === undefined) return this.values().next().value; if (amount < 0) return this.last(amount * -1); - amount = Math.min(this.size, amount); + if (amount >= this.size) return [...this.values()]; + const iter = this.values(); - return Array.from({ length: amount }, (): Value => iter.next().value!); + // eslint-disable-next-line unicorn/no-new-array + const results: Value[] = new Array(amount); + for (let index = 0; index < amount; index++) { + results[index] = iter.next().value!; + } + + return results; } /** @@ -102,9 +109,16 @@ export class Collection extends Map { public firstKey(amount?: number): Key | Key[] | undefined { if (amount === undefined) return this.keys().next().value; if (amount < 0) return this.lastKey(amount * -1); - amount = Math.min(this.size, amount); + if (amount >= this.size) return [...this.keys()]; + const iter = this.keys(); - return Array.from({ length: amount }, (): Key => iter.next().value!); + // eslint-disable-next-line unicorn/no-new-array + const results: Key[] = new Array(amount); + for (let index = 0; index < amount; index++) { + results[index] = iter.next().value!; + } + + return results; } /** @@ -117,11 +131,12 @@ export class Collection extends Map { public last(): Value | undefined; public last(amount: number): Value[]; public last(amount?: number): Value | Value[] | undefined { - const arr = [...this.values()]; - if (amount === undefined) return arr[arr.length - 1]; - if (amount < 0) return this.first(amount * -1); + if (amount === undefined) return this.at(-1); if (!amount) return []; - return arr.slice(-amount); + if (amount < 0) return this.first(amount * -1); + + const arr = [...this.values()]; + return arr.slice(amount * -1); } /** @@ -134,11 +149,12 @@ export class Collection extends Map { public lastKey(): Key | undefined; public lastKey(amount: number): Key[]; public lastKey(amount?: number): Key | Key[] | undefined { - const arr = [...this.keys()]; - if (amount === undefined) return arr[arr.length - 1]; - if (amount < 0) return this.firstKey(amount * -1); + if (amount === undefined) return this.keyAt(-1); if (!amount) return []; - return arr.slice(-amount); + if (amount < 0) return this.firstKey(amount * -1); + + const arr = [...this.keys()]; + return arr.slice(amount * -1); } /** @@ -148,10 +164,21 @@ export class Collection extends Map { * * @param index - The index of the element to obtain */ - public at(index: number) { - index = Math.floor(index); - const arr = [...this.values()]; - return arr.at(index); + public at(index: number): Value | undefined { + index = Math.trunc(index); + if (index >= 0) { + if (index >= this.size) return undefined; + } else { + index += this.size; + if (index < 0) return undefined; + } + + const iter = this.values(); + for (let skip = 0; skip < index; skip++) { + iter.next(); + } + + return iter.next().value!; } /** @@ -161,10 +188,21 @@ export class Collection extends Map { * * @param index - The index of the key to obtain */ - public keyAt(index: number) { - index = Math.floor(index); - const arr = [...this.keys()]; - return arr.at(index); + public keyAt(index: number): Key | undefined { + index = Math.trunc(index); + if (index >= 0) { + if (index >= this.size) return undefined; + } else { + index += this.size; + if (index < 0) return undefined; + } + + const iter = this.keys(); + for (let skip = 0; skip < index; skip++) { + iter.next(); + } + + return iter.next().value!; } /** @@ -176,13 +214,17 @@ export class Collection extends Map { public random(): Value | undefined; public random(amount: number): Value[]; public random(amount?: number): Value | Value[] | undefined { - const arr = [...this.values()]; - if (amount === undefined) return arr[Math.floor(Math.random() * arr.length)]; - if (!arr.length || !amount) return []; - return Array.from( - { length: Math.min(amount, arr.length) }, - (): Value => arr.splice(Math.floor(Math.random() * arr.length), 1)[0]!, - ); + if (amount === undefined) return this.at(Math.floor(Math.random() * this.size)); + amount = Math.min(this.size, amount); + if (!amount) return []; + + const values = [...this.values()]; + for (let sourceIndex = 0; sourceIndex < amount; sourceIndex++) { + const targetIndex = sourceIndex + Math.floor(Math.random() * (values.length - sourceIndex)); + [values[sourceIndex], values[targetIndex]] = [values[targetIndex]!, values[sourceIndex]!]; + } + + return values.slice(0, amount); } /** @@ -194,13 +236,17 @@ export class Collection extends Map { public randomKey(): Key | undefined; public randomKey(amount: number): Key[]; public randomKey(amount?: number): Key | Key[] | undefined { - const arr = [...this.keys()]; - if (amount === undefined) return arr[Math.floor(Math.random() * arr.length)]; - if (!arr.length || !amount) return []; - return Array.from( - { length: Math.min(amount, arr.length) }, - (): Key => arr.splice(Math.floor(Math.random() * arr.length), 1)[0]!, - ); + if (amount === undefined) return this.keyAt(Math.floor(Math.random() * this.size)); + amount = Math.min(this.size, amount); + if (!amount) return []; + + const keys = [...this.keys()]; + for (let sourceIndex = 0; sourceIndex < amount; sourceIndex++) { + const targetIndex = sourceIndex + Math.floor(Math.random() * (keys.length - sourceIndex)); + [keys[sourceIndex], keys[targetIndex]] = [keys[targetIndex]!, keys[sourceIndex]!]; + } + + return keys.slice(0, amount); } /** @@ -511,10 +557,14 @@ export class Collection extends Map { if (typeof fn !== 'function') throw new TypeError(`${fn} is not a function`); if (thisArg !== undefined) fn = fn.bind(thisArg); const iter = this.entries(); - return Array.from({ length: this.size }, (): NewValue => { + // eslint-disable-next-line unicorn/no-new-array + const results: NewValue[] = new Array(this.size); + for (let index = 0; index < this.size; index++) { const [key, value] = iter.next().value!; - return fn(value, key, this); - }); + results[index] = fn(value, key, this); + } + + return results; } /** @@ -959,12 +1009,14 @@ export class Collection extends Map { const hasInSelf = this.has(key); const hasInOther = other.has(key); - if (hasInSelf && hasInOther) { - const result = whenInBoth(this.get(key)!, other.get(key)!, key); - if (result.keep) coll.set(key, result.value); - } else if (hasInSelf) { - const result = whenInSelf(this.get(key)!, key); - if (result.keep) coll.set(key, result.value); + if (hasInSelf) { + if (hasInOther) { + const result = whenInBoth(this.get(key)!, other.get(key)!, key); + if (result.keep) coll.set(key, result.value); + } else { + const result = whenInSelf(this.get(key)!, key); + if (result.keep) coll.set(key, result.value); + } } else if (hasInOther) { const result = whenInOther(other.get(key)!, key); if (result.keep) coll.set(key, result.value); @@ -995,8 +1047,8 @@ export class Collection extends Map { * collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); * ``` */ - public toSorted(compareFunction: Comparator = Collection.defaultSort) { - return new this.constructor[Symbol.species](this).sort((av, bv, ak, bk) => compareFunction(av, bv, ak, bk)); + public toSorted(compareFunction: Comparator = Collection.defaultSort): Collection { + return new this.constructor[Symbol.species](this).sort(compareFunction); } public toJSON() { From c97310681da0fab68ac17fb100fbfcb249dec402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= <9092381+Renegade334@users.noreply.github.com> Date: Thu, 7 Nov 2024 11:12:53 +0000 Subject: [PATCH 15/22] types(collection): simplify ambient constructor declaration (#10549) - deduplicates constructor definition - removes Collection's "internal" JSDoc description block - removes unnecessary `extends` clause Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- packages/collection/src/collection.ts | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/packages/collection/src/collection.ts b/packages/collection/src/collection.ts index 5fa11cb1ebb9..8f842ef68ad6 100644 --- a/packages/collection/src/collection.ts +++ b/packages/collection/src/collection.ts @@ -1,14 +1,4 @@ /* eslint-disable no-param-reassign */ -/** - * @internal - */ -export interface CollectionConstructor { - new (): Collection; - new (entries?: readonly (readonly [Key, Value])[] | null): Collection; - new (iterable: Iterable): Collection; - readonly prototype: Collection; - readonly [Symbol.species]: CollectionConstructor; -} /** * Represents an immutable version of a collection @@ -19,13 +9,13 @@ export type ReadonlyCollection = Omit< > & ReadonlyMap; -/** - * Separate interface for the constructor so that emitted js does not have a constructor that overwrites itself - * - * @internal - */ -export interface Collection extends Map { - constructor: CollectionConstructor; +export interface Collection { + /** + * Ambient declaration to allow `this.constructor[@@species]` in class methods. + * + * @internal + */ + constructor: typeof Collection & { readonly [Symbol.species]: typeof Collection }; } /** From f2f7f1f65b29149ac04db60e624a600036277810 Mon Sep 17 00:00:00 2001 From: cobalt <61329810+cobaltt7@users.noreply.github.com> Date: Sun, 10 Nov 2024 18:42:04 -0600 Subject: [PATCH 16/22] refactor(formatters): Change `:_:` emoji name placeholder (#10567) * Change `:_:` emoji name placeholder * Update tests * Format --- .../formatters/__tests__/formatters.test.ts | 34 +++++++++++-------- packages/formatters/src/formatters.ts | 8 ++--- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/packages/formatters/__tests__/formatters.test.ts b/packages/formatters/__tests__/formatters.test.ts index 1b745439943d..c91f4c694348 100644 --- a/packages/formatters/__tests__/formatters.test.ts +++ b/packages/formatters/__tests__/formatters.test.ts @@ -174,31 +174,37 @@ describe('Message formatters', () => { }); describe('formatEmoji', () => { - test('GIVEN static emojiId THEN returns "<:_:${emojiId}>"', () => { - expect<`<:_:851461487498493952>`>(formatEmoji('851461487498493952')).toEqual('<:_:851461487498493952>'); + test('GIVEN static emojiId THEN returns "<:emoji:${emojiId}>"', () => { + expect<`<:emoji:851461487498493952>`>(formatEmoji('851461487498493952')).toEqual('<:emoji:851461487498493952>'); }); - test('GIVEN static emojiId WITH animated explicitly false THEN returns "<:_:[emojiId]>"', () => { - expect<`<:_:851461487498493952>`>(formatEmoji('851461487498493952', false)).toEqual('<:_:851461487498493952>'); + test('GIVEN static emojiId WITH animated explicitly false THEN returns "<:emoji:[emojiId]>"', () => { + expect<`<:emoji:851461487498493952>`>(formatEmoji('851461487498493952', false)).toEqual( + '<:emoji:851461487498493952>', + ); }); - test('GIVEN animated emojiId THEN returns ""', () => { - expect<``>(formatEmoji('827220205352255549', true)).toEqual(''); + test('GIVEN animated emojiId THEN returns ""', () => { + expect<``>(formatEmoji('827220205352255549', true)).toEqual( + '', + ); }); - test('GIVEN static id in options object THEN returns "<:_:${id}>"', () => { - expect<`<:_:851461487498493952>`>(formatEmoji({ id: '851461487498493952' })).toEqual('<:_:851461487498493952>'); + test('GIVEN static id in options object THEN returns "<:emoji:${id}>"', () => { + expect<`<:emoji:851461487498493952>`>(formatEmoji({ id: '851461487498493952' })).toEqual( + '<:emoji:851461487498493952>', + ); }); - test('GIVEN static id in options object WITH animated explicitly false THEN returns "<:_:${id}>"', () => { - expect<`<:_:851461487498493952>`>(formatEmoji({ animated: false, id: '851461487498493952' })).toEqual( - '<:_:851461487498493952>', + test('GIVEN static id in options object WITH animated explicitly false THEN returns "<:emoji:${id}>"', () => { + expect<`<:emoji:851461487498493952>`>(formatEmoji({ animated: false, id: '851461487498493952' })).toEqual( + '<:emoji:851461487498493952>', ); }); - test('GIVEN animated id in options object THEN returns ""', () => { - expect<``>(formatEmoji({ animated: true, id: '827220205352255549' })).toEqual( - '', + test('GIVEN animated id in options object THEN returns ""', () => { + expect<``>(formatEmoji({ animated: true, id: '827220205352255549' })).toEqual( + '', ); }); diff --git a/packages/formatters/src/formatters.ts b/packages/formatters/src/formatters.ts index 9c4e11932949..915ec331c816 100644 --- a/packages/formatters/src/formatters.ts +++ b/packages/formatters/src/formatters.ts @@ -323,7 +323,7 @@ export function chatInputApplicationCommandMention< * @typeParam EmojiId - This is inferred by the supplied emoji id * @param emojiId - The emoji id to format */ -export function formatEmoji(emojiId: EmojiId, animated?: false): `<:_:${EmojiId}>`; +export function formatEmoji(emojiId: EmojiId, animated?: false): `<:emoji:${EmojiId}>`; /** * Formats an animated emoji id into a fully qualified emoji identifier. @@ -332,7 +332,7 @@ export function formatEmoji(emojiId: EmojiId, animate * @param emojiId - The emoji id to format * @param animated - Whether the emoji is animated */ -export function formatEmoji(emojiId: EmojiId, animated?: true): ``; +export function formatEmoji(emojiId: EmojiId, animated?: true): ``; /** * Formats an emoji id into a fully qualified emoji identifier. @@ -344,7 +344,7 @@ export function formatEmoji(emojiId: EmojiId, animate export function formatEmoji( emojiId: EmojiId, animated?: boolean, -): `<:_:${EmojiId}>` | ``; +): `<:emoji:${EmojiId}>` | ``; /** * Formats a non-animated emoji id and name into a fully qualified emoji identifier. @@ -393,7 +393,7 @@ export function formatEmoji const { id, animated: isAnimated, name: emojiName } = options; - return `<${isAnimated ? 'a' : ''}:${emojiName ?? '_'}:${id}>`; + return `<${isAnimated ? 'a' : ''}:${emojiName ?? 'emoji'}:${id}>`; } /** From b8f5a68297b9614489ba74fcd59b568ee8f58072 Mon Sep 17 00:00:00 2001 From: Naiyar <137700126+imnaiyar@users.noreply.github.com> Date: Mon, 11 Nov 2024 21:18:52 +0530 Subject: [PATCH 17/22] 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> --- .../src/structures/interfaces/InteractionResponses.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/discord.js/src/structures/interfaces/InteractionResponses.js b/packages/discord.js/src/structures/interfaces/InteractionResponses.js index 7b1cfb1429f0..e448e19fd17e 100644 --- a/packages/discord.js/src/structures/interfaces/InteractionResponses.js +++ b/packages/discord.js/src/structures/interfaces/InteractionResponses.js @@ -174,6 +174,8 @@ class InteractionResponses { * .catch(console.error); */ async deleteReply(message = '@original') { + if (!this.deferred && !this.replied) throw new DiscordjsError(ErrorCodes.InteractionNotReplied); + await this.webhook.deleteMessage(message); } From e2df0e0dbc224cf8915e30b4906b832e36a6840a Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Tue, 12 Nov 2024 06:50:35 +0000 Subject: [PATCH 18/22] docs: Remove Node.js 10 notice (#10593) docs: remove Node.js 10 notice --- packages/collection/src/collection.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/collection/src/collection.ts b/packages/collection/src/collection.ts index 8f842ef68ad6..2b4868a5fb10 100644 --- a/packages/collection/src/collection.ts +++ b/packages/collection/src/collection.ts @@ -831,7 +831,6 @@ export class Collection extends Map { /** * The sort method sorts the items of a collection in place and returns it. - * The sort is not necessarily stable in Node 10 or older. * The default sort order is according to string Unicode code points. * * @param compareFunction - Specifies a function that defines the sort order. @@ -1026,7 +1025,6 @@ export class Collection extends Map { /** * The sorted method sorts the items of a collection and returns it. - * The sort is not necessarily stable in Node 10 or older. * The default sort order is according to string Unicode code points. * * @param compareFunction - Specifies a function that defines the sort order. From 6775175459338ab1c3299a3dc3f49797cf2b7049 Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Thu, 14 Nov 2024 21:00:04 +0000 Subject: [PATCH 19/22] 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 --------- Co-authored-by: Danial Raza --- .../handlers/VOICE_CHANNEL_EFFECT_SEND.js | 16 +++++ .../src/client/websocket/handlers/index.js | 1 + packages/discord.js/src/index.js | 1 + .../src/structures/VoiceChannelEffect.js | 69 +++++++++++++++++++ packages/discord.js/src/util/APITypes.js | 5 ++ packages/discord.js/src/util/Events.js | 2 + packages/discord.js/typings/index.d.ts | 17 +++++ 7 files changed, 111 insertions(+) create mode 100644 packages/discord.js/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js create mode 100644 packages/discord.js/src/structures/VoiceChannelEffect.js diff --git a/packages/discord.js/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js b/packages/discord.js/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js new file mode 100644 index 000000000000..350fdb90647f --- /dev/null +++ b/packages/discord.js/src/client/websocket/handlers/VOICE_CHANNEL_EFFECT_SEND.js @@ -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; + + /** + * Emmited 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)); +}; diff --git a/packages/discord.js/src/client/websocket/handlers/index.js b/packages/discord.js/src/client/websocket/handlers/index.js index 4af0714a12bf..24c3f86a4aeb 100644 --- a/packages/discord.js/src/client/websocket/handlers/index.js +++ b/packages/discord.js/src/client/websocket/handlers/index.js @@ -60,6 +60,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')], diff --git a/packages/discord.js/src/index.js b/packages/discord.js/src/index.js index cdedc8aa45c0..7c5c17cd63df 100644 --- a/packages/discord.js/src/index.js +++ b/packages/discord.js/src/index.js @@ -206,6 +206,7 @@ exports.ThreadOnlyChannel = require('./structures/ThreadOnlyChannel'); exports.Typing = require('./structures/Typing'); 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'); diff --git a/packages/discord.js/src/structures/VoiceChannelEffect.js b/packages/discord.js/src/structures/VoiceChannelEffect.js new file mode 100644 index 000000000000..ee42ea6f72b9 --- /dev/null +++ b/packages/discord.js/src/structures/VoiceChannelEffect.js @@ -0,0 +1,69 @@ +'use strict'; + +const { Emoji } = require('./Emoji'); + +/** + * Represents an effect used in a {@link VoiceChannel}. + */ +class VoiceChannelEffect { + constructor(data, guild) { + /** + * The guild where the effect was sent from. + * @type {Guild} + */ + this.guild = guild; + + /** + * The id of the channel the effect was sent in. + * @type {Snowflake} + */ + this.channelId = data.channel_id; + + /** + * The id of the user that sent the effect. + * @type {Snowflake} + */ + this.userId = data.user_id; + + /** + * The emoji of the effect. + * @type {?Emoji} + */ + this.emoji = data.emoji ? new Emoji(guild.client, data.emoji) : null; + + /** + * The animation type of the effect. + * @type {?VoiceChannelEffectSendAnimationType} + */ + this.animationType = data.animation_type ?? null; + + /** + * The animation id of the effect. + * @type {?number} + */ + this.animationId = data.animation_id ?? null; + + /** + * The id of the soundboard sound for soundboard effects. + * @type {?(Snowflake|number)} + */ + this.soundId = data.sound_id ?? null; + + /** + * The volume of the soundboard sound [0-1] for soundboard effects. + * @type {?number} + */ + this.soundVolume = data.sound_volume ?? null; + } + + /** + * The channel the effect was sent in. + * @type {?VoiceChannel} + * @readonly + */ + get channel() { + return this.guild.channels.cache.get(this.channelId) ?? null; + } +} + +module.exports = VoiceChannelEffect; diff --git a/packages/discord.js/src/util/APITypes.js b/packages/discord.js/src/util/APITypes.js index 8737623d62e6..0032dd4363a9 100644 --- a/packages/discord.js/src/util/APITypes.js +++ b/packages/discord.js/src/util/APITypes.js @@ -589,6 +589,11 @@ * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/VideoQualityMode} */ +/** + * @external VoiceChannelEffectSendAnimationType + * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/VoiceChannelEffectSendAnimationType} + */ + /** * @external WebhookType * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/WebhookType} diff --git a/packages/discord.js/src/util/Events.js b/packages/discord.js/src/util/Events.js index 1ab65a13d8f5..a4b658e5cfba 100644 --- a/packages/discord.js/src/util/Events.js +++ b/packages/discord.js/src/util/Events.js @@ -72,6 +72,7 @@ * @property {string} ThreadUpdate threadUpdate * @property {string} TypingStart typingStart * @property {string} UserUpdate userUpdate + * @property {string} VoiceChannelEffectSend voiceChannelEffectSend * @property {string} VoiceServerUpdate voiceServerUpdate * @property {string} VoiceStateUpdate voiceStateUpdate * @property {string} Warn warn @@ -154,6 +155,7 @@ module.exports = { ThreadUpdate: 'threadUpdate', TypingStart: 'typingStart', UserUpdate: 'userUpdate', + VoiceChannelEffectSend: 'voiceChannelEffectSend', VoiceServerUpdate: 'voiceServerUpdate', VoiceStateUpdate: 'voiceStateUpdate', Warn: 'warn', diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index afb73b20a91b..dc2aac43f714 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -166,6 +166,8 @@ import { GuildScheduledEventRecurrenceRuleFrequency, GatewaySendPayload, GatewayDispatchPayload, + VoiceChannelEffectSendAnimationType, + GatewayVoiceChannelEffectSendDispatchData, } from 'discord-api-types/v10'; import { ChildProcess } from 'node:child_process'; import { EventEmitter } from 'node:events'; @@ -3557,6 +3559,19 @@ export class VoiceChannel extends BaseGuildVoiceChannel { public type: ChannelType.GuildVoice; } +export class VoiceChannelEffect { + private constructor(data: GatewayVoiceChannelEffectSendDispatchData, guild: Guild); + public guild: Guild; + public channelId: Snowflake; + public userId: Snowflake; + public emoji: Emoji | null; + public animationType: VoiceChannelEffectSendAnimationType | null; + public animationId: number | null; + public soundId: Snowflake | number | null; + public soundVolume: number | null; + public get channel(): VoiceChannel | null; +} + export class VoiceRegion { private constructor(data: RawVoiceRegionData); public custom: boolean; @@ -5163,6 +5178,7 @@ export interface ClientEvents { threadUpdate: [oldThread: AnyThreadChannel, newThread: AnyThreadChannel]; typingStart: [typing: Typing]; userUpdate: [oldUser: User | PartialUser, newUser: User]; + voiceChannelEffectSend: [voiceChannelEffect: VoiceChannelEffect]; voiceStateUpdate: [oldState: VoiceState, newState: VoiceState]; webhooksUpdate: [channel: TextChannel | AnnouncementChannel | VoiceChannel | ForumChannel | MediaChannel]; interactionCreate: [interaction: Interaction]; @@ -5357,6 +5373,7 @@ export enum Events { ThreadMembersUpdate = 'threadMembersUpdate', UserUpdate = 'userUpdate', PresenceUpdate = 'presenceUpdate', + VoiceChannelEffectSend = 'voiceChannelEffectSend', VoiceServerUpdate = 'voiceServerUpdate', VoiceStateUpdate = 'voiceStateUpdate', TypingStart = 'typingStart', From 3669d5e1120ed0a6e8d8cb8c48414d7841efd594 Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:04:31 +0000 Subject: [PATCH 20/22] docs(channel): Clarify emoji parameter (#10595) * docs(channel): clarify emoji parameter * docs: actually add `@example` * docs: clarify the kind of encoding Co-Authored-By: Vlad Frangu --------- Co-authored-by: Vlad Frangu Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- packages/core/src/api/channel.ts | 50 ++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/packages/core/src/api/channel.ts b/packages/core/src/api/channel.ts index a651c4ff430c..3ed52c0c51fe 100644 --- a/packages/core/src/api/channel.ts +++ b/packages/core/src/api/channel.ts @@ -92,9 +92,17 @@ export class ChannelsAPI { * @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 + * @param emoji - The emoji to get the reactions for. URL encoding happens internally * @param query - The query options for fetching the reactions * @param options - The options for fetching the message reactions + * @example + * ```ts + * // Unicode. + * await api.channels.getMessageReactions('1234567890', '1234567890', '👍'); + * + * // Custom emoji. + * await api.channels.getMessageReactions('1234567890', '1234567890', 'emoji_name:1234567890'); + * ``` */ public async getMessageReactions( channelId: Snowflake, @@ -115,8 +123,16 @@ export class ChannelsAPI { * @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 + * @param emoji - The emoji to delete the reaction for. URL encoding happens internally * @param options - The options for deleting the reaction + * @example + * ```ts + * // Unicode. + * await api.channels.deleteOwnMessageReaction('1234567890', '1234567890', '👍'); + * + * // Custom emoji. + * await api.channels.deleteOwnMessageReaction('1234567890', '1234567890', 'emoji_name:1234567890'); + * ``` */ public async deleteOwnMessageReaction( channelId: Snowflake, @@ -135,9 +151,17 @@ export class ChannelsAPI { * @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 + * @param emoji - The emoji to delete the reaction for. URL encoding happens internally * @param userId - The id of the user to delete the reaction for * @param options - The options for deleting the reaction + * @example + * ```ts + * // Unicode. + * await api.channels.deleteUserMessageReaction('1234567890', '1234567890', '👍', '1234567890'); + * + * // Custom emoji. + * await api.channels.deleteUserMessageReaction('1234567890', '1234567890', 'emoji_name:1234567890', '1234567890'); + * ``` */ public async deleteUserMessageReaction( channelId: Snowflake, @@ -173,8 +197,16 @@ export class ChannelsAPI { * @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 + * @param emoji - The emoji to delete the reactions for. URL encoding happens internally * @param options - The options for deleting the reactions + * @example + * ```ts + * // Unicode. + * await api.channels.deleteAllMessageReactionsForEmoji('1234567890', '1234567890', '👍'); + * + * // Custom emoji. + * await api.channels.deleteAllMessageReactionsForEmoji('1234567890', '1234567890', 'emoji_name:1234567890'); + * ``` */ public async deleteAllMessageReactionsForEmoji( channelId: Snowflake, @@ -191,8 +223,16 @@ export class ChannelsAPI { * @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 + * @param emoji - The emoji to add the reaction with. URL encoding happens internally * @param options - The options for adding the reaction + * @example + * ```ts + * // Unicode. + * await api.channels.addMessageReaction('1234567890', '1234567890', '👍'); + * + * // Custom emoji. + * await api.channels.addMessageReaction('1234567890', '1234567890', 'emoji_name:1234567890'); + * ``` */ public async addMessageReaction( channelId: Snowflake, From c45d912c98e3373d1f78ce4e10380e704eee1b45 Mon Sep 17 00:00:00 2001 From: Qjuh <76154676+Qjuh@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:28:06 +0100 Subject: [PATCH 21/22] refactor(GuildAuditLogsEntry)!: add type guard for narrowing (#10521) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: removed `GuildAuditLogsEntry.Targets.All` which wasn’t used anywhere --------- Co-authored-by: Almeida --- .../src/structures/GuildAuditLogsEntry.js | 10 ++++- packages/discord.js/typings/index.d.ts | 38 +++++++++++-------- packages/discord.js/typings/index.test-d.ts | 6 +-- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/packages/discord.js/src/structures/GuildAuditLogsEntry.js b/packages/discord.js/src/structures/GuildAuditLogsEntry.js index 53c5b3d3fcd3..2d6430b42659 100644 --- a/packages/discord.js/src/structures/GuildAuditLogsEntry.js +++ b/packages/discord.js/src/structures/GuildAuditLogsEntry.js @@ -14,7 +14,6 @@ const Partials = require('../util/Partials'); const { flatten } = require('../util/Util'); const Targets = { - All: 'All', Guild: 'Guild', GuildScheduledEvent: 'GuildScheduledEvent', Channel: 'Channel', @@ -503,6 +502,15 @@ class GuildAuditLogsEntry { return new Date(this.createdTimestamp); } + /** + * Checks whether this GuildAuditLogsEntry is of the specified {@link AuditLogEvent} type. + * @param {AuditLogEvent} action The type to check for + * @returns {boolean} + */ + isAction(action) { + return this.action === action; + } + toJSON() { return flatten(this, { createdTimestamp: true }); } diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index dc2aac43f714..8d30577c7262 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -1467,9 +1467,9 @@ export class Guild extends AnonymousGuild { public editOnboarding(options: GuildOnboardingEditOptions): Promise; public editWelcomeScreen(options: WelcomeScreenEditOptions): Promise; public equals(guild: Guild): boolean; - public fetchAuditLogs( + public fetchAuditLogs( options?: GuildAuditLogsFetchOptions, - ): Promise>; + ): Promise>; public fetchIntegrations(): Promise>; public fetchOnboarding(): Promise; public fetchOwner(options?: BaseFetchOptions): Promise; @@ -1515,7 +1515,7 @@ export class Guild extends AnonymousGuild { public toJSON(): unknown; } -export class GuildAuditLogs { +export class GuildAuditLogs { private constructor(guild: Guild, data: RawGuildAuditLogData); private applicationCommands: Collection; private webhooks: Collection>; @@ -1527,36 +1527,40 @@ export class GuildAuditLogs { private constructor(guild: Guild, data: RawGuildAuditLogEntryData, logs?: GuildAuditLogs); public static Targets: GuildAuditLogsTargets; - public action: TResolvedType; + public action: TAction; public actionType: TActionType; public changes: AuditLogChange[]; public get createdAt(): Date; public get createdTimestamp(): number; public executorId: Snowflake | null; public executor: User | null; - public extra: TResolvedType extends keyof GuildAuditLogsEntryExtraField - ? GuildAuditLogsEntryExtraField[TResolvedType] - : null; + public extra: TAction extends keyof GuildAuditLogsEntryExtraField ? GuildAuditLogsEntryExtraField[TAction] : null; public id: Snowflake; public reason: string | null; public targetId: Snowflake | null; - public target: TTargetType extends keyof GuildAuditLogsEntryTargetField - ? GuildAuditLogsEntryTargetField[TTargetType] + public target: TTargetType extends keyof GuildAuditLogsEntryTargetField + ? GuildAuditLogsEntryTargetField[TTargetType] : Role | GuildEmoji | { id: Snowflake } | null; public targetType: TTargetType; public static actionType(action: AuditLogEvent): GuildAuditLogsActionType; public static targetType(target: AuditLogEvent): GuildAuditLogsTargetType; + public isAction( + action: TCheckAction, + ): this is GuildAuditLogsEntry< + TCheckAction, + TCheckAction extends keyof GuildAuditLogsTypes ? GuildAuditLogsTypes[TCheckAction][1] : 'All', + TCheckAction extends keyof GuildAuditLogsTypes ? GuildAuditLogsTypes[TCheckAction][0] : 'Unknown' + >; public toJSON(): unknown; } @@ -5726,12 +5730,14 @@ export interface GuildAuditLogsEntryExtraField { }; } -export interface GuildAuditLogsEntryTargetField { +export interface GuildAuditLogsEntryTargetField { User: User | null; Guild: Guild; Webhook: Webhook; Invite: Invite; - Message: TActionType extends AuditLogEvent.MessageBulkDelete ? Guild | { id: Snowflake } : User; + Emoji: GuildEmoji; + Role: Role; + Message: TAction extends AuditLogEvent.MessageBulkDelete ? Guild | { id: Snowflake } : User; Integration: Integration; Channel: NonThreadGuildBasedChannel | { id: Snowflake; [x: string]: unknown }; Thread: AnyThreadChannel | { id: Snowflake; [x: string]: unknown }; @@ -5753,7 +5759,7 @@ export interface GuildAuditLogsFetchOptions>>( guild.fetchAuditLogs({ type: AuditLogEvent.IntegrationUpdate }), ); -expectType>>(guild.fetchAuditLogs({ type: null })); +expectType>>(guild.fetchAuditLogs({ type: null })); expectType>>(guild.fetchAuditLogs()); expectType | undefined>>( @@ -2144,10 +2144,10 @@ expectAssignable al.entries.first()), ); -expectType | undefined>>( +expectType | undefined>>( guild.fetchAuditLogs({ type: null }).then(al => al.entries.first()), ); -expectType | undefined>>( +expectType | undefined>>( guild.fetchAuditLogs().then(al => al.entries.first()), ); From 51a017a14e0dfdac2a15990f88dc8182b75be6e8 Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Sun, 17 Nov 2024 12:18:46 +0000 Subject: [PATCH 22/22] test: Fix builder methods in type test (#10599) * test: fix builder methods in type test * test: remove unused import --- packages/builders/__tests__/components/button.test.ts | 1 - packages/builders/__tests__/types.test.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/builders/__tests__/components/button.test.ts b/packages/builders/__tests__/components/button.test.ts index 7da9abe0fb42..9302a1e4e6e3 100644 --- a/packages/builders/__tests__/components/button.test.ts +++ b/packages/builders/__tests__/components/button.test.ts @@ -5,7 +5,6 @@ import { type APIButtonComponentWithURL, } from 'discord-api-types/v10'; import { describe, test, expect } from 'vitest'; -import { ButtonBuilder } from '../../src/components/button/Button.js'; import { PrimaryButtonBuilder, PremiumButtonBuilder, LinkButtonBuilder } from '../../src/index.js'; const longStr = diff --git a/packages/builders/__tests__/types.test.ts b/packages/builders/__tests__/types.test.ts index de94dbbda9b0..cac2e595a649 100644 --- a/packages/builders/__tests__/types.test.ts +++ b/packages/builders/__tests__/types.test.ts @@ -16,6 +16,6 @@ type BuilderPropsOnly = Pick< } >; -expectTypeOf(getBuilder().addStringOption(getStringOption())).toMatchTypeOf(); +expectTypeOf(getBuilder().addStringOptions(getStringOption())).toMatchTypeOf(); -expectTypeOf(getBuilder().addSubcommand(getSubcommand())).toMatchTypeOf(); +expectTypeOf(getBuilder().addSubcommands(getSubcommand())).toMatchTypeOf();