mirror of
https://github.com/discordjs/discord.js.git
synced 2026-05-23 12:00:09 +00:00
Compare commits
111 Commits
@discordjs
...
@discordjs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
512b0c67b9 | ||
|
|
532c3842bc | ||
|
|
edace17a13 | ||
|
|
d3154cf8f1 | ||
|
|
45552faf02 | ||
|
|
ebfd52695e | ||
|
|
595bded8a5 | ||
|
|
c74c632cdb | ||
|
|
fc003050de | ||
|
|
8702978057 | ||
|
|
c2b18d6d8b | ||
|
|
519aa3abe8 | ||
|
|
89c076c89e | ||
|
|
f224a07381 | ||
|
|
8e1e1be0c2 | ||
|
|
193a5e9e20 | ||
|
|
73c6bc2c36 | ||
|
|
b7f1ebc334 | ||
|
|
92aea94411 | ||
|
|
41dee5177d | ||
|
|
bbde371324 | ||
|
|
66b971899a | ||
|
|
43235d43fe | ||
|
|
31df3d21cd | ||
|
|
2663d76709 | ||
|
|
44a1e85847 | ||
|
|
d2e1924fa6 | ||
|
|
68dd260dee | ||
|
|
5e66f85f55 | ||
|
|
46060419a9 | ||
|
|
7c1b73cc69 | ||
|
|
95db597fc8 | ||
|
|
0047a49b73 | ||
|
|
32dff01f29 | ||
|
|
efa50fc3fa | ||
|
|
aa61c20ffd | ||
|
|
d48136bee1 | ||
|
|
46bf8f0146 | ||
|
|
7280d4e82e | ||
|
|
bd2914cc98 | ||
|
|
77804cfd55 | ||
|
|
8fea3ed978 | ||
|
|
05c63cd9a1 | ||
|
|
8d69b24b5c | ||
|
|
9baee4b2ce | ||
|
|
c986a99104 | ||
|
|
2b9e4cf9d0 | ||
|
|
1af2f4ed0e | ||
|
|
3fbfe9f1ae | ||
|
|
b901ff7c4c | ||
|
|
5f8915f6d1 | ||
|
|
ff42d7af72 | ||
|
|
0fdbabea98 | ||
|
|
e9944b3d2d | ||
|
|
2b9833cd36 | ||
|
|
7b2a2e3a15 | ||
|
|
6087088579 | ||
|
|
622acbcbf0 | ||
|
|
b2754d4a0e | ||
|
|
53cbb0e36d | ||
|
|
7ce6f2fc8a | ||
|
|
76042f0538 | ||
|
|
dedaa5d657 | ||
|
|
ed00a10e1f | ||
|
|
ae1deac2bf | ||
|
|
a367e2c8c9 | ||
|
|
7678f1176a | ||
|
|
4cca33d9b0 | ||
|
|
388783d7dd | ||
|
|
bda31284bf | ||
|
|
76968b4bc1 | ||
|
|
34343c6afa | ||
|
|
56c9396b71 | ||
|
|
21c283f964 | ||
|
|
13471fa1b7 | ||
|
|
b1ded63e42 | ||
|
|
565fc0192a | ||
|
|
33533b7284 | ||
|
|
be38f57926 | ||
|
|
f79ba52c7a | ||
|
|
72e0c99454 | ||
|
|
3d06c9d872 | ||
|
|
831aafa733 | ||
|
|
5faf074c14 | ||
|
|
297e959f48 | ||
|
|
1fc87a9698 | ||
|
|
366f7174d0 | ||
|
|
97c3237a70 | ||
|
|
c12217829b | ||
|
|
0873f9a4c3 | ||
|
|
6c77fee41b | ||
|
|
cda8d88ad5 | ||
|
|
665bf1486a | ||
|
|
99136d6be8 | ||
|
|
896dc8b21e | ||
|
|
651f2d036a | ||
|
|
2adee06b6e | ||
|
|
495bc60345 | ||
|
|
d9d578391a | ||
|
|
3c74aa2049 | ||
|
|
799fa54fa4 | ||
|
|
8a74f144ac | ||
|
|
dea68400a3 | ||
|
|
c13f18e90e | ||
|
|
aff772c7aa | ||
|
|
4594896b54 | ||
|
|
a11ff75631 | ||
|
|
9257a09abb | ||
|
|
4810f7c863 | ||
|
|
18ce10a9af | ||
|
|
ed1c1737df |
@@ -9,7 +9,9 @@
|
||||
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="npm version" /></a>
|
||||
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="npm downloads" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Tests status" /></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2" alt="Code coverage" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/commits/main"><img src="https://img.shields.io/github/last-commit/discordjs/discord.js.svg?logo=github&logoColor=ffffff" alt="Last commit." /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/graphs/contributors"><img src="https://img.shields.io/github/contributors/discordjs/discord.js.svg?maxAge=3600&logo=github&logoColor=fff&color=00c7be" alt="contributors" /></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2" alt="Code coverage" /></a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
|
||||
|
||||
@@ -29,6 +29,7 @@ export async function ParameterNode({
|
||||
{description ? <Badges node={parameter} /> : null}
|
||||
{parameter.name}
|
||||
{parameter.isOptional ? '?' : ''}: <ExcerptNode node={parameter.typeExcerpt} version={version} />
|
||||
{parameter.defaultValue ? ` = ${parameter.defaultValue}` : ''}
|
||||
</span>
|
||||
{description && parameter.description?.length ? (
|
||||
<div className="mt-4 pl-4">
|
||||
|
||||
@@ -51,7 +51,13 @@ export async function PropertyNode({
|
||||
<LinkIcon aria-hidden size={16} />
|
||||
</Link>
|
||||
{property.displayName}
|
||||
{property.isOptional ? '?' : ''} : <ExcerptNode node={property.typeExcerpt} version={version} />
|
||||
{property.isOptional ? '?' : ''} : <ExcerptNode node={property.typeExcerpt} version={version} />{' '}
|
||||
{property.summary?.defaultValueBlock.length
|
||||
? `= ${property.summary.defaultValueBlock.reduce(
|
||||
(acc: string, def: { kind: string; text: string }) => `${acc}${def.text}`,
|
||||
'',
|
||||
)}`
|
||||
: ''}
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function fetchDependencies({
|
||||
|
||||
return Object.entries<string>(parsedDependencies)
|
||||
.filter(([key]) => key.startsWith('@discordjs/') && !key.includes('api-extractor'))
|
||||
.map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${value.replaceAll('.', '-')}`);
|
||||
.map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${sanitizeVersion(value)}`);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -36,8 +36,12 @@ export async function fetchDependencies({
|
||||
|
||||
return Object.entries<string>(parsedDependencies)
|
||||
.filter(([key]) => key.startsWith('@discordjs/') && !key.includes('api-extractor'))
|
||||
.map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${value.replaceAll('.', '-')}`);
|
||||
.map(([key, value]) => `${key.replace('@discordjs/', '').replaceAll('.', '-')}-${sanitizeVersion(value)}`);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeVersion(version: string) {
|
||||
return version.replaceAll('.', '-').replace(/^[\^~]/, '');
|
||||
}
|
||||
|
||||
14
package.json
14
package.json
@@ -6,19 +6,19 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "turbo run build --concurrency=4",
|
||||
"build:affected": "turbo run build --filter=...[origin/main] --concurrency=4",
|
||||
"build:affected": "turbo run build --filter=...[origin/v14] --concurrency=4",
|
||||
"build:apps": "turbo run build:local --filter=...{apps/*} --concurrency=4",
|
||||
"build:apps:affected": "turbo run build:local --filter=...{apps/*}[origin/main] --concurrency=4",
|
||||
"build:apps:affected": "turbo run build:local --filter=...{apps/*}[origin/v14] --concurrency=4",
|
||||
"test": "turbo run test --concurrency=4",
|
||||
"test:affected": "turbo run test --filter=...[origin/main] --concurrency=4",
|
||||
"test:affected": "turbo run test --filter=...[origin/v14] --concurrency=4",
|
||||
"lint": "turbo run lint --concurrency=4",
|
||||
"lint:affected": "turbo run lint --filter=...[origin/main] --concurrency=4",
|
||||
"lint:affected": "turbo run lint --filter=...[origin/v14] --concurrency=4",
|
||||
"format": "turbo run format --concurrency=4",
|
||||
"format:affected": "turbo run format --filter=...[origin/main] --concurrency=4",
|
||||
"format:affected": "turbo run format --filter=...[origin/v14] --concurrency=4",
|
||||
"fmt": "turbo run format --concurrency=4",
|
||||
"fmt:affected": "turbo run format --filter=...[origin/main] --concurrency=4",
|
||||
"fmt:affected": "turbo run format --filter=...[origin/v14] --concurrency=4",
|
||||
"docs": "turbo run docs --concurrency=4",
|
||||
"docs:affected": "turbo run docs --filter=...[origin/main] --concurrency=4",
|
||||
"docs:affected": "turbo run docs --filter=...[origin/v14] --concurrency=4",
|
||||
"prepare": "is-ci || husky",
|
||||
"update": "pnpm --recursive update --interactive",
|
||||
"update:latest": "pnpm --recursive update --interactive --latest",
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { IExcerptTokenRange } from './Excerpt.js';
|
||||
* @public
|
||||
*/
|
||||
export interface IApiParameterOptions {
|
||||
defaultValue: string | undefined;
|
||||
isOptional: boolean;
|
||||
isRest: boolean;
|
||||
parameterName: string;
|
||||
@@ -124,6 +125,7 @@ export function ApiParameterListMixin<TBaseClass extends IApiItemConstructor>(
|
||||
isOptional: Boolean(parameterOptions.isOptional),
|
||||
isRest: Boolean(parameterOptions.isRest),
|
||||
parent: this,
|
||||
defaultValue: parameterOptions.defaultValue,
|
||||
});
|
||||
|
||||
this[_parameters].push(parameter);
|
||||
@@ -171,6 +173,7 @@ export function ApiParameterListMixin<TBaseClass extends IApiItemConstructor>(
|
||||
parameterTypeTokenRange: parameter.parameterTypeExcerpt.tokenRange,
|
||||
isOptional: parameter.isOptional,
|
||||
isRest: parameter.isRest,
|
||||
defaultValue: parameter.defaultValue,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ const MinifyJSONMapping = {
|
||||
constraintTokenRange: 'ctr',
|
||||
dependencies: 'dp',
|
||||
defaultTypeTokenRange: 'dtr',
|
||||
defaultValue: 'dv',
|
||||
docComment: 'd',
|
||||
endIndex: 'en',
|
||||
excerptTokens: 'ex',
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
@@ -1510,15 +1516,17 @@ export class ApiModelGenerator {
|
||||
const excerptTokens: IExcerptToken[] = [
|
||||
{
|
||||
kind: ExcerptTokenKind.Content,
|
||||
text: `on('${name}', (${
|
||||
jsDoc.params?.length ? `${jsDoc.params[0]?.name}${jsDoc.params[0]?.nullable ? '?' : ''}: ` : ') => {})'
|
||||
text: `public on(eventName: '${name}', listener: (${
|
||||
jsDoc.params?.length
|
||||
? `${jsDoc.params[0]?.name}${jsDoc.params[0]?.optional ? '?' : ''}: `
|
||||
: ') => void): this;'
|
||||
}`,
|
||||
},
|
||||
];
|
||||
const parameters: IApiParameterOptions[] = [];
|
||||
for (let index = 0; index < (jsDoc.params?.length ?? 0) - 1; index++) {
|
||||
const parameter = jsDoc.params![index]!;
|
||||
const newTokens = this._mapVarType(parameter.type);
|
||||
const newTokens = this._mapVarType(parameter.type, parameter.nullable);
|
||||
parameters.push({
|
||||
parameterName: parameter.name,
|
||||
parameterTypeTokenRange: {
|
||||
@@ -1527,6 +1535,7 @@ export class ApiModelGenerator {
|
||||
},
|
||||
isOptional: Boolean(parameter.optional),
|
||||
isRest: parameter.name.startsWith('...'),
|
||||
defaultValue: parameter.default?.toString(),
|
||||
});
|
||||
excerptTokens.push(...newTokens);
|
||||
excerptTokens.push({
|
||||
@@ -1537,7 +1546,7 @@ export class ApiModelGenerator {
|
||||
|
||||
if (jsDoc.params?.length) {
|
||||
const parameter = jsDoc.params![jsDoc.params.length - 1]!;
|
||||
const newTokens = this._mapVarType(parameter.type);
|
||||
const newTokens = this._mapVarType(parameter.type, parameter.nullable);
|
||||
parameters.push({
|
||||
parameterName: parameter.name,
|
||||
parameterTypeTokenRange: {
|
||||
@@ -1546,11 +1555,12 @@ export class ApiModelGenerator {
|
||||
},
|
||||
isOptional: Boolean(parameter.optional),
|
||||
isRest: parameter.name.startsWith('...'),
|
||||
defaultValue: parameter.default?.toString(),
|
||||
});
|
||||
excerptTokens.push(...newTokens);
|
||||
excerptTokens.push({
|
||||
kind: ExcerptTokenKind.Content,
|
||||
text: `) => {})`,
|
||||
text: `) => void): this;`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1638,6 +1648,7 @@ export class ApiModelGenerator {
|
||||
private _captureParameters(
|
||||
nodesToCapture: IExcerptBuilderNodeToCapture[],
|
||||
parameterNodes: ts.NodeArray<ts.ParameterDeclaration>,
|
||||
jsDoc?: DocgenParamJson[] | undefined,
|
||||
): IApiParameterOptions[] {
|
||||
const parameters: IApiParameterOptions[] = [];
|
||||
for (const parameter of parameterNodes) {
|
||||
@@ -1648,6 +1659,9 @@ export class ApiModelGenerator {
|
||||
parameterTypeTokenRange,
|
||||
isOptional: this._collector.typeChecker.isOptionalParameter(parameter),
|
||||
isRest: Boolean(parameter.dotDotDotToken),
|
||||
defaultValue:
|
||||
parameter.initializer?.getText() ??
|
||||
jsDoc?.find((param) => param.name === parameter.name.getText().trim())?.default?.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1751,7 +1765,7 @@ export class ApiModelGenerator {
|
||||
return input;
|
||||
}
|
||||
|
||||
return input.replaceAll(/(?<char>[{}])/g, '\\$<char>');
|
||||
return input.replaceAll(/(?<char>[@{}])/g, '\\$<char>');
|
||||
}
|
||||
|
||||
private _fixLinkTags(input?: string): string | undefined {
|
||||
@@ -1773,7 +1787,7 @@ export class ApiModelGenerator {
|
||||
.replaceAll('* ', '\n * * ');
|
||||
}
|
||||
|
||||
private _mapVarType(typey: DocgenVarTypeJson): IExcerptToken[] {
|
||||
private _mapVarType(typey: DocgenVarTypeJson, nullable?: boolean): IExcerptToken[] {
|
||||
const mapper = Array.isArray(typey) ? typey : (typey.types ?? []);
|
||||
const lookup: { [K in ts.SyntaxKind]?: string } = {
|
||||
[ts.SyntaxKind.ClassDeclaration]: 'class',
|
||||
@@ -1816,7 +1830,22 @@ export class ApiModelGenerator {
|
||||
{ kind: ExcerptTokenKind.Content, text: symbol ?? '' },
|
||||
];
|
||||
}, []);
|
||||
return index === 0 ? result : [{ kind: ExcerptTokenKind.Content, text: ' | ' }, ...result];
|
||||
return index === 0
|
||||
? mapper.length === 1 && (nullable || ('nullable' in typey && typey.nullable))
|
||||
? [
|
||||
...result,
|
||||
{ kind: ExcerptTokenKind.Content, text: ' | ' },
|
||||
{ kind: ExcerptTokenKind.Reference, text: 'null' },
|
||||
]
|
||||
: result
|
||||
: index === mapper.length - 1 && (nullable || ('nullable' in typey && typey.nullable))
|
||||
? [
|
||||
{ kind: ExcerptTokenKind.Content, text: ' | ' },
|
||||
...result,
|
||||
{ kind: ExcerptTokenKind.Content, text: ' | ' },
|
||||
{ kind: ExcerptTokenKind.Reference, text: 'null' },
|
||||
]
|
||||
: [{ kind: ExcerptTokenKind.Content, text: ' | ' }, ...result];
|
||||
})
|
||||
.filter((excerpt) => excerpt.text.length);
|
||||
}
|
||||
@@ -1831,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,
|
||||
@@ -1843,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,
|
||||
@@ -1866,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(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1890,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 ? '?' : ''
|
||||
}: `,
|
||||
});
|
||||
@@ -1900,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] ?? []);
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<a href="https://www.npmjs.com/package/@discordjs/brokers"><img src="https://img.shields.io/npm/v/@discordjs/brokers.svg?maxAge=3600" alt="npm version" /></a>
|
||||
<a href="https://www.npmjs.com/package/@discordjs/brokers"><img src="https://img.shields.io/npm/dt/@discordjs/brokers.svg?maxAge=3600" alt="npm downloads" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=brokers" alt="Code coverage" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/commits/main/packages/brokers"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fbrokers"></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=brokers" alt="Code coverage" /></a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
|
||||
@@ -23,7 +24,7 @@
|
||||
|
||||
## Installation
|
||||
|
||||
**Node.js 18 or newer is required.**
|
||||
**Node.js 20 or newer is required.**
|
||||
|
||||
```sh
|
||||
npm install @discordjs/brokers
|
||||
|
||||
@@ -30,8 +30,10 @@ body = """
|
||||
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
|
||||
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
|
||||
{% if commit.breaking %}\
|
||||
{% for breakingChange in commit.footers %}\
|
||||
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
|
||||
{% for footer in commit.footers %}\
|
||||
{% if footer.breaking %}\
|
||||
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
|
||||
{% endif %}\
|
||||
{% endfor %}\
|
||||
{% endif %}\
|
||||
{% endfor %}
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
"vitest": "^2.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
"node": ">=20"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -2,30 +2,6 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
# [@discordjs/builders@1.12.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.11.3...@discordjs/builders@1.12.0) - (2025-10-08)
|
||||
|
||||
## Features
|
||||
|
||||
- **builders:** Modal select menus in builders v1 (#11138) ([ac683b9](https://github.com/discordjs/discord.js/commit/ac683b9d040635de8514c80a9d433d9c6d63701b))
|
||||
|
||||
# [@discordjs/builders@1.11.3](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.11.2...@discordjs/builders@1.11.3) - (2025-08-10)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **contextMenuCommands:** Remove regular expression validation (#10996) ([4906aae](https://github.com/discordjs/discord.js/commit/4906aaea4c0e6e868fa658d3359026eb662fbcb8))
|
||||
|
||||
# [@discordjs/builders@1.11.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.10.1...@discordjs/builders@1.11.0) - (2025-04-25)
|
||||
|
||||
## Features
|
||||
|
||||
- Components v2 in builders v1 (#10787) ([118e682](https://github.com/discordjs/discord.js/commit/118e6826821b3b90f5923e40f167747e0658cfd1))
|
||||
|
||||
# [@discordjs/builders@1.10.1](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.10.0...@discordjs/builders@1.10.1) - (2025-02-10)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **EmbedBuilder:** Allow empty `name` and `value` on fields (#10747) ([49ef3a8](https://github.com/discordjs/discord.js/commit/49ef3a833eab23d426d5c667e28aa493ddc9cb6c))
|
||||
|
||||
# [@discordjs/builders@1.9.0](https://github.com/discordjs/discord.js/compare/@discordjs/builders@1.8.2...@discordjs/builders@1.9.0) - (2024-09-01)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<a href="https://www.npmjs.com/package/@discordjs/builders"><img src="https://img.shields.io/npm/v/@discordjs/builders.svg?maxAge=3600" alt="npm version" /></a>
|
||||
<a href="https://www.npmjs.com/package/@discordjs/builders"><img src="https://img.shields.io/npm/dt/@discordjs/builders.svg?maxAge=3600" alt="npm downloads" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=builders" alt="Code coverage" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/commits/main/packages/builders"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fbuilders"></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=builders" alt="Code coverage" /></a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
|
||||
@@ -23,7 +24,7 @@
|
||||
|
||||
## Installation
|
||||
|
||||
**Node.js 16.11.0 or newer is required.**
|
||||
**Node.js 18 or newer is required.**
|
||||
|
||||
```sh
|
||||
npm install @discordjs/builders
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
ButtonStyle,
|
||||
ComponentType,
|
||||
type APIActionRowComponent,
|
||||
type APIComponentInMessageActionRow,
|
||||
type APIMessageActionRowComponent,
|
||||
} from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import {
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
StringSelectMenuOptionBuilder,
|
||||
} from '../../src/index.js';
|
||||
|
||||
const rowWithButtonData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
@@ -25,7 +25,7 @@ const rowWithButtonData: APIActionRowComponent<APIComponentInMessageActionRow> =
|
||||
],
|
||||
};
|
||||
|
||||
const rowWithSelectMenuData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
const rowWithSelectMenuData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
@@ -57,7 +57,7 @@ describe('Action Row Components', () => {
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||
const actionRowData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
const actionRowData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
@@ -92,7 +92,7 @@ describe('Action Row Components', () => {
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||
const rowWithButtonData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
@@ -104,7 +104,7 @@ describe('Action Row Components', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const rowWithSelectMenuData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
const rowWithSelectMenuData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ComponentType,
|
||||
TextInputStyle,
|
||||
type APIButtonComponent,
|
||||
type APIComponentInMessageActionRow,
|
||||
type APIMessageActionRowComponent,
|
||||
type APISelectMenuComponent,
|
||||
type APITextInputComponent,
|
||||
type APIActionRowComponent,
|
||||
@@ -27,7 +27,7 @@ describe('createComponentBuilder', () => {
|
||||
);
|
||||
|
||||
test('GIVEN an action row component THEN returns a ActionRowBuilder', () => {
|
||||
const actionRow: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
const actionRow: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
components: [],
|
||||
type: ComponentType.ActionRow,
|
||||
};
|
||||
|
||||
@@ -100,7 +100,7 @@ describe('Text Input Components', () => {
|
||||
.setPlaceholder('hello')
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.toJSON();
|
||||
}).not.toThrowError();
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid input THEN valid JSON outputs are given', () => {
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
import { type APIContainerComponent, ComponentType, SeparatorSpacingSize } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { ActionRowBuilder } from '../../../src/components/ActionRow.js';
|
||||
import { createComponentBuilder } from '../../../src/components/Components.js';
|
||||
import { ButtonBuilder } from '../../../src/components/button/Button.js';
|
||||
import { ContainerBuilder } from '../../../src/components/v2/Container.js';
|
||||
import { FileBuilder } from '../../../src/components/v2/File.js';
|
||||
import { MediaGalleryBuilder } from '../../../src/components/v2/MediaGallery.js';
|
||||
import { SectionBuilder } from '../../../src/components/v2/Section.js';
|
||||
import { SeparatorBuilder } from '../../../src/components/v2/Separator.js';
|
||||
import { TextDisplayBuilder } from '../../../src/components/v2/TextDisplay.js';
|
||||
|
||||
const containerWithTextDisplay: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
id: 123,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const containerWithSeparatorData: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.Separator,
|
||||
id: 1_234,
|
||||
spacing: SeparatorSpacingSize.Small,
|
||||
divider: false,
|
||||
},
|
||||
],
|
||||
accent_color: 0x00ff00,
|
||||
};
|
||||
|
||||
const containerWithSeparatorDataNoColor: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.Separator,
|
||||
id: 1_234,
|
||||
spacing: SeparatorSpacingSize.Small,
|
||||
divider: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('Container Components', () => {
|
||||
describe('Assertion Tests', () => {
|
||||
test('GIVEN valid components THEN do not throw', () => {
|
||||
expect(() =>
|
||||
new ContainerBuilder().addActionRowComponents(
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder()),
|
||||
),
|
||||
).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addFileComponents(new FileBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addMediaGalleryComponents(new MediaGalleryBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addSectionComponents(new SectionBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addSeparatorComponents(new SeparatorBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addTextDisplayComponents(new TextDisplayBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().spliceComponents(0, 0, new SeparatorBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addSeparatorComponents([new SeparatorBuilder()])).not.toThrowError();
|
||||
expect(() =>
|
||||
new ContainerBuilder().spliceComponents(0, 0, [{ type: ComponentType.Separator }]),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||
const containerData: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
type: ComponentType.Separator,
|
||||
spacing: SeparatorSpacingSize.Large,
|
||||
divider: true,
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
type: ComponentType.File,
|
||||
file: {
|
||||
url: 'attachment://file.png',
|
||||
},
|
||||
spoiler: false,
|
||||
},
|
||||
],
|
||||
accent_color: 0xff00ff,
|
||||
spoiler: true,
|
||||
};
|
||||
|
||||
expect(new ContainerBuilder(containerData).toJSON()).toEqual(containerData);
|
||||
expect(() => createComponentBuilder({ type: ComponentType.Container, components: [] })).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||
const containerWithTextDisplay: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
id: 123,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const containerWithSeparatorData: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.Separator,
|
||||
id: 1_234,
|
||||
spacing: SeparatorSpacingSize.Small,
|
||||
divider: false,
|
||||
},
|
||||
],
|
||||
accent_color: 0x00ff00,
|
||||
};
|
||||
|
||||
expect(new ContainerBuilder(containerWithTextDisplay).toJSON()).toEqual(containerWithTextDisplay);
|
||||
expect(new ContainerBuilder(containerWithSeparatorData).toJSON()).toEqual(containerWithSeparatorData);
|
||||
expect(() => createComponentBuilder({ type: ComponentType.Container, components: [] })).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given 2', () => {
|
||||
const textDisplay = new TextDisplayBuilder().setContent('test').setId(123);
|
||||
const separator = new SeparatorBuilder().setId(1_234).setSpacing(SeparatorSpacingSize.Small).setDivider(false);
|
||||
|
||||
expect(new ContainerBuilder().addTextDisplayComponents(textDisplay).toJSON()).toEqual(containerWithTextDisplay);
|
||||
expect(new ContainerBuilder().addSeparatorComponents(separator).toJSON()).toEqual(
|
||||
containerWithSeparatorDataNoColor,
|
||||
);
|
||||
expect(new ContainerBuilder().addTextDisplayComponents([textDisplay]).toJSON()).toEqual(containerWithTextDisplay);
|
||||
expect(new ContainerBuilder().addSeparatorComponents([separator]).toJSON()).toEqual(
|
||||
containerWithSeparatorDataNoColor,
|
||||
);
|
||||
});
|
||||
|
||||
test('GIVEN valid accent color THEN valid JSON output is given', () => {
|
||||
expect(
|
||||
new ContainerBuilder({
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
})
|
||||
.setAccentColor([255, 0, 255])
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accent_color: 0xff00ff,
|
||||
});
|
||||
expect(
|
||||
new ContainerBuilder({
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
})
|
||||
.setAccentColor(0xff00ff)
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accent_color: 0xff00ff,
|
||||
});
|
||||
expect(
|
||||
new ContainerBuilder({
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
})
|
||||
.setAccentColor([255, 0, 255])
|
||||
.clearAccentColor()
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(new ContainerBuilder(containerWithSeparatorData).clearAccentColor().toJSON()).toEqual(
|
||||
containerWithSeparatorDataNoColor,
|
||||
);
|
||||
});
|
||||
|
||||
test('GIVEN valid method parameters THEN valid JSON is given', () => {
|
||||
expect(
|
||||
new ContainerBuilder()
|
||||
.addTextDisplayComponents(new TextDisplayBuilder().setId(3).clearId().setContent('test'))
|
||||
.setSpoiler()
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
spoiler: true,
|
||||
});
|
||||
expect(
|
||||
new ContainerBuilder()
|
||||
.addTextDisplayComponents({ type: ComponentType.TextDisplay, content: 'test' })
|
||||
.setSpoiler(false)
|
||||
.setId(5)
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
spoiler: false,
|
||||
id: 5,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { FileBuilder } from '../../../src/components/v2/File';
|
||||
|
||||
const dummy = {
|
||||
type: ComponentType.File as const,
|
||||
file: { url: 'attachment://owo.png' },
|
||||
};
|
||||
|
||||
describe('File', () => {
|
||||
describe('File url', () => {
|
||||
test('GIVEN a file with a pre-defined url THEN return valid toJSON data', () => {
|
||||
const file = new FileBuilder({ file: { url: 'attachment://owo.png' } });
|
||||
expect(file.toJSON()).toEqual({ ...dummy, file: { url: 'attachment://owo.png' } });
|
||||
});
|
||||
|
||||
test('GIVEN a file using File#setURL THEN return valid toJSON data', () => {
|
||||
const file = new FileBuilder();
|
||||
file.setURL('attachment://uwu.png');
|
||||
|
||||
expect(file.toJSON()).toEqual({ ...dummy, file: { url: 'attachment://uwu.png' } });
|
||||
});
|
||||
|
||||
test('GIVEN a file with an invalid url THEN throws error', () => {
|
||||
const file = new FileBuilder();
|
||||
|
||||
expect(() => file.setURL('https://google.com')).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('File spoiler', () => {
|
||||
test('GIVEN a file with a pre-defined spoiler status THEN return valid toJSON data', () => {
|
||||
const file = new FileBuilder({ ...dummy, spoiler: true });
|
||||
expect(file.toJSON()).toEqual({ ...dummy, spoiler: true });
|
||||
});
|
||||
|
||||
test('GIVEN a file using File#setSpoiler THEN return valid toJSON data', () => {
|
||||
const file = new FileBuilder({ ...dummy });
|
||||
file.setSpoiler(false);
|
||||
|
||||
expect(file.toJSON()).toEqual({ ...dummy, spoiler: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,150 +0,0 @@
|
||||
import { type APIMediaGalleryItem, type APIMediaGalleryComponent, ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { createComponentBuilder } from '../../../src/components/Components.js';
|
||||
import { MediaGalleryBuilder } from '../../../src/components/v2/MediaGallery.js';
|
||||
import { MediaGalleryItemBuilder } from '../../../src/components/v2/MediaGalleryItem.js';
|
||||
|
||||
const galleryHttpsDisplay: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
description: 'test',
|
||||
spoiler: false,
|
||||
media: { url: 'https://discord.com/logo.png' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const galleryAttachmentData: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
media: { url: 'attachment://file.png' },
|
||||
},
|
||||
],
|
||||
id: 123,
|
||||
};
|
||||
|
||||
describe('Media Gallery Components', () => {
|
||||
describe('Assertion Tests', () => {
|
||||
test('GIVEN an empty media gallery THEN throws error', () => {
|
||||
const gallery = new MediaGalleryBuilder();
|
||||
expect(() => gallery.toJSON()).toThrow();
|
||||
});
|
||||
|
||||
test('GIVEN valid items THEN do not throw', () => {
|
||||
expect(() => new MediaGalleryBuilder().addItems(new MediaGalleryItemBuilder())).not.toThrowError();
|
||||
expect(() => new MediaGalleryBuilder().spliceItems(0, 0, new MediaGalleryItemBuilder())).not.toThrowError();
|
||||
expect(() => new MediaGalleryBuilder().addItems([new MediaGalleryItemBuilder()])).not.toThrowError();
|
||||
expect(() => new MediaGalleryBuilder().spliceItems(0, 0, [new MediaGalleryItemBuilder()])).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||
const mediaGalleryData: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
media: { url: 'attachment://file.png' },
|
||||
description: 'test',
|
||||
spoiler: false,
|
||||
},
|
||||
{
|
||||
media: { url: 'https://discord.js.org/logo.jpg' },
|
||||
spoiler: true,
|
||||
},
|
||||
],
|
||||
id: 1_234,
|
||||
};
|
||||
|
||||
expect(new MediaGalleryBuilder(mediaGalleryData).toJSON()).toEqual(mediaGalleryData);
|
||||
expect(() => createComponentBuilder({ type: ComponentType.MediaGallery, items: [] })).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||
const galleryHttpsDisplay: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
description: 'test',
|
||||
spoiler: false,
|
||||
media: { url: 'https://discord.com/logo.png' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const galleryAttachmentData: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
media: { url: 'attachment://file.png' },
|
||||
},
|
||||
],
|
||||
id: 123,
|
||||
};
|
||||
|
||||
expect(new MediaGalleryBuilder(galleryHttpsDisplay).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder(galleryAttachmentData).toJSON()).toEqual(galleryAttachmentData);
|
||||
expect(() => createComponentBuilder({ type: ComponentType.MediaGallery, items: [] })).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given 2', () => {
|
||||
const item1 = new MediaGalleryItemBuilder()
|
||||
.setDescription('test')
|
||||
.setSpoiler(false)
|
||||
.setURL('https://discord.com/logo.png');
|
||||
const item2 = new MediaGalleryItemBuilder().setURL('attachment://file.png');
|
||||
|
||||
expect(new MediaGalleryBuilder().addItems(item1).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder().addItems(item2).setId(123).toJSON()).toEqual(galleryAttachmentData);
|
||||
expect(new MediaGalleryBuilder().addItems([item1]).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder().addItems([item2]).setId(123).toJSON()).toEqual(galleryAttachmentData);
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON options THEN valid JSON output is given 2', () => {
|
||||
const item1: APIMediaGalleryItem = {
|
||||
description: 'test',
|
||||
spoiler: false,
|
||||
media: { url: 'https://discord.com/logo.png' },
|
||||
};
|
||||
const item2 = {
|
||||
media: { url: 'attachment://file.png' },
|
||||
};
|
||||
|
||||
expect(new MediaGalleryBuilder().addItems(item1).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder().addItems(item2).setId(123).toJSON()).toEqual(galleryAttachmentData);
|
||||
expect(new MediaGalleryBuilder().addItems([item1]).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder().addItems([item2]).setId(123).toJSON()).toEqual(galleryAttachmentData);
|
||||
});
|
||||
|
||||
test('GIVEN valid builder callback THEN valid JSON output is given', () => {
|
||||
const item1 = new MediaGalleryItemBuilder()
|
||||
.setDescription('test')
|
||||
.setSpoiler(false)
|
||||
.setURL('https://discord.com/logo.png');
|
||||
const item2 = new MediaGalleryItemBuilder().setURL('attachment://file.png');
|
||||
|
||||
expect(
|
||||
new MediaGalleryBuilder()
|
||||
.addItems((item) => item.setDescription('test').setSpoiler(false).setURL('https://discord.com/logo.png'))
|
||||
.toJSON(),
|
||||
).toEqual(galleryHttpsDisplay);
|
||||
expect(
|
||||
new MediaGalleryBuilder()
|
||||
.spliceItems(0, 0, (item) => item.setURL('attachment://file.png'))
|
||||
.setId(123)
|
||||
.toJSON(),
|
||||
).toEqual(galleryAttachmentData);
|
||||
expect(
|
||||
new MediaGalleryBuilder()
|
||||
.addItems([(item) => item.setDescription('test').setSpoiler(false).setURL('https://discord.com/logo.png')])
|
||||
.toJSON(),
|
||||
).toEqual(galleryHttpsDisplay);
|
||||
expect(
|
||||
new MediaGalleryBuilder()
|
||||
.spliceItems(0, 0, [(item) => item.setDescription('test').clearDescription().setURL('attachment://file.png')])
|
||||
.setId(123)
|
||||
.toJSON(),
|
||||
).toEqual(galleryAttachmentData);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,191 +0,0 @@
|
||||
import { type APISectionComponent, ButtonStyle, ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { createComponentBuilder } from '../../../src/components/Components.js';
|
||||
import { ButtonBuilder } from '../../../src/components/button/Button.js';
|
||||
import { SectionBuilder } from '../../../src/components/v2/Section.js';
|
||||
import { TextDisplayBuilder } from '../../../src/components/v2/TextDisplay.js';
|
||||
import { ThumbnailBuilder } from '../../../src/components/v2/Thumbnail.js';
|
||||
|
||||
const sectionWithButtonData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Button,
|
||||
label: 'test',
|
||||
custom_id: '123',
|
||||
style: ButtonStyle.Primary,
|
||||
},
|
||||
};
|
||||
|
||||
const sectionWithThumbnailData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Thumbnail,
|
||||
media: { url: 'attachment://file.png' },
|
||||
spoiler: true,
|
||||
description: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Section Components', () => {
|
||||
describe('Assertion Tests', () => {
|
||||
test('GIVEN valid components THEN do not throw', () => {
|
||||
expect(() => new SectionBuilder().addTextDisplayComponents(new TextDisplayBuilder())).not.toThrowError();
|
||||
expect(() => new SectionBuilder().spliceTextDisplayComponents(0, 0, new TextDisplayBuilder())).not.toThrowError();
|
||||
expect(() => new SectionBuilder().addTextDisplayComponents([new TextDisplayBuilder()])).not.toThrowError();
|
||||
expect(() =>
|
||||
new SectionBuilder().spliceTextDisplayComponents(0, 0, [new TextDisplayBuilder()]),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||
const sectionData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
id: 123,
|
||||
},
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Thumbnail,
|
||||
media: { url: 'attachment://file.png' },
|
||||
},
|
||||
};
|
||||
|
||||
expect(new SectionBuilder(sectionData).toJSON()).toEqual(sectionData);
|
||||
expect(() =>
|
||||
createComponentBuilder({
|
||||
type: ComponentType.Section,
|
||||
components: [],
|
||||
accessory: { type: ComponentType.Thumbnail, media: { url: 'https://discord.com/logo.png' } },
|
||||
}),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||
const sectionWithButtonData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Button,
|
||||
label: 'test',
|
||||
custom_id: '123',
|
||||
style: ButtonStyle.Primary,
|
||||
},
|
||||
};
|
||||
|
||||
const sectionWithThumbnailData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Thumbnail,
|
||||
media: { url: 'attachment://file.png' },
|
||||
spoiler: true,
|
||||
description: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
expect(new SectionBuilder(sectionWithButtonData).toJSON()).toEqual(sectionWithButtonData);
|
||||
expect(new SectionBuilder(sectionWithThumbnailData).toJSON()).toEqual(sectionWithThumbnailData);
|
||||
expect(() =>
|
||||
createComponentBuilder({
|
||||
type: ComponentType.Section,
|
||||
components: [],
|
||||
accessory: {
|
||||
type: ComponentType.Button,
|
||||
label: 'test',
|
||||
custom_id: '123',
|
||||
style: ButtonStyle.Primary,
|
||||
},
|
||||
}),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given 2', () => {
|
||||
const button = new ButtonBuilder().setLabel('test').setStyle(ButtonStyle.Primary).setCustomId('123');
|
||||
const thumbnail = new ThumbnailBuilder().setDescription('test').setSpoiler().setURL('attachment://file.png');
|
||||
const textDisplay = new TextDisplayBuilder().setContent('test');
|
||||
|
||||
expect(new SectionBuilder().addTextDisplayComponents(textDisplay).setButtonAccessory(button).toJSON()).toEqual(
|
||||
sectionWithButtonData,
|
||||
);
|
||||
expect(
|
||||
new SectionBuilder().addTextDisplayComponents(textDisplay).setThumbnailAccessory(thumbnail).toJSON(),
|
||||
).toEqual(sectionWithThumbnailData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents([textDisplay])
|
||||
.setButtonAccessory((button) => button.setLabel('test').setStyle(ButtonStyle.Primary).setCustomId('123'))
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents([textDisplay])
|
||||
.setThumbnailAccessory((thumbnail) =>
|
||||
thumbnail.setDescription('test').setSpoiler().setURL('attachment://file.png'),
|
||||
)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithThumbnailData);
|
||||
});
|
||||
|
||||
test('GIVEN valid builder callback THEN valid JSON output is given', () => {
|
||||
const button = new ButtonBuilder().setLabel('test').setStyle(ButtonStyle.Primary).setCustomId('123');
|
||||
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents((textDisplay) => textDisplay.setContent('test'))
|
||||
.setButtonAccessory(button)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.spliceTextDisplayComponents(0, 0, (textDisplay) => textDisplay.setContent('test'))
|
||||
.setButtonAccessory(button)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents([(textDisplay) => textDisplay.setContent('test')])
|
||||
.setButtonAccessory(button)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.spliceTextDisplayComponents(0, 0, [(textDisplay) => textDisplay.setContent('test')])
|
||||
.setButtonAccessory(button)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,35 +0,0 @@
|
||||
import { ComponentType, SeparatorSpacingSize } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { SeparatorBuilder } from '../../../src/components/v2/Separator';
|
||||
|
||||
describe('Separator', () => {
|
||||
describe('Divider', () => {
|
||||
test('GIVEN a separator with a pre-defined divider THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder({ divider: true });
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator, divider: true });
|
||||
});
|
||||
|
||||
test('GIVEN a separator with a set divider THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder().setDivider(false);
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator, divider: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('Spacing', () => {
|
||||
test('GIVEN a separator with a pre-defined spacing THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder({ spacing: SeparatorSpacingSize.Small });
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator, spacing: SeparatorSpacingSize.Small });
|
||||
});
|
||||
|
||||
test('GIVEN a separator with a set spacing THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Large);
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator, spacing: SeparatorSpacingSize.Large });
|
||||
});
|
||||
|
||||
test('GIVEN a separator with a set spacing THEN clear spacing THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder({ spacing: SeparatorSpacingSize.Small });
|
||||
separator.clearSpacing();
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { TextDisplayBuilder } from '../../../src/components/v2/TextDisplay';
|
||||
|
||||
describe('TextDisplay', () => {
|
||||
describe('TextDisplay content', () => {
|
||||
test('GIVEN a text display with a pre-defined content THEN return valid toJSON data', () => {
|
||||
const textDisplay = new TextDisplayBuilder({ content: 'foo' });
|
||||
expect(textDisplay.toJSON()).toEqual({ type: ComponentType.TextDisplay, content: 'foo' });
|
||||
});
|
||||
|
||||
test('GIVEN a text display with a set content THEN return valid toJSON data', () => {
|
||||
const textDisplay = new TextDisplayBuilder().setContent('foo');
|
||||
expect(textDisplay.toJSON()).toEqual({ type: ComponentType.TextDisplay, content: 'foo' });
|
||||
});
|
||||
|
||||
test('GIVEN a text display with a pre-defined content THEN overwritten content THEN return valid toJSON data', () => {
|
||||
const textDisplay = new TextDisplayBuilder({ content: 'foo' });
|
||||
textDisplay.setContent('bar');
|
||||
expect(textDisplay.toJSON()).toEqual({ type: ComponentType.TextDisplay, content: 'bar' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,69 +0,0 @@
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { ThumbnailBuilder } from '../../../src/components/v2/Thumbnail';
|
||||
|
||||
const dummy = {
|
||||
type: ComponentType.Thumbnail as const,
|
||||
media: { url: 'https://google.com' },
|
||||
};
|
||||
|
||||
describe('Thumbnail', () => {
|
||||
describe('Thumbnail url', () => {
|
||||
test('GIVEN a thumbnail with a pre-defined url THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ media: { url: 'https://google.com' } });
|
||||
expect(thumbnail.toJSON()).toEqual({ type: ComponentType.Thumbnail, media: { url: 'https://google.com' } });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with a set url THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder().setURL('https://google.com');
|
||||
expect(thumbnail.toJSON()).toEqual({ type: ComponentType.Thumbnail, media: { url: 'https://google.com' } });
|
||||
});
|
||||
|
||||
test.each(['owo', 'discord://user'])('GIVEN a thumbnail with an invalid URL (%s) THEN throws error', (input) => {
|
||||
const thumbnail = new ThumbnailBuilder();
|
||||
|
||||
expect(() => thumbnail.setURL(input)).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Thumbnail description', () => {
|
||||
test('GIVEN a thumbnail with a pre-defined description THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ ...dummy, description: 'foo' });
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy, description: 'foo' });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with a set description THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ ...dummy });
|
||||
thumbnail.setDescription('foo');
|
||||
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy, description: 'foo' });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with a pre-defined description THEN unset description THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ description: 'foo', ...dummy });
|
||||
thumbnail.clearDescription();
|
||||
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with an invalid description THEN throws error', () => {
|
||||
const thumbnail = new ThumbnailBuilder();
|
||||
|
||||
expect(() => thumbnail.setDescription('a'.repeat(1_025))).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Thumbnail spoiler', () => {
|
||||
test('GIVEN a thumbnail with a pre-defined spoiler status THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ ...dummy, spoiler: true });
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy, spoiler: true });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with a set spoiler status THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ ...dummy });
|
||||
thumbnail.setSpoiler(false);
|
||||
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy, spoiler: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,8 +16,8 @@ describe('Context Menu Commands', () => {
|
||||
// Too short of a name
|
||||
expect(() => ContextMenuCommandAssertions.validateName('')).toThrowError();
|
||||
|
||||
// This should be fine, even with trailing and leading spaces (API trims it).
|
||||
expect(() => ContextMenuCommandAssertions.validateName(' 🩵 ABC 123 $%^& ')).not.toThrowError();
|
||||
// Invalid characters used
|
||||
expect(() => ContextMenuCommandAssertions.validateName('ABC123$%^&')).toThrowError();
|
||||
|
||||
// Too long of a name
|
||||
expect(() =>
|
||||
@@ -60,6 +60,8 @@ describe('Context Menu Commands', () => {
|
||||
});
|
||||
|
||||
test('GIVEN invalid name THEN throw error', () => {
|
||||
expect(() => getBuilder().setName('$$$')).toThrowError();
|
||||
|
||||
expect(() => getBuilder().setName(' ')).toThrowError();
|
||||
});
|
||||
|
||||
@@ -164,7 +166,7 @@ describe('Context Menu Commands', () => {
|
||||
});
|
||||
|
||||
describe('integration types', () => {
|
||||
test('GIVEN a builder with valid integraton types THEN does not throw an error', () => {
|
||||
test('GIVEN a builder with valid integration types THEN does not throw an error', () => {
|
||||
expect(() =>
|
||||
getBuilder().setIntegrationTypes([
|
||||
ApplicationIntegrationType.GuildInstall,
|
||||
|
||||
@@ -565,7 +565,7 @@ describe('Slash Commands', () => {
|
||||
});
|
||||
|
||||
describe('integration types', () => {
|
||||
test('GIVEN a builder with valid integraton types THEN does not throw an error', () => {
|
||||
test('GIVEN a builder with valid integration types THEN does not throw an error', () => {
|
||||
expect(() =>
|
||||
getBuilder().setIntegrationTypes([
|
||||
ApplicationIntegrationType.GuildInstall,
|
||||
|
||||
@@ -324,16 +324,12 @@ describe('Embed', () => {
|
||||
test('GIVEN an embed using Embed#addFields THEN returns valid toJSON data', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
embed.addFields({ name: 'foo', value: 'bar' });
|
||||
embed.addFields([
|
||||
{ name: 'foo', value: 'bar' },
|
||||
{ name: '', value: '' },
|
||||
]);
|
||||
embed.addFields([{ name: 'foo', value: 'bar' }]);
|
||||
|
||||
expect(embed.toJSON()).toStrictEqual({
|
||||
fields: [
|
||||
{ name: 'foo', value: 'bar' },
|
||||
{ name: 'foo', value: 'bar' },
|
||||
{ name: '', value: '' },
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -385,24 +381,38 @@ describe('Embed', () => {
|
||||
expect(() => embed.setFields(Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' })))).toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN invalid field amount THEN throws error', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
describe('GIVEN invalid field amount THEN throws error', () => {
|
||||
test('1', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
|
||||
expect(() =>
|
||||
embed.addFields(...Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
embed.addFields(...Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||
).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
test('GIVEN invalid field name length THEN throws error', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
describe('GIVEN invalid field name THEN throws error', () => {
|
||||
test('2', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
|
||||
expect(() => embed.addFields({ name: 'a'.repeat(257), value: 'bar' })).toThrowError();
|
||||
expect(() => embed.addFields({ name: '', value: 'bar' })).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
test('GIVEN invalid field value length THEN throws error', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
describe('GIVEN invalid field name length THEN throws error', () => {
|
||||
test('3', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
|
||||
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1_025) })).toThrowError();
|
||||
expect(() => embed.addFields({ name: 'a'.repeat(257), value: 'bar' })).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('GIVEN invalid field value length THEN throws error', () => {
|
||||
test('4', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
|
||||
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1_025) })).toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,8 +30,10 @@ body = """
|
||||
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
|
||||
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
|
||||
{% if commit.breaking %}\
|
||||
{% for breakingChange in commit.footers %}\
|
||||
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
|
||||
{% for footer in commit.footers %}\
|
||||
{% if footer.breaking %}\
|
||||
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
|
||||
{% endif %}\
|
||||
{% endfor %}\
|
||||
{% endif %}\
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@discordjs/builders",
|
||||
"version": "1.12.0",
|
||||
"version": "1.9.0",
|
||||
"description": "A set of builders that you can use when creating your bot",
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
@@ -68,7 +68,7 @@
|
||||
"@discordjs/formatters": "workspace:^",
|
||||
"@discordjs/util": "workspace:^",
|
||||
"@sapphire/shapeshift": "^4.0.0",
|
||||
"discord-api-types": "^0.38.26",
|
||||
"discord-api-types": "^0.37.119",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"ts-mixer": "^6.0.4",
|
||||
"tslib": "^2.6.3"
|
||||
@@ -91,7 +91,7 @@
|
||||
"vitest": "^2.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.11.0"
|
||||
"node": ">=18"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import {
|
||||
type APIActionRowComponent,
|
||||
ComponentType,
|
||||
type APIComponentInMessageActionRow,
|
||||
type APIComponentInModalActionRow,
|
||||
type APIComponentInActionRow,
|
||||
type APIMessageActionRowComponent,
|
||||
type APIModalActionRowComponent,
|
||||
type APIActionRowComponentTypes,
|
||||
} from 'discord-api-types/v10';
|
||||
import { normalizeArray, type RestOrArray } from '../util/normalizeArray.js';
|
||||
import { ComponentBuilder } from './Component.js';
|
||||
@@ -18,6 +18,13 @@ import type { StringSelectMenuBuilder } from './selectMenu/StringSelectMenu.js';
|
||||
import type { UserSelectMenuBuilder } from './selectMenu/UserSelectMenu.js';
|
||||
import type { TextInputBuilder } from './textInput/TextInput.js';
|
||||
|
||||
/**
|
||||
* The builders that may be used for messages.
|
||||
*/
|
||||
export type MessageComponentBuilder =
|
||||
| ActionRowBuilder<MessageActionRowComponentBuilder>
|
||||
| MessageActionRowComponentBuilder;
|
||||
|
||||
/**
|
||||
* The builders that may be used for modals.
|
||||
*/
|
||||
@@ -50,7 +57,7 @@ export type AnyComponentBuilder = MessageActionRowComponentBuilder | ModalAction
|
||||
* @typeParam ComponentType - The types of components this action row holds
|
||||
*/
|
||||
export class ActionRowBuilder<ComponentType extends AnyComponentBuilder> extends ComponentBuilder<
|
||||
APIActionRowComponent<APIComponentInMessageActionRow | APIComponentInModalActionRow>
|
||||
APIActionRowComponent<APIMessageActionRowComponent | APIModalActionRowComponent>
|
||||
> {
|
||||
/**
|
||||
* The components within this action row.
|
||||
@@ -91,7 +98,7 @@ export class ActionRowBuilder<ComponentType extends AnyComponentBuilder> extends
|
||||
* .addComponents(button2, button3);
|
||||
* ```
|
||||
*/
|
||||
public constructor({ components, ...data }: Partial<APIActionRowComponent<APIComponentInActionRow>> = {}) {
|
||||
public constructor({ components, ...data }: Partial<APIActionRowComponent<APIActionRowComponentTypes>> = {}) {
|
||||
super({ type: ComponentType.ActionRow, ...data });
|
||||
this.components = (components?.map((component) => createComponentBuilder(component)) ?? []) as ComponentType[];
|
||||
}
|
||||
|
||||
@@ -3,13 +3,6 @@ import { ButtonStyle, ChannelType, type APIMessageComponentEmoji } from 'discord
|
||||
import { isValidationEnabled } from '../util/validation.js';
|
||||
import { StringSelectMenuOptionBuilder } from './selectMenu/StringSelectMenuOption.js';
|
||||
|
||||
export const idValidator = s
|
||||
.number()
|
||||
.safeInt()
|
||||
.greaterThanOrEqual(1)
|
||||
.lessThan(4_294_967_296) // 2^32 - 1
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const customIdValidator = s
|
||||
.string()
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
import type { JSONEncodable } from '@discordjs/util';
|
||||
import type {
|
||||
APIActionRowComponent,
|
||||
APIComponentInActionRow,
|
||||
APIActionRowComponentTypes,
|
||||
APIBaseComponent,
|
||||
ComponentType,
|
||||
APIMessageComponent,
|
||||
APIModalComponent,
|
||||
} from 'discord-api-types/v10';
|
||||
import { idValidator } from './Assertions';
|
||||
|
||||
/**
|
||||
* Any action row component data represented as an object.
|
||||
*/
|
||||
export type AnyAPIActionRowComponent =
|
||||
| APIActionRowComponent<APIComponentInActionRow>
|
||||
| APIComponentInActionRow
|
||||
| APIMessageComponent
|
||||
| APIModalComponent;
|
||||
export type AnyAPIActionRowComponent = APIActionRowComponent<APIActionRowComponentTypes> | APIActionRowComponentTypes;
|
||||
|
||||
/**
|
||||
* The base component builder that contains common symbols for all sorts of components.
|
||||
@@ -49,22 +42,4 @@ export abstract class ComponentBuilder<
|
||||
public constructor(data: Partial<DataType>) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id (not the custom id) for this component.
|
||||
*
|
||||
* @param id - The id for this component
|
||||
*/
|
||||
public setId(id: number) {
|
||||
this.data.id = idValidator.parse(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the id of this component, defaulting to a default incremented id.
|
||||
*/
|
||||
public clearId() {
|
||||
this.data.id = undefined;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,18 @@
|
||||
import type { JSONEncodable } from '@discordjs/util';
|
||||
import { ComponentType, type APIMessageComponent, type APIModalComponent } from 'discord-api-types/v10';
|
||||
import {
|
||||
ActionRowBuilder,
|
||||
type MessageActionRowComponentBuilder,
|
||||
type AnyComponentBuilder,
|
||||
type MessageComponentBuilder,
|
||||
type ModalComponentBuilder,
|
||||
} from './ActionRow.js';
|
||||
import { ComponentBuilder } from './Component.js';
|
||||
import { ButtonBuilder } from './button/Button.js';
|
||||
import { LabelBuilder } from './label/Label.js';
|
||||
import { ChannelSelectMenuBuilder } from './selectMenu/ChannelSelectMenu.js';
|
||||
import { MentionableSelectMenuBuilder } from './selectMenu/MentionableSelectMenu.js';
|
||||
import { RoleSelectMenuBuilder } from './selectMenu/RoleSelectMenu.js';
|
||||
import { StringSelectMenuBuilder } from './selectMenu/StringSelectMenu.js';
|
||||
import { UserSelectMenuBuilder } from './selectMenu/UserSelectMenu.js';
|
||||
import { TextInputBuilder } from './textInput/TextInput.js';
|
||||
import { ContainerBuilder } from './v2/Container.js';
|
||||
import { FileBuilder } from './v2/File.js';
|
||||
import { MediaGalleryBuilder } from './v2/MediaGallery.js';
|
||||
import { SectionBuilder } from './v2/Section.js';
|
||||
import { SeparatorBuilder } from './v2/Separator.js';
|
||||
import { TextDisplayBuilder } from './v2/TextDisplay.js';
|
||||
import { ThumbnailBuilder } from './v2/Thumbnail.js';
|
||||
|
||||
/**
|
||||
* The builders that may be used for messages.
|
||||
*/
|
||||
export type MessageComponentBuilder =
|
||||
| ActionRowBuilder<MessageActionRowComponentBuilder>
|
||||
| ContainerBuilder
|
||||
| FileBuilder
|
||||
| MediaGalleryBuilder
|
||||
| MessageActionRowComponentBuilder
|
||||
| SectionBuilder
|
||||
| SeparatorBuilder
|
||||
| TextDisplayBuilder
|
||||
| ThumbnailBuilder;
|
||||
|
||||
/**
|
||||
* Components here are mapped to their respective builder.
|
||||
@@ -73,38 +50,6 @@ export interface MappedComponentTypes {
|
||||
* The channel select component type is associated with a {@link ChannelSelectMenuBuilder}.
|
||||
*/
|
||||
[ComponentType.ChannelSelect]: ChannelSelectMenuBuilder;
|
||||
/**
|
||||
* The file component type is associated with a {@link FileBuilder}.
|
||||
*/
|
||||
[ComponentType.File]: FileBuilder;
|
||||
/**
|
||||
* The separator component type is associated with a {@link SeparatorBuilder}.
|
||||
*/
|
||||
[ComponentType.Separator]: SeparatorBuilder;
|
||||
/**
|
||||
* The container component type is associated with a {@link ContainerBuilder}.
|
||||
*/
|
||||
[ComponentType.Container]: ContainerBuilder;
|
||||
/**
|
||||
* The text display component type is associated with a {@link TextDisplayBuilder}.
|
||||
*/
|
||||
[ComponentType.TextDisplay]: TextDisplayBuilder;
|
||||
/**
|
||||
* The thumbnail component type is associated with a {@link ThumbnailBuilder}.
|
||||
*/
|
||||
[ComponentType.Thumbnail]: ThumbnailBuilder;
|
||||
/**
|
||||
* The section component type is associated with a {@link SectionBuilder}.
|
||||
*/
|
||||
[ComponentType.Section]: SectionBuilder;
|
||||
/**
|
||||
* The media gallery component type is associated with a {@link MediaGalleryBuilder}.
|
||||
*/
|
||||
[ComponentType.MediaGallery]: MediaGalleryBuilder;
|
||||
/**
|
||||
* The label component type is associated with a {@link LabelBuilder}.
|
||||
*/
|
||||
[ComponentType.Label]: LabelBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,46 +97,8 @@ export function createComponentBuilder(
|
||||
return new MentionableSelectMenuBuilder(data);
|
||||
case ComponentType.ChannelSelect:
|
||||
return new ChannelSelectMenuBuilder(data);
|
||||
case ComponentType.File:
|
||||
return new FileBuilder(data);
|
||||
case ComponentType.Container:
|
||||
return new ContainerBuilder(data);
|
||||
case ComponentType.Section:
|
||||
return new SectionBuilder(data);
|
||||
case ComponentType.Separator:
|
||||
return new SeparatorBuilder(data);
|
||||
case ComponentType.TextDisplay:
|
||||
return new TextDisplayBuilder(data);
|
||||
case ComponentType.Thumbnail:
|
||||
return new ThumbnailBuilder(data);
|
||||
case ComponentType.MediaGallery:
|
||||
return new MediaGalleryBuilder(data);
|
||||
case ComponentType.Label:
|
||||
return new LabelBuilder(data);
|
||||
default:
|
||||
// @ts-expect-error This case can still occur if we get a newer unsupported component type
|
||||
throw new Error(`Cannot properly serialize component type: ${data.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
function isBuilder<Builder extends JSONEncodable<any>>(
|
||||
builder: unknown,
|
||||
Constructor: new () => Builder,
|
||||
): builder is Builder {
|
||||
return builder instanceof Constructor;
|
||||
}
|
||||
|
||||
export function resolveBuilder<ComponentType extends Record<PropertyKey, any>, Builder extends JSONEncodable<any>>(
|
||||
builder: Builder | ComponentType | ((builder: Builder) => Builder),
|
||||
Constructor: new (data?: ComponentType) => Builder,
|
||||
) {
|
||||
if (isBuilder(builder, Constructor)) {
|
||||
return builder;
|
||||
}
|
||||
|
||||
if (typeof builder === 'function') {
|
||||
return builder(new Constructor());
|
||||
}
|
||||
|
||||
return new Constructor(builder);
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { s } from '@sapphire/shapeshift';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { isValidationEnabled } from '../../util/validation.js';
|
||||
import { idValidator } from '../Assertions.js';
|
||||
import {
|
||||
selectMenuChannelPredicate,
|
||||
selectMenuMentionablePredicate,
|
||||
selectMenuRolePredicate,
|
||||
selectMenuStringPredicate,
|
||||
selectMenuUserPredicate,
|
||||
} from '../selectMenu/Assertions.js';
|
||||
import { textInputPredicate } from '../textInput/Assertions.js';
|
||||
|
||||
export const labelPredicate = s
|
||||
.object({
|
||||
id: idValidator.optional(),
|
||||
type: s.literal(ComponentType.Label),
|
||||
label: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(45),
|
||||
description: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100).optional(),
|
||||
component: s.union([
|
||||
textInputPredicate,
|
||||
selectMenuUserPredicate,
|
||||
selectMenuRolePredicate,
|
||||
selectMenuMentionablePredicate,
|
||||
selectMenuChannelPredicate,
|
||||
selectMenuStringPredicate,
|
||||
]),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
@@ -1,198 +0,0 @@
|
||||
import type {
|
||||
APIChannelSelectComponent,
|
||||
APILabelComponent,
|
||||
APIMentionableSelectComponent,
|
||||
APIRoleSelectComponent,
|
||||
APIStringSelectComponent,
|
||||
APITextInputComponent,
|
||||
APIUserSelectComponent,
|
||||
} from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { ComponentBuilder } from '../Component.js';
|
||||
import { createComponentBuilder, resolveBuilder } from '../Components.js';
|
||||
import { ChannelSelectMenuBuilder } from '../selectMenu/ChannelSelectMenu.js';
|
||||
import { MentionableSelectMenuBuilder } from '../selectMenu/MentionableSelectMenu.js';
|
||||
import { RoleSelectMenuBuilder } from '../selectMenu/RoleSelectMenu.js';
|
||||
import { StringSelectMenuBuilder } from '../selectMenu/StringSelectMenu.js';
|
||||
import { UserSelectMenuBuilder } from '../selectMenu/UserSelectMenu.js';
|
||||
import { TextInputBuilder } from '../textInput/TextInput.js';
|
||||
import { labelPredicate } from './Assertions.js';
|
||||
|
||||
export interface LabelBuilderData extends Partial<Omit<APILabelComponent, 'component'>> {
|
||||
component?:
|
||||
| ChannelSelectMenuBuilder
|
||||
| MentionableSelectMenuBuilder
|
||||
| RoleSelectMenuBuilder
|
||||
| StringSelectMenuBuilder
|
||||
| TextInputBuilder
|
||||
| UserSelectMenuBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for labels.
|
||||
*/
|
||||
export class LabelBuilder extends ComponentBuilder<LabelBuilderData> {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public override readonly data: LabelBuilderData;
|
||||
|
||||
/**
|
||||
* Creates a new label.
|
||||
*
|
||||
* @param data - The API data to create this label with
|
||||
* @example
|
||||
* Creating a label from an API data object:
|
||||
* ```ts
|
||||
* const label = new LabelBuilder({
|
||||
* label: "label",
|
||||
* component,
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a label using setters and API data:
|
||||
* ```ts
|
||||
* const label = new LabelBuilder({
|
||||
* label: 'label',
|
||||
* component,
|
||||
* }).setLabel('new text');
|
||||
* ```
|
||||
*/
|
||||
public constructor(data: Partial<APILabelComponent> = {}) {
|
||||
super({ type: ComponentType.Label });
|
||||
|
||||
const { component, ...rest } = data;
|
||||
|
||||
this.data = {
|
||||
...rest,
|
||||
component: component ? createComponentBuilder(component) : undefined,
|
||||
type: ComponentType.Label,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label for this label.
|
||||
*
|
||||
* @param label - The label to use
|
||||
*/
|
||||
public setLabel(label: string) {
|
||||
this.data.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description for this label.
|
||||
*
|
||||
* @param description - The description to use
|
||||
*/
|
||||
public setDescription(description: string) {
|
||||
this.data.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the description for this label.
|
||||
*/
|
||||
public clearDescription() {
|
||||
this.data.description = undefined;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a string select menu component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setStringSelectMenuComponent(
|
||||
input:
|
||||
| APIStringSelectComponent
|
||||
| StringSelectMenuBuilder
|
||||
| ((builder: StringSelectMenuBuilder) => StringSelectMenuBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, StringSelectMenuBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a user select menu component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setUserSelectMenuComponent(
|
||||
input: APIUserSelectComponent | UserSelectMenuBuilder | ((builder: UserSelectMenuBuilder) => UserSelectMenuBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, UserSelectMenuBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a role select menu component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setRoleSelectMenuComponent(
|
||||
input: APIRoleSelectComponent | RoleSelectMenuBuilder | ((builder: RoleSelectMenuBuilder) => RoleSelectMenuBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, RoleSelectMenuBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a mentionable select menu component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setMentionableSelectMenuComponent(
|
||||
input:
|
||||
| APIMentionableSelectComponent
|
||||
| MentionableSelectMenuBuilder
|
||||
| ((builder: MentionableSelectMenuBuilder) => MentionableSelectMenuBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, MentionableSelectMenuBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a channel select menu component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setChannelSelectMenuComponent(
|
||||
input:
|
||||
| APIChannelSelectComponent
|
||||
| ChannelSelectMenuBuilder
|
||||
| ((builder: ChannelSelectMenuBuilder) => ChannelSelectMenuBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, ChannelSelectMenuBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a text input component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setTextInputComponent(
|
||||
input: APITextInputComponent | TextInputBuilder | ((builder: TextInputBuilder) => TextInputBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, TextInputBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public override toJSON(): APILabelComponent {
|
||||
const { component, ...rest } = this.data;
|
||||
|
||||
const data = {
|
||||
...rest,
|
||||
// The label predicate validates the component.
|
||||
component: component?.toJSON(),
|
||||
};
|
||||
|
||||
labelPredicate.parse(data);
|
||||
|
||||
return data as APILabelComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import { Result, s } from '@sapphire/shapeshift';
|
||||
import { ChannelType, ComponentType, SelectMenuDefaultValueType } from 'discord-api-types/v10';
|
||||
import { isValidationEnabled } from '../../util/validation.js';
|
||||
import { customIdValidator, emojiValidator, idValidator } from '../Assertions.js';
|
||||
import { labelValidator } from '../textInput/Assertions.js';
|
||||
|
||||
const selectMenuBasePredicate = s.object({
|
||||
id: idValidator.optional(),
|
||||
placeholder: s.string().lengthLessThanOrEqual(150).optional(),
|
||||
min_values: s.number().greaterThanOrEqual(0).lessThanOrEqual(25).optional(),
|
||||
max_values: s.number().greaterThanOrEqual(0).lessThanOrEqual(25).optional(),
|
||||
custom_id: customIdValidator,
|
||||
disabled: s.boolean().optional(),
|
||||
});
|
||||
|
||||
export const selectMenuChannelPredicate = selectMenuBasePredicate
|
||||
.extend({
|
||||
type: s.literal(ComponentType.ChannelSelect),
|
||||
channel_types: s.nativeEnum(ChannelType).array().optional(),
|
||||
default_values: s
|
||||
.object({ id: s.string(), type: s.literal(SelectMenuDefaultValueType.Channel) })
|
||||
.array()
|
||||
.lengthLessThanOrEqual(25)
|
||||
.optional(),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const selectMenuMentionablePredicate = selectMenuBasePredicate
|
||||
.extend({
|
||||
type: s.literal(ComponentType.MentionableSelect),
|
||||
default_values: s
|
||||
.object({
|
||||
id: s.string(),
|
||||
type: s.literal([SelectMenuDefaultValueType.Role, SelectMenuDefaultValueType.User]),
|
||||
})
|
||||
.array()
|
||||
.lengthLessThanOrEqual(25)
|
||||
.optional(),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const selectMenuRolePredicate = selectMenuBasePredicate
|
||||
.extend({
|
||||
type: s.literal(ComponentType.RoleSelect),
|
||||
default_values: s
|
||||
.object({ id: s.string(), type: s.literal(SelectMenuDefaultValueType.Role) })
|
||||
.array()
|
||||
.lengthLessThanOrEqual(25)
|
||||
.optional(),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const selectMenuUserPredicate = selectMenuBasePredicate
|
||||
.extend({
|
||||
type: s.literal(ComponentType.UserSelect),
|
||||
default_values: s
|
||||
.object({ id: s.string(), type: s.literal(SelectMenuDefaultValueType.User) })
|
||||
.array()
|
||||
.lengthLessThanOrEqual(25)
|
||||
.optional(),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const selectMenuStringOptionPredicate = s
|
||||
.object({
|
||||
label: labelValidator,
|
||||
value: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100),
|
||||
description: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100).optional(),
|
||||
emoji: emojiValidator.optional(),
|
||||
default: s.boolean().optional(),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const selectMenuStringPredicate = selectMenuBasePredicate
|
||||
.extend({
|
||||
type: s.literal(ComponentType.StringSelect),
|
||||
options: selectMenuStringOptionPredicate.array().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(25),
|
||||
})
|
||||
.reshape((value) => {
|
||||
if (value.min_values !== undefined && value.options.length < value.min_values) {
|
||||
return Result.err(new RangeError(`The number of options must be greater than or equal to min_values`));
|
||||
}
|
||||
|
||||
if (value.min_values !== undefined && value.max_values !== undefined && value.min_values > value.max_values) {
|
||||
return Result.err(
|
||||
new RangeError(`The maximum amount of options must be greater than or equal to the minimum amount of options`),
|
||||
);
|
||||
}
|
||||
|
||||
return Result.ok(value);
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { APISelectMenuComponent } from 'discord-api-types/v10';
|
||||
import { customIdValidator, disabledValidator, minMaxValidator, placeholderValidator } from '../Assertions.js';
|
||||
import { ComponentBuilder } from '../Component.js';
|
||||
import { requiredValidator } from '../textInput/Assertions.js';
|
||||
|
||||
/**
|
||||
* The base select menu builder that contains common symbols for select menu builders.
|
||||
@@ -61,17 +60,6 @@ export abstract class BaseSelectMenuBuilder<
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this select menu is required.
|
||||
*
|
||||
* @remarks Only for use in modals.
|
||||
* @param required - Whether this select menu is required
|
||||
*/
|
||||
public setRequired(required = true) {
|
||||
this.data.required = requiredValidator.parse(required);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { s } from '@sapphire/shapeshift';
|
||||
import { ComponentType, TextInputStyle } from 'discord-api-types/v10';
|
||||
import { TextInputStyle } from 'discord-api-types/v10';
|
||||
import { isValidationEnabled } from '../../util/validation.js';
|
||||
import { customIdValidator, idValidator } from '../Assertions.js';
|
||||
import { customIdValidator } from '../Assertions.js';
|
||||
|
||||
export const textInputStyleValidator = s.nativeEnum(TextInputStyle).setValidationEnabled(isValidationEnabled);
|
||||
export const textInputStyleValidator = s.nativeEnum(TextInputStyle);
|
||||
export const minLengthValidator = s
|
||||
.number()
|
||||
.int()
|
||||
@@ -16,7 +16,7 @@ export const maxLengthValidator = s
|
||||
.greaterThanOrEqual(1)
|
||||
.lessThanOrEqual(4_000)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
export const requiredValidator = s.boolean().setValidationEnabled(isValidationEnabled);
|
||||
export const requiredValidator = s.boolean();
|
||||
export const valueValidator = s.string().lengthLessThanOrEqual(4_000).setValidationEnabled(isValidationEnabled);
|
||||
export const placeholderValidator = s.string().lengthLessThanOrEqual(100).setValidationEnabled(isValidationEnabled);
|
||||
export const labelValidator = s
|
||||
@@ -25,21 +25,8 @@ export const labelValidator = s
|
||||
.lengthLessThanOrEqual(45)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const textInputPredicate = s
|
||||
.object({
|
||||
type: s.literal(ComponentType.TextInput),
|
||||
custom_id: customIdValidator,
|
||||
style: textInputStyleValidator,
|
||||
id: idValidator.optional(),
|
||||
min_length: minLengthValidator.optional(),
|
||||
max_length: maxLengthValidator.optional(),
|
||||
placeholder: placeholderValidator.optional(),
|
||||
value: valueValidator.optional(),
|
||||
required: requiredValidator.optional(),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export function validateRequiredParameters(customId?: string, style?: TextInputStyle) {
|
||||
export function validateRequiredParameters(customId?: string, style?: TextInputStyle, label?: string) {
|
||||
customIdValidator.parse(customId);
|
||||
textInputStyleValidator.parse(style);
|
||||
labelValidator.parse(label);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export class TextInputBuilder
|
||||
* ```ts
|
||||
* const textInput = new TextInputBuilder({
|
||||
* custom_id: 'a cool text input',
|
||||
* placeholder: 'Type something',
|
||||
* label: 'Type something',
|
||||
* style: TextInputStyle.Short,
|
||||
* });
|
||||
* ```
|
||||
@@ -38,7 +38,7 @@ export class TextInputBuilder
|
||||
* Creating a text input using setters and API data:
|
||||
* ```ts
|
||||
* const textInput = new TextInputBuilder({
|
||||
* placeholder: 'Type something else',
|
||||
* label: 'Type something else',
|
||||
* })
|
||||
* .setCustomId('woah')
|
||||
* .setStyle(TextInputStyle.Paragraph);
|
||||
@@ -62,7 +62,6 @@ export class TextInputBuilder
|
||||
* Sets the label for this text input.
|
||||
*
|
||||
* @param label - The label to use
|
||||
* @deprecated Use a label builder to create a label (and optionally a description) instead.
|
||||
*/
|
||||
public setLabel(label: string) {
|
||||
this.data.label = labelValidator.parse(label);
|
||||
@@ -133,7 +132,7 @@ export class TextInputBuilder
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public toJSON(): APITextInputComponent {
|
||||
validateRequiredParameters(this.data.custom_id, this.data.style);
|
||||
validateRequiredParameters(this.data.custom_id, this.data.style, this.data.label);
|
||||
|
||||
return {
|
||||
...this.data,
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import { s } from '@sapphire/shapeshift';
|
||||
import { SeparatorSpacingSize } from 'discord-api-types/v10';
|
||||
import { colorPredicate } from '../../messages/embed/Assertions';
|
||||
import { isValidationEnabled } from '../../util/validation';
|
||||
import { ComponentBuilder } from '../Component';
|
||||
import { ButtonBuilder } from '../button/Button';
|
||||
import type { ContainerComponentBuilder } from './Container';
|
||||
import type { MediaGalleryItemBuilder } from './MediaGalleryItem';
|
||||
import type { TextDisplayBuilder } from './TextDisplay';
|
||||
import { ThumbnailBuilder } from './Thumbnail';
|
||||
|
||||
export const unfurledMediaItemPredicate = s
|
||||
.object({
|
||||
url: s
|
||||
.string()
|
||||
.url(
|
||||
{ allowedProtocols: ['http:', 'https:', 'attachment:'] },
|
||||
{ message: 'Invalid protocol for media URL. Must be http:, https:, or attachment:' },
|
||||
),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const descriptionPredicate = s
|
||||
.string()
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
.lengthLessThanOrEqual(1_024)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const filePredicate = s
|
||||
.object({
|
||||
url: s
|
||||
.string()
|
||||
.url({ allowedProtocols: ['attachment:'] }, { message: 'Invalid protocol for file URL. Must be attachment:' }),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const spoilerPredicate = s.boolean();
|
||||
|
||||
export const dividerPredicate = s.boolean();
|
||||
|
||||
export const spacingPredicate = s.nativeEnum(SeparatorSpacingSize);
|
||||
|
||||
export const textDisplayContentPredicate = s
|
||||
.string()
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
.lengthLessThanOrEqual(4_000)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const accessoryPredicate = s
|
||||
.instance(ButtonBuilder)
|
||||
.or(s.instance(ThumbnailBuilder))
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const containerColorPredicate = colorPredicate.nullish();
|
||||
|
||||
export function assertReturnOfBuilder<ReturnType extends MediaGalleryItemBuilder | TextDisplayBuilder>(
|
||||
input: unknown,
|
||||
ExpectedInstanceOf: new () => ReturnType,
|
||||
): asserts input is ReturnType {
|
||||
s.instance(ExpectedInstanceOf).setValidationEnabled(isValidationEnabled).parse(input);
|
||||
}
|
||||
|
||||
export function validateComponentArray<
|
||||
ReturnType extends ContainerComponentBuilder | MediaGalleryItemBuilder = ContainerComponentBuilder,
|
||||
>(input: unknown, min: number, max: number, ExpectedInstanceOf?: new () => ReturnType): asserts input is ReturnType[] {
|
||||
(ExpectedInstanceOf ? s.instance(ExpectedInstanceOf) : s.instance(ComponentBuilder))
|
||||
.array()
|
||||
.lengthGreaterThanOrEqual(min)
|
||||
.lengthLessThanOrEqual(max)
|
||||
.setValidationEnabled(isValidationEnabled)
|
||||
.parse(input);
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
/* eslint-disable jsdoc/check-param-names */
|
||||
|
||||
import type {
|
||||
APIActionRowComponent,
|
||||
APIComponentInContainer,
|
||||
APIComponentInMessageActionRow,
|
||||
APIContainerComponent,
|
||||
APIFileComponent,
|
||||
APIMediaGalleryComponent,
|
||||
APISectionComponent,
|
||||
APISeparatorComponent,
|
||||
APITextDisplayComponent,
|
||||
} from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import type { RGBTuple } from '../../index.js';
|
||||
import { MediaGalleryBuilder, SectionBuilder } from '../../index.js';
|
||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
|
||||
import type { AnyComponentBuilder, MessageActionRowComponentBuilder } from '../ActionRow.js';
|
||||
import { ActionRowBuilder } from '../ActionRow.js';
|
||||
import { ComponentBuilder } from '../Component.js';
|
||||
import { createComponentBuilder, resolveBuilder } from '../Components.js';
|
||||
import { containerColorPredicate, spoilerPredicate } from './Assertions.js';
|
||||
import { FileBuilder } from './File.js';
|
||||
import { SeparatorBuilder } from './Separator.js';
|
||||
import { TextDisplayBuilder } from './TextDisplay.js';
|
||||
|
||||
/**
|
||||
* The builders that may be used within a container.
|
||||
*/
|
||||
export type ContainerComponentBuilder =
|
||||
| ActionRowBuilder<AnyComponentBuilder>
|
||||
| FileBuilder
|
||||
| MediaGalleryBuilder
|
||||
| SectionBuilder
|
||||
| SeparatorBuilder
|
||||
| TextDisplayBuilder;
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for a container.
|
||||
*/
|
||||
export class ContainerBuilder extends ComponentBuilder<APIContainerComponent> {
|
||||
/**
|
||||
* The components within this container.
|
||||
*/
|
||||
public readonly components: ContainerComponentBuilder[];
|
||||
|
||||
/**
|
||||
* Creates a new container from API data.
|
||||
*
|
||||
* @param data - The API data to create this container with
|
||||
* @example
|
||||
* Creating a container from an API data object:
|
||||
* ```ts
|
||||
* const container = new ContainerBuilder({
|
||||
* components: [
|
||||
* {
|
||||
* content: "Some text here",
|
||||
* type: ComponentType.TextDisplay,
|
||||
* },
|
||||
* ],
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a container using setters and API data:
|
||||
* ```ts
|
||||
* const container = new ContainerBuilder({
|
||||
* components: [
|
||||
* {
|
||||
* content: "# Heading",
|
||||
* type: ComponentType.TextDisplay,
|
||||
* },
|
||||
* ],
|
||||
* })
|
||||
* .addComponents(separator, section);
|
||||
* ```
|
||||
*/
|
||||
public constructor({ components, ...data }: Partial<APIContainerComponent> = {}) {
|
||||
super({ type: ComponentType.Container, ...data });
|
||||
this.components = (components?.map((component) => createComponentBuilder(component)) ??
|
||||
[]) as ContainerComponentBuilder[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the accent color of this container.
|
||||
*
|
||||
* @param color - The color to use
|
||||
*/
|
||||
public setAccentColor(color?: RGBTuple | number): this {
|
||||
// Data assertions
|
||||
containerColorPredicate.parse(color);
|
||||
|
||||
if (Array.isArray(color)) {
|
||||
const [red, green, blue] = color;
|
||||
this.data.accent_color = (red << 16) + (green << 8) + blue;
|
||||
return this;
|
||||
}
|
||||
|
||||
this.data.accent_color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the accent color of this container.
|
||||
*/
|
||||
public clearAccentColor() {
|
||||
this.data.accent_color = undefined;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds action row components to this container.
|
||||
*
|
||||
* @param components - The action row components to add
|
||||
*/
|
||||
public addActionRowComponents<ComponentType extends MessageActionRowComponentBuilder>(
|
||||
...components: RestOrArray<
|
||||
| ActionRowBuilder<ComponentType>
|
||||
| APIActionRowComponent<APIComponentInMessageActionRow>
|
||||
| ((builder: ActionRowBuilder<ComponentType>) => ActionRowBuilder<ComponentType>)
|
||||
>
|
||||
) {
|
||||
this.components.push(
|
||||
...normalizeArray(components).map((component) => resolveBuilder(component, ActionRowBuilder<ComponentType>)),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds file components to this container.
|
||||
*
|
||||
* @param components - The file components to add
|
||||
*/
|
||||
public addFileComponents(
|
||||
...components: RestOrArray<APIFileComponent | FileBuilder | ((builder: FileBuilder) => FileBuilder)>
|
||||
) {
|
||||
this.components.push(...normalizeArray(components).map((component) => resolveBuilder(component, FileBuilder)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds media gallery components to this container.
|
||||
*
|
||||
* @param components - The media gallery components to add
|
||||
*/
|
||||
public addMediaGalleryComponents(
|
||||
...components: RestOrArray<
|
||||
APIMediaGalleryComponent | MediaGalleryBuilder | ((builder: MediaGalleryBuilder) => MediaGalleryBuilder)
|
||||
>
|
||||
) {
|
||||
this.components.push(
|
||||
...normalizeArray(components).map((component) => resolveBuilder(component, MediaGalleryBuilder)),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds section components to this container.
|
||||
*
|
||||
* @param components - The section components to add
|
||||
*/
|
||||
public addSectionComponents(
|
||||
...components: RestOrArray<APISectionComponent | SectionBuilder | ((builder: SectionBuilder) => SectionBuilder)>
|
||||
) {
|
||||
this.components.push(...normalizeArray(components).map((component) => resolveBuilder(component, SectionBuilder)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds separator components to this container.
|
||||
*
|
||||
* @param components - The separator components to add
|
||||
*/
|
||||
public addSeparatorComponents(
|
||||
...components: RestOrArray<
|
||||
APISeparatorComponent | SeparatorBuilder | ((builder: SeparatorBuilder) => SeparatorBuilder)
|
||||
>
|
||||
) {
|
||||
this.components.push(...normalizeArray(components).map((component) => resolveBuilder(component, SeparatorBuilder)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds text display components to this container.
|
||||
*
|
||||
* @param components - The text display components to add
|
||||
*/
|
||||
public addTextDisplayComponents(
|
||||
...components: RestOrArray<
|
||||
APITextDisplayComponent | TextDisplayBuilder | ((builder: TextDisplayBuilder) => TextDisplayBuilder)
|
||||
>
|
||||
) {
|
||||
this.components.push(
|
||||
...normalizeArray(components).map((component) => resolveBuilder(component, TextDisplayBuilder)),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes, replaces, or inserts components for this container.
|
||||
*
|
||||
* @param index - The index to start removing, replacing or inserting components
|
||||
* @param deleteCount - The amount of components to remove
|
||||
* @param components - The components to set
|
||||
*/
|
||||
public spliceComponents(
|
||||
index: number,
|
||||
deleteCount: number,
|
||||
...components: RestOrArray<APIComponentInContainer | ContainerComponentBuilder>
|
||||
) {
|
||||
this.components.splice(
|
||||
index,
|
||||
deleteCount,
|
||||
...normalizeArray(components).map((component) =>
|
||||
component instanceof ComponentBuilder ? component : createComponentBuilder(component),
|
||||
),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the spoiler status of this container.
|
||||
*
|
||||
* @param spoiler - The spoiler status to use
|
||||
*/
|
||||
public setSpoiler(spoiler = true) {
|
||||
this.data.spoiler = spoilerPredicate.parse(spoiler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public toJSON(): APIContainerComponent {
|
||||
return {
|
||||
...this.data,
|
||||
components: this.components.map((component) => component.toJSON()),
|
||||
} as APIContainerComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import { ComponentType, type APIFileComponent } from 'discord-api-types/v10';
|
||||
import { ComponentBuilder } from '../Component';
|
||||
import { filePredicate, spoilerPredicate } from './Assertions';
|
||||
|
||||
export class FileBuilder extends ComponentBuilder<APIFileComponent> {
|
||||
/**
|
||||
* Creates a new file from API data.
|
||||
*
|
||||
* @param data - The API data to create this file with
|
||||
* @example
|
||||
* Creating a file from an API data object:
|
||||
* ```ts
|
||||
* const file = new FileBuilder({
|
||||
* spoiler: true,
|
||||
* file: {
|
||||
* url: 'attachment://file.png',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a file using setters and API data:
|
||||
* ```ts
|
||||
* const file = new FileBuilder({
|
||||
* file: {
|
||||
* url: 'attachment://image.jpg',
|
||||
* },
|
||||
* })
|
||||
* .setSpoiler(false);
|
||||
* ```
|
||||
*/
|
||||
public constructor(data: Partial<APIFileComponent> = {}) {
|
||||
super({ type: ComponentType.File, ...data, file: data.file ? { url: data.file.url } : undefined });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the spoiler status of this file.
|
||||
*
|
||||
* @param spoiler - The spoiler status to use
|
||||
*/
|
||||
public setSpoiler(spoiler = true) {
|
||||
this.data.spoiler = spoilerPredicate.parse(spoiler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the media URL of this file.
|
||||
*
|
||||
* @param url - The URL to use
|
||||
*/
|
||||
public setURL(url: string) {
|
||||
this.data.file = filePredicate.parse({ url });
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public override toJSON(): APIFileComponent {
|
||||
filePredicate.parse(this.data.file);
|
||||
|
||||
return { ...this.data, file: { ...this.data.file } } as APIFileComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/* eslint-disable jsdoc/check-param-names */
|
||||
|
||||
import type { APIMediaGalleryComponent, APIMediaGalleryItem } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
|
||||
import { ComponentBuilder } from '../Component.js';
|
||||
import { resolveBuilder } from '../Components.js';
|
||||
import { assertReturnOfBuilder, validateComponentArray } from './Assertions.js';
|
||||
import { MediaGalleryItemBuilder } from './MediaGalleryItem.js';
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for a container.
|
||||
*/
|
||||
export class MediaGalleryBuilder extends ComponentBuilder<APIMediaGalleryComponent> {
|
||||
/**
|
||||
* The components within this container.
|
||||
*/
|
||||
public readonly items: MediaGalleryItemBuilder[];
|
||||
|
||||
/**
|
||||
* Creates a new media gallery from API data.
|
||||
*
|
||||
* @param data - The API data to create this media gallery with
|
||||
* @example
|
||||
* Creating a media gallery from an API data object:
|
||||
* ```ts
|
||||
* const mediaGallery = new MediaGalleryBuilder({
|
||||
* items: [
|
||||
* {
|
||||
* description: "Some text here",
|
||||
* media: {
|
||||
* url: 'https://cdn.discordapp.com/embed/avatars/2.png',
|
||||
* },
|
||||
* },
|
||||
* ],
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a media gallery using setters and API data:
|
||||
* ```ts
|
||||
* const mediaGallery = new MediaGalleryBuilder({
|
||||
* items: [
|
||||
* {
|
||||
* description: "alt text",
|
||||
* media: {
|
||||
* url: 'https://cdn.discordapp.com/embed/avatars/5.png',
|
||||
* },
|
||||
* },
|
||||
* ],
|
||||
* })
|
||||
* .addItems(item2, item3);
|
||||
* ```
|
||||
*/
|
||||
public constructor({ items, ...data }: Partial<APIMediaGalleryComponent> = {}) {
|
||||
super({ type: ComponentType.MediaGallery, ...data });
|
||||
this.items = items?.map((item) => new MediaGalleryItemBuilder(item)) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds items to this media gallery.
|
||||
*
|
||||
* @param items - The items to add
|
||||
*/
|
||||
public addItems(
|
||||
...items: RestOrArray<
|
||||
APIMediaGalleryItem | MediaGalleryItemBuilder | ((builder: MediaGalleryItemBuilder) => MediaGalleryItemBuilder)
|
||||
>
|
||||
) {
|
||||
this.items.push(
|
||||
...normalizeArray(items).map((input) => {
|
||||
const result = resolveBuilder(input, MediaGalleryItemBuilder);
|
||||
|
||||
assertReturnOfBuilder(result, MediaGalleryItemBuilder);
|
||||
return result;
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes, replaces, or inserts media gallery items for this media gallery.
|
||||
*
|
||||
* @param index - The index to start removing, replacing or inserting items
|
||||
* @param deleteCount - The amount of items to remove
|
||||
* @param items - The items to insert
|
||||
*/
|
||||
public spliceItems(
|
||||
index: number,
|
||||
deleteCount: number,
|
||||
...items: RestOrArray<
|
||||
APIMediaGalleryItem | MediaGalleryItemBuilder | ((builder: MediaGalleryItemBuilder) => MediaGalleryItemBuilder)
|
||||
>
|
||||
) {
|
||||
this.items.splice(
|
||||
index,
|
||||
deleteCount,
|
||||
...normalizeArray(items).map((input) => {
|
||||
const result = resolveBuilder(input, MediaGalleryItemBuilder);
|
||||
|
||||
assertReturnOfBuilder(result, MediaGalleryItemBuilder);
|
||||
return result;
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public toJSON(): APIMediaGalleryComponent {
|
||||
validateComponentArray(this.items, 1, 10, MediaGalleryItemBuilder);
|
||||
return {
|
||||
...this.data,
|
||||
items: this.items.map((item) => item.toJSON()),
|
||||
} as APIMediaGalleryComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import type { JSONEncodable } from '@discordjs/util';
|
||||
import type { APIMediaGalleryItem } from 'discord-api-types/v10';
|
||||
import { descriptionPredicate, spoilerPredicate, unfurledMediaItemPredicate } from './Assertions';
|
||||
|
||||
export class MediaGalleryItemBuilder implements JSONEncodable<APIMediaGalleryItem> {
|
||||
/**
|
||||
* The API data associated with this media gallery item.
|
||||
*/
|
||||
public readonly data: Partial<APIMediaGalleryItem>;
|
||||
|
||||
/**
|
||||
* Creates a new media gallery item from API data.
|
||||
*
|
||||
* @param data - The API data to create this media gallery item with
|
||||
* @example
|
||||
* Creating a media gallery item from an API data object:
|
||||
* ```ts
|
||||
* const item = new MediaGalleryItemBuilder({
|
||||
* description: "Some text here",
|
||||
* media: {
|
||||
* url: 'https://cdn.discordapp.com/embed/avatars/2.png',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a media gallery item using setters and API data:
|
||||
* ```ts
|
||||
* const item = new MediaGalleryItemBuilder({
|
||||
* media: {
|
||||
* url: 'https://cdn.discordapp.com/embed/avatars/5.png',
|
||||
* },
|
||||
* })
|
||||
* .setDescription("alt text");
|
||||
* ```
|
||||
*/
|
||||
public constructor(data: Partial<APIMediaGalleryItem> = {}) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description of this media gallery item.
|
||||
*
|
||||
* @param description - The description to use
|
||||
*/
|
||||
public setDescription(description: string) {
|
||||
this.data.description = descriptionPredicate.parse(description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the description of this media gallery item.
|
||||
*/
|
||||
public clearDescription() {
|
||||
this.data.description = undefined;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the spoiler status of this media gallery item.
|
||||
*
|
||||
* @param spoiler - The spoiler status to use
|
||||
*/
|
||||
public setSpoiler(spoiler = true) {
|
||||
this.data.spoiler = spoilerPredicate.parse(spoiler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the media URL of this media gallery item.
|
||||
*
|
||||
* @param url - The URL to use
|
||||
*/
|
||||
public setURL(url: string) {
|
||||
this.data.media = unfurledMediaItemPredicate.parse({ url });
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this builder to API-compatible JSON data.
|
||||
*
|
||||
* @remarks
|
||||
* This method runs validations on the data before serializing it.
|
||||
* As such, it may throw an error if the data is invalid.
|
||||
*/
|
||||
public toJSON(): APIMediaGalleryItem {
|
||||
unfurledMediaItemPredicate.parse(this.data.media);
|
||||
|
||||
return { ...this.data } as APIMediaGalleryItem;
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/* eslint-disable jsdoc/check-param-names */
|
||||
|
||||
import type {
|
||||
APIButtonComponent,
|
||||
APISectionComponent,
|
||||
APITextDisplayComponent,
|
||||
APIThumbnailComponent,
|
||||
} from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { ButtonBuilder, ThumbnailBuilder } from '../../index.js';
|
||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
|
||||
import { ComponentBuilder } from '../Component.js';
|
||||
import { createComponentBuilder, resolveBuilder } from '../Components.js';
|
||||
import { accessoryPredicate, assertReturnOfBuilder, validateComponentArray } from './Assertions.js';
|
||||
import { TextDisplayBuilder } from './TextDisplay.js';
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for a section.
|
||||
*/
|
||||
export class SectionBuilder extends ComponentBuilder<APISectionComponent> {
|
||||
/**
|
||||
* The components within this section.
|
||||
*/
|
||||
public readonly components: ComponentBuilder[];
|
||||
|
||||
/**
|
||||
* The accessory of this section.
|
||||
*/
|
||||
public readonly accessory?: ButtonBuilder | ThumbnailBuilder;
|
||||
|
||||
/**
|
||||
* Creates a new section from API data.
|
||||
*
|
||||
* @param data - The API data to create this section with
|
||||
* @example
|
||||
* Creating a section from an API data object:
|
||||
* ```ts
|
||||
* const section = new SectionBuilder({
|
||||
* components: [
|
||||
* {
|
||||
* content: "Some text here",
|
||||
* type: ComponentType.TextDisplay,
|
||||
* },
|
||||
* ],
|
||||
* accessory: {
|
||||
* media: {
|
||||
* url: 'https://cdn.discordapp.com/embed/avatars/3.png',
|
||||
* },
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a section using setters and API data:
|
||||
* ```ts
|
||||
* const section = new SectionBuilder({
|
||||
* components: [
|
||||
* {
|
||||
* content: "# Heading",
|
||||
* type: ComponentType.TextDisplay,
|
||||
* },
|
||||
* ],
|
||||
* })
|
||||
* .setPrimaryButtonAccessory(button);
|
||||
* ```
|
||||
*/
|
||||
public constructor({ components, accessory, ...data }: Partial<APISectionComponent> = {}) {
|
||||
super({ type: ComponentType.Section, ...data });
|
||||
this.components = (components?.map((component) => createComponentBuilder(component)) ?? []) as ComponentBuilder[];
|
||||
this.accessory = accessory ? createComponentBuilder(accessory) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the accessory of this section to a button.
|
||||
*
|
||||
* @param accessory - The accessory to use
|
||||
*/
|
||||
public setButtonAccessory(
|
||||
accessory: APIButtonComponent | ButtonBuilder | ((builder: ButtonBuilder) => ButtonBuilder),
|
||||
): this {
|
||||
Reflect.set(this, 'accessory', accessoryPredicate.parse(resolveBuilder(accessory, ButtonBuilder)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the accessory of this section to a thumbnail.
|
||||
*
|
||||
* @param accessory - The accessory to use
|
||||
*/
|
||||
public setThumbnailAccessory(
|
||||
accessory: APIThumbnailComponent | ThumbnailBuilder | ((builder: ThumbnailBuilder) => ThumbnailBuilder),
|
||||
): this {
|
||||
Reflect.set(this, 'accessory', accessoryPredicate.parse(resolveBuilder(accessory, ThumbnailBuilder)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds text display components to this section.
|
||||
*
|
||||
* @param components - The text display components to add
|
||||
*/
|
||||
public addTextDisplayComponents(
|
||||
...components: RestOrArray<TextDisplayBuilder | ((builder: TextDisplayBuilder) => TextDisplayBuilder)>
|
||||
) {
|
||||
this.components.push(
|
||||
...normalizeArray(components).map((input) => {
|
||||
const result = resolveBuilder(input, TextDisplayBuilder);
|
||||
|
||||
assertReturnOfBuilder(result, TextDisplayBuilder);
|
||||
return result;
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes, replaces, or inserts text display components for this section.
|
||||
*
|
||||
* @param index - The index to start removing, replacing or inserting text display components
|
||||
* @param deleteCount - The amount of text display components to remove
|
||||
* @param components - The text display components to insert
|
||||
*/
|
||||
public spliceTextDisplayComponents(
|
||||
index: number,
|
||||
deleteCount: number,
|
||||
...components: RestOrArray<
|
||||
APITextDisplayComponent | TextDisplayBuilder | ((builder: TextDisplayBuilder) => TextDisplayBuilder)
|
||||
>
|
||||
) {
|
||||
this.components.splice(
|
||||
index,
|
||||
deleteCount,
|
||||
...normalizeArray(components).map((input) => {
|
||||
const result = resolveBuilder(input, TextDisplayBuilder);
|
||||
|
||||
assertReturnOfBuilder(result, TextDisplayBuilder);
|
||||
return result;
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public toJSON(): APISectionComponent {
|
||||
validateComponentArray(this.components, 1, 3, TextDisplayBuilder);
|
||||
return {
|
||||
...this.data,
|
||||
components: this.components.map((component) => component.toJSON()),
|
||||
accessory: accessoryPredicate.parse(this.accessory).toJSON(),
|
||||
} as APISectionComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import type { SeparatorSpacingSize, APISeparatorComponent } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { ComponentBuilder } from '../Component';
|
||||
import { dividerPredicate, spacingPredicate } from './Assertions';
|
||||
|
||||
export class SeparatorBuilder extends ComponentBuilder<APISeparatorComponent> {
|
||||
/**
|
||||
* Creates a new separator from API data.
|
||||
*
|
||||
* @param data - The API data to create this separator with
|
||||
* @example
|
||||
* Creating a separator from an API data object:
|
||||
* ```ts
|
||||
* const separator = new SeparatorBuilder({
|
||||
* spacing: SeparatorSpacingSize.Small,
|
||||
* divider: true,
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a separator using setters and API data:
|
||||
* ```ts
|
||||
* const separator = new SeparatorBuilder({
|
||||
* spacing: SeparatorSpacingSize.Large,
|
||||
* })
|
||||
* .setDivider(false);
|
||||
* ```
|
||||
*/
|
||||
public constructor(data: Partial<APISeparatorComponent> = {}) {
|
||||
super({
|
||||
type: ComponentType.Separator,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this separator should show a divider line.
|
||||
*
|
||||
* @param divider - Whether to show a divider line
|
||||
*/
|
||||
public setDivider(divider = true) {
|
||||
this.data.divider = dividerPredicate.parse(divider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the spacing of this separator.
|
||||
*
|
||||
* @param spacing - The spacing to use
|
||||
*/
|
||||
public setSpacing(spacing: SeparatorSpacingSize) {
|
||||
this.data.spacing = spacingPredicate.parse(spacing);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the spacing of this separator.
|
||||
*/
|
||||
public clearSpacing() {
|
||||
this.data.spacing = undefined;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public override toJSON(): APISeparatorComponent {
|
||||
return { ...this.data } as APISeparatorComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import type { APITextDisplayComponent } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { ComponentBuilder } from '../Component';
|
||||
import { textDisplayContentPredicate } from './Assertions';
|
||||
|
||||
export class TextDisplayBuilder extends ComponentBuilder<APITextDisplayComponent> {
|
||||
/**
|
||||
* Creates a new text display from API data.
|
||||
*
|
||||
* @param data - The API data to create this text display with
|
||||
* @example
|
||||
* Creating a text display from an API data object:
|
||||
* ```ts
|
||||
* const textDisplay = new TextDisplayBuilder({
|
||||
* content: 'some text',
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a text display using setters and API data:
|
||||
* ```ts
|
||||
* const textDisplay = new TextDisplayBuilder({
|
||||
* content: 'old text',
|
||||
* })
|
||||
* .setContent('new text');
|
||||
* ```
|
||||
*/
|
||||
public constructor(data: Partial<APITextDisplayComponent> = {}) {
|
||||
super({
|
||||
type: ComponentType.TextDisplay,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text of this text display.
|
||||
*
|
||||
* @param content - The text to use
|
||||
*/
|
||||
public setContent(content: string) {
|
||||
this.data.content = textDisplayContentPredicate.parse(content);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public override toJSON(): APITextDisplayComponent {
|
||||
textDisplayContentPredicate.parse(this.data.content);
|
||||
|
||||
return { ...this.data } as APITextDisplayComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import type { APIThumbnailComponent } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { ComponentBuilder } from '../Component';
|
||||
import { descriptionPredicate, spoilerPredicate, unfurledMediaItemPredicate } from './Assertions';
|
||||
|
||||
export class ThumbnailBuilder extends ComponentBuilder<APIThumbnailComponent> {
|
||||
/**
|
||||
* Creates a new thumbnail from API data.
|
||||
*
|
||||
* @param data - The API data to create this thumbnail with
|
||||
* @example
|
||||
* Creating a thumbnail from an API data object:
|
||||
* ```ts
|
||||
* const thumbnail = new ThumbnailBuilder({
|
||||
* description: 'some text',
|
||||
* media: {
|
||||
* url: 'https://cdn.discordapp.com/embed/avatars/4.png',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a thumbnail using setters and API data:
|
||||
* ```ts
|
||||
* const thumbnail = new ThumbnailBuilder({
|
||||
* media: {
|
||||
* url: 'attachment://image.png',
|
||||
* },
|
||||
* })
|
||||
* .setDescription('alt text');
|
||||
* ```
|
||||
*/
|
||||
public constructor(data: Partial<APIThumbnailComponent> = {}) {
|
||||
super({
|
||||
type: ComponentType.Thumbnail,
|
||||
...data,
|
||||
media: data.media ? { url: data.media.url } : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description of this thumbnail.
|
||||
*
|
||||
* @param description - The description to use
|
||||
*/
|
||||
public setDescription(description: string) {
|
||||
this.data.description = descriptionPredicate.parse(description);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the description of this thumbnail.
|
||||
*/
|
||||
public clearDescription() {
|
||||
this.data.description = undefined;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the spoiler status of this thumbnail.
|
||||
*
|
||||
* @param spoiler - The spoiler status to use
|
||||
*/
|
||||
public setSpoiler(spoiler = true) {
|
||||
this.data.spoiler = spoilerPredicate.parse(spoiler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the media URL of this thumbnail.
|
||||
*
|
||||
* @param url - The URL to use
|
||||
*/
|
||||
public setURL(url: string) {
|
||||
this.data.media = unfurledMediaItemPredicate.parse({ url });
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public override toJSON(): APIThumbnailComponent {
|
||||
unfurledMediaItemPredicate.parse(this.data.media);
|
||||
|
||||
return { ...this.data } as APIThumbnailComponent;
|
||||
}
|
||||
}
|
||||
@@ -34,19 +34,6 @@ export {
|
||||
export * from './components/selectMenu/StringSelectMenuOption.js';
|
||||
export * from './components/selectMenu/UserSelectMenu.js';
|
||||
|
||||
export * from './components/label/Label.js';
|
||||
export * as LabelAssertions from './components/label/Assertions.js';
|
||||
|
||||
export * as ComponentsV2Assertions from './components/v2/Assertions.js';
|
||||
export * from './components/v2/Container.js';
|
||||
export * from './components/v2/File.js';
|
||||
export * from './components/v2/MediaGallery.js';
|
||||
export * from './components/v2/MediaGalleryItem.js';
|
||||
export * from './components/v2/Section.js';
|
||||
export * from './components/v2/Separator.js';
|
||||
export * from './components/v2/TextDisplay.js';
|
||||
export * from './components/v2/Thumbnail.js';
|
||||
|
||||
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions.js';
|
||||
export * from './interactions/slashCommands/SlashCommandBuilder.js';
|
||||
export * from './interactions/slashCommands/SlashCommandSubcommands.js';
|
||||
|
||||
@@ -7,7 +7,8 @@ const namePredicate = s
|
||||
.string()
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
.lengthLessThanOrEqual(32)
|
||||
.regex(/\S/)
|
||||
// eslint-disable-next-line prefer-named-capture-group
|
||||
.regex(/^( *[\p{P}\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}]+ *)+$/u)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
const typePredicate = s
|
||||
.union([s.literal(ApplicationCommandType.User), s.literal(ApplicationCommandType.Message)])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { s } from '@sapphire/shapeshift';
|
||||
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
|
||||
import { customIdValidator } from '../../components/Assertions.js';
|
||||
import { LabelBuilder } from '../../components/label/Label.js';
|
||||
import { isValidationEnabled } from '../../util/validation.js';
|
||||
|
||||
export const titleValidator = s
|
||||
@@ -10,7 +9,7 @@ export const titleValidator = s
|
||||
.lengthLessThanOrEqual(45)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
export const componentsValidator = s
|
||||
.union([s.instance(ActionRowBuilder), s.instance(LabelBuilder)])
|
||||
.instance(ActionRowBuilder)
|
||||
.array()
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
@@ -18,7 +17,7 @@ export const componentsValidator = s
|
||||
export function validateRequiredParameters(
|
||||
customId?: string,
|
||||
title?: string,
|
||||
components?: (ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder)[],
|
||||
components?: ActionRowBuilder<ModalActionRowComponentBuilder>[],
|
||||
) {
|
||||
customIdValidator.parse(customId);
|
||||
titleValidator.parse(title);
|
||||
|
||||
@@ -2,18 +2,13 @@
|
||||
|
||||
import type { JSONEncodable } from '@discordjs/util';
|
||||
import type {
|
||||
APITextInputComponent,
|
||||
APIActionRowComponent,
|
||||
APIComponentInModalActionRow,
|
||||
APILabelComponent,
|
||||
APIModalActionRowComponent,
|
||||
APIModalInteractionResponseCallbackData,
|
||||
} from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
|
||||
import { customIdValidator } from '../../components/Assertions.js';
|
||||
import { createComponentBuilder, resolveBuilder } from '../../components/Components.js';
|
||||
import { LabelBuilder } from '../../components/label/Label.js';
|
||||
import { TextInputBuilder } from '../../components/textInput/TextInput.js';
|
||||
import { createComponentBuilder } from '../../components/Components.js';
|
||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
|
||||
import { titleValidator, validateRequiredParameters } from './Assertions.js';
|
||||
|
||||
@@ -29,7 +24,7 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
|
||||
/**
|
||||
* The components within this modal.
|
||||
*/
|
||||
public readonly components: (ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder)[] = [];
|
||||
public readonly components: ActionRowBuilder<ModalActionRowComponentBuilder>[] = [];
|
||||
|
||||
/**
|
||||
* Creates a new modal from API data.
|
||||
@@ -38,10 +33,8 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
|
||||
*/
|
||||
public constructor({ components, ...data }: Partial<APIModalInteractionResponseCallbackData> = {}) {
|
||||
this.data = { ...data };
|
||||
this.components = (components?.map((component) => createComponentBuilder(component)) ?? []) as (
|
||||
| ActionRowBuilder<ModalActionRowComponentBuilder>
|
||||
| LabelBuilder
|
||||
)[];
|
||||
this.components = (components?.map((component) => createComponentBuilder(component)) ??
|
||||
[]) as ActionRowBuilder<ModalActionRowComponentBuilder>[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,150 +61,28 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
|
||||
* Adds components to this modal.
|
||||
*
|
||||
* @param components - The components to add
|
||||
* @deprecated Use {@link ModalBuilder.addLabelComponents} or {@link ModalBuilder.addActionRowComponents} instead
|
||||
*/
|
||||
public addComponents(
|
||||
...components: RestOrArray<
|
||||
| ActionRowBuilder<ModalActionRowComponentBuilder>
|
||||
| APIActionRowComponent<APIComponentInModalActionRow>
|
||||
| APILabelComponent
|
||||
| APITextInputComponent
|
||||
| LabelBuilder
|
||||
| TextInputBuilder
|
||||
ActionRowBuilder<ModalActionRowComponentBuilder> | APIActionRowComponent<APIModalActionRowComponent>
|
||||
>
|
||||
) {
|
||||
this.components.push(
|
||||
...normalizeArray(components).map((component, idx) => {
|
||||
if (component instanceof ActionRowBuilder || component instanceof LabelBuilder) {
|
||||
return component;
|
||||
}
|
||||
|
||||
if (component instanceof TextInputBuilder) {
|
||||
return new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(component);
|
||||
}
|
||||
|
||||
if ('type' in component) {
|
||||
if (component.type === ComponentType.ActionRow) {
|
||||
return new ActionRowBuilder<ModalActionRowComponentBuilder>(component);
|
||||
}
|
||||
|
||||
if (component.type === ComponentType.Label) {
|
||||
return new LabelBuilder(component);
|
||||
}
|
||||
|
||||
if (component.type === ComponentType.TextInput) {
|
||||
return new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
|
||||
new TextInputBuilder(component),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw new TypeError(`Invalid component passed in ModalBuilder.addComponents at index ${idx}!`);
|
||||
}),
|
||||
...normalizeArray(components).map((component) =>
|
||||
component instanceof ActionRowBuilder
|
||||
? component
|
||||
: new ActionRowBuilder<ModalActionRowComponentBuilder>(component),
|
||||
),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds label components to this modal.
|
||||
*
|
||||
* @param components - The components to add
|
||||
*/
|
||||
public addLabelComponents(
|
||||
...components: RestOrArray<APILabelComponent | LabelBuilder | ((builder: LabelBuilder) => LabelBuilder)>
|
||||
) {
|
||||
const normalized = normalizeArray(components);
|
||||
const resolved = normalized.map((label) => resolveBuilder(label, LabelBuilder));
|
||||
|
||||
this.components.push(...resolved);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds action rows to this modal.
|
||||
*
|
||||
* @param components - The components to add
|
||||
* @deprecated Use {@link ModalBuilder.addLabelComponents} instead
|
||||
*/
|
||||
public addActionRowComponents(
|
||||
...components: RestOrArray<
|
||||
| ActionRowBuilder<ModalActionRowComponentBuilder>
|
||||
| APIActionRowComponent<APIComponentInModalActionRow>
|
||||
| ((
|
||||
builder: ActionRowBuilder<ModalActionRowComponentBuilder>,
|
||||
) => ActionRowBuilder<ModalActionRowComponentBuilder>)
|
||||
>
|
||||
) {
|
||||
const normalized = normalizeArray(components);
|
||||
const resolved = normalized.map((row) => resolveBuilder(row, ActionRowBuilder<ModalActionRowComponentBuilder>));
|
||||
|
||||
this.components.push(...resolved);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the labels for this modal.
|
||||
*
|
||||
* @param components - The components to set
|
||||
*/
|
||||
public setLabelComponents(
|
||||
...components: RestOrArray<APILabelComponent | LabelBuilder | ((builder: LabelBuilder) => LabelBuilder)>
|
||||
) {
|
||||
const normalized = normalizeArray(components);
|
||||
this.spliceLabelComponents(0, this.components.length, ...normalized);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes, replaces, or inserts labels for this modal.
|
||||
*
|
||||
* @remarks
|
||||
* This method behaves similarly
|
||||
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
|
||||
* The maximum amount of labels that can be added is 5.
|
||||
*
|
||||
* It's useful for modifying and adjusting order of the already-existing labels of a modal.
|
||||
* @example
|
||||
* Remove the first label:
|
||||
* ```ts
|
||||
* modal.spliceLabelComponents(0, 1);
|
||||
* ```
|
||||
* @example
|
||||
* Remove the first n labels:
|
||||
* ```ts
|
||||
* const n = 4;
|
||||
* modal.spliceLabelComponents(0, n);
|
||||
* ```
|
||||
* @example
|
||||
* Remove the last label:
|
||||
* ```ts
|
||||
* modal.spliceLabelComponents(-1, 1);
|
||||
* ```
|
||||
* @param index - The index to start at
|
||||
* @param deleteCount - The number of labels to remove
|
||||
* @param labels - The replacing label objects
|
||||
*/
|
||||
public spliceLabelComponents(
|
||||
index: number,
|
||||
deleteCount: number,
|
||||
...labels: (APILabelComponent | LabelBuilder | ((builder: LabelBuilder) => LabelBuilder))[]
|
||||
): this {
|
||||
const resolved = labels.map((label) => resolveBuilder(label, LabelBuilder));
|
||||
this.components.splice(index, deleteCount, ...resolved);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets components for this modal.
|
||||
*
|
||||
* @param components - The components to set
|
||||
* @deprecated Use {@link ModalBuilder.setLabelComponents} instead
|
||||
*/
|
||||
public setComponents(...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder>) {
|
||||
public setComponents(...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder>>) {
|
||||
this.components.splice(0, this.components.length, ...normalizeArray(components));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,17 @@ import { s } from '@sapphire/shapeshift';
|
||||
import type { APIEmbedField } from 'discord-api-types/v10';
|
||||
import { isValidationEnabled } from '../../util/validation.js';
|
||||
|
||||
export const fieldNamePredicate = s.string().lengthLessThanOrEqual(256).setValidationEnabled(isValidationEnabled);
|
||||
export const fieldNamePredicate = s
|
||||
.string()
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
.lengthLessThanOrEqual(256)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const fieldValuePredicate = s.string().lengthLessThanOrEqual(1_024).setValidationEnabled(isValidationEnabled);
|
||||
export const fieldValuePredicate = s
|
||||
.string()
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
.lengthLessThanOrEqual(1_024)
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const fieldInlinePredicate = s.boolean().optional();
|
||||
|
||||
@@ -24,10 +32,7 @@ export function validateFieldLength(amountAdding: number, fields?: APIEmbedField
|
||||
fieldLengthPredicate.parse((fields?.length ?? 0) + amountAdding);
|
||||
}
|
||||
|
||||
export const authorNamePredicate = fieldNamePredicate
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
.nullable()
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
export const authorNamePredicate = fieldNamePredicate.nullable().setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const imageURLPredicate = s
|
||||
.string()
|
||||
@@ -91,7 +96,4 @@ export const embedFooterPredicate = s
|
||||
|
||||
export const timestampPredicate = s.union([s.number(), s.date()]).nullable().setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const titlePredicate = fieldNamePredicate
|
||||
.lengthGreaterThanOrEqual(1)
|
||||
.nullable()
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
export const titlePredicate = fieldNamePredicate.nullable().setValidationEnabled(isValidationEnabled);
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<a href="https://www.npmjs.com/package/@discordjs/collection"><img src="https://img.shields.io/npm/v/@discordjs/collection.svg?maxAge=3600" alt="npm version" /></a>
|
||||
<a href="https://www.npmjs.com/package/@discordjs/collection"><img src="https://img.shields.io/npm/dt/@discordjs/collection.svg?maxAge=3600" alt="npm downloads" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=collection" alt="Code coverage" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/commits/main/packages/collection"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fcollection"></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=collection" alt="Code coverage" /></a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
|
||||
|
||||
@@ -30,8 +30,10 @@ body = """
|
||||
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
|
||||
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
|
||||
{% if commit.breaking %}\
|
||||
{% for breakingChange in commit.footers %}\
|
||||
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
|
||||
{% for footer in commit.footers %}\
|
||||
{% if footer.breaking %}\
|
||||
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
|
||||
{% endif %}\
|
||||
{% endfor %}\
|
||||
{% endif %}\
|
||||
{% endfor %}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<a href="https://www.npmjs.com/package/@discordjs/core"><img src="https://img.shields.io/npm/v/@discordjs/core.svg?maxAge=3600" alt="npm version" /></a>
|
||||
<a href="https://www.npmjs.com/package/@discordjs/core"><img src="https://img.shields.io/npm/dt/@discordjs/core.svg?maxAge=3600" alt="npm downloads" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=core" alt="Code coverage" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/commits/main/packages/core"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fcore"></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2&flag=core" alt="Code coverage" /></a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
|
||||
@@ -23,7 +24,7 @@
|
||||
|
||||
## Installation
|
||||
|
||||
**Node.js 18 or newer is required.**
|
||||
**Node.js 20 or newer is required.**
|
||||
|
||||
```sh
|
||||
npm install @discordjs/core
|
||||
|
||||
@@ -30,8 +30,10 @@ body = """
|
||||
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
|
||||
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
|
||||
{% if commit.breaking %}\
|
||||
{% for breakingChange in commit.footers %}\
|
||||
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
|
||||
{% for footer in commit.footers %}\
|
||||
{% if footer.breaking %}\
|
||||
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
|
||||
{% endif %}\
|
||||
{% endfor %}\
|
||||
{% endif %}\
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@discordjs/core",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"description": "A thinly abstracted wrapper around the rest API, and gateway.",
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
@@ -90,7 +90,7 @@
|
||||
"vitest": "^2.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
"node": ">=20"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -45,7 +45,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Sends a message in a channel
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#create-message}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#create-message}
|
||||
* @param channelId - The id of the channel to send the message in
|
||||
* @param body - The data for sending the message
|
||||
* @param options - The options for sending the message
|
||||
@@ -65,7 +65,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Edits a message
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#edit-message}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#edit-message}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to edit
|
||||
* @param body - The data for editing the message
|
||||
@@ -87,7 +87,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Fetches the reactions for a message
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#get-reactions}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#get-reactions}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to get the reactions for
|
||||
* @param emoji - The emoji to get the reactions for
|
||||
@@ -110,7 +110,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Deletes a reaction for the current user
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#delete-own-reaction}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#delete-own-reaction}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to delete the reaction for
|
||||
* @param emoji - The emoji to delete the reaction for
|
||||
@@ -130,7 +130,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Deletes a reaction for a user
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#delete-user-reaction}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#delete-user-reaction}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to delete the reaction for
|
||||
* @param emoji - The emoji to delete the reaction for
|
||||
@@ -152,7 +152,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Deletes all reactions for a message
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#delete-all-reactions}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#delete-all-reactions}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to delete the reactions for
|
||||
* @param options - The options for deleting the reactions
|
||||
@@ -168,7 +168,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Deletes all reactions of an emoji for a message
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#delete-all-reactions-for-emoji}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to delete the reactions for
|
||||
* @param emoji - The emoji to delete the reactions for
|
||||
@@ -186,7 +186,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Adds a reaction to a message
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#create-reaction}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#create-reaction}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to add the reaction to
|
||||
* @param emoji - The emoji to add the reaction with
|
||||
@@ -242,7 +242,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Fetches the messages of a channel
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#get-channel-messages}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#get-channel-messages}
|
||||
* @param channelId - The id of the channel to fetch messages from
|
||||
* @param query - The query options for fetching messages
|
||||
* @param options - The options for fetching the messages
|
||||
@@ -299,7 +299,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Deletes a message
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#delete-message}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#delete-message}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to delete
|
||||
* @param options - The options for deleting the message
|
||||
@@ -315,7 +315,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Bulk deletes messages
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#bulk-delete-messages}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#bulk-delete-messages}
|
||||
* @param channelId - The id of the channel the messages are in
|
||||
* @param messageIds - The ids of the messages to delete
|
||||
* @param options - The options for deleting the messages
|
||||
@@ -331,7 +331,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Fetches a message
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#get-channel-message}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#get-channel-message}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to fetch
|
||||
* @param options - The options for fetching the message
|
||||
@@ -345,7 +345,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Crossposts a message
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#crosspost-message}
|
||||
* @see {@link https://discord.com/developers/docs/resources/message#crosspost-message}
|
||||
* @param channelId - The id of the channel the message is in
|
||||
* @param messageId - The id of the message to crosspost
|
||||
* @param options - The options for crossposting the message
|
||||
@@ -452,7 +452,7 @@ export class ChannelsAPI {
|
||||
/**
|
||||
* Creates a new forum post
|
||||
*
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#start-thread-in-forum-channel}
|
||||
* @see {@link https://discord.com/developers/docs/resources/channel#start-thread-in-forum-or-media-channel}
|
||||
* @param channelId - The id of the forum channel to start the thread in
|
||||
* @param body - The data for starting the thread
|
||||
* @param options - The options for starting the thread
|
||||
|
||||
@@ -258,7 +258,7 @@ export class InteractionsAPI {
|
||||
* @param interactionId - The id of the interaction
|
||||
* @param interactionToken - The token of the interaction
|
||||
* @param options - The options for sending the premium required response
|
||||
* @deprecated Sending a premium-style button is the new Discord behaviour.
|
||||
* @deprecated Sending a premium-style button is the new Discord behavior.
|
||||
*/
|
||||
public async sendPremiumRequired(
|
||||
interactionId: Snowflake,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<p>
|
||||
<a href="https://discord.gg/djs"><img src="https://img.shields.io/discord/222078108977594368?color=5865F2&logo=discord&logoColor=white" alt="Discord server" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Build status" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/commits/main/packages/create-discord-bot"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fcreate-discord-bot"></a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
|
||||
|
||||
@@ -30,8 +30,10 @@ body = """
|
||||
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
|
||||
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
|
||||
{% if commit.breaking %}\
|
||||
{% for breakingChange in commit.footers %}\
|
||||
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
|
||||
{% for footer in commit.footers %}\
|
||||
{% if footer.breaking %}\
|
||||
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
|
||||
{% endif %}\
|
||||
{% endfor %}\
|
||||
{% endif %}\
|
||||
{% endfor %}
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
"no-void": "error",
|
||||
"no-warning-comments": "warn",
|
||||
"prefer-promise-reject-errors": "error",
|
||||
"require-await": "warn",
|
||||
"require-await": "off",
|
||||
"wrap-iife": "error",
|
||||
"yoda": "error",
|
||||
|
||||
|
||||
@@ -2,6 +2,148 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
# [14.18.0](https://github.com/discordjs/discord.js/compare/14.17.3...14.18.0) - (2025-02-10)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Guild:** Type error with permissionOverwrites (#10527) ([8e1e1be](https://github.com/discordjs/discord.js/commit/8e1e1be0c23a0a063a6b530ac8cee30cf7629644))
|
||||
- Incorrect relative path (#10734) ([b7f1ebc](https://github.com/discordjs/discord.js/commit/b7f1ebc334e110be3208c476b61b82a69386fd84))
|
||||
- **PresenceUpdate:** Correctly add user regardless of their properties (#10672) ([7c1b73c](https://github.com/discordjs/discord.js/commit/7c1b73cc697fd3b85011bdb2c098ca3a3f863b1f))
|
||||
- **InteractionResponses:** Mark replied true for followUps (#10688) ([32dff01](https://github.com/discordjs/discord.js/commit/32dff01f291271bde3cfb354964ed140a6fa82d7))
|
||||
|
||||
## Documentation
|
||||
|
||||
- Use link tags to render links on the documentation (#10731) ([66b9718](https://github.com/discordjs/discord.js/commit/66b971899ab702240642e3ae2d189fd9e7efc701))
|
||||
- **Message:** Improve message snapshots description (#10709) ([31df3d2](https://github.com/discordjs/discord.js/commit/31df3d21cdc53400672924bc7c5dc7fd3053630b))
|
||||
|
||||
## Features
|
||||
|
||||
- Message forwards (#10733) ([89c076c](https://github.com/discordjs/discord.js/commit/89c076c89e90e8f5912786e8899ced9e8eea6003))
|
||||
- Incident Actions (#10727) ([41dee51](https://github.com/discordjs/discord.js/commit/41dee5177d9cb15f667e60a34619882222bf249c))
|
||||
- **website:** Type parameters links, builtin doc links, default values (#10515) ([43235d4](https://github.com/discordjs/discord.js/commit/43235d43fe76e26805c52dcff13519652bcb6a4a))
|
||||
- **PartialGroupDMChannel:** Add missing properties (#10502) ([5e66f85](https://github.com/discordjs/discord.js/commit/5e66f85f55724a583921252b035eb2097345fec8))
|
||||
- **Subscription:** Add `renewalSkuIds` (#10662) ([efa50fc](https://github.com/discordjs/discord.js/commit/efa50fc3fa463b09bde11c1640daa2abb8c22686))
|
||||
- **website:** Include reexported members in docs (#10518) ([aa61c20](https://github.com/discordjs/discord.js/commit/aa61c20ffdac3f3a0dca224f9e48e614309ecb2e))
|
||||
|
||||
## Refactor
|
||||
|
||||
- Use throw instead of Promise.reject (#10712) ([2663d76](https://github.com/discordjs/discord.js/commit/2663d767099f2e14a23f9cbfb868f279ffb253d1))
|
||||
- Remove data resolver exports (#10701) ([4606041](https://github.com/discordjs/discord.js/commit/46060419a9593dc5132ba6f13b58d0c18613679b))
|
||||
- **IntegrationApplication:** Move common properties to Application (#10627) ([95db597](https://github.com/discordjs/discord.js/commit/95db597fc844e7951b07cfb5741e27086ac7451a))
|
||||
|
||||
## Styling
|
||||
|
||||
- Prettier ([92aea94](https://github.com/discordjs/discord.js/commit/92aea944119638b12c03be0f627f20fe5fe5145e))
|
||||
|
||||
## Typings
|
||||
|
||||
- Fix recurrence rule types (#10694) ([193a5e9](https://github.com/discordjs/discord.js/commit/193a5e9e20fc4832592b2a3b6f142752121f43d5))
|
||||
- **ThreadOnlyChannel:** Remove incorrect `messages` property (#10708) ([44a1e85](https://github.com/discordjs/discord.js/commit/44a1e858473a51809cb1e6114d6a659fe28587f0))
|
||||
- Add `undefined` to `flags` for `exactOptionalPropertyTypes` (#10707) ([d2e1924](https://github.com/discordjs/discord.js/commit/d2e1924fa6a06120879a1158d501a899db3d6d96))
|
||||
- Allow only ephemeral for defer reply (#10696) ([68dd260](https://github.com/discordjs/discord.js/commit/68dd260dee1a7b0bbd4fcdff1b39283ea8dcedec))
|
||||
- Remove createComponent and createComponentBuilder (#10687) ([0047a49](https://github.com/discordjs/discord.js/commit/0047a49b7395acf0936702f233e7fb89e9f352fe))
|
||||
|
||||
# [14.17.3](https://github.com/discordjs/discord.js/compare/14.17.2...14.17.3) - (2025-01-08)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Message:** Ensure channel is defined for clean content (#10681) ([46bf8f0](https://github.com/discordjs/discord.js/commit/46bf8f0146b67d7c480a3512ade1edbfb16e7a26))
|
||||
- Use `resolve()` for `PermissionOverwrites` (#10686) ([7280d4e](https://github.com/discordjs/discord.js/commit/7280d4e82eb47ce7cb3964057d7d56a62179cf18))
|
||||
|
||||
# [14.17.2](https://github.com/discordjs/discord.js/compare/14.17.1...14.17.2) - (2025-01-02)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **InteractionResponses:** Check correct property for deprecation ([77804cf](https://github.com/discordjs/discord.js/commit/77804cfd559691d9b8c85aec8c494cd6c14c4ea7))
|
||||
|
||||
# [14.17.0](https://github.com/discordjs/discord.js/compare/14.16.3...14.17.0) - (2025-01-01)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **InteractionResponses:** Do not use `in` if a string is passed ([ff42d7a](https://github.com/discordjs/discord.js/commit/ff42d7af72e940ae72c61d2c5164ae68f2708b96))
|
||||
- Use Message#interactionMetadata (#10654) ([6087088](https://github.com/discordjs/discord.js/commit/60870885790eb1857ed4c2969c9c404e356a1299))
|
||||
- **InteractionResponses:** Properly resolve message flags (#10661) ([b2754d4](https://github.com/discordjs/discord.js/commit/b2754d4a0ec250ae84057d0f07c078376f54829c))
|
||||
- **ThreadChannel:** Make `ownerId` always present (#10618) ([7678f11](https://github.com/discordjs/discord.js/commit/7678f1176a645878261361faef0429f9cf7f4810))
|
||||
- **MessageReaction:** Address `undefined` burst properties (#10597) ([76968b4](https://github.com/discordjs/discord.js/commit/76968b4bc14b8a66825f9140d130b1e04c11855a))
|
||||
- **ThreadChannel:** Address parameter type on fetchOwner() (#10592) ([56c9396](https://github.com/discordjs/discord.js/commit/56c9396b717d4dec2410ca13938ce238ec21215d))
|
||||
- **InteractionResponses:** Throw error on deleting response of unacknowledged interaction (#10587) ([21c283f](https://github.com/discordjs/discord.js/commit/21c283f964ab9e331db53cc0c21ca64980372488))
|
||||
- **GuildScheduledEvent:** Handle null recurrence_rule (#10543) ([831aafa](https://github.com/discordjs/discord.js/commit/831aafa733e8eea55534c4c39b87775d2e2f56c4))
|
||||
|
||||
## Documentation
|
||||
|
||||
- Correct discord-api-types URLs (#10622) ([76042f0](https://github.com/discordjs/discord.js/commit/76042f05386edcbadc5ad4ded22e8b15c7b6f8ec))
|
||||
- Typos (#10628) ([388783d](https://github.com/discordjs/discord.js/commit/388783d7dd718aae519801b90aa781d07b7fb64e))
|
||||
- Add note about idempotence to role add/remove routes (#10586) ([565fc01](https://github.com/discordjs/discord.js/commit/565fc0192a5ed2642ff1bd615c59678b5c3cd24b))
|
||||
- **Client:** Fix incorrect managers descriptions ([f79ba52](https://github.com/discordjs/discord.js/commit/f79ba52c7a1334d987e9873a8a411e92d5140116))
|
||||
- **discord.js:** Remove `utf-8-validate` (#10531) ([297e959](https://github.com/discordjs/discord.js/commit/297e959f48abbfd3af58cc29cdcef139d3579821))
|
||||
|
||||
## Features
|
||||
|
||||
- **ClientApplication:** Add webhook events (#10588) ([7b2a2e3](https://github.com/discordjs/discord.js/commit/7b2a2e3a154afd69ff892da615ea75c46730f226))
|
||||
- **InteractionResponses:** Support `with_response` query parameter (#10636) ([622acbc](https://github.com/discordjs/discord.js/commit/622acbcbf02c3b8e0eae4296964c3e745e19378d))
|
||||
- **ClientApplication:** Add webhook events (#10588) ([ae1deac](https://github.com/discordjs/discord.js/commit/ae1deac2bf37aecda4c044bf5c28d03930bd763b))
|
||||
- **EntitlementManager:** Support get entitlement (#10606) ([a367e2c](https://github.com/discordjs/discord.js/commit/a367e2c8c99ab3bfb83cdbfb65e7a5020b50b7f7))
|
||||
- Add subscriptions (#10541) ([4cca33d](https://github.com/discordjs/discord.js/commit/4cca33d9b0759294c9a2dfec39d80a24a2cc1595))
|
||||
- Emit reaction type on gateway events (#10598) ([bda3128](https://github.com/discordjs/discord.js/commit/bda31284bf46515747e002e86ea35d0b6910e269))
|
||||
- Voice Channel Effect Send (#10318) ([34343c6](https://github.com/discordjs/discord.js/commit/34343c6afae65205d3b17b60fdd202d0937d6a46))
|
||||
- **GuildMember:** Banners (#10384) ([b1ded63](https://github.com/discordjs/discord.js/commit/b1ded63e42e7349f535df4680509b9393dd8f288))
|
||||
- Add ApplicationEmoji to EmojiResolvable and MessageReaction#emoji (#10477) ([1fc87a9](https://github.com/discordjs/discord.js/commit/1fc87a96987fe69722502d7574500926a4e0bfde))
|
||||
- Recurring scheduled events (#10447) ([97c3237](https://github.com/discordjs/discord.js/commit/97c3237a70027f71bb3f046357a55bb730daca14))
|
||||
- Message forwarding (#10464) ([c122178](https://github.com/discordjs/discord.js/commit/c12217829b46f7a60266f65af4af19cdbfcd7906))
|
||||
|
||||
## Refactor
|
||||
|
||||
- **FetchApplicationCommandOptions:** Use `Locale` over `LocaleString` (#10625) ([7ce6f2f](https://github.com/discordjs/discord.js/commit/7ce6f2fc8a8756532d71a542186d10a0aa951471))
|
||||
- Use `cache.get()` for snowflakes, `resolve()` otherwise (#10626) ([dedaa5d](https://github.com/discordjs/discord.js/commit/dedaa5d657f15491910ec05102ce72affc822b97))
|
||||
- Remove extra traversing (#10580) ([33533b7](https://github.com/discordjs/discord.js/commit/33533b72849d9741dae8c979734b45abbf3657a7))
|
||||
- **InteractionResponses:** Deprecate ephemeral response option (#10574) ([be38f57](https://github.com/discordjs/discord.js/commit/be38f5792602ed1a79a9638aa8e629e7ad6bdd0d))
|
||||
- Deprecate `reason` parameter on adding and removing thread members (#10551) ([72e0c99](https://github.com/discordjs/discord.js/commit/72e0c994547f2a9c99b320870e14d7f1643f3851))
|
||||
- Deprecate fetching user flags (#10550) ([3d06c9d](https://github.com/discordjs/discord.js/commit/3d06c9d872b2e79356f1239f7d0eb0577a4bcedf))
|
||||
|
||||
## Testing
|
||||
|
||||
- Remove unused test (#10638) ([53cbb0e](https://github.com/discordjs/discord.js/commit/53cbb0e36d4ab191cbc15a022d752da14c2e0ace))
|
||||
|
||||
## Typings
|
||||
|
||||
- Add missing `Caches` managers (#10540) ([13471fa](https://github.com/discordjs/discord.js/commit/13471fa1b7c44b236db9fe9b1a64dacd41b14b76))
|
||||
- Remove newMessage partial on messageUpdate event typing (#10526) ([5faf074](https://github.com/discordjs/discord.js/commit/5faf074c145044f0edefafab97fd07a8dfb8bc30))
|
||||
|
||||
# [14.16.3](https://github.com/discordjs/discord.js/compare/14.16.2...14.16.3) - (2024-09-29)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **BaseInteraction:** Add missing props (#10517) ([6c77fee](https://github.com/discordjs/discord.js/commit/6c77fee41b1aabc243bff623debd157a4c7fad6a)) by @monbrey
|
||||
- `GuildChannel#guildId` not being patched to `undefined` (#10505) ([2adee06](https://github.com/discordjs/discord.js/commit/2adee06b6e92b7854ebb1c2bfd04940aab68dd10)) by @Qjuh
|
||||
|
||||
## Typings
|
||||
|
||||
- **MessageEditOptions:** Omit `poll` (#10509) ([665bf14](https://github.com/discordjs/discord.js/commit/665bf1486aec62e9528f5f7b5a6910ae6b5a6c9c)) by @TAEMBO
|
||||
|
||||
# [14.16.2](https://github.com/discordjs/discord.js/compare/14.16.1...14.16.2) - (2024-09-12)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **ApplicationCommand:** Incorrect comparison in equals method (#10497) ([3c74aa2](https://github.com/discordjs/discord.js/commit/3c74aa204909323ff6d05991438bee2c583e838b)) by @monbrey
|
||||
- Type guard for sendable text-based channels (#10482) ([dea6840](https://github.com/discordjs/discord.js/commit/dea68400a38edb90b8b4242d64be14968943130d)) by @vladfrangu
|
||||
|
||||
## Documentation
|
||||
|
||||
- Update discord documentation links (#10484) ([799fa54](https://github.com/discordjs/discord.js/commit/799fa54fa4434144855be2f7a0bbac6ff8ce9d0b)) by @sdanialraza
|
||||
- **Message:** Mark `interaction` as deprecated (#10481) ([c13f18e](https://github.com/discordjs/discord.js/commit/c13f18e90eb6eb315397c095e948993856428757)) by @sdanialraza
|
||||
- **ApplicationEmojiManager:** Fix fetch example (#10480) ([4594896](https://github.com/discordjs/discord.js/commit/4594896b5404c6a34e07544951c59ff8f3657184)) by @sdanialraza
|
||||
|
||||
## Typings
|
||||
|
||||
- Export GroupDM helper type (#10478) ([aff772c](https://github.com/discordjs/discord.js/commit/aff772c7aa3b3de58780a94588d1f3576a434f32)) by @Qjuh
|
||||
|
||||
# [14.16.1](https://github.com/discordjs/discord.js/compare/14.16.0...14.16.1) - (2024-09-02)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Message:** Reacting returning undefined (#10475) ([9257a09](https://github.com/discordjs/discord.js/commit/9257a09abbf80558ed2d5d209a2f6bd2a4b3d799)) by @vladfrangu
|
||||
- **Transformers:** Pass client to recursive call (#10474) ([4810f7c](https://github.com/discordjs/discord.js/commit/4810f7c8637dacf77d0442bd84e0d579e1f1d3bd)) by @SpaceEEC
|
||||
|
||||
# [14.16.0](https://github.com/discordjs/discord.js/compare/14.15.3...14.16.0) - (2024-09-01)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/v/discord.js.svg?maxAge=3600" alt="npm version" /></a>
|
||||
<a href="https://www.npmjs.com/package/discord.js"><img src="https://img.shields.io/npm/dt/discord.js.svg?maxAge=3600" alt="npm downloads" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/actions"><img src="https://github.com/discordjs/discord.js/actions/workflows/test.yml/badge.svg" alt="Tests status" /></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js" ><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2" alt="Code coverage" /></a>
|
||||
<a href="https://github.com/discordjs/discord.js/commits/main/packages/discord.js"><img alt="Last commit." src="https://img.shields.io/github/last-commit/discordjs/discord.js?logo=github&logoColor=ffffff&path=packages%2Fdiscord.js"></a>
|
||||
<a href="https://codecov.io/gh/discordjs/discord.js"><img src="https://codecov.io/gh/discordjs/discord.js/branch/main/graph/badge.svg?precision=2" alt="Code coverage" /></a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"><img src="https://raw.githubusercontent.com/discordjs/discord.js/main/.github/powered-by-vercel.svg" alt="Vercel" /></a>
|
||||
@@ -29,7 +30,7 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
|
||||
|
||||
## Installation
|
||||
|
||||
**Node.js 16.11.0 or newer is required.**
|
||||
**Node.js 18 or newer is required.**
|
||||
|
||||
```sh
|
||||
npm install discord.js
|
||||
@@ -42,7 +43,6 @@ bun add discord.js
|
||||
|
||||
- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for WebSocket data compression and inflation (`npm install zlib-sync`)
|
||||
- [bufferutil](https://www.npmjs.com/package/bufferutil) for a much faster WebSocket connection (`npm install bufferutil`)
|
||||
- [utf-8-validate](https://www.npmjs.com/package/utf-8-validate) in combination with `bufferutil` for much faster WebSocket processing (`npm install utf-8-validate`)
|
||||
- [@discordjs/voice](https://www.npmjs.com/package/@discordjs/voice) for interacting with the Discord Voice API (`npm install @discordjs/voice`)
|
||||
|
||||
## Example usage
|
||||
|
||||
@@ -30,8 +30,10 @@ body = """
|
||||
{{ commit.message | upper_first }} ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
|
||||
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif %}\
|
||||
{% if commit.breaking %}\
|
||||
{% for breakingChange in commit.footers %}\
|
||||
\n{% raw %} {% endraw %}- **{{ breakingChange.token }}{{ breakingChange.separator }}** {{ breakingChange.value }}\
|
||||
{% for footer in commit.footers %}\
|
||||
{% if footer.breaking %}\
|
||||
\n{% raw %} {% endraw %}- **{{ footer.token }}{{ footer.separator }}** {{ footer.value }}\
|
||||
{% endif %}\
|
||||
{% endfor %}\
|
||||
{% endif %}\
|
||||
{% endfor %}
|
||||
@@ -67,8 +69,7 @@ commit_parsers = [
|
||||
{ body = ".*security", group = "Security"},
|
||||
]
|
||||
filter_commits = true
|
||||
tag_pattern = "[0-9]*"
|
||||
skip_tags = "v[0-9]*|@discordjs*"
|
||||
tag_pattern = "^[0-9]+"
|
||||
ignore_tags = ""
|
||||
topo_order = false
|
||||
sort_commits = "newest"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "discord.js",
|
||||
"version": "14.16.0",
|
||||
"version": "14.18.0",
|
||||
"description": "A powerful library for interacting with the Discord API",
|
||||
"scripts": {
|
||||
"test": "pnpm run docs:test && pnpm run test:typescript",
|
||||
@@ -65,16 +65,17 @@
|
||||
"homepage": "https://discord.js.org",
|
||||
"funding": "https://github.com/discordjs/discord.js?sponsor",
|
||||
"dependencies": {
|
||||
"@discordjs/builders": "workspace:^",
|
||||
"@discordjs/builders": "^1.11.0",
|
||||
"@discordjs/collection": "1.5.3",
|
||||
"@discordjs/formatters": "workspace:^",
|
||||
"@discordjs/formatters": "^0.6.0",
|
||||
"@discordjs/rest": "workspace:^",
|
||||
"@discordjs/util": "workspace:^",
|
||||
"@discordjs/ws": "1.1.1",
|
||||
"@discordjs/ws": "^1.2.1",
|
||||
"@sapphire/snowflake": "3.5.3",
|
||||
"discord-api-types": "^0.38.1",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"lodash.snakecase": "4.1.1",
|
||||
"magic-bytes.js": "^1.10.0",
|
||||
"tslib": "^2.6.3",
|
||||
"undici": "6.21.1"
|
||||
},
|
||||
@@ -98,7 +99,7 @@
|
||||
"typescript": "~5.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.11.0"
|
||||
"node": ">=18"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
|
||||
@@ -18,6 +18,7 @@ const ClientPresence = require('../structures/ClientPresence');
|
||||
const GuildPreview = require('../structures/GuildPreview');
|
||||
const GuildTemplate = require('../structures/GuildTemplate');
|
||||
const Invite = require('../structures/Invite');
|
||||
const { SoundboardSound } = require('../structures/SoundboardSound');
|
||||
const { Sticker } = require('../structures/Sticker');
|
||||
const StickerPack = require('../structures/StickerPack');
|
||||
const VoiceRegion = require('../structures/VoiceRegion');
|
||||
@@ -107,20 +108,20 @@ class Client extends BaseClient {
|
||||
: null;
|
||||
|
||||
/**
|
||||
* All of the {@link User} objects that have been cached at any point, mapped by their ids
|
||||
* The user manager of this client
|
||||
* @type {UserManager}
|
||||
*/
|
||||
this.users = new UserManager(this);
|
||||
|
||||
/**
|
||||
* All of the guilds the client is currently handling, mapped by their ids -
|
||||
* A manager of all the guilds the client is currently handling -
|
||||
* as long as sharding isn't being used, this will be *every* guild the bot is a member of
|
||||
* @type {GuildManager}
|
||||
*/
|
||||
this.guilds = new GuildManager(this);
|
||||
|
||||
/**
|
||||
* All of the {@link BaseChannel}s that the client is currently handling, mapped by their ids -
|
||||
* All of the {@link BaseChannel}s that the client is currently handling -
|
||||
* as long as sharding isn't being used, this will be *every* channel in *every* guild the bot
|
||||
* is a member of. Note that DM channels will not be initially cached, and thus not be present
|
||||
* in the Manager without their explicit fetching or use.
|
||||
@@ -174,7 +175,7 @@ class Client extends BaseClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* All custom emojis that the client has access to, mapped by their ids
|
||||
* A manager of all the custom emojis that the client has access to
|
||||
* @type {BaseGuildEmojiManager}
|
||||
* @readonly
|
||||
*/
|
||||
@@ -390,6 +391,19 @@ class Client extends BaseClient {
|
||||
return this.fetchStickerPacks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the list of default soundboard sounds.
|
||||
* @returns {Promise<Collection<string, SoundboardSound>>}
|
||||
* @example
|
||||
* client.fetchDefaultSoundboardSounds()
|
||||
* .then(sounds => console.log(`Available soundboard sounds are: ${sounds.map(sound => sound.name).join(', ')}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchDefaultSoundboardSounds() {
|
||||
const data = await this.rest.get(Routes.soundboardDefaultSounds());
|
||||
return new Collection(data.map(sound => [sound.sound_id, new SoundboardSound(this, sound)]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a guild preview from Discord, available for all guilds the bot is in and all Discoverable guilds.
|
||||
* @param {GuildResolvable} guild The guild to fetch the preview for
|
||||
|
||||
@@ -111,6 +111,14 @@ class GenericAction {
|
||||
getThreadMember(id, manager) {
|
||||
return this.getPayload({ user_id: id }, manager, id, Partials.ThreadMember, false);
|
||||
}
|
||||
|
||||
getSoundboardSound(data, guild) {
|
||||
return this.getPayload(data, guild.soundboardSounds, data.sound_id, Partials.SoundboardSound);
|
||||
}
|
||||
|
||||
spreadInjectedData(data) {
|
||||
return Object.fromEntries(Object.getOwnPropertySymbols(data).map(symbol => [symbol, data[symbol]]));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GenericAction;
|
||||
|
||||
@@ -43,6 +43,7 @@ class ActionsManager {
|
||||
this.register(require('./GuildScheduledEventUpdate'));
|
||||
this.register(require('./GuildScheduledEventUserAdd'));
|
||||
this.register(require('./GuildScheduledEventUserRemove'));
|
||||
this.register(require('./GuildSoundboardSoundDelete.js'));
|
||||
this.register(require('./GuildStickerCreate'));
|
||||
this.register(require('./GuildStickerDelete'));
|
||||
this.register(require('./GuildStickerUpdate'));
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action.js');
|
||||
const Events = require('../../util/Events.js');
|
||||
|
||||
class GuildSoundboardSoundDeleteAction extends Action {
|
||||
handle(data) {
|
||||
const guild = this.client.guilds.cache.get(data.guild_id);
|
||||
|
||||
if (!guild) return {};
|
||||
|
||||
const soundboardSound = this.getSoundboardSound(data, guild);
|
||||
|
||||
if (soundboardSound) {
|
||||
guild.soundboardSounds.cache.delete(soundboardSound.soundId);
|
||||
|
||||
/**
|
||||
* Emitted whenever a soundboard sound is deleted in a guild.
|
||||
* @event Client#guildSoundboardSoundDelete
|
||||
* @param {SoundboardSound} soundboardSound The soundboard sound that was deleted
|
||||
*/
|
||||
this.client.emit(Events.GuildSoundboardSoundDelete, soundboardSound);
|
||||
}
|
||||
|
||||
return { soundboardSound };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GuildSoundboardSoundDeleteAction;
|
||||
@@ -6,7 +6,11 @@ const Events = require('../../util/Events');
|
||||
class MessageCreateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id, author: data.author });
|
||||
const channel = this.getChannel({
|
||||
id: data.channel_id,
|
||||
author: data.author,
|
||||
...('guild_id' in data && { guild_id: data.guild_id }),
|
||||
});
|
||||
if (channel) {
|
||||
if (!channel.isTextBased()) return {};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ const Events = require('../../util/Events');
|
||||
class MessageDeleteAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
||||
let message;
|
||||
if (channel) {
|
||||
if (!channel.isTextBased()) return {};
|
||||
|
||||
@@ -5,7 +5,7 @@ const Events = require('../../util/Events');
|
||||
|
||||
class MessagePollVoteAddAction extends Action {
|
||||
handle(data) {
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
||||
if (!channel?.isTextBased()) return false;
|
||||
|
||||
const message = this.getMessage(data, channel);
|
||||
|
||||
@@ -5,7 +5,7 @@ const Events = require('../../util/Events');
|
||||
|
||||
class MessagePollVoteRemoveAction extends Action {
|
||||
handle(data) {
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
||||
if (!channel?.isTextBased()) return false;
|
||||
|
||||
const message = this.getMessage(data, channel);
|
||||
|
||||
@@ -23,7 +23,13 @@ class MessageReactionAdd extends Action {
|
||||
if (!user) return false;
|
||||
|
||||
// Verify channel
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id, user_id: data.user_id });
|
||||
const channel = this.getChannel({
|
||||
id: data.channel_id,
|
||||
...('guild_id' in data && { guild_id: data.guild_id }),
|
||||
user_id: data.user_id,
|
||||
...this.spreadInjectedData(data),
|
||||
});
|
||||
|
||||
if (!channel?.isTextBased()) return false;
|
||||
|
||||
// Verify message
|
||||
@@ -45,6 +51,7 @@ class MessageReactionAdd extends Action {
|
||||
/**
|
||||
* Provides additional information about altered reaction
|
||||
* @typedef {Object} MessageReactionEventDetails
|
||||
* @property {ReactionType} type The type of the reaction
|
||||
* @property {boolean} burst Determines whether a super reaction was used
|
||||
*/
|
||||
/**
|
||||
@@ -54,7 +61,7 @@ class MessageReactionAdd extends Action {
|
||||
* @param {User} user The user that applied the guild or reaction emoji
|
||||
* @param {MessageReactionEventDetails} details Details of adding the reaction
|
||||
*/
|
||||
this.client.emit(Events.MessageReactionAdd, reaction, user, { burst: data.burst });
|
||||
this.client.emit(Events.MessageReactionAdd, reaction, user, { type: data.type, burst: data.burst });
|
||||
|
||||
return { message, reaction, user };
|
||||
}
|
||||
|
||||
@@ -19,7 +19,11 @@ class MessageReactionRemove extends Action {
|
||||
if (!user) return false;
|
||||
|
||||
// Verify channel
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id, user_id: data.user_id });
|
||||
const channel = this.getChannel({
|
||||
id: data.channel_id,
|
||||
...('guild_id' in data && { guild_id: data.guild_id }),
|
||||
user_id: data.user_id,
|
||||
});
|
||||
if (!channel?.isTextBased()) return false;
|
||||
|
||||
// Verify message
|
||||
@@ -37,7 +41,7 @@ class MessageReactionRemove extends Action {
|
||||
* @param {User} user The user whose emoji or reaction emoji was removed
|
||||
* @param {MessageReactionEventDetails} details Details of removing the reaction
|
||||
*/
|
||||
this.client.emit(Events.MessageReactionRemove, reaction, user, { burst: data.burst });
|
||||
this.client.emit(Events.MessageReactionRemove, reaction, user, { type: data.type, burst: data.burst });
|
||||
|
||||
return { message, reaction, user };
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ const Events = require('../../util/Events');
|
||||
class MessageReactionRemoveAll extends Action {
|
||||
handle(data) {
|
||||
// Verify channel
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
||||
if (!channel?.isTextBased()) return false;
|
||||
|
||||
// Verify message
|
||||
|
||||
@@ -5,7 +5,7 @@ const Events = require('../../util/Events');
|
||||
|
||||
class MessageReactionRemoveEmoji extends Action {
|
||||
handle(data) {
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
||||
if (!channel?.isTextBased()) return false;
|
||||
|
||||
const message = this.getMessage(data, channel);
|
||||
|
||||
@@ -4,7 +4,7 @@ const Action = require('./Action');
|
||||
|
||||
class MessageUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
||||
if (channel) {
|
||||
if (!channel.isTextBased()) return {};
|
||||
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
const Action = require('./Action');
|
||||
const Events = require('../../util/Events');
|
||||
const Partials = require('../../util/Partials');
|
||||
|
||||
class PresenceUpdateAction extends Action {
|
||||
handle(data) {
|
||||
let user = this.client.users.cache.get(data.user.id);
|
||||
if (!user && data.user.username) user = this.client.users._add(data.user);
|
||||
if (!user && ('username' in data.user || this.client.options.partials.includes(Partials.User))) {
|
||||
user = this.client.users._add(data.user);
|
||||
}
|
||||
if (!user) return;
|
||||
|
||||
if (data.user.username) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class ThreadListSyncAction extends Action {
|
||||
|
||||
if (data.channel_ids) {
|
||||
for (const id of data.channel_ids) {
|
||||
const channel = client.channels.resolve(id);
|
||||
const channel = client.channels.cache.get(id);
|
||||
if (channel) this.removeStale(channel);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -6,7 +6,7 @@ const Events = require('../../util/Events');
|
||||
|
||||
class TypingStart extends Action {
|
||||
handle(data) {
|
||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
||||
if (!channel) return;
|
||||
|
||||
if (!channel.isTextBased()) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const Events = require('../../../util/Events.js');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const guild = client.guilds.cache.get(data.guild_id);
|
||||
|
||||
if (!guild) return;
|
||||
|
||||
const soundboardSounds = new Collection();
|
||||
|
||||
for (const soundboardSound of data.soundboard_sounds) {
|
||||
soundboardSounds.set(soundboardSound.sound_id, guild.soundboardSounds._add(soundboardSound));
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever multiple guild soundboard sounds are updated.
|
||||
* @event Client#guildSoundboardSoundsUpdate
|
||||
* @param {Collection<Snowflake, SoundboardSound>} soundboardSounds The updated soundboard sounds
|
||||
* @param {Guild} guild The guild that the soundboard sounds are from
|
||||
*/
|
||||
client.emit(Events.GuildSoundboardSoundsUpdate, soundboardSounds, guild);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const Events = require('../../../util/Events.js');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const guild = client.guilds.cache.get(data.guild_id);
|
||||
|
||||
if (!guild) return;
|
||||
|
||||
const soundboardSound = guild.soundboardSounds._add(data);
|
||||
|
||||
/**
|
||||
* Emitted whenever a guild soundboard sound is created.
|
||||
* @event Client#guildSoundboardSoundCreate
|
||||
* @param {SoundboardSound} soundboardSound The created guild soundboard sound
|
||||
*/
|
||||
client.emit(Events.GuildSoundboardSoundCreate, soundboardSound);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
client.actions.GuildSoundboardSoundDelete.handle(data);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const Events = require('../../../util/Events.js');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const guild = client.guilds.cache.get(data.guild_id);
|
||||
|
||||
if (!guild) return;
|
||||
|
||||
const oldGuildSoundboardSound = guild.soundboardSounds.cache.get(data.sound_id)?._clone() ?? null;
|
||||
const newGuildSoundboardSound = guild.soundboardSounds._add(data);
|
||||
|
||||
/**
|
||||
* Emitted whenever a guild soundboard sound is updated.
|
||||
* @event Client#guildSoundboardSoundUpdate
|
||||
* @param {?SoundboardSound} oldGuildSoundboardSound The guild soundboard sound before the update
|
||||
* @param {SoundboardSound} newGuildSoundboardSound The guild soundboard sound after the update
|
||||
*/
|
||||
client.emit(Events.GuildSoundboardSoundUpdate, oldGuildSoundboardSound, newGuildSoundboardSound);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const Events = require('../../../util/Events.js');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const guild = client.guilds.cache.get(data.guild_id);
|
||||
|
||||
if (!guild) return;
|
||||
|
||||
const soundboardSounds = new Collection();
|
||||
|
||||
for (const soundboardSound of data.soundboard_sounds) {
|
||||
soundboardSounds.set(soundboardSound.sound_id, guild.soundboardSounds._add(soundboardSound));
|
||||
}
|
||||
|
||||
/**
|
||||
* Emitted whenever soundboard sounds are received (all soundboard sounds come from the same guild).
|
||||
* @event Client#soundboardSounds
|
||||
* @param {Collection<Snowflake, SoundboardSound>} soundboardSounds The sounds received
|
||||
* @param {Guild} guild The guild that the soundboard sounds are from
|
||||
*/
|
||||
client.emit(Events.SoundboardSounds, soundboardSounds, guild);
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const Events = require('../../../util/Events');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const subscription = client.application.subscriptions._add(data);
|
||||
|
||||
/**
|
||||
* Emitted whenever a subscription is created.
|
||||
* @event Client#subscriptionCreate
|
||||
* @param {Subscription} subscription The subscription that was created
|
||||
*/
|
||||
client.emit(Events.SubscriptionCreate, subscription);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const Events = require('../../../util/Events');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const subscription = client.application.subscriptions._add(data, false);
|
||||
|
||||
client.application.subscriptions.cache.delete(subscription.id);
|
||||
|
||||
/**
|
||||
* Emitted whenever a subscription is deleted.
|
||||
* @event Client#subscriptionDelete
|
||||
* @param {Subscription} subscription The subscription that was deleted
|
||||
*/
|
||||
client.emit(Events.SubscriptionDelete, subscription);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const Events = require('../../../util/Events');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const oldSubscription = client.application.subscriptions.cache.get(data.id)?._clone() ?? null;
|
||||
const newSubscription = client.application.subscriptions._add(data);
|
||||
|
||||
/**
|
||||
* Emitted whenever a subscription is updated - i.e. when a user's subscription renews.
|
||||
* @event Client#subscriptionUpdate
|
||||
* @param {?Subscription} oldSubscription The subscription before the update
|
||||
* @param {Subscription} newSubscription The subscription after the update
|
||||
*/
|
||||
client.emit(Events.SubscriptionUpdate, oldSubscription, newSubscription);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const VoiceChannelEffect = require('../../../structures/VoiceChannelEffect');
|
||||
const Events = require('../../../util/Events');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const guild = client.guilds.cache.get(data.guild_id);
|
||||
if (!guild) return;
|
||||
|
||||
/**
|
||||
* Emitted when someone sends an effect, such as an emoji reaction, in a voice channel the client is connected to.
|
||||
* @event Client#voiceChannelEffectSend
|
||||
* @param {VoiceChannelEffect} voiceChannelEffect The sent voice channel effect
|
||||
*/
|
||||
client.emit(Events.VoiceChannelEffectSend, new VoiceChannelEffect(data, guild));
|
||||
};
|
||||
@@ -32,6 +32,10 @@ const handlers = Object.fromEntries([
|
||||
['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')],
|
||||
['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')],
|
||||
['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')],
|
||||
['GUILD_SOUNDBOARD_SOUNDS_UPDATE', require('./GUILD_SOUNDBOARD_SOUNDS_UPDATE.js')],
|
||||
['GUILD_SOUNDBOARD_SOUND_CREATE', require('./GUILD_SOUNDBOARD_SOUND_CREATE.js')],
|
||||
['GUILD_SOUNDBOARD_SOUND_DELETE', require('./GUILD_SOUNDBOARD_SOUND_DELETE.js')],
|
||||
['GUILD_SOUNDBOARD_SOUND_UPDATE', require('./GUILD_SOUNDBOARD_SOUND_UPDATE.js')],
|
||||
['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')],
|
||||
['GUILD_UPDATE', require('./GUILD_UPDATE')],
|
||||
['INTERACTION_CREATE', require('./INTERACTION_CREATE')],
|
||||
@@ -50,9 +54,13 @@ const handlers = Object.fromEntries([
|
||||
['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')],
|
||||
['READY', require('./READY')],
|
||||
['RESUMED', require('./RESUMED')],
|
||||
['SOUNDBOARD_SOUNDS', require('./SOUNDBOARD_SOUNDS.js')],
|
||||
['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')],
|
||||
['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')],
|
||||
['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')],
|
||||
['SUBSCRIPTION_CREATE', require('./SUBSCRIPTION_CREATE')],
|
||||
['SUBSCRIPTION_DELETE', require('./SUBSCRIPTION_DELETE')],
|
||||
['SUBSCRIPTION_UPDATE', require('./SUBSCRIPTION_UPDATE')],
|
||||
['THREAD_CREATE', require('./THREAD_CREATE')],
|
||||
['THREAD_DELETE', require('./THREAD_DELETE')],
|
||||
['THREAD_LIST_SYNC', require('./THREAD_LIST_SYNC')],
|
||||
@@ -61,6 +69,7 @@ const handlers = Object.fromEntries([
|
||||
['THREAD_UPDATE', require('./THREAD_UPDATE')],
|
||||
['TYPING_START', require('./TYPING_START')],
|
||||
['USER_UPDATE', require('./USER_UPDATE')],
|
||||
['VOICE_CHANNEL_EFFECT_SEND', require('./VOICE_CHANNEL_EFFECT_SEND')],
|
||||
['VOICE_SERVER_UPDATE', require('./VOICE_SERVER_UPDATE')],
|
||||
['VOICE_STATE_UPDATE', require('./VOICE_STATE_UPDATE')],
|
||||
['WEBHOOKS_UPDATE', require('./WEBHOOKS_UPDATE')],
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
* @property {'GuildChannelUnowned'} GuildChannelUnowned
|
||||
* @property {'GuildOwned'} GuildOwned
|
||||
* @property {'GuildMembersTimeout'} GuildMembersTimeout
|
||||
* @property {'GuildSoundboardSoundsTimeout'} GuildSoundboardSoundsTimeout
|
||||
* @property {'GuildUncachedMe'} GuildUncachedMe
|
||||
* @property {'ChannelNotCached'} ChannelNotCached
|
||||
* @property {'StageChannelResolve'} StageChannelResolve
|
||||
@@ -131,6 +132,8 @@
|
||||
* @property {'MissingManageEmojisAndStickersPermission'} MissingManageEmojisAndStickersPermission
|
||||
* <warn>This property is deprecated. Use `MissingManageGuildExpressionsPermission` instead.</warn>
|
||||
*
|
||||
|
||||
* @property {'NotGuildSoundboardSound'} NotGuildSoundboardSound
|
||||
* @property {'NotGuildSticker'} NotGuildSticker
|
||||
|
||||
* @property {'ReactionResolveUser'} ReactionResolveUser
|
||||
@@ -266,6 +269,7 @@ const keys = [
|
||||
'GuildChannelUnowned',
|
||||
'GuildOwned',
|
||||
'GuildMembersTimeout',
|
||||
'GuildSoundboardSoundsTimeout',
|
||||
'GuildUncachedMe',
|
||||
'ChannelNotCached',
|
||||
'StageChannelResolve',
|
||||
@@ -290,6 +294,7 @@ const keys = [
|
||||
'MissingManageGuildExpressionsPermission',
|
||||
'MissingManageEmojisAndStickersPermission',
|
||||
|
||||
'NotGuildSoundboardSound',
|
||||
'NotGuildSticker',
|
||||
|
||||
'ReactionResolveUser',
|
||||
|
||||
@@ -91,11 +91,13 @@ const Messages = {
|
||||
[DjsErrorCodes.GuildChannelUnowned]: "The fetched channel does not belong to this manager's guild.",
|
||||
[DjsErrorCodes.GuildOwned]: 'Guild is owned by the client.',
|
||||
[DjsErrorCodes.GuildMembersTimeout]: "Members didn't arrive in time.",
|
||||
[DjsErrorCodes.GuildSoundboardSoundsTimeout]: "Soundboard sounds didn't arrive in time.",
|
||||
[DjsErrorCodes.GuildUncachedMe]: 'The client user as a member of this guild is uncached.',
|
||||
[DjsErrorCodes.ChannelNotCached]: 'Could not find the channel where this message came from in the cache!',
|
||||
[DjsErrorCodes.StageChannelResolve]: 'Could not resolve channel to a stage channel.',
|
||||
[DjsErrorCodes.GuildScheduledEventResolve]: 'Could not resolve the guild scheduled event.',
|
||||
[DjsErrorCodes.FetchOwnerId]: type => `Couldn't resolve the ${type} ownerId to fetch the ${type} member.`,
|
||||
[DjsErrorCodes.FetchOwnerId]: type =>
|
||||
`Couldn't resolve the ${type} ownerId to fetch the ${type} ${type === 'group DM' ? 'owner' : 'member'}.`,
|
||||
|
||||
[DjsErrorCodes.InvalidType]: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
|
||||
[DjsErrorCodes.InvalidElement]: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`,
|
||||
@@ -117,6 +119,8 @@ const Messages = {
|
||||
[DjsErrorCodes.MissingManageEmojisAndStickersPermission]: guild =>
|
||||
`Client must have Manage Emojis and Stickers permission in guild ${guild} to see emoji authors.`,
|
||||
|
||||
[DjsErrorCodes.NotGuildSoundboardSound]: action =>
|
||||
`Soundboard sound is a default (non-guild) soundboard sound and can't be ${action}.`,
|
||||
[DjsErrorCodes.NotGuildSticker]: 'Sticker is a standard (non-guild) sticker and has no author.',
|
||||
|
||||
[DjsErrorCodes.ReactionResolveUser]: "Couldn't resolve the user id to remove from the reaction.",
|
||||
|
||||
@@ -29,7 +29,6 @@ exports.ChannelFlagsBitField = require('./util/ChannelFlagsBitField');
|
||||
exports.Collection = require('@discordjs/collection').Collection;
|
||||
exports.Constants = require('./util/Constants');
|
||||
exports.Colors = require('./util/Colors');
|
||||
__exportStar(require('./util/DataResolver.js'), exports);
|
||||
exports.Events = require('./util/Events');
|
||||
exports.Formatters = require('./util/Formatters');
|
||||
exports.GuildMemberFlagsBitField = require('./util/GuildMemberFlagsBitField').GuildMemberFlagsBitField;
|
||||
@@ -76,6 +75,7 @@ exports.GuildMemberManager = require('./managers/GuildMemberManager');
|
||||
exports.GuildMemberRoleManager = require('./managers/GuildMemberRoleManager');
|
||||
exports.GuildMessageManager = require('./managers/GuildMessageManager');
|
||||
exports.GuildScheduledEventManager = require('./managers/GuildScheduledEventManager');
|
||||
exports.GuildSoundboardSoundManager = require('./managers/GuildSoundboardSoundManager.js').GuildSoundboardSoundManager;
|
||||
exports.GuildStickerManager = require('./managers/GuildStickerManager');
|
||||
exports.GuildTextThreadManager = require('./managers/GuildTextThreadManager');
|
||||
exports.MessageManager = require('./managers/MessageManager');
|
||||
@@ -85,6 +85,7 @@ exports.ReactionManager = require('./managers/ReactionManager');
|
||||
exports.ReactionUserManager = require('./managers/ReactionUserManager');
|
||||
exports.RoleManager = require('./managers/RoleManager');
|
||||
exports.StageInstanceManager = require('./managers/StageInstanceManager');
|
||||
exports.SubscriptionManager = require('./managers/SubscriptionManager').SubscriptionManager;
|
||||
exports.ThreadManager = require('./managers/ThreadManager');
|
||||
exports.ThreadMemberManager = require('./managers/ThreadMemberManager');
|
||||
exports.UserManager = require('./managers/UserManager');
|
||||
@@ -123,12 +124,14 @@ exports.CommandInteraction = require('./structures/CommandInteraction');
|
||||
exports.Collector = require('./structures/interfaces/Collector');
|
||||
exports.CommandInteractionOptionResolver = require('./structures/CommandInteractionOptionResolver');
|
||||
exports.Component = require('./structures/Component');
|
||||
exports.ContainerComponent = require('./structures/ContainerComponent');
|
||||
exports.ContextMenuCommandInteraction = require('./structures/ContextMenuCommandInteraction');
|
||||
exports.DMChannel = require('./structures/DMChannel');
|
||||
exports.Embed = require('./structures/Embed');
|
||||
exports.EmbedBuilder = require('./structures/EmbedBuilder');
|
||||
exports.Emoji = require('./structures/Emoji').Emoji;
|
||||
exports.Entitlement = require('./structures/Entitlement').Entitlement;
|
||||
exports.FileComponent = require('./structures/FileComponent');
|
||||
exports.ForumChannel = require('./structures/ForumChannel');
|
||||
exports.Guild = require('./structures/Guild').Guild;
|
||||
exports.GuildAuditLogs = require('./structures/GuildAuditLogs');
|
||||
@@ -146,6 +149,9 @@ exports.GuildScheduledEvent = require('./structures/GuildScheduledEvent').GuildS
|
||||
exports.GuildTemplate = require('./structures/GuildTemplate');
|
||||
exports.Integration = require('./structures/Integration');
|
||||
exports.IntegrationApplication = require('./structures/IntegrationApplication');
|
||||
exports.InteractionCallback = require('./structures/InteractionCallback');
|
||||
exports.InteractionCallbackResource = require('./structures/InteractionCallbackResource');
|
||||
exports.InteractionCallbackResponse = require('./structures/InteractionCallbackResponse');
|
||||
exports.BaseInteraction = require('./structures/BaseInteraction');
|
||||
exports.InteractionCollector = require('./structures/InteractionCollector');
|
||||
exports.InteractionResponse = require('./structures/InteractionResponse');
|
||||
@@ -158,6 +164,8 @@ exports.Attachment = require('./structures/Attachment');
|
||||
exports.AttachmentBuilder = require('./structures/AttachmentBuilder');
|
||||
exports.ModalBuilder = require('./structures/ModalBuilder');
|
||||
exports.MediaChannel = require('./structures/MediaChannel');
|
||||
exports.MediaGalleryComponent = require('./structures/MediaGalleryComponent');
|
||||
exports.MediaGalleryItem = require('./structures/MediaGalleryItem');
|
||||
exports.MessageCollector = require('./structures/MessageCollector');
|
||||
exports.MessageComponentInteraction = require('./structures/MessageComponentInteraction');
|
||||
exports.MessageContextMenuCommandInteraction = require('./structures/MessageContextMenuCommandInteraction');
|
||||
@@ -177,6 +185,7 @@ exports.ReactionCollector = require('./structures/ReactionCollector');
|
||||
exports.ReactionEmoji = require('./structures/ReactionEmoji');
|
||||
exports.RichPresenceAssets = require('./structures/Presence').RichPresenceAssets;
|
||||
exports.Role = require('./structures/Role').Role;
|
||||
exports.SectionComponent = require('./structures/SectionComponent');
|
||||
exports.SelectMenuBuilder = require('./structures/SelectMenuBuilder');
|
||||
exports.ChannelSelectMenuBuilder = require('./structures/ChannelSelectMenuBuilder');
|
||||
exports.MentionableSelectMenuBuilder = require('./structures/MentionableSelectMenuBuilder');
|
||||
@@ -198,23 +207,30 @@ exports.RoleSelectMenuInteraction = require('./structures/RoleSelectMenuInteract
|
||||
exports.StringSelectMenuInteraction = require('./structures/StringSelectMenuInteraction');
|
||||
exports.UserSelectMenuInteraction = require('./structures/UserSelectMenuInteraction');
|
||||
exports.SelectMenuOptionBuilder = require('./structures/SelectMenuOptionBuilder');
|
||||
exports.SeparatorComponent = require('./structures/SeparatorComponent');
|
||||
exports.SKU = require('./structures/SKU').SKU;
|
||||
exports.SoundboardSound = require('./structures/SoundboardSound.js').SoundboardSound;
|
||||
exports.StringSelectMenuOptionBuilder = require('./structures/StringSelectMenuOptionBuilder');
|
||||
exports.StageChannel = require('./structures/StageChannel');
|
||||
exports.StageInstance = require('./structures/StageInstance').StageInstance;
|
||||
exports.Subscription = require('./structures/Subscription').Subscription;
|
||||
exports.Sticker = require('./structures/Sticker').Sticker;
|
||||
exports.StickerPack = require('./structures/StickerPack');
|
||||
exports.Team = require('./structures/Team');
|
||||
exports.TeamMember = require('./structures/TeamMember');
|
||||
exports.TextChannel = require('./structures/TextChannel');
|
||||
exports.TextDisplayComponent = require('./structures/TextDisplayComponent');
|
||||
exports.TextInputBuilder = require('./structures/TextInputBuilder');
|
||||
exports.TextInputComponent = require('./structures/TextInputComponent');
|
||||
exports.ThreadChannel = require('./structures/ThreadChannel');
|
||||
exports.ThreadMember = require('./structures/ThreadMember');
|
||||
exports.ThreadOnlyChannel = require('./structures/ThreadOnlyChannel');
|
||||
exports.ThumbnailComponent = require('./structures/ThumbnailComponent');
|
||||
exports.Typing = require('./structures/Typing');
|
||||
exports.UnfurledMediaItem = require('./structures/UnfurledMediaItem');
|
||||
exports.User = require('./structures/User');
|
||||
exports.UserContextMenuCommandInteraction = require('./structures/UserContextMenuCommandInteraction');
|
||||
exports.VoiceChannelEffect = require('./structures/VoiceChannelEffect');
|
||||
exports.VoiceChannel = require('./structures/VoiceChannel');
|
||||
exports.VoiceRegion = require('./structures/VoiceRegion');
|
||||
exports.VoiceState = require('./structures/VoiceState');
|
||||
|
||||
@@ -82,7 +82,7 @@ class ApplicationCommandManager extends CachedManager {
|
||||
* Options used to fetch Application Commands from Discord
|
||||
* @typedef {BaseFetchOptions} FetchApplicationCommandOptions
|
||||
* @property {Snowflake} [guildId] The guild's id to fetch commands for, for when the guild is not cached
|
||||
* @property {LocaleString} [locale] The locale to use when fetching this command
|
||||
* @property {Locale} [locale] The locale to use when fetching this command
|
||||
* @property {boolean} [withLocalizations] Whether to fetch all localization data
|
||||
*/
|
||||
|
||||
|
||||
@@ -65,12 +65,12 @@ class ApplicationEmojiManager extends CachedManager {
|
||||
* @returns {Promise<ApplicationEmoji|Collection<Snowflake, ApplicationEmoji>>}
|
||||
* @example
|
||||
* // Fetch all emojis from the application
|
||||
* message.application.emojis.fetch()
|
||||
* application.emojis.fetch()
|
||||
* .then(emojis => console.log(`There are ${emojis.size} emojis.`))
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Fetch a single emoji
|
||||
* message.application.emojis.fetch('222078108977594368')
|
||||
* application.emojis.fetch('222078108977594368')
|
||||
* .then(emoji => console.log(`The emoji name is: ${emoji.name}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const CachedManager = require('./CachedManager');
|
||||
const ApplicationEmoji = require('../structures/ApplicationEmoji');
|
||||
const GuildEmoji = require('../structures/GuildEmoji');
|
||||
const ReactionEmoji = require('../structures/ReactionEmoji');
|
||||
const { parseEmoji } = require('../util/Util');
|
||||
@@ -25,7 +26,8 @@ class BaseGuildEmojiManager extends CachedManager {
|
||||
* * A Snowflake
|
||||
* * A GuildEmoji object
|
||||
* * A ReactionEmoji object
|
||||
* @typedef {Snowflake|GuildEmoji|ReactionEmoji} EmojiResolvable
|
||||
* * An ApplicationEmoji object
|
||||
* @typedef {Snowflake|GuildEmoji|ReactionEmoji|ApplicationEmoji} EmojiResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -34,7 +36,8 @@ class BaseGuildEmojiManager extends CachedManager {
|
||||
* @returns {?GuildEmoji}
|
||||
*/
|
||||
resolve(emoji) {
|
||||
if (emoji instanceof ReactionEmoji) return super.resolve(emoji.id);
|
||||
if (emoji instanceof ReactionEmoji) return this.cache.get(emoji.id) ?? null;
|
||||
if (emoji instanceof ApplicationEmoji) return this.cache.get(emoji.id) ?? null;
|
||||
return super.resolve(emoji);
|
||||
}
|
||||
|
||||
@@ -45,6 +48,7 @@ class BaseGuildEmojiManager extends CachedManager {
|
||||
*/
|
||||
resolveId(emoji) {
|
||||
if (emoji instanceof ReactionEmoji) return emoji.id;
|
||||
if (emoji instanceof ApplicationEmoji) return emoji.id;
|
||||
return super.resolveId(emoji);
|
||||
}
|
||||
|
||||
@@ -65,6 +69,7 @@ class BaseGuildEmojiManager extends CachedManager {
|
||||
const emojiResolvable = this.resolve(emoji);
|
||||
if (emojiResolvable) return emojiResolvable.identifier;
|
||||
if (emoji instanceof ReactionEmoji) return emoji.identifier;
|
||||
if (emoji instanceof ApplicationEmoji) return emoji.identifier;
|
||||
if (typeof emoji === 'string') {
|
||||
const res = parseEmoji(emoji);
|
||||
if (res?.name.length) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user