mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-19 13:03:31 +01:00
feat: Add Modals and Text Inputs (#7023)
Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com> Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: Ryan Munro <monbrey@gmail.com> Co-authored-by: Vitor <milagre.vitor@gmail.com>
This commit is contained in:
@@ -1,4 +1,10 @@
|
|||||||
import { APIActionRowComponent, APIMessageComponent, ButtonStyle, ComponentType } from 'discord-api-types/v9';
|
import {
|
||||||
|
APIActionRowComponent,
|
||||||
|
APIActionRowComponentTypes,
|
||||||
|
APIMessageActionRowComponent,
|
||||||
|
ButtonStyle,
|
||||||
|
ComponentType,
|
||||||
|
} from 'discord-api-types/v9';
|
||||||
import { ActionRow, ButtonComponent, createComponent, SelectMenuComponent, SelectMenuOption } from '../../src';
|
import { ActionRow, ButtonComponent, createComponent, SelectMenuComponent, SelectMenuOption } from '../../src';
|
||||||
|
|
||||||
const rowWithButtonData: APIActionRowComponent<APIMessageComponent> = {
|
const rowWithButtonData: APIActionRowComponent<APIMessageComponent> = {
|
||||||
@@ -43,7 +49,7 @@ describe('Action Row Components', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||||
const actionRowData: APIActionRowComponent<APIMessageComponent> = {
|
const actionRowData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||||
type: ComponentType.ActionRow,
|
type: ComponentType.ActionRow,
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
@@ -75,10 +81,43 @@ describe('Action Row Components', () => {
|
|||||||
expect(new ActionRow(actionRowData).toJSON()).toEqual(actionRowData);
|
expect(new ActionRow(actionRowData).toJSON()).toEqual(actionRowData);
|
||||||
expect(new ActionRow().toJSON()).toEqual({ type: ComponentType.ActionRow, components: [] });
|
expect(new ActionRow().toJSON()).toEqual({ type: ComponentType.ActionRow, components: [] });
|
||||||
expect(() => createComponent({ type: ComponentType.ActionRow, components: [] })).not.toThrowError();
|
expect(() => createComponent({ type: ComponentType.ActionRow, components: [] })).not.toThrowError();
|
||||||
// @ts-expect-error
|
|
||||||
expect(() => createComponent({ type: 42, components: [] })).toThrowError();
|
expect(() => createComponent({ type: 42, components: [] })).toThrowError();
|
||||||
});
|
});
|
||||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||||
|
const rowWithButtonData: APIActionRowComponent<APIActionRowComponentTypes> = {
|
||||||
|
type: ComponentType.ActionRow,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: ComponentType.Button,
|
||||||
|
label: 'test',
|
||||||
|
custom_id: '123',
|
||||||
|
style: ButtonStyle.Primary,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const rowWithSelectMenuData: APIActionRowComponent<APIActionRowComponentTypes> = {
|
||||||
|
type: ComponentType.ActionRow,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: ComponentType.SelectMenu,
|
||||||
|
custom_id: '1234',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'one',
|
||||||
|
value: 'one',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'two',
|
||||||
|
value: 'two',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
max_values: 10,
|
||||||
|
min_values: 12,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
const button = new ButtonComponent().setLabel('test').setStyle(ButtonStyle.Primary).setCustomId('123');
|
const button = new ButtonComponent().setLabel('test').setStyle(ButtonStyle.Primary).setCustomId('123');
|
||||||
const selectMenu = new SelectMenuComponent()
|
const selectMenu = new SelectMenuComponent()
|
||||||
.setCustomId('1234')
|
.setCustomId('1234')
|
||||||
|
|||||||
126
packages/builders/__tests__/components/textInput.test.ts
Normal file
126
packages/builders/__tests__/components/textInput.test.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { APITextInputComponent, ComponentType, TextInputStyle } from 'discord-api-types/v9';
|
||||||
|
import {
|
||||||
|
labelValidator,
|
||||||
|
maxLengthValidator,
|
||||||
|
minLengthValidator,
|
||||||
|
placeholderValidator,
|
||||||
|
valueValidator,
|
||||||
|
textInputStyleValidator,
|
||||||
|
} from '../../src/components/textInput/Assertions';
|
||||||
|
import { TextInputComponent } from '../../src/components/textInput/TextInput';
|
||||||
|
|
||||||
|
const superLongStr = 'a'.repeat(5000);
|
||||||
|
|
||||||
|
const textInputComponent = () => new TextInputComponent();
|
||||||
|
|
||||||
|
describe('Text Input Components', () => {
|
||||||
|
describe('Assertion Tests', () => {
|
||||||
|
test('GIVEN valid label THEN validator does not throw', () => {
|
||||||
|
expect(() => labelValidator.parse('foobar')).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid label THEN validator does throw', () => {
|
||||||
|
expect(() => labelValidator.parse(24)).toThrowError();
|
||||||
|
expect(() => labelValidator.parse(undefined)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid style THEN validator does not throw', () => {
|
||||||
|
expect(() => textInputStyleValidator.parse(TextInputStyle.Paragraph)).not.toThrowError();
|
||||||
|
expect(() => textInputStyleValidator.parse(TextInputStyle.Short)).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid style THEN validator does throw', () => {
|
||||||
|
expect(() => textInputStyleValidator.parse(24)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid min length THEN validator does not throw', () => {
|
||||||
|
expect(() => minLengthValidator.parse(10)).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid min length THEN validator does throw', () => {
|
||||||
|
expect(() => minLengthValidator.parse(-1)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid max length THEN validator does not throw', () => {
|
||||||
|
expect(() => maxLengthValidator.parse(10)).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid min length THEN validator does throw', () => {
|
||||||
|
expect(() => maxLengthValidator.parse(4001)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid value THEN validator does not throw', () => {
|
||||||
|
expect(() => valueValidator.parse('foobar')).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid value THEN validator does throw', () => {
|
||||||
|
expect(() => valueValidator.parse(superLongStr)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid placeholder THEN validator does not throw', () => {
|
||||||
|
expect(() => placeholderValidator.parse('foobar')).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid value THEN validator does throw', () => {
|
||||||
|
expect(() => placeholderValidator.parse(superLongStr)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid fields THEN builder does not throw', () => {
|
||||||
|
expect(() => {
|
||||||
|
textInputComponent().setCustomId('foobar').setLabel('test').setStyle(TextInputStyle.Paragraph).toJSON();
|
||||||
|
}).not.toThrowError();
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
textInputComponent()
|
||||||
|
.setCustomId('foobar')
|
||||||
|
.setLabel('test')
|
||||||
|
.setMaxLength(100)
|
||||||
|
.setMinLength(1)
|
||||||
|
.setPlaceholder('bar')
|
||||||
|
.setRequired(true)
|
||||||
|
.setStyle(TextInputStyle.Paragraph)
|
||||||
|
.toJSON();
|
||||||
|
}).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid fields THEN builder throws', () => {
|
||||||
|
expect(() => textInputComponent().toJSON()).toThrowError();
|
||||||
|
expect(() => {
|
||||||
|
textInputComponent()
|
||||||
|
.setCustomId('test')
|
||||||
|
.setMaxLength(100)
|
||||||
|
.setPlaceholder('hello')
|
||||||
|
.setStyle(TextInputStyle.Paragraph)
|
||||||
|
.toJSON();
|
||||||
|
}).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid input THEN valid JSON outputs are given', () => {
|
||||||
|
const textInputData: APITextInputComponent = {
|
||||||
|
type: ComponentType.TextInput,
|
||||||
|
label: 'label',
|
||||||
|
custom_id: 'custom id',
|
||||||
|
placeholder: 'placeholder',
|
||||||
|
max_length: 100,
|
||||||
|
min_length: 10,
|
||||||
|
value: 'value',
|
||||||
|
required: false,
|
||||||
|
style: TextInputStyle.Paragraph,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(new TextInputComponent(textInputData).toJSON()).toEqual(textInputData);
|
||||||
|
expect(
|
||||||
|
textInputComponent()
|
||||||
|
.setCustomId(textInputData.custom_id)
|
||||||
|
.setLabel(textInputData.label)
|
||||||
|
.setPlaceholder(textInputData.placeholder)
|
||||||
|
.setMaxLength(textInputData.max_length)
|
||||||
|
.setMinLength(textInputData.min_length)
|
||||||
|
.setValue(textInputData.value)
|
||||||
|
.setRequired(textInputData.required)
|
||||||
|
.setStyle(textInputData.style)
|
||||||
|
.toJSON(),
|
||||||
|
).toEqual(textInputData);
|
||||||
|
});
|
||||||
|
});
|
||||||
88
packages/builders/__tests__/interactions/modal.test.ts
Normal file
88
packages/builders/__tests__/interactions/modal.test.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { APIModalInteractionResponseCallbackData, ComponentType, TextInputStyle } from 'discord-api-types/v9';
|
||||||
|
import { ActionRow, ButtonComponent, Modal, ModalActionRowComponent, TextInputComponent } from '../../src';
|
||||||
|
import {
|
||||||
|
componentsValidator,
|
||||||
|
titleValidator,
|
||||||
|
validateRequiredParameters,
|
||||||
|
} from '../../src/interactions/modals/Assertions';
|
||||||
|
|
||||||
|
const modal = () => new Modal();
|
||||||
|
|
||||||
|
describe('Modals', () => {
|
||||||
|
describe('Assertion Tests', () => {
|
||||||
|
test('GIVEN valid title THEN validator does not throw', () => {
|
||||||
|
expect(() => titleValidator.parse('foobar')).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid title THEN validator does throw', () => {
|
||||||
|
expect(() => titleValidator.parse(42)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid components THEN validator does not throw', () => {
|
||||||
|
expect(() => componentsValidator.parse([new ActionRow(), new ActionRow()])).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid components THEN validator does throw', () => {
|
||||||
|
expect(() => componentsValidator.parse([new ButtonComponent(), new TextInputComponent()])).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid required parameters THEN validator does not throw', () => {
|
||||||
|
expect(() => validateRequiredParameters('123', 'title', [new ActionRow(), new ActionRow()])).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid required parameters THEN validator does throw', () => {
|
||||||
|
expect(() =>
|
||||||
|
// @ts-expect-error
|
||||||
|
validateRequiredParameters('123', undefined, [new ActionRow(), new ButtonComponent()]),
|
||||||
|
).toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid fields THEN builder does not throw', () => {
|
||||||
|
expect(() => modal().setTitle('test').setCustomId('foobar').setComponents(new ActionRow())).not.toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN invalid fields THEN builder does throw', () => {
|
||||||
|
expect(() =>
|
||||||
|
// @ts-expect-error
|
||||||
|
modal().setTitle('test').setCustomId('foobar').setComponents([new ActionRow()]).toJSON(),
|
||||||
|
).toThrowError();
|
||||||
|
expect(() => modal().setTitle('test').setCustomId('foobar').toJSON()).toThrowError();
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(() => modal().setTitle('test').setCustomId(42).toJSON()).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GIVEN valid input THEN valid JSON outputs are given', () => {
|
||||||
|
const modalData: APIModalInteractionResponseCallbackData = {
|
||||||
|
title: 'title',
|
||||||
|
custom_id: 'custom id',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: ComponentType.ActionRow,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: ComponentType.TextInput,
|
||||||
|
label: 'label',
|
||||||
|
style: TextInputStyle.Paragraph,
|
||||||
|
custom_id: 'custom id',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(new Modal(modalData).toJSON()).toEqual(modalData);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
modal()
|
||||||
|
.setTitle(modalData.title)
|
||||||
|
.setCustomId('custom id')
|
||||||
|
.setComponents(
|
||||||
|
new ActionRow<ModalActionRowComponent>().addComponents(
|
||||||
|
new TextInputComponent().setCustomId('custom id').setLabel('label').setStyle(TextInputStyle.Paragraph),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toJSON(),
|
||||||
|
).toEqual(modalData);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"homepage": "https://discord.js.org",
|
"homepage": "https://discord.js.org",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sindresorhus/is": "^4.4.0",
|
"@sindresorhus/is": "^4.4.0",
|
||||||
"discord-api-types": "^0.27.0",
|
"discord-api-types": "^0.27.3",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"ts-mixer": "^6.0.0",
|
"ts-mixer": "^6.0.0",
|
||||||
"tslib": "^2.3.1",
|
"tslib": "^2.3.1",
|
||||||
|
|||||||
@@ -1,26 +1,42 @@
|
|||||||
import { type APIActionRowComponent, ComponentType, APIMessageComponent } from 'discord-api-types/v9';
|
import {
|
||||||
import type { ButtonComponent, SelectMenuComponent } from '..';
|
APIActionRowComponent,
|
||||||
|
APIMessageActionRowComponent,
|
||||||
|
APIModalActionRowComponent,
|
||||||
|
ComponentType,
|
||||||
|
} from 'discord-api-types/v9';
|
||||||
|
import type { ButtonComponent, SelectMenuComponent, TextInputComponent } from '../index';
|
||||||
import { Component } from './Component';
|
import { Component } from './Component';
|
||||||
import { createComponent } from './Components';
|
import { createComponent } from './Components';
|
||||||
import isEqual from 'fast-deep-equal';
|
import isEqual from 'fast-deep-equal';
|
||||||
|
|
||||||
export type MessageComponent = ActionRowComponent | ActionRow;
|
export type MessageComponent = MessageActionRowComponent | ActionRow<MessageActionRowComponent>;
|
||||||
|
export type ModalComponent = ModalActionRowComponent | ActionRow<ModalActionRowComponent>;
|
||||||
|
|
||||||
export type ActionRowComponent = ButtonComponent | SelectMenuComponent;
|
export type MessageActionRowComponent = ButtonComponent | SelectMenuComponent;
|
||||||
|
export type ModalActionRowComponent = TextInputComponent;
|
||||||
|
|
||||||
// TODO: Add valid form component types
|
|
||||||
/**
|
/**
|
||||||
* Represents an action row component
|
* Represents an action row component
|
||||||
*/
|
*/
|
||||||
export class ActionRow<T extends ActionRowComponent = ActionRowComponent> extends Component<
|
export class ActionRow<
|
||||||
Omit<Partial<APIActionRowComponent<APIMessageComponent>> & { type: ComponentType.ActionRow }, 'components'>
|
T extends ModalActionRowComponent | MessageActionRowComponent = ModalActionRowComponent | MessageActionRowComponent,
|
||||||
|
> extends Component<
|
||||||
|
Omit<
|
||||||
|
Partial<APIActionRowComponent<APIMessageActionRowComponent | APIModalActionRowComponent>> & {
|
||||||
|
type: ComponentType.ActionRow;
|
||||||
|
},
|
||||||
|
'components'
|
||||||
|
>
|
||||||
> {
|
> {
|
||||||
/**
|
/**
|
||||||
* The components within this action row
|
* The components within this action row
|
||||||
*/
|
*/
|
||||||
public readonly components: T[];
|
public readonly components: T[];
|
||||||
|
|
||||||
public constructor({ components, ...data }: Partial<APIActionRowComponent<APIMessageComponent>> = {}) {
|
public constructor({
|
||||||
|
components,
|
||||||
|
...data
|
||||||
|
}: Partial<APIActionRowComponent<APIMessageActionRowComponent | APIModalActionRowComponent>> = {}) {
|
||||||
super({ type: ComponentType.ActionRow, ...data });
|
super({ type: ComponentType.ActionRow, ...data });
|
||||||
this.components = (components?.map((c) => createComponent(c)) ?? []) as T[];
|
this.components = (components?.map((c) => createComponent(c)) ?? []) as T[];
|
||||||
}
|
}
|
||||||
@@ -44,14 +60,14 @@ export class ActionRow<T extends ActionRowComponent = ActionRowComponent> extend
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public toJSON(): APIActionRowComponent<APIMessageComponent> {
|
public toJSON(): APIActionRowComponent<ReturnType<T['toJSON']>> {
|
||||||
return {
|
return {
|
||||||
...this.data,
|
...this.data,
|
||||||
components: this.components.map((component) => component.toJSON()),
|
components: this.components.map((component) => component.toJSON()) as ReturnType<T['toJSON']>[],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public equals(other: APIActionRowComponent<APIMessageComponent> | ActionRow) {
|
public equals(other: APIActionRowComponent<APIMessageActionRowComponent | APIModalActionRowComponent> | ActionRow) {
|
||||||
if (other instanceof ActionRow) {
|
if (other instanceof ActionRow) {
|
||||||
return isEqual(other.data, this.data) && isEqual(other.components, this.components);
|
return isEqual(other.data, this.data) && isEqual(other.components, this.components);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import type { JSONEncodable } from '../util/jsonEncodable';
|
import type { JSONEncodable } from '../util/jsonEncodable';
|
||||||
import type {
|
import type {
|
||||||
|
APIActionRowComponent,
|
||||||
APIActionRowComponentTypes,
|
APIActionRowComponentTypes,
|
||||||
APIBaseComponent,
|
APIBaseComponent,
|
||||||
|
APIMessageActionRowComponent,
|
||||||
|
APIModalActionRowComponent,
|
||||||
APIMessageComponent,
|
APIMessageComponent,
|
||||||
ComponentType,
|
ComponentType,
|
||||||
|
APIModalComponent,
|
||||||
} from 'discord-api-types/v9';
|
} from 'discord-api-types/v9';
|
||||||
import type { Equatable } from '../util/equatable';
|
import type { Equatable } from '../util/equatable';
|
||||||
|
|
||||||
@@ -14,16 +18,33 @@ export abstract class Component<
|
|||||||
DataType extends Partial<APIBaseComponent<ComponentType>> & {
|
DataType extends Partial<APIBaseComponent<ComponentType>> & {
|
||||||
type: ComponentType;
|
type: ComponentType;
|
||||||
} = APIBaseComponent<ComponentType>,
|
} = APIBaseComponent<ComponentType>,
|
||||||
> implements JSONEncodable<APIMessageComponent>, Equatable<Component | APIActionRowComponentTypes>
|
> implements
|
||||||
|
JSONEncodable<
|
||||||
|
| APIModalComponent
|
||||||
|
| APIMessageComponent
|
||||||
|
| APIActionRowComponent<APIModalActionRowComponent | APIMessageActionRowComponent>
|
||||||
|
>,
|
||||||
|
Equatable<
|
||||||
|
| Component
|
||||||
|
| APIActionRowComponentTypes
|
||||||
|
| APIActionRowComponent<APIModalActionRowComponent | APIMessageActionRowComponent>
|
||||||
|
>
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The API data associated with this component
|
* The API data associated with this component
|
||||||
*/
|
*/
|
||||||
public readonly data: DataType;
|
public readonly data: DataType;
|
||||||
|
|
||||||
public abstract toJSON(): APIMessageComponent;
|
public abstract toJSON():
|
||||||
|
| APIActionRowComponentTypes
|
||||||
|
| APIActionRowComponent<APIModalActionRowComponent | APIMessageActionRowComponent>;
|
||||||
|
|
||||||
public abstract equals(other: Component | APIActionRowComponentTypes): boolean;
|
public abstract equals(
|
||||||
|
other:
|
||||||
|
| Component
|
||||||
|
| APIActionRowComponentTypes
|
||||||
|
| APIActionRowComponent<APIModalActionRowComponent | APIMessageActionRowComponent>,
|
||||||
|
): boolean;
|
||||||
|
|
||||||
public constructor(data: DataType) {
|
public constructor(data: DataType) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { APIMessageComponent, ComponentType } from 'discord-api-types/v9';
|
import { APIBaseComponent, APIMessageComponent, APIModalComponent, ComponentType } from 'discord-api-types/v9';
|
||||||
import { ActionRow, ButtonComponent, Component, SelectMenuComponent } from '../index';
|
import { ActionRow, ButtonComponent, Component, SelectMenuComponent, TextInputComponent } from '../index';
|
||||||
import type { MessageComponent } from './ActionRow';
|
import type { MessageComponent, ModalActionRowComponent } from './ActionRow';
|
||||||
|
|
||||||
export interface MappedComponentTypes {
|
export interface MappedComponentTypes {
|
||||||
[ComponentType.ActionRow]: ActionRow;
|
[ComponentType.ActionRow]: ActionRow;
|
||||||
[ComponentType.Button]: ButtonComponent;
|
[ComponentType.Button]: ButtonComponent;
|
||||||
[ComponentType.SelectMenu]: SelectMenuComponent;
|
[ComponentType.SelectMenu]: SelectMenuComponent;
|
||||||
|
[ComponentType.TextInput]: TextInputComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,10 +14,10 @@ export interface MappedComponentTypes {
|
|||||||
* @param data The api data to transform to a component class
|
* @param data The api data to transform to a component class
|
||||||
*/
|
*/
|
||||||
export function createComponent<T extends keyof MappedComponentTypes>(
|
export function createComponent<T extends keyof MappedComponentTypes>(
|
||||||
data: APIMessageComponent & { type: T },
|
data: (APIMessageComponent | APIModalComponent) & { type: T },
|
||||||
): MappedComponentTypes[T];
|
): MappedComponentTypes[T];
|
||||||
export function createComponent<C extends MessageComponent>(data: C): C;
|
export function createComponent<C extends MessageComponent | ModalActionRowComponent>(data: C): C;
|
||||||
export function createComponent(data: APIMessageComponent | MessageComponent): Component {
|
export function createComponent(data: APIModalComponent | APIMessageComponent | Component): Component {
|
||||||
if (data instanceof Component) {
|
if (data instanceof Component) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -28,8 +29,9 @@ export function createComponent(data: APIMessageComponent | MessageComponent): C
|
|||||||
return new ButtonComponent(data);
|
return new ButtonComponent(data);
|
||||||
case ComponentType.SelectMenu:
|
case ComponentType.SelectMenu:
|
||||||
return new SelectMenuComponent(data);
|
return new SelectMenuComponent(data);
|
||||||
|
case ComponentType.TextInput:
|
||||||
|
return new TextInputComponent(data);
|
||||||
default:
|
default:
|
||||||
// @ts-expect-error
|
throw new Error(`Cannot serialize component type: ${(data as APIBaseComponent<ComponentType>).type}`);
|
||||||
throw new Error(`Cannot serialize component type: ${data.type as number}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
packages/builders/src/components/textInput/Assertions.ts
Normal file
17
packages/builders/src/components/textInput/Assertions.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { TextInputStyle } from 'discord-api-types/v9';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { customIdValidator } from '../Assertions';
|
||||||
|
|
||||||
|
export const textInputStyleValidator = z.nativeEnum(TextInputStyle);
|
||||||
|
export const minLengthValidator = z.number().int().min(0).max(4000);
|
||||||
|
export const maxLengthValidator = z.number().int().min(1).max(4000);
|
||||||
|
export const requiredValidator = z.boolean();
|
||||||
|
export const valueValidator = z.string().max(4000);
|
||||||
|
export const placeholderValidator = z.string().max(100);
|
||||||
|
export const labelValidator = z.string().min(1).max(45);
|
||||||
|
|
||||||
|
export function validateRequiredParameters(customId?: string, style?: TextInputStyle, label?: string) {
|
||||||
|
customIdValidator.parse(customId);
|
||||||
|
textInputStyleValidator.parse(style);
|
||||||
|
labelValidator.parse(label);
|
||||||
|
}
|
||||||
37
packages/builders/src/components/textInput/TextInput.ts
Normal file
37
packages/builders/src/components/textInput/TextInput.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import type { APITextInputComponent } from 'discord-api-types/v9';
|
||||||
|
import {
|
||||||
|
maxLengthValidator,
|
||||||
|
minLengthValidator,
|
||||||
|
placeholderValidator,
|
||||||
|
requiredValidator,
|
||||||
|
valueValidator,
|
||||||
|
validateRequiredParameters,
|
||||||
|
} from './Assertions';
|
||||||
|
import { UnsafeTextInputComponent } from './UnsafeTextInput';
|
||||||
|
|
||||||
|
export class TextInputComponent extends UnsafeTextInputComponent {
|
||||||
|
public override setMinLength(minLength: number) {
|
||||||
|
return super.setMinLength(minLengthValidator.parse(minLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override setMaxLength(maxLength: number) {
|
||||||
|
return super.setMaxLength(maxLengthValidator.parse(maxLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override setRequired(required = true) {
|
||||||
|
return super.setRequired(requiredValidator.parse(required));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override setValue(value: string) {
|
||||||
|
return super.setValue(valueValidator.parse(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override setPlaceholder(placeholder: string) {
|
||||||
|
return super.setPlaceholder(placeholderValidator.parse(placeholder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override toJSON(): APITextInputComponent {
|
||||||
|
validateRequiredParameters(this.data.custom_id, this.data.style, this.data.label);
|
||||||
|
return super.toJSON();
|
||||||
|
}
|
||||||
|
}
|
||||||
154
packages/builders/src/components/textInput/UnsafeTextInput.ts
Normal file
154
packages/builders/src/components/textInput/UnsafeTextInput.ts
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import { ComponentType, type TextInputStyle, type APITextInputComponent } from 'discord-api-types/v9';
|
||||||
|
import { Component } from '../../index';
|
||||||
|
import isEqual from 'fast-deep-equal';
|
||||||
|
|
||||||
|
export class UnsafeTextInputComponent extends Component<
|
||||||
|
Partial<APITextInputComponent> & { type: ComponentType.TextInput }
|
||||||
|
> {
|
||||||
|
public constructor(data?: APITextInputComponent & { type?: ComponentType.TextInput }) {
|
||||||
|
super({ type: ComponentType.TextInput, ...data });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The style of this text input
|
||||||
|
*/
|
||||||
|
public get style() {
|
||||||
|
return this.data.style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The custom id of this text input
|
||||||
|
*/
|
||||||
|
public get customId() {
|
||||||
|
return this.data.custom_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The label for this text input
|
||||||
|
*/
|
||||||
|
public get label() {
|
||||||
|
return this.data.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The placeholder text for this text input
|
||||||
|
*/
|
||||||
|
public get placeholder() {
|
||||||
|
return this.data.placeholder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default value for this text input
|
||||||
|
*/
|
||||||
|
public get value() {
|
||||||
|
return this.data.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum length of this text input
|
||||||
|
*/
|
||||||
|
public get minLength() {
|
||||||
|
return this.data.min_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum length of this text input
|
||||||
|
*/
|
||||||
|
public get maxLength() {
|
||||||
|
return this.data.max_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this text input is required
|
||||||
|
*/
|
||||||
|
public get required() {
|
||||||
|
return this.data.required;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: UnsafeTextInputComponent | APITextInputComponent): boolean {
|
||||||
|
if (other instanceof UnsafeTextInputComponent) {
|
||||||
|
return isEqual(other.data, this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEqual(other, this.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,12 @@ export * from './components/ActionRow';
|
|||||||
export * from './components/button/Button';
|
export * from './components/button/Button';
|
||||||
export * from './components/Component';
|
export * from './components/Component';
|
||||||
export * from './components/Components';
|
export * from './components/Components';
|
||||||
|
export * from './components/textInput/TextInput';
|
||||||
|
export * as TextInputAssertions from './components/textInput/Assertions';
|
||||||
|
export * from './components/textInput/UnsafeTextInput';
|
||||||
|
export * from './interactions/modals/UnsafeModal';
|
||||||
|
export * from './interactions/modals/Modal';
|
||||||
|
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/button/UnsafeButton';
|
||||||
|
|||||||
16
packages/builders/src/interactions/modals/Assertions.ts
Normal file
16
packages/builders/src/interactions/modals/Assertions.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { ActionRow, type ModalActionRowComponent } from '../..';
|
||||||
|
import { customIdValidator } from '../../components/Assertions';
|
||||||
|
|
||||||
|
export const titleValidator = z.string().min(1).max(45);
|
||||||
|
export const componentsValidator = z.array(z.instanceof(ActionRow)).min(1);
|
||||||
|
|
||||||
|
export function validateRequiredParameters(
|
||||||
|
customId?: string,
|
||||||
|
title?: string,
|
||||||
|
components?: ActionRow<ModalActionRowComponent>[],
|
||||||
|
) {
|
||||||
|
customIdValidator.parse(customId);
|
||||||
|
titleValidator.parse(title);
|
||||||
|
componentsValidator.parse(components);
|
||||||
|
}
|
||||||
19
packages/builders/src/interactions/modals/Modal.ts
Normal file
19
packages/builders/src/interactions/modals/Modal.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import type { APIModalInteractionResponseCallbackData } from 'discord-api-types/v9';
|
||||||
|
import { customIdValidator } from '../../components/Assertions';
|
||||||
|
import { titleValidator, validateRequiredParameters } from './Assertions';
|
||||||
|
import { UnsafeModal } from './UnsafeModal';
|
||||||
|
|
||||||
|
export class Modal extends UnsafeModal {
|
||||||
|
public override setCustomId(customId: string): this {
|
||||||
|
return super.setCustomId(customIdValidator.parse(customId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override setTitle(title: string) {
|
||||||
|
return super.setTitle(titleValidator.parse(title));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override toJSON(): APIModalInteractionResponseCallbackData {
|
||||||
|
validateRequiredParameters(this.data.custom_id, this.data.title, this.components);
|
||||||
|
return super.toJSON();
|
||||||
|
}
|
||||||
|
}
|
||||||
80
packages/builders/src/interactions/modals/UnsafeModal.ts
Normal file
80
packages/builders/src/interactions/modals/UnsafeModal.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import type {
|
||||||
|
APIActionRowComponent,
|
||||||
|
APIModalActionRowComponent,
|
||||||
|
APIModalInteractionResponseCallbackData,
|
||||||
|
} from 'discord-api-types/v9';
|
||||||
|
import { ActionRow, createComponent, JSONEncodable, ModalActionRowComponent } from '../../index';
|
||||||
|
|
||||||
|
export class UnsafeModal implements JSONEncodable<APIModalInteractionResponseCallbackData> {
|
||||||
|
protected readonly data: Partial<Omit<APIModalInteractionResponseCallbackData, 'components'>>;
|
||||||
|
public readonly components: ActionRow<ModalActionRowComponent>[] = [];
|
||||||
|
|
||||||
|
public constructor({ components, ...data }: Partial<APIModalInteractionResponseCallbackData> = {}) {
|
||||||
|
this.data = { ...data };
|
||||||
|
this.components = (components?.map((c) => createComponent(c)) ?? []) as ActionRow<ModalActionRowComponent>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The custom id of this modal
|
||||||
|
*/
|
||||||
|
public get customId() {
|
||||||
|
return this.data.custom_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The title of this modal
|
||||||
|
*/
|
||||||
|
public get title() {
|
||||||
|
return this.data.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: (ActionRow<ModalActionRowComponent> | APIActionRowComponent<APIModalActionRowComponent>)[]
|
||||||
|
) {
|
||||||
|
this.components.push(
|
||||||
|
...components.map((component) =>
|
||||||
|
component instanceof ActionRow ? component : new ActionRow<ModalActionRowComponent>(component),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the components in this modal
|
||||||
|
* @param components The components to set this modal to
|
||||||
|
*/
|
||||||
|
public setComponents(...components: ActionRow<ModalActionRowComponent>[]) {
|
||||||
|
this.components.splice(0, this.components.length, ...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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"@discordjs/rest": "workspace:^",
|
"@discordjs/rest": "workspace:^",
|
||||||
"@sapphire/snowflake": "^3.1.0",
|
"@sapphire/snowflake": "^3.1.0",
|
||||||
"@types/ws": "^8.2.2",
|
"@types/ws": "^8.2.2",
|
||||||
"discord-api-types": "^0.27.0",
|
"discord-api-types": "^0.27.3",
|
||||||
"lodash.snakecase": "^4.1.1",
|
"lodash.snakecase": "^4.1.1",
|
||||||
"undici": "^4.14.1",
|
"undici": "^4.14.1",
|
||||||
"ws": "^8.5.0"
|
"ws": "^8.5.0"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const AutocompleteInteraction = require('../../structures/AutocompleteInteractio
|
|||||||
const ButtonInteraction = require('../../structures/ButtonInteraction');
|
const ButtonInteraction = require('../../structures/ButtonInteraction');
|
||||||
const ChatInputCommandInteraction = require('../../structures/ChatInputCommandInteraction');
|
const ChatInputCommandInteraction = require('../../structures/ChatInputCommandInteraction');
|
||||||
const MessageContextMenuCommandInteraction = require('../../structures/MessageContextMenuCommandInteraction');
|
const MessageContextMenuCommandInteraction = require('../../structures/MessageContextMenuCommandInteraction');
|
||||||
|
const ModalSubmitInteraction = require('../../structures/ModalSubmitInteraction');
|
||||||
const SelectMenuInteraction = require('../../structures/SelectMenuInteraction');
|
const SelectMenuInteraction = require('../../structures/SelectMenuInteraction');
|
||||||
const UserContextMenuCommandInteraction = require('../../structures/UserContextMenuCommandInteraction');
|
const UserContextMenuCommandInteraction = require('../../structures/UserContextMenuCommandInteraction');
|
||||||
const Events = require('../../util/Events');
|
const Events = require('../../util/Events');
|
||||||
@@ -57,6 +58,9 @@ class InteractionCreateAction extends Action {
|
|||||||
case InteractionType.ApplicationCommandAutocomplete:
|
case InteractionType.ApplicationCommandAutocomplete:
|
||||||
InteractionClass = AutocompleteInteraction;
|
InteractionClass = AutocompleteInteraction;
|
||||||
break;
|
break;
|
||||||
|
case InteractionType.ModalSubmit:
|
||||||
|
InteractionClass = ModalSubmitInteraction;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
client.emit(Events.Debug, `[INTERACTION] Received interaction with unknown type: ${data.type}`);
|
client.emit(Events.Debug, `[INTERACTION] Received interaction with unknown type: ${data.type}`);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -141,6 +141,10 @@ const Messages = {
|
|||||||
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP: 'No subcommand group specified for interaction.',
|
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP: 'No subcommand group specified for interaction.',
|
||||||
AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION: 'No focused option for autocomplete interaction.',
|
AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION: 'No focused option for autocomplete interaction.',
|
||||||
|
|
||||||
|
MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND: customId => `Required field with custom id "${customId}" not found.`,
|
||||||
|
MODAL_SUBMIT_INTERACTION_FIELD_TYPE: (customId, type, expected) =>
|
||||||
|
`Field with custom id "${customId}" is of type: ${type}; expected ${expected}.`,
|
||||||
|
|
||||||
INVITE_MISSING_SCOPES: 'At least one valid scope must be provided for the invite',
|
INVITE_MISSING_SCOPES: 'At least one valid scope must be provided for the invite',
|
||||||
|
|
||||||
NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`,
|
NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`,
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ exports.MessageContextMenuCommandInteraction = require('./structures/MessageCont
|
|||||||
exports.MessageMentions = require('./structures/MessageMentions');
|
exports.MessageMentions = require('./structures/MessageMentions');
|
||||||
exports.MessagePayload = require('./structures/MessagePayload');
|
exports.MessagePayload = require('./structures/MessagePayload');
|
||||||
exports.MessageReaction = require('./structures/MessageReaction');
|
exports.MessageReaction = require('./structures/MessageReaction');
|
||||||
|
exports.Modal = require('./structures/Modal');
|
||||||
|
exports.ModalSubmitInteraction = require('./structures/ModalSubmitInteraction');
|
||||||
|
exports.ModalSubmitFieldsResolver = require('./structures/ModalSubmitFieldsResolver');
|
||||||
exports.NewsChannel = require('./structures/NewsChannel');
|
exports.NewsChannel = require('./structures/NewsChannel');
|
||||||
exports.OAuth2Guild = require('./structures/OAuth2Guild');
|
exports.OAuth2Guild = require('./structures/OAuth2Guild');
|
||||||
exports.PartialGroupDMChannel = require('./structures/PartialGroupDMChannel');
|
exports.PartialGroupDMChannel = require('./structures/PartialGroupDMChannel');
|
||||||
@@ -143,6 +146,7 @@ exports.StoreChannel = require('./structures/StoreChannel');
|
|||||||
exports.Team = require('./structures/Team');
|
exports.Team = require('./structures/Team');
|
||||||
exports.TeamMember = require('./structures/TeamMember');
|
exports.TeamMember = require('./structures/TeamMember');
|
||||||
exports.TextChannel = require('./structures/TextChannel');
|
exports.TextChannel = require('./structures/TextChannel');
|
||||||
|
exports.TextInputComponent = require('./structures/TextInputComponent');
|
||||||
exports.ThreadChannel = require('./structures/ThreadChannel');
|
exports.ThreadChannel = require('./structures/ThreadChannel');
|
||||||
exports.ThreadMember = require('./structures/ThreadMember');
|
exports.ThreadMember = require('./structures/ThreadMember');
|
||||||
exports.Typing = require('./structures/Typing');
|
exports.Typing = require('./structures/Typing');
|
||||||
@@ -193,6 +197,7 @@ exports.RESTJSONErrorCodes = require('discord-api-types/v9').RESTJSONErrorCodes;
|
|||||||
exports.StageInstancePrivacyLevel = require('discord-api-types/v9').StageInstancePrivacyLevel;
|
exports.StageInstancePrivacyLevel = require('discord-api-types/v9').StageInstancePrivacyLevel;
|
||||||
exports.StickerType = require('discord-api-types/v9').StickerType;
|
exports.StickerType = require('discord-api-types/v9').StickerType;
|
||||||
exports.StickerFormatType = require('discord-api-types/v9').StickerFormatType;
|
exports.StickerFormatType = require('discord-api-types/v9').StickerFormatType;
|
||||||
|
exports.TextInputStyle = require('discord-api-types/v9').TextInputStyle;
|
||||||
exports.UserFlags = require('discord-api-types/v9').UserFlags;
|
exports.UserFlags = require('discord-api-types/v9').UserFlags;
|
||||||
exports.WebhookType = require('discord-api-types/v9').WebhookType;
|
exports.WebhookType = require('discord-api-types/v9').WebhookType;
|
||||||
exports.UnsafeButtonComponent = require('@discordjs/builders').UnsafeButtonComponent;
|
exports.UnsafeButtonComponent = require('@discordjs/builders').UnsafeButtonComponent;
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ class CommandInteraction extends Interaction {
|
|||||||
editReply() {}
|
editReply() {}
|
||||||
deleteReply() {}
|
deleteReply() {}
|
||||||
followUp() {}
|
followUp() {}
|
||||||
|
showModal() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
InteractionResponses.applyToClass(CommandInteraction, ['deferUpdate', 'update']);
|
InteractionResponses.applyToClass(CommandInteraction, ['deferUpdate', 'update']);
|
||||||
|
|||||||
@@ -191,6 +191,14 @@ class Interaction extends Base {
|
|||||||
return this.isContextMenuCommand() && this.commandType === ApplicationCommandType.Message;
|
return this.isContextMenuCommand() && this.commandType === ApplicationCommandType.Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this interaction is a {@link ModalSubmitInteraction}
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isModalSubmit() {
|
||||||
|
return this.type === InteractionType.ModalSubmit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether this interaction is an {@link AutocompleteInteraction}
|
* Indicates whether this interaction is an {@link AutocompleteInteraction}
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ class MessageComponentInteraction extends Interaction {
|
|||||||
followUp() {}
|
followUp() {}
|
||||||
deferUpdate() {}
|
deferUpdate() {}
|
||||||
update() {}
|
update() {}
|
||||||
|
showModal() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
InteractionResponses.applyToClass(MessageComponentInteraction);
|
InteractionResponses.applyToClass(MessageComponentInteraction);
|
||||||
|
|||||||
12
packages/discord.js/src/structures/Modal.js
Normal file
12
packages/discord.js/src/structures/Modal.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Modal: BuildersModal } = require('@discordjs/builders');
|
||||||
|
const Transformers = require('../util/Transformers');
|
||||||
|
|
||||||
|
class Modal extends BuildersModal {
|
||||||
|
constructor(data) {
|
||||||
|
super(Transformers.toSnakeCase(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Modal;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Collection } = require('@discordjs/collection');
|
||||||
|
const { ComponentType } = require('discord-api-types/v9');
|
||||||
|
const { TypeError } = require('../errors');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the serialized fields from a modal submit interaction
|
||||||
|
*/
|
||||||
|
class ModalSubmitFieldsResolver {
|
||||||
|
constructor(components) {
|
||||||
|
/**
|
||||||
|
* The components within the modal
|
||||||
|
* @type {Array<ActionRow<ModalFieldData>>} The components in the modal
|
||||||
|
*/
|
||||||
|
this.components = components;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The extracted fields from the modal
|
||||||
|
* @type {Collection<string, ModalFieldData>} The fields in the modal
|
||||||
|
*/
|
||||||
|
this.fields = components.reduce((accumulator, next) => {
|
||||||
|
next.components.forEach(c => accumulator.set(c.customId, c));
|
||||||
|
return accumulator;
|
||||||
|
}, new Collection());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a field given a custom id from a component
|
||||||
|
* @param {string} customId The custom id of the component
|
||||||
|
* @returns {ModalFieldData}
|
||||||
|
*/
|
||||||
|
getField(customId) {
|
||||||
|
const field = this.fields.get(customId);
|
||||||
|
if (!field) throw new TypeError('MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND', customId);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of a text input component given a custom id
|
||||||
|
* @param {string} customId The custom id of the text input component
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getTextInputValue(customId) {
|
||||||
|
const field = this.getField(customId);
|
||||||
|
const expectedType = ComponentType.TextInput;
|
||||||
|
if (field.type !== expectedType) {
|
||||||
|
throw new TypeError('MODAL_SUBMIT_INTERACTION_FIELD_TYPE', customId, field.type, expectedType);
|
||||||
|
}
|
||||||
|
return field.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ModalSubmitFieldsResolver;
|
||||||
93
packages/discord.js/src/structures/ModalSubmitInteraction.js
Normal file
93
packages/discord.js/src/structures/ModalSubmitInteraction.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { createComponent } = require('@discordjs/builders');
|
||||||
|
const Interaction = require('./Interaction');
|
||||||
|
const InteractionWebhook = require('./InteractionWebhook');
|
||||||
|
const ModalSubmitFieldsResolver = require('./ModalSubmitFieldsResolver');
|
||||||
|
const InteractionResponses = require('./interfaces/InteractionResponses');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ModalFieldData
|
||||||
|
* @property {string} value The value of the field
|
||||||
|
* @property {ComponentType} type The component type of the field
|
||||||
|
* @property {string} customId The custom id of the field
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a modal interaction
|
||||||
|
* @implements {InteractionResponses}
|
||||||
|
*/
|
||||||
|
class ModalSubmitInteraction extends Interaction {
|
||||||
|
constructor(client, data) {
|
||||||
|
super(client, data);
|
||||||
|
/**
|
||||||
|
* The custom id of the modal.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.customId = data.data.custom_id;
|
||||||
|
|
||||||
|
if ('message' in data) {
|
||||||
|
/**
|
||||||
|
* The message associated with this interaction
|
||||||
|
* @type {?(Message|APIMessage)}
|
||||||
|
*/
|
||||||
|
this.message = this.channel?.messages._add(data.message) ?? data.message;
|
||||||
|
} else {
|
||||||
|
this.message = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The components within the modal
|
||||||
|
* @type {ActionRow[]}
|
||||||
|
*/
|
||||||
|
this.components = data.data.components?.map(c => createComponent(c)) ?? [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fields within the modal
|
||||||
|
* @type {ModalSubmitFieldsResolver}
|
||||||
|
*/
|
||||||
|
this.fields = new ModalSubmitFieldsResolver(this.components);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An associated interaction webhook, can be used to further interact with this interaction
|
||||||
|
* @type {InteractionWebhook}
|
||||||
|
*/
|
||||||
|
this.webhook = new InteractionWebhook(this.client, this.applicationId, this.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms component data to discord.js-compatible data
|
||||||
|
* @param {*} rawComponent The data to transform
|
||||||
|
* @returns {ModalFieldData[]}
|
||||||
|
*/
|
||||||
|
static transformComponent(rawComponent) {
|
||||||
|
return {
|
||||||
|
value: rawComponent.value,
|
||||||
|
type: rawComponent.type,
|
||||||
|
customId: rawComponent.custom_id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this is from a {@link MessageComponentInteraction}.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isFromMessage() {
|
||||||
|
return Boolean(this.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are here only for documentation purposes - they are implemented by InteractionResponses
|
||||||
|
/* eslint-disable no-empty-function */
|
||||||
|
deferReply() {}
|
||||||
|
reply() {}
|
||||||
|
fetchReply() {}
|
||||||
|
editReply() {}
|
||||||
|
deleteReply() {}
|
||||||
|
followUp() {}
|
||||||
|
deferUpdate() {}
|
||||||
|
update() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
InteractionResponses.applyToClass(ModalSubmitInteraction, 'showModal');
|
||||||
|
|
||||||
|
module.exports = ModalSubmitInteraction;
|
||||||
12
packages/discord.js/src/structures/TextInputComponent.js
Normal file
12
packages/discord.js/src/structures/TextInputComponent.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { TextInputComponent: BuildersTextInputComponent } = require('@discordjs/builders');
|
||||||
|
const Transformers = require('../util/Transformers');
|
||||||
|
|
||||||
|
class TextInputComponent extends BuildersTextInputComponent {
|
||||||
|
constructor(data) {
|
||||||
|
super(Transformers.toSnakeCase(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TextInputComponent;
|
||||||
@@ -1,9 +1,18 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const { isJSONEncodable } = require('@discordjs/builders');
|
||||||
const { InteractionResponseType, MessageFlags, Routes } = require('discord-api-types/v9');
|
const { InteractionResponseType, MessageFlags, Routes } = require('discord-api-types/v9');
|
||||||
const { Error } = require('../../errors');
|
const { Error } = require('../../errors');
|
||||||
|
const Transformers = require('../../util/Transformers');
|
||||||
const MessagePayload = require('../MessagePayload');
|
const MessagePayload = require('../MessagePayload');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ModalData
|
||||||
|
* @property {string} title The title of the modal
|
||||||
|
* @property {string} customId The custom id of the modal
|
||||||
|
* @property {ActionRowData[]} components The components within this modal
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for classes that support shared interaction response types.
|
* Interface for classes that support shared interaction response types.
|
||||||
* @interface
|
* @interface
|
||||||
@@ -225,6 +234,21 @@ class InteractionResponses {
|
|||||||
return options.fetchReply ? this.fetchReply() : undefined;
|
return options.fetchReply ? this.fetchReply() : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a modal component
|
||||||
|
* @param {APIModal|ModalData|Modal} modal The modal to show
|
||||||
|
*/
|
||||||
|
async showModal(modal) {
|
||||||
|
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
|
||||||
|
await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
|
||||||
|
body: {
|
||||||
|
type: InteractionResponseType.Modal,
|
||||||
|
data: isJSONEncodable(modal) ? modal.toJSON() : Transformers.toSnakeCase(modal),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.replied = true;
|
||||||
|
}
|
||||||
|
|
||||||
static applyToClass(structure, ignore = []) {
|
static applyToClass(structure, ignore = []) {
|
||||||
const props = [
|
const props = [
|
||||||
'deferReply',
|
'deferReply',
|
||||||
@@ -235,6 +259,7 @@ class InteractionResponses {
|
|||||||
'followUp',
|
'followUp',
|
||||||
'deferUpdate',
|
'deferUpdate',
|
||||||
'update',
|
'update',
|
||||||
|
'showModal',
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const prop of props) {
|
for (const prop of props) {
|
||||||
|
|||||||
@@ -1,44 +1,46 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
// This file contains the typedefs for camel-cased json data
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} BaseComponentData
|
* @typedef {Object} BaseComponentData
|
||||||
* @property {ComponentType} type
|
* @property {ComponentType} type The type of component
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {BaseComponentData} ActionRowData
|
* @typedef {BaseComponentData} ActionRowData
|
||||||
* @property {ComponentData[]} components
|
* @property {ComponentData[]} components The components in this action row
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {BaseComponentData} ButtonComponentData
|
* @typedef {BaseComponentData} ButtonComponentData
|
||||||
* @property {ButtonStyle} style
|
* @property {ButtonStyle} style The style of the button
|
||||||
* @property {?boolean} disabled
|
* @property {?boolean} disabled Whether this button is disabled
|
||||||
* @property {string} label
|
* @property {string} label The label of this button
|
||||||
* @property {?APIComponentEmoji} emoji
|
* @property {?APIComponentEmoji} emoji The emoji on this button
|
||||||
* @property {?string} customId
|
* @property {?string} customId The custom id of the button
|
||||||
* @property {?string} url
|
* @property {?string} url The URL of the button
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} SelectMenuComponentOptionData
|
* @typedef {object} SelectMenuComponentOptionData
|
||||||
* @property {string} label
|
* @property {string} label The label of the option
|
||||||
* @property {string} value
|
* @property {string} value The value of the option
|
||||||
* @property {?string} description
|
* @property {?string} description The description of the option
|
||||||
* @property {?APIComponentEmoji} emoji
|
* @property {?APIComponentEmoji} emoji The emoji on the option
|
||||||
* @property {?boolean} default
|
* @property {?boolean} default Whether this option is selected by default
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {BaseComponentData} SelectMenuComponentData
|
* @typedef {BaseComponentData} SelectMenuComponentData
|
||||||
* @property {string} customId
|
* @property {string} customId The custom id of the select menu
|
||||||
* @property {?boolean} disabled
|
* @property {?boolean} disabled Whether the select menu is disabled or not
|
||||||
* @property {?number} maxValues
|
* @property {?number} maxValues The maximum amount of options that can be selected
|
||||||
* @property {?number} minValues
|
* @property {?number} minValues The minimum amount of options that can be selected
|
||||||
* @property {?SelectMenuComponentOptionData[]} options
|
* @property {?SelectMenuComponentOptionData[]} options The options in this select menu
|
||||||
* @property {?string} placeholder
|
* @property {?string} placeholder The placeholder of the select menu
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {ActionRowData|ButtonComponentData|SelectMenuComponentData} ComponentData
|
* @typedef {ActionRowData|ButtonComponentData|SelectMenuComponentData} MessageComponentData
|
||||||
|
/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {ActionRowData|ButtonComponentData|SelectMenuComponentData|TextInputComponentData} ComponentData
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,47 +2,47 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} EmbedData
|
* @typedef {Object} EmbedData
|
||||||
* @property {?string} title
|
* @property {?string} title The title of the embed
|
||||||
* @property {?EmbedType} type
|
* @property {?EmbedType} type The type of the embed
|
||||||
* @property {?string} description
|
* @property {?string} description The description of the embed
|
||||||
* @property {?string} url
|
* @property {?string} url The URL of the embed
|
||||||
* @property {?string} timestamp
|
* @property {?string} timestamp The timestamp on the embed
|
||||||
* @property {?number} color
|
* @property {?number} color The color of the embed
|
||||||
* @property {?EmbedFooterData} footer
|
* @property {?EmbedFooterData} footer The footer of the embed
|
||||||
* @property {?EmbedImageData} image
|
* @property {?EmbedImageData} image The image of the embed
|
||||||
* @property {?EmbedImageData} thumbnail
|
* @property {?EmbedImageData} thumbnail The thumbnail of the embed
|
||||||
* @property {?EmbedProviderData} provider
|
* @property {?EmbedProviderData} provider The provider of the embed
|
||||||
* @property {?EmbedAuthorData} author
|
* @property {?EmbedAuthorData} author The author in the embed
|
||||||
* @property {?EmbedFieldData[]} fields
|
* @property {?EmbedFieldData[]} fields The fields in this embed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} EmbedFooterData
|
* @typedef {Object} EmbedFooterData
|
||||||
* @property {string} text
|
* @property {string} text The text of the footer
|
||||||
* @property {?string} iconURL
|
* @property {?string} iconURL The URL of the icon
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} EmbedImageData
|
* @typedef {Object} EmbedImageData
|
||||||
* @property {?string} url
|
* @property {?string} url The URL of the image
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} EmbedProviderData
|
* @typedef {Object} EmbedProviderData
|
||||||
* @property {?string} name
|
* @property {?string} name The name of the provider
|
||||||
* @property {?string} url
|
* @property {?string} url The URL of the provider
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} EmbedAuthorData
|
* @typedef {Object} EmbedAuthorData
|
||||||
* @property {string} name
|
* @property {string} name The name of the author
|
||||||
* @property {?string} url
|
* @property {?string} url The URL of the author
|
||||||
* @property {?string} iconURL
|
* @property {?string} iconURL The icon URL of the author
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} EmbedFieldData
|
* @typedef {Object} EmbedFieldData
|
||||||
* @property {string} name
|
* @property {string} name The name of the field
|
||||||
* @property {string} value
|
* @property {string} value The value of the field
|
||||||
* @property {?boolean} inline
|
* @property {?boolean} inline Whether to inline this field
|
||||||
*/
|
*/
|
||||||
|
|||||||
165
packages/discord.js/typings/index.d.ts
vendored
165
packages/discord.js/typings/index.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ActionRow as BuilderActionRow,
|
ActionRow as BuilderActionRow,
|
||||||
ActionRowComponent,
|
MessageActionRowComponent,
|
||||||
blockQuote,
|
blockQuote,
|
||||||
bold,
|
bold,
|
||||||
ButtonComponent as BuilderButtonComponent,
|
ButtonComponent as BuilderButtonComponent,
|
||||||
@@ -14,9 +14,11 @@ import {
|
|||||||
inlineCode,
|
inlineCode,
|
||||||
italic,
|
italic,
|
||||||
memberNicknameMention,
|
memberNicknameMention,
|
||||||
|
Modal as BuilderModal,
|
||||||
quote,
|
quote,
|
||||||
roleMention,
|
roleMention,
|
||||||
SelectMenuComponent as BuilderSelectMenuComponent,
|
SelectMenuComponent as BuilderSelectMenuComponent,
|
||||||
|
TextInputComponent as BuilderTextInputComponent,
|
||||||
spoiler,
|
spoiler,
|
||||||
strikethrough,
|
strikethrough,
|
||||||
time,
|
time,
|
||||||
@@ -24,6 +26,7 @@ import {
|
|||||||
TimestampStylesString,
|
TimestampStylesString,
|
||||||
underscore,
|
underscore,
|
||||||
userMention,
|
userMention,
|
||||||
|
ModalActionRowComponent,
|
||||||
} from '@discordjs/builders';
|
} from '@discordjs/builders';
|
||||||
import { Collection } from '@discordjs/collection';
|
import { Collection } from '@discordjs/collection';
|
||||||
import { BaseImageURLOptions, ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
|
import { BaseImageURLOptions, ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
|
||||||
@@ -95,6 +98,13 @@ import {
|
|||||||
APIMessageComponentEmoji,
|
APIMessageComponentEmoji,
|
||||||
EmbedType,
|
EmbedType,
|
||||||
APIActionRowComponentTypes,
|
APIActionRowComponentTypes,
|
||||||
|
APIModalInteractionResponseCallbackData,
|
||||||
|
APIModalSubmitInteraction,
|
||||||
|
APIMessageActionRowComponent,
|
||||||
|
TextInputStyle,
|
||||||
|
APITextInputComponent,
|
||||||
|
APIModalActionRowComponent,
|
||||||
|
APIModalComponent,
|
||||||
} from 'discord-api-types/v9';
|
} from 'discord-api-types/v9';
|
||||||
import { ChildProcess } from 'node:child_process';
|
import { ChildProcess } from 'node:child_process';
|
||||||
import { EventEmitter } from 'node:events';
|
import { EventEmitter } from 'node:events';
|
||||||
@@ -198,17 +208,23 @@ export interface BaseComponentData {
|
|||||||
type?: ComponentType;
|
type?: ComponentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ActionRowComponentData = ButtonComponentData | SelectMenuComponentData;
|
export type MessageActionRowComponentData = ButtonComponentData | SelectMenuComponentData;
|
||||||
|
export type ModalActionRowComponentData = TextInputComponentData;
|
||||||
|
|
||||||
export interface ActionRowData extends BaseComponentData {
|
export interface ActionRowData<T extends MessageActionRowComponentData | ModalActionRowComponentData>
|
||||||
components: ActionRowComponentData[];
|
extends BaseComponentData {
|
||||||
|
components: T[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ActionRow<T extends ActionRowComponent = ActionRowComponent> extends BuilderActionRow<T> {
|
export class ActionRow<
|
||||||
|
T extends MessageActionRowComponent | ModalActionRowComponent = MessageActionRowComponent,
|
||||||
|
> extends BuilderActionRow<T> {
|
||||||
constructor(
|
constructor(
|
||||||
data?:
|
data?:
|
||||||
| ActionRowData
|
| ActionRowData<MessageActionRowComponentData | ModalActionRowComponentData>
|
||||||
| (Omit<APIActionRowComponent<APIMessageComponent>, 'type'> & { type?: ComponentType.ActionRow }),
|
| (Omit<APIActionRowComponent<APIMessageActionRowComponent | APIModalActionRowComponent>, 'type'> & {
|
||||||
|
type?: ComponentType.ActionRow;
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,6 +352,7 @@ export interface InteractionResponseFields<Cached extends CacheType = CacheType>
|
|||||||
deferReply(options?: InteractionDeferReplyOptions): Promise<void>;
|
deferReply(options?: InteractionDeferReplyOptions): Promise<void>;
|
||||||
fetchReply(): Promise<GuildCacheMessage<Cached>>;
|
fetchReply(): Promise<GuildCacheMessage<Cached>>;
|
||||||
followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
|
followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
|
||||||
|
showModal(modal: Modal): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class CommandInteraction<Cached extends CacheType = CacheType> extends Interaction<Cached> {
|
export abstract class CommandInteraction<Cached extends CacheType = CacheType> extends Interaction<Cached> {
|
||||||
@@ -374,6 +391,7 @@ export abstract class CommandInteraction<Cached extends CacheType = CacheType> e
|
|||||||
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
|
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
|
||||||
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
|
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
|
||||||
public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>;
|
public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>;
|
||||||
|
public showModal(modal: Modal): Promise<void>;
|
||||||
private transformOption(
|
private transformOption(
|
||||||
option: APIApplicationCommandOption,
|
option: APIApplicationCommandOption,
|
||||||
resolved: APIApplicationCommandInteractionData['resolved'],
|
resolved: APIApplicationCommandInteractionData['resolved'],
|
||||||
@@ -490,6 +508,14 @@ export class SelectMenuComponent extends BuilderSelectMenuComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TextInputComponent extends BuilderTextInputComponent {
|
||||||
|
public constructor(data?: TextInputComponentData | APITextInputComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Modal extends BuilderModal {
|
||||||
|
public constructor(data?: ModalData | APIModalActionRowComponent);
|
||||||
|
}
|
||||||
|
|
||||||
export interface EmbedData {
|
export interface EmbedData {
|
||||||
title?: string;
|
title?: string;
|
||||||
type?: EmbedType;
|
type?: EmbedType;
|
||||||
@@ -1355,6 +1381,7 @@ export class Interaction<Cached extends CacheType = CacheType> extends Base {
|
|||||||
public isMessageComponent(): this is MessageComponentInteraction<Cached>;
|
public isMessageComponent(): this is MessageComponentInteraction<Cached>;
|
||||||
public isSelectMenu(): this is SelectMenuInteraction<Cached>;
|
public isSelectMenu(): this is SelectMenuInteraction<Cached>;
|
||||||
public isRepliable(): this is this & InteractionResponseFields<Cached>;
|
public isRepliable(): this is this & InteractionResponseFields<Cached>;
|
||||||
|
public isModalSubmit(): this is ModalSubmitInteraction<Cached>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InteractionCollector<T extends Interaction> extends Collector<Snowflake, T> {
|
export class InteractionCollector<T extends Interaction> extends Collector<Snowflake, T> {
|
||||||
@@ -1447,17 +1474,19 @@ export class LimitedCollection<K, V> extends Collection<K, V> {
|
|||||||
public keepOverLimit: ((value: V, key: K, collection: this) => boolean) | null;
|
public keepOverLimit: ((value: V, key: K, collection: this) => boolean) | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageCollectorOptionsParams<T extends ComponentType, Cached extends boolean = boolean> =
|
export type MessageComponentType = Exclude<ComponentType, ComponentType.TextInput>;
|
||||||
|
|
||||||
|
export type MessageCollectorOptionsParams<T extends MessageComponentType, Cached extends boolean = boolean> =
|
||||||
| {
|
| {
|
||||||
componentType?: T;
|
componentType?: T;
|
||||||
} & MessageComponentCollectorOptions<MappedInteractionTypes<Cached>[T]>;
|
} & MessageComponentCollectorOptions<MappedInteractionTypes<Cached>[T]>;
|
||||||
|
|
||||||
export type MessageChannelCollectorOptionsParams<T extends ComponentType, Cached extends boolean = boolean> =
|
export type MessageChannelCollectorOptionsParams<T extends MessageComponentType, Cached extends boolean = boolean> =
|
||||||
| {
|
| {
|
||||||
componentType?: T;
|
componentType?: T;
|
||||||
} & MessageChannelComponentCollectorOptions<MappedInteractionTypes<Cached>[T]>;
|
} & MessageChannelComponentCollectorOptions<MappedInteractionTypes<Cached>[T]>;
|
||||||
|
|
||||||
export type AwaitMessageCollectorOptionsParams<T extends ComponentType, Cached extends boolean = boolean> =
|
export type AwaitMessageCollectorOptionsParams<T extends MessageComponentType, Cached extends boolean = boolean> =
|
||||||
| { componentType?: T } & Pick<
|
| { componentType?: T } & Pick<
|
||||||
InteractionCollectorOptions<MappedInteractionTypes<Cached>[T]>,
|
InteractionCollectorOptions<MappedInteractionTypes<Cached>[T]>,
|
||||||
keyof AwaitMessageComponentOptions<any>
|
keyof AwaitMessageComponentOptions<any>
|
||||||
@@ -1490,7 +1519,7 @@ export class Message<Cached extends boolean = boolean> extends Base {
|
|||||||
public get channel(): If<Cached, GuildTextBasedChannel, TextBasedChannel>;
|
public get channel(): If<Cached, GuildTextBasedChannel, TextBasedChannel>;
|
||||||
public channelId: Snowflake;
|
public channelId: Snowflake;
|
||||||
public get cleanContent(): string;
|
public get cleanContent(): string;
|
||||||
public components: ActionRow<ActionRowComponent>[];
|
public components: ActionRow<MessageActionRowComponent>[];
|
||||||
public content: string;
|
public content: string;
|
||||||
public get createdAt(): Date;
|
public get createdAt(): Date;
|
||||||
public createdTimestamp: number;
|
public createdTimestamp: number;
|
||||||
@@ -1522,12 +1551,12 @@ export class Message<Cached extends boolean = boolean> extends Base {
|
|||||||
public webhookId: Snowflake | null;
|
public webhookId: Snowflake | null;
|
||||||
public flags: Readonly<MessageFlagsBitField>;
|
public flags: Readonly<MessageFlagsBitField>;
|
||||||
public reference: MessageReference | null;
|
public reference: MessageReference | null;
|
||||||
public awaitMessageComponent<T extends ComponentType = ComponentType.ActionRow>(
|
public awaitMessageComponent<T extends MessageComponentType = ComponentType.ActionRow>(
|
||||||
options?: AwaitMessageCollectorOptionsParams<T, Cached>,
|
options?: AwaitMessageCollectorOptionsParams<T, Cached>,
|
||||||
): Promise<MappedInteractionTypes<Cached>[T]>;
|
): Promise<MappedInteractionTypes<Cached>[T]>;
|
||||||
public awaitReactions(options?: AwaitReactionsOptions): Promise<Collection<Snowflake | string, MessageReaction>>;
|
public awaitReactions(options?: AwaitReactionsOptions): Promise<Collection<Snowflake | string, MessageReaction>>;
|
||||||
public createReactionCollector(options?: ReactionCollectorOptions): ReactionCollector;
|
public createReactionCollector(options?: ReactionCollectorOptions): ReactionCollector;
|
||||||
public createMessageComponentCollector<T extends ComponentType = ComponentType.ActionRow>(
|
public createMessageComponentCollector<T extends MessageComponentType = ComponentType.ActionRow>(
|
||||||
options?: MessageCollectorOptionsParams<T, Cached>,
|
options?: MessageCollectorOptionsParams<T, Cached>,
|
||||||
): InteractionCollector<MappedInteractionTypes<Cached>[T]>;
|
): InteractionCollector<MappedInteractionTypes<Cached>[T]>;
|
||||||
public delete(): Promise<Message>;
|
public delete(): Promise<Message>;
|
||||||
@@ -1541,7 +1570,7 @@ export class Message<Cached extends boolean = boolean> extends Base {
|
|||||||
public react(emoji: EmojiIdentifierResolvable): Promise<MessageReaction>;
|
public react(emoji: EmojiIdentifierResolvable): Promise<MessageReaction>;
|
||||||
public removeAttachments(): Promise<Message>;
|
public removeAttachments(): Promise<Message>;
|
||||||
public reply(options: string | MessagePayload | ReplyMessageOptions): Promise<Message>;
|
public reply(options: string | MessagePayload | ReplyMessageOptions): Promise<Message>;
|
||||||
public resolveComponent(customId: string): ActionRowComponent | null;
|
public resolveComponent(customId: string): MessageActionRowComponent | null;
|
||||||
public startThread(options: StartThreadOptions): Promise<ThreadChannel>;
|
public startThread(options: StartThreadOptions): Promise<ThreadChannel>;
|
||||||
public suppressEmbeds(suppress?: boolean): Promise<Message>;
|
public suppressEmbeds(suppress?: boolean): Promise<Message>;
|
||||||
public toJSON(): unknown;
|
public toJSON(): unknown;
|
||||||
@@ -1590,10 +1619,10 @@ export class MessageComponentInteraction<Cached extends CacheType = CacheType> e
|
|||||||
protected constructor(client: Client, data: RawMessageComponentInteractionData);
|
protected constructor(client: Client, data: RawMessageComponentInteractionData);
|
||||||
public get component(): CacheTypeReducer<
|
public get component(): CacheTypeReducer<
|
||||||
Cached,
|
Cached,
|
||||||
ActionRowComponent,
|
MessageActionRowComponent,
|
||||||
Exclude<APIMessageComponent, APIActionRowComponent<APIMessageComponent>>,
|
Exclude<APIMessageComponent, APIActionRowComponent<APIMessageActionRowComponent>>,
|
||||||
ActionRowComponent | Exclude<APIMessageComponent, APIActionRowComponent<APIMessageComponent>>,
|
MessageActionRowComponent | Exclude<APIMessageComponent, APIActionRowComponent<APIMessageActionRowComponent>>,
|
||||||
ActionRowComponent | Exclude<APIMessageComponent, APIActionRowComponent<APIMessageComponent>>
|
MessageActionRowComponent | Exclude<APIMessageComponent, APIActionRowComponent<APIMessageActionRowComponent>>
|
||||||
>;
|
>;
|
||||||
public componentType: Exclude<ComponentType, ComponentType.ActionRow>;
|
public componentType: Exclude<ComponentType, ComponentType.ActionRow>;
|
||||||
public customId: string;
|
public customId: string;
|
||||||
@@ -1618,6 +1647,7 @@ export class MessageComponentInteraction<Cached extends CacheType = CacheType> e
|
|||||||
public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>;
|
public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>;
|
||||||
public update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
|
public update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
|
||||||
public update(options: string | MessagePayload | InteractionUpdateOptions): Promise<void>;
|
public update(options: string | MessagePayload | InteractionUpdateOptions): Promise<void>;
|
||||||
|
public showModal(modal: Modal): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MessageContextMenuCommandInteraction<
|
export class MessageContextMenuCommandInteraction<
|
||||||
@@ -1706,6 +1736,57 @@ export class MessageReaction {
|
|||||||
public toJSON(): unknown;
|
public toJSON(): unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ModalFieldData {
|
||||||
|
value: string;
|
||||||
|
type: ComponentType;
|
||||||
|
customId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ModalSubmitFieldsResolver {
|
||||||
|
constructor(components: ModalFieldData[][]);
|
||||||
|
public fields: Collection<string, ModalFieldData>;
|
||||||
|
public getField(customId: string): ModalFieldData;
|
||||||
|
public getTextInputValue(customId: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModalMessageModalSubmitInteraction<Cached extends CacheType = CacheType>
|
||||||
|
extends ModalSubmitInteraction<Cached> {
|
||||||
|
message: GuildCacheMessage<Cached> | null;
|
||||||
|
update(options: InteractionUpdateOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
|
||||||
|
update(options: string | MessagePayload | InteractionUpdateOptions): Promise<void>;
|
||||||
|
deferUpdate(options: InteractionDeferUpdateOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
|
||||||
|
deferUpdate(options?: InteractionDeferUpdateOptions): Promise<void>;
|
||||||
|
inGuild(): this is ModalMessageModalSubmitInteraction<'raw' | 'cached'>;
|
||||||
|
inCachedGuild(): this is ModalMessageModalSubmitInteraction<'cached'>;
|
||||||
|
inRawGuild(): this is ModalMessageModalSubmitInteraction<'raw'>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModalSubmitActionRow {
|
||||||
|
type: ComponentType.ActionRow;
|
||||||
|
components: ModalFieldData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ModalSubmitInteraction<Cached extends CacheType = CacheType> extends Interaction<Cached> {
|
||||||
|
private constructor(client: Client, data: APIModalSubmitInteraction);
|
||||||
|
public readonly customId: string;
|
||||||
|
// TODO: fix this type when #7517 is implemented
|
||||||
|
public readonly components: ModalSubmitActionRow[];
|
||||||
|
public readonly fields: ModalSubmitFieldsResolver;
|
||||||
|
public readonly webhook: InteractionWebhook;
|
||||||
|
public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
|
||||||
|
public reply(options: string | MessagePayload | InteractionReplyOptions): Promise<void>;
|
||||||
|
public deleteReply(): Promise<void>;
|
||||||
|
public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise<GuildCacheMessage<Cached>>;
|
||||||
|
public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise<GuildCacheMessage<Cached>>;
|
||||||
|
public deferReply(options?: InteractionDeferReplyOptions): Promise<void>;
|
||||||
|
public fetchReply(): Promise<GuildCacheMessage<Cached>>;
|
||||||
|
public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise<GuildCacheMessage<Cached>>;
|
||||||
|
public inGuild(): this is ModalSubmitInteraction<'raw' | 'cached'>;
|
||||||
|
public inCachedGuild(): this is ModalSubmitInteraction<'cached'>;
|
||||||
|
public inRawGuild(): this is ModalSubmitInteraction<'raw'>;
|
||||||
|
public isFromMessage(): this is ModalMessageModalSubmitInteraction<Cached>;
|
||||||
|
}
|
||||||
|
|
||||||
export class NewsChannel extends BaseGuildTextChannel {
|
export class NewsChannel extends BaseGuildTextChannel {
|
||||||
public threads: ThreadManager<AllowedThreadTypeForNewsChannel>;
|
public threads: ThreadManager<AllowedThreadTypeForNewsChannel>;
|
||||||
public type: ChannelType.GuildNews;
|
public type: ChannelType.GuildNews;
|
||||||
@@ -2381,7 +2462,10 @@ export class Formatters extends null {
|
|||||||
public static userMention: typeof userMention;
|
public static userMention: typeof userMention;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComponentData = ActionRowComponentData | ButtonComponentData | SelectMenuComponentData;
|
export type ComponentData =
|
||||||
|
| MessageActionRowComponentData
|
||||||
|
| ModalActionRowComponentData
|
||||||
|
| ActionRowData<MessageActionRowComponentData | ModalActionRowComponentData>;
|
||||||
|
|
||||||
export class VoiceChannel extends BaseGuildVoiceChannel {
|
export class VoiceChannel extends BaseGuildVoiceChannel {
|
||||||
public get speakable(): boolean;
|
public get speakable(): boolean;
|
||||||
@@ -3132,8 +3216,8 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields {
|
|||||||
lastMessageId: Snowflake | null;
|
lastMessageId: Snowflake | null;
|
||||||
get lastMessage(): Message | null;
|
get lastMessage(): Message | null;
|
||||||
lastPinTimestamp: number | null;
|
lastPinTimestamp: number | null;
|
||||||
get lastPinAt(): Date | null;
|
readonly lastPinAt: Date | null;
|
||||||
awaitMessageComponent<T extends ComponentType = ComponentType.ActionRow>(
|
awaitMessageComponent<T extends MessageComponentType = ComponentType.ActionRow>(
|
||||||
options?: AwaitMessageCollectorOptionsParams<T, true>,
|
options?: AwaitMessageCollectorOptionsParams<T, true>,
|
||||||
): Promise<MappedInteractionTypes[T]>;
|
): Promise<MappedInteractionTypes[T]>;
|
||||||
awaitMessages(options?: AwaitMessagesOptions): Promise<Collection<Snowflake, Message>>;
|
awaitMessages(options?: AwaitMessagesOptions): Promise<Collection<Snowflake, Message>>;
|
||||||
@@ -3141,7 +3225,7 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields {
|
|||||||
messages: Collection<Snowflake, Message> | readonly MessageResolvable[] | number,
|
messages: Collection<Snowflake, Message> | readonly MessageResolvable[] | number,
|
||||||
filterOld?: boolean,
|
filterOld?: boolean,
|
||||||
): Promise<Collection<Snowflake, Message>>;
|
): Promise<Collection<Snowflake, Message>>;
|
||||||
createMessageComponentCollector<T extends ComponentType = ComponentType.ActionRow>(
|
createMessageComponentCollector<T extends MessageComponentType = ComponentType.ActionRow>(
|
||||||
options?: MessageChannelCollectorOptionsParams<T, true>,
|
options?: MessageChannelCollectorOptionsParams<T, true>,
|
||||||
): InteractionCollector<MappedInteractionTypes[T]>;
|
): InteractionCollector<MappedInteractionTypes[T]>;
|
||||||
createMessageCollector(options?: MessageCollectorOptions): MessageCollector;
|
createMessageCollector(options?: MessageCollectorOptions): MessageCollector;
|
||||||
@@ -4518,7 +4602,7 @@ export type ActionRowComponentOptions =
|
|||||||
| (Required<BaseComponentData> & ButtonComponentData)
|
| (Required<BaseComponentData> & ButtonComponentData)
|
||||||
| (Required<BaseComponentData> & SelectMenuComponentData);
|
| (Required<BaseComponentData> & SelectMenuComponentData);
|
||||||
|
|
||||||
export type MessageActionRowComponentResolvable = ActionRowComponent | ActionRowComponentOptions;
|
export type MessageActionRowComponentResolvable = MessageActionRowComponent | ActionRowComponentOptions;
|
||||||
|
|
||||||
export interface MessageActivity {
|
export interface MessageActivity {
|
||||||
partyId: string;
|
partyId: string;
|
||||||
@@ -4548,7 +4632,7 @@ export interface MessageCollectorOptions extends CollectorOptions<[Message]> {
|
|||||||
maxProcessed?: number;
|
maxProcessed?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageComponent = Component | ActionRow<ActionRowComponent> | ButtonComponent | SelectMenuComponent;
|
export type MessageComponent = Component | ActionRow<MessageActionRowComponent> | ButtonComponent | SelectMenuComponent;
|
||||||
|
|
||||||
export type MessageComponentCollectorOptions<T extends MessageComponentInteraction> = Omit<
|
export type MessageComponentCollectorOptions<T extends MessageComponentInteraction> = Omit<
|
||||||
InteractionCollectorOptions<T>,
|
InteractionCollectorOptions<T>,
|
||||||
@@ -4567,7 +4651,11 @@ export interface MessageEditOptions {
|
|||||||
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
||||||
flags?: BitFieldResolvable<MessageFlagsString, number>;
|
flags?: BitFieldResolvable<MessageFlagsString, number>;
|
||||||
allowedMentions?: MessageMentionOptions;
|
allowedMentions?: MessageMentionOptions;
|
||||||
components?: (ActionRow<ActionRowComponent> | (Required<BaseComponentData> & ActionRowData))[];
|
components?: (
|
||||||
|
| ActionRow<MessageActionRowComponent>
|
||||||
|
| (Required<BaseComponentData> & ActionRowData<MessageActionRowComponentData>)
|
||||||
|
| APIActionRowComponent<APIMessageActionRowComponent>
|
||||||
|
)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MessageEvent {
|
export interface MessageEvent {
|
||||||
@@ -4605,9 +4693,9 @@ export interface MessageOptions {
|
|||||||
content?: string | null;
|
content?: string | null;
|
||||||
embeds?: (Embed | APIEmbed)[];
|
embeds?: (Embed | APIEmbed)[];
|
||||||
components?: (
|
components?: (
|
||||||
| ActionRow<ActionRowComponent>
|
| ActionRow<MessageActionRowComponent>
|
||||||
| (Required<BaseComponentData> & ActionRowData)
|
| (Required<BaseComponentData> & ActionRowData<MessageActionRowComponentData>)
|
||||||
| APIActionRowComponent<APIActionRowComponentTypes>
|
| APIActionRowComponent<APIMessageActionRowComponent>
|
||||||
)[];
|
)[];
|
||||||
allowedMentions?: MessageMentionOptions;
|
allowedMentions?: MessageMentionOptions;
|
||||||
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[];
|
||||||
@@ -4658,6 +4746,23 @@ export interface SelectMenuComponentOptionData {
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TextInputComponentData extends BaseComponentData {
|
||||||
|
customId: string;
|
||||||
|
style: TextInputStyle;
|
||||||
|
label: string;
|
||||||
|
minLength?: number;
|
||||||
|
maxLength?: number;
|
||||||
|
required?: boolean;
|
||||||
|
value?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModalData {
|
||||||
|
customId: string;
|
||||||
|
title: string;
|
||||||
|
components: ActionRowData<ModalActionRowComponentData>[];
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageTarget =
|
export type MessageTarget =
|
||||||
| Interaction
|
| Interaction
|
||||||
| InteractionWebhook
|
| InteractionWebhook
|
||||||
@@ -5156,6 +5261,7 @@ export {
|
|||||||
StageInstancePrivacyLevel,
|
StageInstancePrivacyLevel,
|
||||||
StickerType,
|
StickerType,
|
||||||
StickerFormatType,
|
StickerFormatType,
|
||||||
|
TextInputStyle,
|
||||||
GuildSystemChannelFlags,
|
GuildSystemChannelFlags,
|
||||||
ThreadMemberFlags,
|
ThreadMemberFlags,
|
||||||
UserFlags,
|
UserFlags,
|
||||||
@@ -5166,7 +5272,8 @@ export {
|
|||||||
UnsafeSelectMenuComponent,
|
UnsafeSelectMenuComponent,
|
||||||
SelectMenuOption,
|
SelectMenuOption,
|
||||||
UnsafeSelectMenuOption,
|
UnsafeSelectMenuOption,
|
||||||
ActionRowComponent,
|
MessageActionRowComponent,
|
||||||
UnsafeEmbed,
|
UnsafeEmbed,
|
||||||
|
ModalActionRowComponent,
|
||||||
} from '@discordjs/builders';
|
} from '@discordjs/builders';
|
||||||
export { DiscordAPIError, HTTPError, RateLimitError } from '@discordjs/rest';
|
export { DiscordAPIError, HTTPError, RateLimitError } from '@discordjs/rest';
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ import {
|
|||||||
ActionRow,
|
ActionRow,
|
||||||
ButtonComponent,
|
ButtonComponent,
|
||||||
SelectMenuComponent,
|
SelectMenuComponent,
|
||||||
ActionRowComponent,
|
MessageActionRowComponent,
|
||||||
InteractionResponseFields,
|
InteractionResponseFields,
|
||||||
ThreadChannelType,
|
ThreadChannelType,
|
||||||
Events,
|
Events,
|
||||||
@@ -104,6 +104,7 @@ import {
|
|||||||
Status,
|
Status,
|
||||||
CategoryChannelChildManager,
|
CategoryChannelChildManager,
|
||||||
ActionRowData,
|
ActionRowData,
|
||||||
|
MessageActionRowComponentData,
|
||||||
} from '.';
|
} from '.';
|
||||||
import { expectAssignable, expectDeprecated, expectNotAssignable, expectNotType, expectType } from 'tsd';
|
import { expectAssignable, expectDeprecated, expectNotAssignable, expectNotType, expectType } from 'tsd';
|
||||||
import { Embed } from '@discordjs/builders';
|
import { Embed } from '@discordjs/builders';
|
||||||
@@ -723,11 +724,14 @@ client.on('interactionCreate', async interaction => {
|
|||||||
|
|
||||||
if (!interaction.isCommand()) return;
|
if (!interaction.isCommand()) return;
|
||||||
|
|
||||||
void new ActionRow<ActionRowComponent>();
|
void new ActionRow<MessageActionRowComponent>();
|
||||||
|
|
||||||
const button = new ButtonComponent();
|
const button = new ButtonComponent();
|
||||||
|
|
||||||
const actionRow = new ActionRow<ActionRowComponent>({ type: ComponentType.ActionRow, components: [button.toJSON()] });
|
const actionRow = new ActionRow<MessageActionRowComponent>({
|
||||||
|
type: ComponentType.ActionRow,
|
||||||
|
components: [button.toJSON()],
|
||||||
|
});
|
||||||
|
|
||||||
await interaction.reply({ content: 'Hi!', components: [actionRow] });
|
await interaction.reply({ content: 'Hi!', components: [actionRow] });
|
||||||
|
|
||||||
@@ -1092,11 +1096,11 @@ client.on('interactionCreate', async interaction => {
|
|||||||
|
|
||||||
if (interaction.isMessageComponent()) {
|
if (interaction.isMessageComponent()) {
|
||||||
expectType<MessageComponentInteraction>(interaction);
|
expectType<MessageComponentInteraction>(interaction);
|
||||||
expectType<ActionRowComponent | APIButtonComponent | APISelectMenuComponent>(interaction.component);
|
expectType<MessageActionRowComponent | APIButtonComponent | APISelectMenuComponent>(interaction.component);
|
||||||
expectType<Message | APIMessage>(interaction.message);
|
expectType<Message | APIMessage>(interaction.message);
|
||||||
if (interaction.inCachedGuild()) {
|
if (interaction.inCachedGuild()) {
|
||||||
expectAssignable<MessageComponentInteraction>(interaction);
|
expectAssignable<MessageComponentInteraction>(interaction);
|
||||||
expectType<ActionRowComponent>(interaction.component);
|
expectType<MessageActionRowComponent>(interaction.component);
|
||||||
expectType<Message<true>>(interaction.message);
|
expectType<Message<true>>(interaction.message);
|
||||||
expectType<Guild>(interaction.guild);
|
expectType<Guild>(interaction.guild);
|
||||||
expectAssignable<Promise<Message>>(interaction.reply({ fetchReply: true }));
|
expectAssignable<Promise<Message>>(interaction.reply({ fetchReply: true }));
|
||||||
@@ -1108,7 +1112,7 @@ client.on('interactionCreate', async interaction => {
|
|||||||
expectType<Promise<APIMessage>>(interaction.reply({ fetchReply: true }));
|
expectType<Promise<APIMessage>>(interaction.reply({ fetchReply: true }));
|
||||||
} else if (interaction.inGuild()) {
|
} else if (interaction.inGuild()) {
|
||||||
expectAssignable<MessageComponentInteraction>(interaction);
|
expectAssignable<MessageComponentInteraction>(interaction);
|
||||||
expectType<ActionRowComponent | APIButtonComponent | APISelectMenuComponent>(interaction.component);
|
expectType<MessageActionRowComponent | APIButtonComponent | APISelectMenuComponent>(interaction.component);
|
||||||
expectType<Message | APIMessage>(interaction.message);
|
expectType<Message | APIMessage>(interaction.message);
|
||||||
expectType<Guild | null>(interaction.guild);
|
expectType<Guild | null>(interaction.guild);
|
||||||
expectType<Promise<APIMessage | Message>>(interaction.reply({ fetchReply: true }));
|
expectType<Promise<APIMessage | Message>>(interaction.reply({ fetchReply: true }));
|
||||||
@@ -1336,7 +1340,7 @@ new ButtonComponent({
|
|||||||
style: ButtonStyle.Danger,
|
style: ButtonStyle.Danger,
|
||||||
});
|
});
|
||||||
|
|
||||||
expectNotAssignable<ActionRowData>({
|
expectNotAssignable<ActionRowData<MessageActionRowComponentData>>({
|
||||||
type: ComponentType.ActionRow,
|
type: ComponentType.ActionRow,
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
|
|||||||
17
yarn.lock
17
yarn.lock
@@ -1764,7 +1764,7 @@ __metadata:
|
|||||||
"@typescript-eslint/eslint-plugin": ^5.11.0
|
"@typescript-eslint/eslint-plugin": ^5.11.0
|
||||||
"@typescript-eslint/parser": ^5.11.0
|
"@typescript-eslint/parser": ^5.11.0
|
||||||
babel-plugin-transform-typescript-metadata: ^0.3.2
|
babel-plugin-transform-typescript-metadata: ^0.3.2
|
||||||
discord-api-types: ^0.27.0
|
discord-api-types: ^0.27.3
|
||||||
eslint: ^8.9.0
|
eslint: ^8.9.0
|
||||||
eslint-config-marine: ^9.3.2
|
eslint-config-marine: ^9.3.2
|
||||||
eslint-config-prettier: ^8.3.0
|
eslint-config-prettier: ^8.3.0
|
||||||
@@ -4416,9 +4416,16 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"discord-api-types@npm:^0.27.0":
|
"discord-api-types@npm:^0.27.0":
|
||||||
version: 0.27.0
|
version: 0.27.1
|
||||||
resolution: "discord-api-types@npm:0.27.0"
|
resolution: "discord-api-types@npm:0.27.1"
|
||||||
checksum: 5a74a49ad7e57ea24e67d431de30cc7056d6d422b607c7d5a7dd35c683c8b87d70ec35a0d3929971adb411acc3df2bd6a77c1401ce30b29690bd1305e427265c
|
checksum: 5e3473eb01eb3e7ed2b1313513f165644dc70f1f64fb130a50b40394b41c97b1202f4de00b17df34a9f0916269595a091421955bb1e8dbd8e0475637512f2057
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"discord-api-types@npm:^0.27.3":
|
||||||
|
version: 0.27.3
|
||||||
|
resolution: "discord-api-types@npm:0.27.3"
|
||||||
|
checksum: c22d87e787fae6cffd9d23972a3d196d4b43f2fb6deeed50181e7c9d4e823a4fd30a3e1d0e0b3b48a7c284ae2b39fbe960dee988375c7d4072df445f30ac440e
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -4433,7 +4440,7 @@ __metadata:
|
|||||||
"@sapphire/snowflake": ^3.1.0
|
"@sapphire/snowflake": ^3.1.0
|
||||||
"@types/node": ^16.11.24
|
"@types/node": ^16.11.24
|
||||||
"@types/ws": ^8.2.2
|
"@types/ws": ^8.2.2
|
||||||
discord-api-types: ^0.27.0
|
discord-api-types: ^0.27.3
|
||||||
dtslint: ^4.2.1
|
dtslint: ^4.2.1
|
||||||
eslint: ^8.9.0
|
eslint: ^8.9.0
|
||||||
eslint-config-prettier: ^8.3.0
|
eslint-config-prettier: ^8.3.0
|
||||||
|
|||||||
Reference in New Issue
Block a user