mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-19 21:13:30 +01:00
refactor(builder): remove unsafe*Builders (#8074)
This commit is contained in:
@@ -7,7 +7,9 @@ import {
|
|||||||
} from 'discord-api-types/v10';
|
} from 'discord-api-types/v10';
|
||||||
import { ComponentBuilder } from './Component';
|
import { ComponentBuilder } from './Component';
|
||||||
import { createComponentBuilder } from './Components';
|
import { createComponentBuilder } from './Components';
|
||||||
import type { ButtonBuilder, SelectMenuBuilder, TextInputBuilder } from '..';
|
import type { ButtonBuilder } from './button/Button';
|
||||||
|
import type { SelectMenuBuilder } from './selectMenu/SelectMenu';
|
||||||
|
import type { TextInputBuilder } from './textInput/TextInput';
|
||||||
import { normalizeArray, type RestOrArray } from '../util/normalizeArray';
|
import { normalizeArray, type RestOrArray } from '../util/normalizeArray';
|
||||||
|
|
||||||
export type MessageComponentBuilder =
|
export type MessageComponentBuilder =
|
||||||
|
|||||||
@@ -1,55 +1,78 @@
|
|||||||
import { s } from '@sapphire/shapeshift';
|
import { s } from '@sapphire/shapeshift';
|
||||||
import { APIMessageComponentEmoji, ButtonStyle } from 'discord-api-types/v10';
|
import { APIMessageComponentEmoji, ButtonStyle } from 'discord-api-types/v10';
|
||||||
import type { SelectMenuOptionBuilder } from './selectMenu/SelectMenuOption';
|
import { SelectMenuOptionBuilder } from './selectMenu/SelectMenuOption';
|
||||||
import { UnsafeSelectMenuOptionBuilder } from './selectMenu/UnsafeSelectMenuOption';
|
import { isValidationEnabled } from '../util/validation';
|
||||||
|
|
||||||
export const customIdValidator = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100);
|
export const customIdValidator = s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(100)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const emojiValidator = s.object({
|
export const emojiValidator = s
|
||||||
id: s.string,
|
.object({
|
||||||
name: s.string,
|
id: s.string,
|
||||||
animated: s.boolean,
|
name: s.string,
|
||||||
}).partial.strict;
|
animated: s.boolean,
|
||||||
|
})
|
||||||
|
.partial.strict.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const disabledValidator = s.boolean;
|
export const disabledValidator = s.boolean;
|
||||||
|
|
||||||
export const buttonLabelValidator = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(80);
|
export const buttonLabelValidator = s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(80)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const buttonStyleValidator = s.nativeEnum(ButtonStyle);
|
export const buttonStyleValidator = s.nativeEnum(ButtonStyle);
|
||||||
|
|
||||||
export const placeholderValidator = s.string.lengthLessThanOrEqual(150);
|
export const placeholderValidator = s.string.lengthLessThanOrEqual(150).setValidationEnabled(isValidationEnabled);
|
||||||
export const minMaxValidator = s.number.int.greaterThanOrEqual(0).lessThanOrEqual(25);
|
export const minMaxValidator = s.number.int
|
||||||
|
.greaterThanOrEqual(0)
|
||||||
|
.lessThanOrEqual(25)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const labelValueDescriptionValidator = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100);
|
export const labelValueDescriptionValidator = s.string
|
||||||
export const optionValidator = s.union(
|
.lengthGreaterThanOrEqual(1)
|
||||||
s.object({
|
.lengthLessThanOrEqual(100)
|
||||||
label: labelValueDescriptionValidator,
|
.setValidationEnabled(isValidationEnabled);
|
||||||
value: labelValueDescriptionValidator,
|
export const optionValidator = s
|
||||||
description: labelValueDescriptionValidator.optional,
|
.union(
|
||||||
emoji: emojiValidator.optional,
|
s.object({
|
||||||
default: s.boolean.optional,
|
label: labelValueDescriptionValidator,
|
||||||
}),
|
value: labelValueDescriptionValidator,
|
||||||
s.instance(UnsafeSelectMenuOptionBuilder),
|
description: labelValueDescriptionValidator.optional,
|
||||||
);
|
emoji: emojiValidator.optional,
|
||||||
export const optionsValidator = optionValidator.array.lengthGreaterThanOrEqual(0);
|
default: s.boolean.optional,
|
||||||
export const optionsLengthValidator = s.number.int.greaterThanOrEqual(0).lessThanOrEqual(25);
|
}),
|
||||||
|
s.instance(SelectMenuOptionBuilder),
|
||||||
|
)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
|
export const optionsValidator = optionValidator.array
|
||||||
|
.lengthGreaterThanOrEqual(0)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
export const optionsLengthValidator = s.number.int
|
||||||
|
.greaterThanOrEqual(0)
|
||||||
|
.lessThanOrEqual(25)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export function validateRequiredSelectMenuParameters(options: SelectMenuOptionBuilder[], customId?: string) {
|
export function validateRequiredSelectMenuParameters(options: SelectMenuOptionBuilder[], customId?: string) {
|
||||||
customIdValidator.parse(customId);
|
customIdValidator.parse(customId);
|
||||||
optionsValidator.parse(options);
|
optionsValidator.parse(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const labelValueValidator = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100);
|
|
||||||
export const defaultValidator = s.boolean;
|
export const defaultValidator = s.boolean;
|
||||||
|
|
||||||
export function validateRequiredSelectMenuOptionParameters(label?: string, value?: string) {
|
export function validateRequiredSelectMenuOptionParameters(label?: string, value?: string) {
|
||||||
labelValueValidator.parse(label);
|
labelValueDescriptionValidator.parse(label);
|
||||||
labelValueValidator.parse(value);
|
labelValueDescriptionValidator.parse(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const urlValidator = s.string.url({
|
export const urlValidator = s.string
|
||||||
allowedProtocols: ['http:', 'https:', 'discord:'],
|
.url({
|
||||||
});
|
allowedProtocols: ['http:', 'https:', 'discord:'],
|
||||||
|
})
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export function validateRequiredButtonParameters(
|
export function validateRequiredButtonParameters(
|
||||||
style?: ButtonStyle,
|
style?: ButtonStyle,
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { APIMessageComponent, APIModalComponent, ComponentType } from 'discord-api-types/v10';
|
import { APIMessageComponent, APIModalComponent, ComponentType } from 'discord-api-types/v10';
|
||||||
import type { AnyComponentBuilder, MessageComponentBuilder, ModalComponentBuilder } from './ActionRow';
|
import {
|
||||||
import { ActionRowBuilder, ButtonBuilder, ComponentBuilder, SelectMenuBuilder, TextInputBuilder } from '../index';
|
ActionRowBuilder,
|
||||||
|
type AnyComponentBuilder,
|
||||||
|
type MessageComponentBuilder,
|
||||||
|
type ModalComponentBuilder,
|
||||||
|
} from './ActionRow';
|
||||||
|
import { ComponentBuilder } from './Component';
|
||||||
|
import { ButtonBuilder } from './button/Button';
|
||||||
|
import { SelectMenuBuilder } from './selectMenu/SelectMenu';
|
||||||
|
import { TextInputBuilder } from './textInput/TextInput';
|
||||||
|
|
||||||
export interface MappedComponentTypes {
|
export interface MappedComponentTypes {
|
||||||
[ComponentType.ActionRow]: ActionRowBuilder<AnyComponentBuilder>;
|
[ComponentType.ActionRow]: ActionRowBuilder<AnyComponentBuilder>;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type {
|
import {
|
||||||
|
ComponentType,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
APIMessageComponentEmoji,
|
type APIMessageComponentEmoji,
|
||||||
APIButtonComponent,
|
type APIButtonComponent,
|
||||||
APIButtonComponentWithCustomId,
|
type APIButtonComponentWithURL,
|
||||||
APIButtonComponentWithURL,
|
type APIButtonComponentWithCustomId,
|
||||||
} from 'discord-api-types/v10';
|
} from 'discord-api-types/v10';
|
||||||
import { UnsafeButtonBuilder } from './UnsafeButton';
|
|
||||||
import {
|
import {
|
||||||
buttonLabelValidator,
|
buttonLabelValidator,
|
||||||
buttonStyleValidator,
|
buttonStyleValidator,
|
||||||
@@ -15,36 +15,77 @@ import {
|
|||||||
urlValidator,
|
urlValidator,
|
||||||
validateRequiredButtonParameters,
|
validateRequiredButtonParameters,
|
||||||
} from '../Assertions';
|
} from '../Assertions';
|
||||||
|
import { ComponentBuilder } from '../Component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a validated button component
|
* Represents a button component
|
||||||
*/
|
*/
|
||||||
export class ButtonBuilder extends UnsafeButtonBuilder {
|
export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
|
||||||
public override setStyle(style: ButtonStyle) {
|
public constructor(data?: Partial<APIButtonComponent>) {
|
||||||
return super.setStyle(buttonStyleValidator.parse(style));
|
super({ type: ComponentType.Button, ...data });
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setURL(url: string) {
|
/**
|
||||||
return super.setURL(urlValidator.parse(url));
|
* Sets the style of this button
|
||||||
|
*
|
||||||
|
* @param style - The style of the button
|
||||||
|
*/
|
||||||
|
public setStyle(style: ButtonStyle) {
|
||||||
|
this.data.style = buttonStyleValidator.parse(style);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setCustomId(customId: string) {
|
/**
|
||||||
return super.setCustomId(customIdValidator.parse(customId));
|
* Sets the URL for this button
|
||||||
|
*
|
||||||
|
* @param url - The URL to open when this button is clicked
|
||||||
|
*/
|
||||||
|
public setURL(url: string) {
|
||||||
|
(this.data as APIButtonComponentWithURL).url = urlValidator.parse(url);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setEmoji(emoji: APIMessageComponentEmoji) {
|
/**
|
||||||
return super.setEmoji(emojiValidator.parse(emoji));
|
* Sets the custom id for this button
|
||||||
|
*
|
||||||
|
* @param customId - The custom id to use for this button
|
||||||
|
*/
|
||||||
|
public setCustomId(customId: string) {
|
||||||
|
(this.data as APIButtonComponentWithCustomId).custom_id = customIdValidator.parse(customId);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setDisabled(disabled = true) {
|
/**
|
||||||
return super.setDisabled(disabledValidator.parse(disabled));
|
* Sets the emoji to display on this button
|
||||||
|
*
|
||||||
|
* @param emoji - The emoji to display on this button
|
||||||
|
*/
|
||||||
|
public setEmoji(emoji: APIMessageComponentEmoji) {
|
||||||
|
this.data.emoji = emojiValidator.parse(emoji);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setLabel(label: string) {
|
/**
|
||||||
return super.setLabel(buttonLabelValidator.parse(label));
|
* Sets whether this button is disabled
|
||||||
|
*
|
||||||
|
* @param disabled - Whether to disable this button
|
||||||
|
*/
|
||||||
|
public setDisabled(disabled = true) {
|
||||||
|
this.data.disabled = disabledValidator.parse(disabled);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override toJSON(): APIButtonComponent {
|
/**
|
||||||
|
* Sets the label for this button
|
||||||
|
*
|
||||||
|
* @param label - The label to display on this button
|
||||||
|
*/
|
||||||
|
public setLabel(label: string) {
|
||||||
|
this.data.label = buttonLabelValidator.parse(label);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON(): APIButtonComponent {
|
||||||
validateRequiredButtonParameters(
|
validateRequiredButtonParameters(
|
||||||
this.data.style,
|
this.data.style,
|
||||||
this.data.label,
|
this.data.label,
|
||||||
@@ -52,6 +93,9 @@ export class ButtonBuilder extends UnsafeButtonBuilder {
|
|||||||
(this.data as APIButtonComponentWithCustomId).custom_id,
|
(this.data as APIButtonComponentWithCustomId).custom_id,
|
||||||
(this.data as APIButtonComponentWithURL).url,
|
(this.data as APIButtonComponentWithURL).url,
|
||||||
);
|
);
|
||||||
return super.toJSON();
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
return {
|
||||||
|
...this.data,
|
||||||
|
} as APIButtonComponent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
import {
|
|
||||||
ComponentType,
|
|
||||||
ButtonStyle,
|
|
||||||
type APIMessageComponentEmoji,
|
|
||||||
type APIButtonComponent,
|
|
||||||
type APIButtonComponentWithURL,
|
|
||||||
type APIButtonComponentWithCustomId,
|
|
||||||
} from 'discord-api-types/v10';
|
|
||||||
import { ComponentBuilder } from '../Component';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a non-validated button component
|
|
||||||
*/
|
|
||||||
export class UnsafeButtonBuilder extends ComponentBuilder<APIButtonComponent> {
|
|
||||||
public constructor(data?: Partial<APIButtonComponent>) {
|
|
||||||
super({ type: ComponentType.Button, ...data });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the style of this button
|
|
||||||
*
|
|
||||||
* @param style - The style of the button
|
|
||||||
*/
|
|
||||||
public setStyle(style: ButtonStyle) {
|
|
||||||
this.data.style = style;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the URL for this button
|
|
||||||
*
|
|
||||||
* @param url - The URL to open when this button is clicked
|
|
||||||
*/
|
|
||||||
public setURL(url: string) {
|
|
||||||
(this.data as APIButtonComponentWithURL).url = url;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the custom Id for this button
|
|
||||||
*
|
|
||||||
* @param customId - The custom id to use for this button
|
|
||||||
*/
|
|
||||||
public setCustomId(customId: string) {
|
|
||||||
(this.data as APIButtonComponentWithCustomId).custom_id = customId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the emoji to display on this button
|
|
||||||
*
|
|
||||||
* @param emoji - The emoji to display on this button
|
|
||||||
*/
|
|
||||||
public setEmoji(emoji: APIMessageComponentEmoji) {
|
|
||||||
this.data.emoji = emoji;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether this button is disable or not
|
|
||||||
*
|
|
||||||
* @param disabled - Whether or not to disable this button or not
|
|
||||||
*/
|
|
||||||
public setDisabled(disabled = true) {
|
|
||||||
this.data.disabled = disabled;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the label for this button
|
|
||||||
*
|
|
||||||
* @param label - The label to display on this button
|
|
||||||
*/
|
|
||||||
public setLabel(label: string) {
|
|
||||||
this.data.label = label;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toJSON(): APIButtonComponent {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
return {
|
|
||||||
...this.data,
|
|
||||||
} as APIButtonComponent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { APISelectMenuComponent, APISelectMenuOption } from 'discord-api-types/v10';
|
import { APISelectMenuOption, ComponentType, type APISelectMenuComponent } from 'discord-api-types/v10';
|
||||||
import { UnsafeSelectMenuBuilder } from './UnsafeSelectMenu';
|
import { SelectMenuOptionBuilder } from './SelectMenuOption';
|
||||||
import { UnsafeSelectMenuOptionBuilder } from './UnsafeSelectMenuOption';
|
|
||||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
||||||
import {
|
import {
|
||||||
customIdValidator,
|
customIdValidator,
|
||||||
@@ -11,61 +10,118 @@ import {
|
|||||||
placeholderValidator,
|
placeholderValidator,
|
||||||
validateRequiredSelectMenuParameters,
|
validateRequiredSelectMenuParameters,
|
||||||
} from '../Assertions';
|
} from '../Assertions';
|
||||||
|
import { ComponentBuilder } from '../Component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a validated select menu component
|
* Represents a select menu component
|
||||||
*/
|
*/
|
||||||
export class SelectMenuBuilder extends UnsafeSelectMenuBuilder {
|
export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent> {
|
||||||
public override setPlaceholder(placeholder: string) {
|
/**
|
||||||
return super.setPlaceholder(placeholderValidator.parse(placeholder));
|
* The options within this select menu
|
||||||
|
*/
|
||||||
|
public readonly options: SelectMenuOptionBuilder[];
|
||||||
|
|
||||||
|
public constructor(data?: Partial<APISelectMenuComponent>) {
|
||||||
|
const { options, ...initData } = data ?? {};
|
||||||
|
super({ type: ComponentType.SelectMenu, ...initData });
|
||||||
|
this.options = options?.map((o) => new SelectMenuOptionBuilder(o)) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setMinValues(minValues: number) {
|
/**
|
||||||
return super.setMinValues(minMaxValidator.parse(minValues));
|
* Sets the placeholder for this select menu
|
||||||
|
*
|
||||||
|
* @param placeholder - The placeholder to use for this select menu
|
||||||
|
*/
|
||||||
|
public setPlaceholder(placeholder: string) {
|
||||||
|
this.data.placeholder = placeholderValidator.parse(placeholder);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setMaxValues(maxValues: number) {
|
/**
|
||||||
return super.setMaxValues(minMaxValidator.parse(maxValues));
|
* Sets the minimum values that must be selected in the select menu
|
||||||
|
*
|
||||||
|
* @param minValues - The minimum values that must be selected
|
||||||
|
*/
|
||||||
|
public setMinValues(minValues: number) {
|
||||||
|
this.data.min_values = minMaxValidator.parse(minValues);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setCustomId(customId: string) {
|
/**
|
||||||
return super.setCustomId(customIdValidator.parse(customId));
|
* Sets the maximum values that must be selected in the select menu
|
||||||
|
*
|
||||||
|
* @param maxValues - The maximum values that must be selected
|
||||||
|
*/
|
||||||
|
public setMaxValues(maxValues: number) {
|
||||||
|
this.data.max_values = minMaxValidator.parse(maxValues);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setDisabled(disabled = true) {
|
/**
|
||||||
return super.setDisabled(disabledValidator.parse(disabled));
|
* Sets the custom id for this select menu
|
||||||
|
*
|
||||||
|
* @param customId - The custom id to use for this select menu
|
||||||
|
*/
|
||||||
|
public setCustomId(customId: string) {
|
||||||
|
this.data.custom_id = customIdValidator.parse(customId);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override addOptions(...options: RestOrArray<UnsafeSelectMenuOptionBuilder | APISelectMenuOption>) {
|
/**
|
||||||
|
* Sets whether this select menu is disabled
|
||||||
|
*
|
||||||
|
* @param disabled - Whether this select menu is disabled
|
||||||
|
*/
|
||||||
|
public setDisabled(disabled = true) {
|
||||||
|
this.data.disabled = disabledValidator.parse(disabled);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds options to this select menu
|
||||||
|
*
|
||||||
|
* @param options - The options to add to this select menu
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public addOptions(...options: RestOrArray<SelectMenuOptionBuilder | APISelectMenuOption>) {
|
||||||
options = normalizeArray(options);
|
options = normalizeArray(options);
|
||||||
optionsLengthValidator.parse(this.options.length + options.length);
|
optionsLengthValidator.parse(this.options.length + options.length);
|
||||||
this.options.push(
|
this.options.push(
|
||||||
...options.map((option) =>
|
...options.map((option) =>
|
||||||
option instanceof UnsafeSelectMenuOptionBuilder
|
option instanceof SelectMenuOptionBuilder
|
||||||
? option
|
? option
|
||||||
: new UnsafeSelectMenuOptionBuilder(optionValidator.parse<APISelectMenuOption>(option)),
|
: new SelectMenuOptionBuilder(optionValidator.parse<APISelectMenuOption>(option)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setOptions(...options: RestOrArray<UnsafeSelectMenuOptionBuilder | APISelectMenuOption>) {
|
/**
|
||||||
|
* Sets the options on this select menu
|
||||||
|
*
|
||||||
|
* @param options - The options to set on this select menu
|
||||||
|
*/
|
||||||
|
public setOptions(...options: RestOrArray<SelectMenuOptionBuilder | APISelectMenuOption>) {
|
||||||
options = normalizeArray(options);
|
options = normalizeArray(options);
|
||||||
optionsLengthValidator.parse(options.length);
|
optionsLengthValidator.parse(options.length);
|
||||||
this.options.splice(
|
this.options.splice(
|
||||||
0,
|
0,
|
||||||
this.options.length,
|
this.options.length,
|
||||||
...options.map((option) =>
|
...options.map((option) =>
|
||||||
option instanceof UnsafeSelectMenuOptionBuilder
|
option instanceof SelectMenuOptionBuilder
|
||||||
? option
|
? option
|
||||||
: new UnsafeSelectMenuOptionBuilder(optionValidator.parse<APISelectMenuOption>(option)),
|
: new SelectMenuOptionBuilder(optionValidator.parse<APISelectMenuOption>(option)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override toJSON(): APISelectMenuComponent {
|
public toJSON(): APISelectMenuComponent {
|
||||||
validateRequiredSelectMenuParameters(this.options, this.data.custom_id);
|
validateRequiredSelectMenuParameters(this.options, this.data.custom_id);
|
||||||
return super.toJSON();
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
return {
|
||||||
|
...this.data,
|
||||||
|
options: this.options.map((o) => o.toJSON()),
|
||||||
|
} as APISelectMenuComponent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,73 @@
|
|||||||
import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10';
|
import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10';
|
||||||
import { UnsafeSelectMenuOptionBuilder } from './UnsafeSelectMenuOption';
|
|
||||||
import {
|
import {
|
||||||
defaultValidator,
|
defaultValidator,
|
||||||
emojiValidator,
|
emojiValidator,
|
||||||
labelValueValidator,
|
labelValueDescriptionValidator,
|
||||||
validateRequiredSelectMenuOptionParameters,
|
validateRequiredSelectMenuOptionParameters,
|
||||||
} from '../Assertions';
|
} from '../Assertions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a validated option within a select menu component
|
* Represents a option within a select menu component
|
||||||
*/
|
*/
|
||||||
export class SelectMenuOptionBuilder extends UnsafeSelectMenuOptionBuilder {
|
export class SelectMenuOptionBuilder {
|
||||||
public override setDescription(description: string) {
|
public constructor(public data: Partial<APISelectMenuOption> = {}) {}
|
||||||
return super.setDescription(labelValueValidator.parse(description));
|
|
||||||
|
/**
|
||||||
|
* Sets the label of this option
|
||||||
|
*
|
||||||
|
* @param label - The label to show on this option
|
||||||
|
*/
|
||||||
|
public setLabel(label: string) {
|
||||||
|
this.data.label = labelValueDescriptionValidator.parse(label);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setDefault(isDefault = true) {
|
/**
|
||||||
return super.setDefault(defaultValidator.parse(isDefault));
|
* Sets the value of this option
|
||||||
|
*
|
||||||
|
* @param value - The value of this option
|
||||||
|
*/
|
||||||
|
public setValue(value: string) {
|
||||||
|
this.data.value = labelValueDescriptionValidator.parse(value);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setEmoji(emoji: APIMessageComponentEmoji) {
|
/**
|
||||||
return super.setEmoji(emojiValidator.parse(emoji));
|
* Sets the description of this option
|
||||||
|
*
|
||||||
|
* @param description - The description of this option
|
||||||
|
*/
|
||||||
|
public setDescription(description: string) {
|
||||||
|
this.data.description = labelValueDescriptionValidator.parse(description);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override toJSON(): APISelectMenuOption {
|
/**
|
||||||
|
* Sets whether this option is selected by default
|
||||||
|
*
|
||||||
|
* @param isDefault - Whether this option is selected by default
|
||||||
|
*/
|
||||||
|
public setDefault(isDefault = true) {
|
||||||
|
this.data.default = defaultValidator.parse(isDefault);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the emoji to display on this option
|
||||||
|
*
|
||||||
|
* @param emoji - The emoji to display on this option
|
||||||
|
*/
|
||||||
|
public setEmoji(emoji: APIMessageComponentEmoji) {
|
||||||
|
this.data.emoji = emojiValidator.parse(emoji);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON(): APISelectMenuOption {
|
||||||
validateRequiredSelectMenuOptionParameters(this.data.label, this.data.value);
|
validateRequiredSelectMenuOptionParameters(this.data.label, this.data.value);
|
||||||
return super.toJSON();
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
return {
|
||||||
|
...this.data,
|
||||||
|
} as APISelectMenuOption;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
import { APISelectMenuOption, ComponentType, type APISelectMenuComponent } from 'discord-api-types/v10';
|
|
||||||
import { UnsafeSelectMenuOptionBuilder } from './UnsafeSelectMenuOption';
|
|
||||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
|
||||||
import { ComponentBuilder } from '../Component';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a non-validated select menu component
|
|
||||||
*/
|
|
||||||
export class UnsafeSelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent> {
|
|
||||||
/**
|
|
||||||
* The options within this select menu
|
|
||||||
*/
|
|
||||||
public readonly options: UnsafeSelectMenuOptionBuilder[];
|
|
||||||
|
|
||||||
public constructor(data?: Partial<APISelectMenuComponent>) {
|
|
||||||
const { options, ...initData } = data ?? {};
|
|
||||||
super({ type: ComponentType.SelectMenu, ...initData });
|
|
||||||
this.options = options?.map((o) => new UnsafeSelectMenuOptionBuilder(o)) ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the placeholder for this select menu
|
|
||||||
*
|
|
||||||
* @param placeholder - The placeholder to use for this select menu
|
|
||||||
*/
|
|
||||||
public setPlaceholder(placeholder: string) {
|
|
||||||
this.data.placeholder = placeholder;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the minimum values that must be selected in the select menu
|
|
||||||
*
|
|
||||||
* @param minValues - The minimum values that must be selected
|
|
||||||
*/
|
|
||||||
public setMinValues(minValues: number) {
|
|
||||||
this.data.min_values = minValues;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximum values that must be selected in the select menu
|
|
||||||
*
|
|
||||||
* @param minValues - The maximum values that must be selected
|
|
||||||
*/
|
|
||||||
public setMaxValues(maxValues: number) {
|
|
||||||
this.data.max_values = maxValues;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the custom Id for this select menu
|
|
||||||
*
|
|
||||||
* @param customId - The custom id to use for this select menu
|
|
||||||
*/
|
|
||||||
public setCustomId(customId: string) {
|
|
||||||
this.data.custom_id = customId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether or not this select menu is disabled
|
|
||||||
*
|
|
||||||
* @param disabled - Whether or not this select menu is disabled
|
|
||||||
*/
|
|
||||||
public setDisabled(disabled = true) {
|
|
||||||
this.data.disabled = disabled;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds options to this select menu
|
|
||||||
*
|
|
||||||
* @param options - The options to add to this select menu
|
|
||||||
*/
|
|
||||||
public addOptions(...options: RestOrArray<UnsafeSelectMenuOptionBuilder | APISelectMenuOption>) {
|
|
||||||
this.options.push(
|
|
||||||
...normalizeArray(options).map((option) =>
|
|
||||||
option instanceof UnsafeSelectMenuOptionBuilder ? option : new UnsafeSelectMenuOptionBuilder(option),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the options on this select menu
|
|
||||||
*
|
|
||||||
* @param options - The options to set on this select menu
|
|
||||||
*/
|
|
||||||
public setOptions(...options: RestOrArray<UnsafeSelectMenuOptionBuilder | APISelectMenuOption>) {
|
|
||||||
this.options.splice(
|
|
||||||
0,
|
|
||||||
this.options.length,
|
|
||||||
...normalizeArray(options).map((option) =>
|
|
||||||
option instanceof UnsafeSelectMenuOptionBuilder ? option : new UnsafeSelectMenuOptionBuilder(option),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toJSON(): APISelectMenuComponent {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
return {
|
|
||||||
...this.data,
|
|
||||||
options: this.options.map((o) => o.toJSON()),
|
|
||||||
} as APISelectMenuComponent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a non-validated option within a select menu component
|
|
||||||
*/
|
|
||||||
export class UnsafeSelectMenuOptionBuilder {
|
|
||||||
public constructor(public data: Partial<APISelectMenuOption> = {}) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the label of this option
|
|
||||||
*
|
|
||||||
* @param label - The label to show on this option
|
|
||||||
*/
|
|
||||||
public setLabel(label: string) {
|
|
||||||
this.data.label = label;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of this option
|
|
||||||
*
|
|
||||||
* @param value - The value of this option
|
|
||||||
*/
|
|
||||||
public setValue(value: string) {
|
|
||||||
this.data.value = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the description of this option.
|
|
||||||
*
|
|
||||||
* @param description - The description of this option
|
|
||||||
*/
|
|
||||||
public setDescription(description: string) {
|
|
||||||
this.data.description = description;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether this option is selected by default
|
|
||||||
*
|
|
||||||
* @param isDefault - Whether this option is selected by default
|
|
||||||
*/
|
|
||||||
public setDefault(isDefault = true) {
|
|
||||||
this.data.default = isDefault;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the emoji to display on this option
|
|
||||||
*
|
|
||||||
* @param emoji - The emoji to display on this option
|
|
||||||
*/
|
|
||||||
public setEmoji(emoji: APIMessageComponentEmoji) {
|
|
||||||
this.data.emoji = emoji;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toJSON(): APISelectMenuOption {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
return {
|
|
||||||
...this.data,
|
|
||||||
} as APISelectMenuOption;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,24 @@
|
|||||||
import { s } from '@sapphire/shapeshift';
|
import { s } from '@sapphire/shapeshift';
|
||||||
import { TextInputStyle } from 'discord-api-types/v10';
|
import { TextInputStyle } from 'discord-api-types/v10';
|
||||||
|
import { isValidationEnabled } from '../../util/validation';
|
||||||
import { customIdValidator } from '../Assertions';
|
import { customIdValidator } from '../Assertions';
|
||||||
|
|
||||||
export const textInputStyleValidator = s.nativeEnum(TextInputStyle);
|
export const textInputStyleValidator = s.nativeEnum(TextInputStyle);
|
||||||
export const minLengthValidator = s.number.int.greaterThanOrEqual(0).lessThanOrEqual(4000);
|
export const minLengthValidator = s.number.int
|
||||||
export const maxLengthValidator = s.number.int.greaterThanOrEqual(1).lessThanOrEqual(4000);
|
.greaterThanOrEqual(0)
|
||||||
|
.lessThanOrEqual(4000)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
export const maxLengthValidator = s.number.int
|
||||||
|
.greaterThanOrEqual(1)
|
||||||
|
.lessThanOrEqual(4000)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
export const requiredValidator = s.boolean;
|
export const requiredValidator = s.boolean;
|
||||||
export const valueValidator = s.string.lengthLessThanOrEqual(4000);
|
export const valueValidator = s.string.lengthLessThanOrEqual(4000).setValidationEnabled(isValidationEnabled);
|
||||||
export const placeholderValidator = s.string.lengthLessThanOrEqual(100);
|
export const placeholderValidator = s.string.lengthLessThanOrEqual(100).setValidationEnabled(isValidationEnabled);
|
||||||
export const labelValidator = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(45);
|
export const labelValidator = s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(45)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export function validateRequiredParameters(customId?: string, style?: TextInputStyle, label?: string) {
|
export function validateRequiredParameters(customId?: string, style?: TextInputStyle, label?: string) {
|
||||||
customIdValidator.parse(customId);
|
customIdValidator.parse(customId);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { APITextInputComponent, TextInputStyle } from 'discord-api-types/v10';
|
import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
|
||||||
|
import isEqual from 'fast-deep-equal';
|
||||||
import {
|
import {
|
||||||
maxLengthValidator,
|
maxLengthValidator,
|
||||||
minLengthValidator,
|
minLengthValidator,
|
||||||
@@ -9,44 +10,108 @@ import {
|
|||||||
labelValidator,
|
labelValidator,
|
||||||
textInputStyleValidator,
|
textInputStyleValidator,
|
||||||
} from './Assertions';
|
} from './Assertions';
|
||||||
import { UnsafeTextInputBuilder } from './UnsafeTextInput';
|
import { isJSONEncodable, type JSONEncodable } from '../../util/jsonEncodable';
|
||||||
import { customIdValidator } from '../Assertions';
|
import { customIdValidator } from '../Assertions';
|
||||||
|
import { ComponentBuilder } from '../Component';
|
||||||
|
|
||||||
export class TextInputBuilder extends UnsafeTextInputBuilder {
|
export class TextInputBuilder extends ComponentBuilder<APITextInputComponent> {
|
||||||
public override setCustomId(customId: string): this {
|
public constructor(data?: APITextInputComponent & { type?: ComponentType.TextInput }) {
|
||||||
return super.setCustomId(customIdValidator.parse(customId));
|
super({ type: ComponentType.TextInput, ...data });
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setLabel(label: string): this {
|
/**
|
||||||
return super.setLabel(labelValidator.parse(label));
|
* Sets the custom id for this text input
|
||||||
|
*
|
||||||
|
* @param customId - The custom id of this text input
|
||||||
|
*/
|
||||||
|
public setCustomId(customId: string) {
|
||||||
|
this.data.custom_id = customIdValidator.parse(customId);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setStyle(style: TextInputStyle): this {
|
/**
|
||||||
return super.setStyle(textInputStyleValidator.parse(style));
|
* Sets the label for this text input
|
||||||
|
*
|
||||||
|
* @param label - The label for this text input
|
||||||
|
*/
|
||||||
|
public setLabel(label: string) {
|
||||||
|
this.data.label = labelValidator.parse(label);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setMinLength(minLength: number) {
|
/**
|
||||||
return super.setMinLength(minLengthValidator.parse(minLength));
|
* Sets the style for this text input
|
||||||
|
*
|
||||||
|
* @param style - The style for this text input
|
||||||
|
*/
|
||||||
|
public setStyle(style: TextInputStyle) {
|
||||||
|
this.data.style = textInputStyleValidator.parse(style);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setMaxLength(maxLength: number) {
|
/**
|
||||||
return super.setMaxLength(maxLengthValidator.parse(maxLength));
|
* Sets the minimum length of text for this text input
|
||||||
|
*
|
||||||
|
* @param minLength - The minimum length of text for this text input
|
||||||
|
*/
|
||||||
|
public setMinLength(minLength: number) {
|
||||||
|
this.data.min_length = minLengthValidator.parse(minLength);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setPlaceholder(placeholder: string) {
|
/**
|
||||||
return super.setPlaceholder(placeholderValidator.parse(placeholder));
|
* Sets the maximum length of text for this text input
|
||||||
|
*
|
||||||
|
* @param maxLength - The maximum length of text for this text input
|
||||||
|
*/
|
||||||
|
public setMaxLength(maxLength: number) {
|
||||||
|
this.data.max_length = maxLengthValidator.parse(maxLength);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setValue(value: string) {
|
/**
|
||||||
return super.setValue(valueValidator.parse(value));
|
* Sets the placeholder of this text input
|
||||||
|
*
|
||||||
|
* @param placeholder - The placeholder of this text input
|
||||||
|
*/
|
||||||
|
public setPlaceholder(placeholder: string) {
|
||||||
|
this.data.placeholder = placeholderValidator.parse(placeholder);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setRequired(required = true) {
|
/**
|
||||||
return super.setRequired(requiredValidator.parse(required));
|
* Sets the value of this text input
|
||||||
|
*
|
||||||
|
* @param value - The value for this text input
|
||||||
|
*/
|
||||||
|
public setValue(value: string) {
|
||||||
|
this.data.value = valueValidator.parse(value);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override toJSON(): APITextInputComponent {
|
/**
|
||||||
|
* Sets whether this text input is required
|
||||||
|
*
|
||||||
|
* @param required - Whether this text input is required
|
||||||
|
*/
|
||||||
|
public setRequired(required = true) {
|
||||||
|
this.data.required = requiredValidator.parse(required);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON(): APITextInputComponent {
|
||||||
validateRequiredParameters(this.data.custom_id, this.data.style, this.data.label);
|
validateRequiredParameters(this.data.custom_id, this.data.style, this.data.label);
|
||||||
return super.toJSON();
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
return {
|
||||||
|
...this.data,
|
||||||
|
} as APITextInputComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public equals(other: JSONEncodable<APITextInputComponent> | APITextInputComponent): boolean {
|
||||||
|
if (isJSONEncodable(other)) {
|
||||||
|
return isEqual(other.toJSON(), this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEqual(other, this.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
|
|
||||||
import isEqual from 'fast-deep-equal';
|
|
||||||
import { ComponentBuilder } from '../../index';
|
|
||||||
|
|
||||||
export class UnsafeTextInputBuilder extends ComponentBuilder<APITextInputComponent> {
|
|
||||||
public constructor(data?: APITextInputComponent & { type?: ComponentType.TextInput }) {
|
|
||||||
super({ type: ComponentType.TextInput, ...data });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the custom id for this text input
|
|
||||||
*
|
|
||||||
* @param customId - The custom id of this text input
|
|
||||||
*/
|
|
||||||
public setCustomId(customId: string) {
|
|
||||||
this.data.custom_id = customId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the label for this text input
|
|
||||||
*
|
|
||||||
* @param label - The label for this text input
|
|
||||||
*/
|
|
||||||
public setLabel(label: string) {
|
|
||||||
this.data.label = label;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the style for this text input
|
|
||||||
*
|
|
||||||
* @param style - The style for this text input
|
|
||||||
*/
|
|
||||||
public setStyle(style: TextInputStyle) {
|
|
||||||
this.data.style = style;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the minimum length of text for this text input
|
|
||||||
*
|
|
||||||
* @param minLength - The minimum length of text for this text input
|
|
||||||
*/
|
|
||||||
public setMinLength(minLength: number) {
|
|
||||||
this.data.min_length = minLength;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximum length of text for this text input
|
|
||||||
*
|
|
||||||
* @param maxLength - The maximum length of text for this text input
|
|
||||||
*/
|
|
||||||
public setMaxLength(maxLength: number) {
|
|
||||||
this.data.max_length = maxLength;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the placeholder of this text input
|
|
||||||
*
|
|
||||||
* @param placeholder - The placeholder of this text input
|
|
||||||
*/
|
|
||||||
public setPlaceholder(placeholder: string) {
|
|
||||||
this.data.placeholder = placeholder;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value of this text input
|
|
||||||
*
|
|
||||||
* @param value - The value for this text input
|
|
||||||
*/
|
|
||||||
public setValue(value: string) {
|
|
||||||
this.data.value = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets whether this text input is required or not
|
|
||||||
*
|
|
||||||
* @param required - Whether this text input is required or not
|
|
||||||
*/
|
|
||||||
public setRequired(required = true) {
|
|
||||||
this.data.required = required;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toJSON(): APITextInputComponent {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
return {
|
|
||||||
...this.data,
|
|
||||||
} as APITextInputComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public equals(other: UnsafeTextInputBuilder | APITextInputComponent): boolean {
|
|
||||||
if (other instanceof UnsafeTextInputBuilder) {
|
|
||||||
return isEqual(other.data, this.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return isEqual(other, this.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
export * as EmbedAssertions from './messages/embed/Assertions';
|
export * as EmbedAssertions from './messages/embed/Assertions';
|
||||||
export * from './messages/embed/Embed';
|
export * from './messages/embed/Embed';
|
||||||
export * from './messages/formatters';
|
export * from './messages/formatters';
|
||||||
export * from './messages/embed/UnsafeEmbed';
|
|
||||||
|
|
||||||
export * as ComponentAssertions from './components/Assertions';
|
export * as ComponentAssertions from './components/Assertions';
|
||||||
export * from './components/ActionRow';
|
export * from './components/ActionRow';
|
||||||
@@ -10,15 +9,10 @@ export * from './components/Component';
|
|||||||
export * from './components/Components';
|
export * from './components/Components';
|
||||||
export * from './components/textInput/TextInput';
|
export * from './components/textInput/TextInput';
|
||||||
export * as TextInputAssertions from './components/textInput/Assertions';
|
export * as TextInputAssertions from './components/textInput/Assertions';
|
||||||
export * from './components/textInput/UnsafeTextInput';
|
|
||||||
export * from './interactions/modals/UnsafeModal';
|
|
||||||
export * from './interactions/modals/Modal';
|
export * from './interactions/modals/Modal';
|
||||||
export * as ModalAssertions from './interactions/modals/Assertions';
|
export * as ModalAssertions from './interactions/modals/Assertions';
|
||||||
export * from './components/selectMenu/SelectMenu';
|
export * from './components/selectMenu/SelectMenu';
|
||||||
export * from './components/selectMenu/SelectMenuOption';
|
export * from './components/selectMenu/SelectMenuOption';
|
||||||
export * from './components/button/UnsafeButton';
|
|
||||||
export * from './components/selectMenu/UnsafeSelectMenu';
|
|
||||||
export * from './components/selectMenu/UnsafeSelectMenuOption';
|
|
||||||
|
|
||||||
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions';
|
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions';
|
||||||
export * from './interactions/slashCommands/SlashCommandBuilder';
|
export * from './interactions/slashCommands/SlashCommandBuilder';
|
||||||
@@ -46,3 +40,4 @@ export * from './util/jsonEncodable';
|
|||||||
export * from './util/equatable';
|
export * from './util/equatable';
|
||||||
export * from './util/componentUtil';
|
export * from './util/componentUtil';
|
||||||
export * from './util/normalizeArray';
|
export * from './util/normalizeArray';
|
||||||
|
export * from './util/validation';
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { s } from '@sapphire/shapeshift';
|
import { s } from '@sapphire/shapeshift';
|
||||||
import { ApplicationCommandType } from 'discord-api-types/v10';
|
import { ApplicationCommandType } from 'discord-api-types/v10';
|
||||||
import type { ContextMenuCommandType } from './ContextMenuCommandBuilder';
|
import type { ContextMenuCommandType } from './ContextMenuCommandBuilder';
|
||||||
|
import { isValidationEnabled } from '../../util/validation';
|
||||||
|
|
||||||
const namePredicate = s.string
|
const namePredicate = s.string
|
||||||
.lengthGreaterThanOrEqual(1)
|
.lengthGreaterThanOrEqual(1)
|
||||||
.lengthLessThanOrEqual(32)
|
.lengthLessThanOrEqual(32)
|
||||||
.regex(/^( *[\p{L}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+ *)+$/u);
|
.regex(/^( *[\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));
|
const typePredicate = s
|
||||||
|
.union(s.literal(ApplicationCommandType.User), s.literal(ApplicationCommandType.Message))
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
const booleanPredicate = s.boolean;
|
const booleanPredicate = s.boolean;
|
||||||
|
|
||||||
export function validateDefaultPermission(value: unknown): asserts value is boolean {
|
export function validateDefaultPermission(value: unknown): asserts value is boolean {
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import { s } from '@sapphire/shapeshift';
|
import { s } from '@sapphire/shapeshift';
|
||||||
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../..';
|
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow';
|
||||||
import { customIdValidator } from '../../components/Assertions';
|
import { customIdValidator } from '../../components/Assertions';
|
||||||
|
import { isValidationEnabled } from '../../util/validation';
|
||||||
|
|
||||||
export const titleValidator = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(45);
|
export const titleValidator = s.string
|
||||||
export const componentsValidator = s.instance(ActionRowBuilder).array.lengthGreaterThanOrEqual(1);
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(45)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
export const componentsValidator = s
|
||||||
|
.instance(ActionRowBuilder)
|
||||||
|
.array.lengthGreaterThanOrEqual(1)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export function validateRequiredParameters(
|
export function validateRequiredParameters(
|
||||||
customId?: string,
|
customId?: string,
|
||||||
|
|||||||
@@ -1,19 +1,81 @@
|
|||||||
import type { APIModalInteractionResponseCallbackData } from 'discord-api-types/v10';
|
import type {
|
||||||
|
APIActionRowComponent,
|
||||||
|
APIModalActionRowComponent,
|
||||||
|
APIModalInteractionResponseCallbackData,
|
||||||
|
} from 'discord-api-types/v10';
|
||||||
import { titleValidator, validateRequiredParameters } from './Assertions';
|
import { titleValidator, validateRequiredParameters } from './Assertions';
|
||||||
import { UnsafeModalBuilder } from './UnsafeModal';
|
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow';
|
||||||
import { customIdValidator } from '../../components/Assertions';
|
import { customIdValidator } from '../../components/Assertions';
|
||||||
|
import { createComponentBuilder } from '../../components/Components';
|
||||||
|
import type { JSONEncodable } from '../../util/jsonEncodable';
|
||||||
|
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
||||||
|
|
||||||
export class ModalBuilder extends UnsafeModalBuilder {
|
export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCallbackData> {
|
||||||
public override setCustomId(customId: string): this {
|
public readonly data: Partial<APIModalInteractionResponseCallbackData>;
|
||||||
return super.setCustomId(customIdValidator.parse(customId));
|
public readonly components: ActionRowBuilder<ModalActionRowComponentBuilder>[] = [];
|
||||||
|
|
||||||
|
public constructor({ components, ...data }: Partial<APIModalInteractionResponseCallbackData> = {}) {
|
||||||
|
this.data = { ...data };
|
||||||
|
this.components = (components?.map((c) => createComponentBuilder(c)) ??
|
||||||
|
[]) as ActionRowBuilder<ModalActionRowComponentBuilder>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setTitle(title: string) {
|
/**
|
||||||
return super.setTitle(titleValidator.parse(title));
|
* Sets the title of the modal
|
||||||
|
*
|
||||||
|
* @param title - The title of the modal
|
||||||
|
*/
|
||||||
|
public setTitle(title: string) {
|
||||||
|
this.data.title = titleValidator.parse(title);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override toJSON(): APIModalInteractionResponseCallbackData {
|
/**
|
||||||
|
* Sets the custom id of the modal
|
||||||
|
*
|
||||||
|
* @param customId - The custom id of this modal
|
||||||
|
*/
|
||||||
|
public setCustomId(customId: string) {
|
||||||
|
this.data.custom_id = customIdValidator.parse(customId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds components to this modal
|
||||||
|
*
|
||||||
|
* @param components - The components to add to this modal
|
||||||
|
*/
|
||||||
|
public addComponents(
|
||||||
|
...components: RestOrArray<
|
||||||
|
ActionRowBuilder<ModalActionRowComponentBuilder> | APIActionRowComponent<APIModalActionRowComponent>
|
||||||
|
>
|
||||||
|
) {
|
||||||
|
this.components.push(
|
||||||
|
...normalizeArray(components).map((component) =>
|
||||||
|
component instanceof ActionRowBuilder
|
||||||
|
? component
|
||||||
|
: new ActionRowBuilder<ModalActionRowComponentBuilder>(component),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the components in this modal
|
||||||
|
*
|
||||||
|
* @param components - The components to set this modal to
|
||||||
|
*/
|
||||||
|
public setComponents(...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder>>) {
|
||||||
|
this.components.splice(0, this.components.length, ...normalizeArray(components));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON(): APIModalInteractionResponseCallbackData {
|
||||||
validateRequiredParameters(this.data.custom_id, this.data.title, this.components);
|
validateRequiredParameters(this.data.custom_id, this.data.title, this.components);
|
||||||
return super.toJSON();
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
return {
|
||||||
|
...this.data,
|
||||||
|
components: this.components.map((component) => component.toJSON()),
|
||||||
|
} as APIModalInteractionResponseCallbackData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import type {
|
|
||||||
APIActionRowComponent,
|
|
||||||
APIModalActionRowComponent,
|
|
||||||
APIModalInteractionResponseCallbackData,
|
|
||||||
} from 'discord-api-types/v10';
|
|
||||||
import { ActionRowBuilder, createComponentBuilder, JSONEncodable, ModalActionRowComponentBuilder } from '../../index';
|
|
||||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
|
||||||
|
|
||||||
export class UnsafeModalBuilder implements JSONEncodable<APIModalInteractionResponseCallbackData> {
|
|
||||||
public readonly data: Partial<APIModalInteractionResponseCallbackData>;
|
|
||||||
public readonly components: ActionRowBuilder<ModalActionRowComponentBuilder>[] = [];
|
|
||||||
|
|
||||||
public constructor({ components, ...data }: Partial<APIModalInteractionResponseCallbackData> = {}) {
|
|
||||||
this.data = { ...data };
|
|
||||||
this.components = (components?.map((c) => createComponentBuilder(c)) ??
|
|
||||||
[]) as ActionRowBuilder<ModalActionRowComponentBuilder>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the title of the modal
|
|
||||||
*
|
|
||||||
* @param title - The title of the modal
|
|
||||||
*/
|
|
||||||
public setTitle(title: string) {
|
|
||||||
this.data.title = title;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the custom id of the modal
|
|
||||||
*
|
|
||||||
* @param customId - The custom id of this modal
|
|
||||||
*/
|
|
||||||
public setCustomId(customId: string) {
|
|
||||||
this.data.custom_id = customId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds components to this modal
|
|
||||||
*
|
|
||||||
* @param components - The components to add to this modal
|
|
||||||
*/
|
|
||||||
public addComponents(
|
|
||||||
...components: RestOrArray<
|
|
||||||
ActionRowBuilder<ModalActionRowComponentBuilder> | APIActionRowComponent<APIModalActionRowComponent>
|
|
||||||
>
|
|
||||||
) {
|
|
||||||
this.components.push(
|
|
||||||
...normalizeArray(components).map((component) =>
|
|
||||||
component instanceof ActionRowBuilder
|
|
||||||
? component
|
|
||||||
: new ActionRowBuilder<ModalActionRowComponentBuilder>(component),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the components in this modal
|
|
||||||
*
|
|
||||||
* @param components - The components to set this modal to
|
|
||||||
*/
|
|
||||||
public setComponents(...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder>>) {
|
|
||||||
this.components.splice(0, this.components.length, ...normalizeArray(components));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toJSON(): APIModalInteractionResponseCallbackData {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
return {
|
|
||||||
...this.data,
|
|
||||||
components: this.components.map((component) => component.toJSON()),
|
|
||||||
} as APIModalInteractionResponseCallbackData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,24 +3,29 @@ import { type APIApplicationCommandOptionChoice, Locale, LocalizationMap } from
|
|||||||
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
|
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
|
||||||
import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
|
import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
|
||||||
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
|
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
|
||||||
|
import { isValidationEnabled } from '../../util/validation';
|
||||||
|
|
||||||
const namePredicate = s.string
|
const namePredicate = s.string
|
||||||
.lengthGreaterThanOrEqual(1)
|
.lengthGreaterThanOrEqual(1)
|
||||||
.lengthLessThanOrEqual(32)
|
.lengthLessThanOrEqual(32)
|
||||||
.regex(/^[\P{Lu}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u);
|
.regex(/^[\P{Lu}\p{N}\p{sc=Devanagari}\p{sc=Thai}_-]+$/u)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export function validateName(name: unknown): asserts name is string {
|
export function validateName(name: unknown): asserts name is string {
|
||||||
namePredicate.parse(name);
|
namePredicate.parse(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const descriptionPredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100);
|
const descriptionPredicate = s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(100)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
const localePredicate = s.nativeEnum(Locale);
|
const localePredicate = s.nativeEnum(Locale);
|
||||||
|
|
||||||
export function validateDescription(description: unknown): asserts description is string {
|
export function validateDescription(description: unknown): asserts description is string {
|
||||||
descriptionPredicate.parse(description);
|
descriptionPredicate.parse(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxArrayLengthPredicate = s.unknown.array.lengthLessThanOrEqual(25);
|
const maxArrayLengthPredicate = s.unknown.array.lengthLessThanOrEqual(25).setValidationEnabled(isValidationEnabled);
|
||||||
export function validateLocale(locale: unknown) {
|
export function validateLocale(locale: unknown) {
|
||||||
return localePredicate.parse(locale);
|
return localePredicate.parse(locale);
|
||||||
}
|
}
|
||||||
@@ -54,7 +59,7 @@ export function validateRequired(required: unknown): asserts required is boolean
|
|||||||
booleanPredicate.parse(required);
|
booleanPredicate.parse(required);
|
||||||
}
|
}
|
||||||
|
|
||||||
const choicesLengthPredicate = s.number.lessThanOrEqual(25);
|
const choicesLengthPredicate = s.number.lessThanOrEqual(25).setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export function validateChoicesLength(amountAdding: number, choices?: APIApplicationCommandOptionChoice[]): void {
|
export function validateChoicesLength(amountAdding: number, choices?: APIApplicationCommandOptionChoice[]): void {
|
||||||
choicesLengthPredicate.parse((choices?.length ?? 0) + amountAdding);
|
choicesLengthPredicate.parse((choices?.length ?? 0) + amountAdding);
|
||||||
@@ -66,9 +71,9 @@ export function assertReturnOfBuilder<
|
|||||||
s.instance(ExpectedInstanceOf).parse(input);
|
s.instance(ExpectedInstanceOf).parse(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const localizationMapPredicate = s.object<LocalizationMap>(
|
export const localizationMapPredicate = s
|
||||||
Object.fromEntries(Object.values(Locale).map((locale) => [locale, s.string.nullish])),
|
.object<LocalizationMap>(Object.fromEntries(Object.values(Locale).map((locale) => [locale, s.string.nullish])))
|
||||||
).strict.nullish;
|
.strict.nullish.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export function validateLocalizationMap(value: unknown): asserts value is LocalizationMap {
|
export function validateLocalizationMap(value: unknown): asserts value is LocalizationMap {
|
||||||
localizationMapPredicate.parse(value);
|
localizationMapPredicate.parse(value);
|
||||||
|
|||||||
@@ -1,57 +1,84 @@
|
|||||||
import { s } from '@sapphire/shapeshift';
|
import { s } from '@sapphire/shapeshift';
|
||||||
import type { APIEmbedField } from 'discord-api-types/v10';
|
import type { APIEmbedField } from 'discord-api-types/v10';
|
||||||
|
import { isValidationEnabled } from '../../util/validation';
|
||||||
|
|
||||||
export const fieldNamePredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(256);
|
export const fieldNamePredicate = s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(256)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const fieldValuePredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(1024);
|
export const fieldValuePredicate = s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(1024)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const fieldInlinePredicate = s.boolean.optional;
|
export const fieldInlinePredicate = s.boolean.optional;
|
||||||
|
|
||||||
export const embedFieldPredicate = s.object({
|
export const embedFieldPredicate = s
|
||||||
name: fieldNamePredicate,
|
.object({
|
||||||
value: fieldValuePredicate,
|
name: fieldNamePredicate,
|
||||||
inline: fieldInlinePredicate,
|
value: fieldValuePredicate,
|
||||||
});
|
inline: fieldInlinePredicate,
|
||||||
|
})
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const embedFieldsArrayPredicate = embedFieldPredicate.array;
|
export const embedFieldsArrayPredicate = embedFieldPredicate.array.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const fieldLengthPredicate = s.number.lessThanOrEqual(25);
|
export const fieldLengthPredicate = s.number.lessThanOrEqual(25).setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export function validateFieldLength(amountAdding: number, fields?: APIEmbedField[]): void {
|
export function validateFieldLength(amountAdding: number, fields?: APIEmbedField[]): void {
|
||||||
fieldLengthPredicate.parse((fields?.length ?? 0) + amountAdding);
|
fieldLengthPredicate.parse((fields?.length ?? 0) + amountAdding);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const authorNamePredicate = fieldNamePredicate.nullable;
|
export const authorNamePredicate = fieldNamePredicate.nullable.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const imageURLPredicate = s.string.url({
|
export const imageURLPredicate = s.string
|
||||||
allowedProtocols: ['http:', 'https:', 'attachment:'],
|
.url({
|
||||||
}).nullish;
|
allowedProtocols: ['http:', 'https:', 'attachment:'],
|
||||||
|
})
|
||||||
|
.nullish.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const urlPredicate = s.string.url({
|
export const urlPredicate = s.string
|
||||||
allowedProtocols: ['http:', 'https:'],
|
.url({
|
||||||
}).nullish;
|
allowedProtocols: ['http:', 'https:'],
|
||||||
|
})
|
||||||
|
.nullish.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const embedAuthorPredicate = s.object({
|
export const embedAuthorPredicate = s
|
||||||
name: authorNamePredicate,
|
.object({
|
||||||
iconURL: imageURLPredicate,
|
name: authorNamePredicate,
|
||||||
url: urlPredicate,
|
iconURL: imageURLPredicate,
|
||||||
});
|
url: urlPredicate,
|
||||||
|
})
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const RGBPredicate = s.number.int.greaterThanOrEqual(0).lessThanOrEqual(255);
|
export const RGBPredicate = s.number.int
|
||||||
|
.greaterThanOrEqual(0)
|
||||||
|
.lessThanOrEqual(255)
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
export const colorPredicate = s.number.int
|
export const colorPredicate = s.number.int
|
||||||
.greaterThanOrEqual(0)
|
.greaterThanOrEqual(0)
|
||||||
.lessThanOrEqual(0xffffff)
|
.lessThanOrEqual(0xffffff)
|
||||||
.or(s.tuple([RGBPredicate, RGBPredicate, RGBPredicate])).nullable;
|
.or(s.tuple([RGBPredicate, RGBPredicate, RGBPredicate]))
|
||||||
|
.nullable.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const descriptionPredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(4096).nullable;
|
export const descriptionPredicate = s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(4096)
|
||||||
|
.nullable.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const footerTextPredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(2048).nullable;
|
export const footerTextPredicate = s.string
|
||||||
|
.lengthGreaterThanOrEqual(1)
|
||||||
|
.lengthLessThanOrEqual(2048)
|
||||||
|
.nullable.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const embedFooterPredicate = s.object({
|
export const embedFooterPredicate = s
|
||||||
text: footerTextPredicate,
|
.object({
|
||||||
iconURL: imageURLPredicate,
|
text: footerTextPredicate,
|
||||||
});
|
iconURL: imageURLPredicate,
|
||||||
|
})
|
||||||
|
.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const timestampPredicate = s.union(s.number, s.date).nullable;
|
export const timestampPredicate = s.union(s.number, s.date).nullable.setValidationEnabled(isValidationEnabled);
|
||||||
|
|
||||||
export const titlePredicate = fieldNamePredicate.nullable;
|
export const titlePredicate = fieldNamePredicate.nullable.setValidationEnabled(isValidationEnabled);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { APIEmbedField } from 'discord-api-types/v10';
|
import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage } from 'discord-api-types/v10';
|
||||||
import {
|
import {
|
||||||
colorPredicate,
|
colorPredicate,
|
||||||
descriptionPredicate,
|
descriptionPredicate,
|
||||||
@@ -11,84 +11,228 @@ import {
|
|||||||
urlPredicate,
|
urlPredicate,
|
||||||
validateFieldLength,
|
validateFieldLength,
|
||||||
} from './Assertions';
|
} from './Assertions';
|
||||||
import { EmbedAuthorOptions, EmbedFooterOptions, RGBTuple, UnsafeEmbedBuilder } from './UnsafeEmbed';
|
|
||||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
||||||
|
|
||||||
|
export type RGBTuple = [red: number, green: number, blue: number];
|
||||||
|
|
||||||
|
export interface IconData {
|
||||||
|
/**
|
||||||
|
* The URL of the icon
|
||||||
|
*/
|
||||||
|
iconURL?: string;
|
||||||
|
/**
|
||||||
|
* The proxy URL of the icon
|
||||||
|
*/
|
||||||
|
proxyIconURL?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EmbedAuthorData = Omit<APIEmbedAuthor, 'icon_url' | 'proxy_icon_url'> & IconData;
|
||||||
|
|
||||||
|
export type EmbedAuthorOptions = Omit<EmbedAuthorData, 'proxyIconURL'>;
|
||||||
|
|
||||||
|
export type EmbedFooterData = Omit<APIEmbedFooter, 'icon_url' | 'proxy_icon_url'> & IconData;
|
||||||
|
|
||||||
|
export type EmbedFooterOptions = Omit<EmbedFooterData, 'proxyIconURL'>;
|
||||||
|
|
||||||
|
export interface EmbedImageData extends Omit<APIEmbedImage, 'proxy_url'> {
|
||||||
|
/**
|
||||||
|
* The proxy URL for the image
|
||||||
|
*/
|
||||||
|
proxyURL?: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Represents a validated embed in a message (image/video preview, rich embed, etc.)
|
* Represents a embed in a message (image/video preview, rich embed, etc.)
|
||||||
*/
|
*/
|
||||||
export class EmbedBuilder extends UnsafeEmbedBuilder {
|
export class EmbedBuilder {
|
||||||
public override addFields(...fields: RestOrArray<APIEmbedField>): this {
|
public readonly data: APIEmbed;
|
||||||
|
|
||||||
|
public constructor(data: APIEmbed = {}) {
|
||||||
|
this.data = { ...data };
|
||||||
|
if (data.timestamp) this.data.timestamp = new Date(data.timestamp).toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds fields to the embed (max 25)
|
||||||
|
*
|
||||||
|
* @param fields The fields to add
|
||||||
|
*/
|
||||||
|
public addFields(...fields: RestOrArray<APIEmbedField>): this {
|
||||||
fields = normalizeArray(fields);
|
fields = normalizeArray(fields);
|
||||||
// Ensure adding these fields won't exceed the 25 field limit
|
// Ensure adding these fields won't exceed the 25 field limit
|
||||||
validateFieldLength(fields.length, this.data.fields);
|
validateFieldLength(fields.length, this.data.fields);
|
||||||
|
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.addFields(...embedFieldsArrayPredicate.parse(fields));
|
embedFieldsArrayPredicate.parse(fields);
|
||||||
|
|
||||||
|
if (this.data.fields) this.data.fields.push(...fields);
|
||||||
|
else this.data.fields = fields;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override spliceFields(index: number, deleteCount: number, ...fields: APIEmbedField[]): this {
|
/**
|
||||||
|
* Removes, replaces, or inserts fields in the embed (max 25)
|
||||||
|
*
|
||||||
|
* @param index The index to start at
|
||||||
|
* @param deleteCount The number of fields to remove
|
||||||
|
* @param fields The replacing field objects
|
||||||
|
*/
|
||||||
|
public spliceFields(index: number, deleteCount: number, ...fields: APIEmbedField[]): this {
|
||||||
// Ensure adding these fields won't exceed the 25 field limit
|
// Ensure adding these fields won't exceed the 25 field limit
|
||||||
validateFieldLength(fields.length - deleteCount, this.data.fields);
|
validateFieldLength(fields.length - deleteCount, this.data.fields);
|
||||||
|
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.spliceFields(index, deleteCount, ...embedFieldsArrayPredicate.parse(fields));
|
embedFieldsArrayPredicate.parse(fields);
|
||||||
|
if (this.data.fields) this.data.fields.splice(index, deleteCount, ...fields);
|
||||||
|
else this.data.fields = fields;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setAuthor(options: EmbedAuthorOptions | null): this {
|
/**
|
||||||
|
* Sets the embed's fields (max 25).
|
||||||
|
* @param fields The fields to set
|
||||||
|
*/
|
||||||
|
public setFields(...fields: RestOrArray<APIEmbedField>) {
|
||||||
|
this.spliceFields(0, this.data.fields?.length ?? 0, ...normalizeArray(fields));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the author of this embed
|
||||||
|
*
|
||||||
|
* @param options The options for the author
|
||||||
|
*/
|
||||||
|
|
||||||
|
public setAuthor(options: EmbedAuthorOptions | null): this {
|
||||||
if (options === null) {
|
if (options === null) {
|
||||||
return super.setAuthor(null);
|
this.data.author = undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data assertions
|
// Data assertions
|
||||||
embedAuthorPredicate.parse(options);
|
embedAuthorPredicate.parse(options);
|
||||||
|
|
||||||
return super.setAuthor(options);
|
this.data.author = { name: options.name, url: options.url, icon_url: options.iconURL };
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setColor(color: number | RGBTuple | null): this {
|
/**
|
||||||
|
* Sets the color of this embed
|
||||||
|
*
|
||||||
|
* @param color The color of the embed
|
||||||
|
*/
|
||||||
|
public setColor(color: number | RGBTuple | null): this {
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.setColor(colorPredicate.parse(color));
|
colorPredicate.parse(color);
|
||||||
|
|
||||||
|
if (Array.isArray(color)) {
|
||||||
|
const [red, green, blue] = color;
|
||||||
|
this.data.color = (red << 16) + (green << 8) + blue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.data.color = color ?? undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setDescription(description: string | null): this {
|
/**
|
||||||
|
* Sets the description of this embed
|
||||||
|
*
|
||||||
|
* @param description The description
|
||||||
|
*/
|
||||||
|
public setDescription(description: string | null): this {
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.setDescription(descriptionPredicate.parse(description));
|
descriptionPredicate.parse(description);
|
||||||
|
|
||||||
|
this.data.description = description ?? undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setFooter(options: EmbedFooterOptions | null): this {
|
/**
|
||||||
|
* Sets the footer of this embed
|
||||||
|
*
|
||||||
|
* @param options The options for the footer
|
||||||
|
*/
|
||||||
|
public setFooter(options: EmbedFooterOptions | null): this {
|
||||||
if (options === null) {
|
if (options === null) {
|
||||||
return super.setFooter(null);
|
this.data.footer = undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data assertions
|
// Data assertions
|
||||||
embedFooterPredicate.parse(options);
|
embedFooterPredicate.parse(options);
|
||||||
|
|
||||||
return super.setFooter(options);
|
this.data.footer = { text: options.text, icon_url: options.iconURL };
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setImage(url: string | null): this {
|
/**
|
||||||
|
* Sets the image of this embed
|
||||||
|
*
|
||||||
|
* @param url The URL of the image
|
||||||
|
*/
|
||||||
|
public setImage(url: string | null): this {
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.setImage(imageURLPredicate.parse(url));
|
imageURLPredicate.parse(url);
|
||||||
|
|
||||||
|
this.data.image = url ? { url } : undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setThumbnail(url: string | null): this {
|
/**
|
||||||
|
* Sets the thumbnail of this embed
|
||||||
|
*
|
||||||
|
* @param url The URL of the thumbnail
|
||||||
|
*/
|
||||||
|
public setThumbnail(url: string | null): this {
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.setThumbnail(imageURLPredicate.parse(url));
|
imageURLPredicate.parse(url);
|
||||||
|
|
||||||
|
this.data.thumbnail = url ? { url } : undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setTimestamp(timestamp: number | Date | null = Date.now()): this {
|
/**
|
||||||
|
* Sets the timestamp of this embed
|
||||||
|
*
|
||||||
|
* @param timestamp The timestamp or date
|
||||||
|
*/
|
||||||
|
public setTimestamp(timestamp: number | Date | null = Date.now()): this {
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.setTimestamp(timestampPredicate.parse(timestamp));
|
timestampPredicate.parse(timestamp);
|
||||||
|
|
||||||
|
this.data.timestamp = timestamp ? new Date(timestamp).toISOString() : undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setTitle(title: string | null): this {
|
/**
|
||||||
|
* Sets the title of this embed
|
||||||
|
*
|
||||||
|
* @param title The title
|
||||||
|
*/
|
||||||
|
public setTitle(title: string | null): this {
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.setTitle(titlePredicate.parse(title));
|
titlePredicate.parse(title);
|
||||||
|
|
||||||
|
this.data.title = title ?? undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setURL(url: string | null): this {
|
/**
|
||||||
|
* Sets the URL of this embed
|
||||||
|
*
|
||||||
|
* @param url The URL
|
||||||
|
*/
|
||||||
|
public setURL(url: string | null): this {
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.setURL(urlPredicate.parse(url));
|
urlPredicate.parse(url);
|
||||||
|
|
||||||
|
this.data.url = url ?? undefined;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the embed to a plain object
|
||||||
|
*/
|
||||||
|
public toJSON(): APIEmbed {
|
||||||
|
return { ...this.data };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,189 +0,0 @@
|
|||||||
import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage } from 'discord-api-types/v10';
|
|
||||||
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
|
||||||
|
|
||||||
export type RGBTuple = [red: number, green: number, blue: number];
|
|
||||||
|
|
||||||
export interface IconData {
|
|
||||||
/**
|
|
||||||
* The URL of the icon
|
|
||||||
*/
|
|
||||||
iconURL?: string;
|
|
||||||
/**
|
|
||||||
* The proxy URL of the icon
|
|
||||||
*/
|
|
||||||
proxyIconURL?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EmbedAuthorData = Omit<APIEmbedAuthor, 'icon_url' | 'proxy_icon_url'> & IconData;
|
|
||||||
|
|
||||||
export type EmbedAuthorOptions = Omit<EmbedAuthorData, 'proxyIconURL'>;
|
|
||||||
|
|
||||||
export type EmbedFooterData = Omit<APIEmbedFooter, 'icon_url' | 'proxy_icon_url'> & IconData;
|
|
||||||
|
|
||||||
export type EmbedFooterOptions = Omit<EmbedFooterData, 'proxyIconURL'>;
|
|
||||||
|
|
||||||
export interface EmbedImageData extends Omit<APIEmbedImage, 'proxy_url'> {
|
|
||||||
/**
|
|
||||||
* The proxy URL for the image
|
|
||||||
*/
|
|
||||||
proxyURL?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a non-validated embed in a message (image/video preview, rich embed, etc.)
|
|
||||||
*/
|
|
||||||
export class UnsafeEmbedBuilder {
|
|
||||||
public readonly data: APIEmbed;
|
|
||||||
|
|
||||||
public constructor(data: APIEmbed = {}) {
|
|
||||||
this.data = { ...data };
|
|
||||||
if (data.timestamp) this.data.timestamp = new Date(data.timestamp).toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds fields to the embed (max 25)
|
|
||||||
*
|
|
||||||
* @param fields - The fields to add
|
|
||||||
*/
|
|
||||||
public addFields(...fields: RestOrArray<APIEmbedField>): this {
|
|
||||||
fields = normalizeArray(fields);
|
|
||||||
if (this.data.fields) this.data.fields.push(...fields);
|
|
||||||
else this.data.fields = fields;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes, replaces, or inserts fields in the embed (max 25)
|
|
||||||
*
|
|
||||||
* @param index - The index to start at
|
|
||||||
* @param deleteCount - The number of fields to remove
|
|
||||||
* @param fields - The replacing field objects
|
|
||||||
*/
|
|
||||||
public spliceFields(index: number, deleteCount: number, ...fields: APIEmbedField[]): this {
|
|
||||||
if (this.data.fields) this.data.fields.splice(index, deleteCount, ...fields);
|
|
||||||
else this.data.fields = fields;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the embed's fields (max 25).
|
|
||||||
*
|
|
||||||
* @param fields - The fields to set
|
|
||||||
*/
|
|
||||||
public setFields(...fields: RestOrArray<APIEmbedField>) {
|
|
||||||
this.spliceFields(0, this.data.fields?.length ?? 0, ...normalizeArray(fields));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the author of this embed
|
|
||||||
*
|
|
||||||
* @param options - The options for the author
|
|
||||||
*/
|
|
||||||
public setAuthor(options: EmbedAuthorOptions | null): this {
|
|
||||||
if (options === null) {
|
|
||||||
this.data.author = undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data.author = { name: options.name, url: options.url, icon_url: options.iconURL };
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the color of this embed
|
|
||||||
*
|
|
||||||
* @param color - The color of the embed
|
|
||||||
*/
|
|
||||||
public setColor(color: number | RGBTuple | null): this {
|
|
||||||
if (Array.isArray(color)) {
|
|
||||||
const [red, green, blue] = color;
|
|
||||||
this.data.color = (red << 16) + (green << 8) + blue;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
this.data.color = color ?? undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the description of this embed
|
|
||||||
*
|
|
||||||
* @param description - The description
|
|
||||||
*/
|
|
||||||
public setDescription(description: string | null): this {
|
|
||||||
this.data.description = description ?? undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the footer of this embed
|
|
||||||
*
|
|
||||||
* @param options - The options for the footer
|
|
||||||
*/
|
|
||||||
public setFooter(options: EmbedFooterOptions | null): this {
|
|
||||||
if (options === null) {
|
|
||||||
this.data.footer = undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data.footer = { text: options.text, icon_url: options.iconURL };
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the image of this embed
|
|
||||||
*
|
|
||||||
* @param url - The URL of the image
|
|
||||||
*/
|
|
||||||
public setImage(url: string | null): this {
|
|
||||||
this.data.image = url ? { url } : undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the thumbnail of this embed
|
|
||||||
*
|
|
||||||
* @param url - The URL of the thumbnail
|
|
||||||
*/
|
|
||||||
public setThumbnail(url: string | null): this {
|
|
||||||
this.data.thumbnail = url ? { url } : undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timestamp of this embed
|
|
||||||
*
|
|
||||||
* @param timestamp - The timestamp or date
|
|
||||||
*/
|
|
||||||
public setTimestamp(timestamp: number | Date | null = Date.now()): this {
|
|
||||||
this.data.timestamp = timestamp ? new Date(timestamp).toISOString() : undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the title of this embed
|
|
||||||
*
|
|
||||||
* @param title - The title
|
|
||||||
*/
|
|
||||||
public setTitle(title: string | null): this {
|
|
||||||
this.data.title = title ?? undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the URL of this embed
|
|
||||||
*
|
|
||||||
* @param url - The URL
|
|
||||||
*/
|
|
||||||
public setURL(url: string | null): this {
|
|
||||||
this.data.url = url ?? undefined;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms the embed to a plain object
|
|
||||||
*/
|
|
||||||
public toJSON(): APIEmbed {
|
|
||||||
return { ...this.data };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
packages/builders/src/util/validation.ts
Normal file
5
packages/builders/src/util/validation.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
let validate = true;
|
||||||
|
|
||||||
|
export const enableValidators = () => (validate = true);
|
||||||
|
export const disableValidators = () => (validate = false);
|
||||||
|
export const isValidationEnabled = () => validate;
|
||||||
2
packages/discord.js/typings/index.d.ts
vendored
2
packages/discord.js/typings/index.d.ts
vendored
@@ -17,7 +17,7 @@ import {
|
|||||||
roleMention,
|
roleMention,
|
||||||
SelectMenuBuilder as BuilderSelectMenuComponent,
|
SelectMenuBuilder as BuilderSelectMenuComponent,
|
||||||
TextInputBuilder as BuilderTextInputComponent,
|
TextInputBuilder as BuilderTextInputComponent,
|
||||||
UnsafeSelectMenuOptionBuilder as BuildersSelectMenuOption,
|
SelectMenuOptionBuilder as BuildersSelectMenuOption,
|
||||||
spoiler,
|
spoiler,
|
||||||
strikethrough,
|
strikethrough,
|
||||||
time,
|
time,
|
||||||
|
|||||||
@@ -132,13 +132,7 @@ import {
|
|||||||
ShardEvents,
|
ShardEvents,
|
||||||
} from '.';
|
} from '.';
|
||||||
import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';
|
import { expectAssignable, expectNotAssignable, expectNotType, expectType } from 'tsd';
|
||||||
import {
|
import type { ContextMenuCommandBuilder, SlashCommandBuilder } from '@discordjs/builders';
|
||||||
ContextMenuCommandBuilder,
|
|
||||||
SlashCommandBuilder,
|
|
||||||
UnsafeButtonBuilder,
|
|
||||||
UnsafeEmbedBuilder,
|
|
||||||
UnsafeSelectMenuBuilder,
|
|
||||||
} from '@discordjs/builders';
|
|
||||||
|
|
||||||
// Test type transformation:
|
// Test type transformation:
|
||||||
declare const serialize: <T>(value: T) => Serialized<T>;
|
declare const serialize: <T>(value: T) => Serialized<T>;
|
||||||
@@ -879,7 +873,6 @@ client.on('messageCreate', async message => {
|
|||||||
type: ComponentType.ActionRow,
|
type: ComponentType.ActionRow,
|
||||||
components: [
|
components: [
|
||||||
new ButtonBuilder(),
|
new ButtonBuilder(),
|
||||||
new UnsafeButtonBuilder(),
|
|
||||||
{ type: ComponentType.Button, label: 'test', style: ButtonStyle.Primary, customId: 'test' },
|
{ type: ComponentType.Button, label: 'test', style: ButtonStyle.Primary, customId: 'test' },
|
||||||
{
|
{
|
||||||
type: ComponentType.Button,
|
type: ComponentType.Button,
|
||||||
@@ -893,7 +886,6 @@ client.on('messageCreate', async message => {
|
|||||||
type: ComponentType.ActionRow,
|
type: ComponentType.ActionRow,
|
||||||
components: [
|
components: [
|
||||||
new SelectMenuBuilder(),
|
new SelectMenuBuilder(),
|
||||||
new UnsafeSelectMenuBuilder(),
|
|
||||||
{
|
{
|
||||||
type: ComponentType.SelectMenu,
|
type: ComponentType.SelectMenu,
|
||||||
label: 'select menu',
|
label: 'select menu',
|
||||||
@@ -903,9 +895,8 @@ client.on('messageCreate', async message => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildersEmbed = new UnsafeEmbedBuilder();
|
|
||||||
const embedData = { description: 'test', color: 0xff0000 };
|
const embedData = { description: 'test', color: 0xff0000 };
|
||||||
channel.send({ components: [row, buttonsRow, selectsRow], embeds: [embed, buildersEmbed, embedData] });
|
channel.send({ components: [row, buttonsRow, selectsRow], embeds: [embed, embedData] });
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on('threadCreate', thread => {
|
client.on('threadCreate', thread => {
|
||||||
|
|||||||
Reference in New Issue
Block a user