@minesa-org/mini-interaction 0.3.13 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -1
- package/dist/builders/FileUploadBuilder.d.ts +3 -23
- package/dist/builders/FileUploadBuilder.js +16 -53
- package/dist/builders/LabelBuilder.d.ts +3 -22
- package/dist/builders/LabelBuilder.js +16 -50
- package/dist/builders/ModalBuilder.d.ts +4 -28
- package/dist/builders/ModalBuilder.js +20 -81
- package/dist/builders/ModalChannelSelectMenuBuilder.d.ts +7 -43
- package/dist/builders/ModalChannelSelectMenuBuilder.js +22 -107
- package/dist/builders/ModalRoleSelectMenuBuilder.d.ts +6 -35
- package/dist/builders/ModalRoleSelectMenuBuilder.js +21 -90
- package/dist/builders/RadioBuilder.d.ts +17 -0
- package/dist/builders/RadioBuilder.js +29 -0
- package/dist/builders/TextInputBuilder.d.ts +3 -32
- package/dist/builders/TextInputBuilder.js +21 -83
- package/dist/builders/__tests__/builders.test.d.ts +1 -0
- package/dist/builders/__tests__/builders.test.js +31 -0
- package/dist/builders/index.d.ts +2 -0
- package/dist/builders/index.js +1 -0
- package/dist/compat/LegacyMiniInteractionAdapter.d.ts +19 -0
- package/dist/compat/LegacyMiniInteractionAdapter.js +23 -0
- package/dist/core/http/DiscordRestClient.d.ts +19 -0
- package/dist/core/http/DiscordRestClient.js +55 -0
- package/dist/core/interactions/InteractionContext.d.ts +25 -0
- package/dist/core/interactions/InteractionContext.js +44 -0
- package/dist/core/interactions/InteractionVerifier.d.ts +8 -0
- package/dist/core/interactions/InteractionVerifier.js +9 -0
- package/dist/core/interactions/__tests__/interaction-context.test.d.ts +1 -0
- package/dist/core/interactions/__tests__/interaction-context.test.js +38 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +8 -0
- package/dist/router/InteractionRouter.d.ts +12 -0
- package/dist/router/InteractionRouter.js +33 -0
- package/dist/types/discord.d.ts +11 -0
- package/dist/types/discord.js +1 -0
- package/dist/types/radio.d.ts +23 -0
- package/dist/types/radio.js +5 -0
- package/dist/types/validation.d.ts +8 -0
- package/dist/types/validation.js +26 -0
- package/package.json +55 -53
package/README.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
1
|
# Mini Interaction
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Discord interactions framework for HTTP/webhook bots with direct Discord API v10 payload compatibility.
|
|
4
|
+
|
|
5
|
+
## Quick start (slash command)
|
|
6
|
+
```ts
|
|
7
|
+
import { LegacyMiniInteractionAdapter } from '@minesa-org/mini-interaction';
|
|
8
|
+
|
|
9
|
+
const adapter = new LegacyMiniInteractionAdapter({ publicKey, applicationId, token });
|
|
10
|
+
adapter.router.onCommand('ping', async (_interaction, ctx) => ctx.reply({ content: 'pong' }));
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Modal example (label + upload + select)
|
|
14
|
+
```ts
|
|
15
|
+
import {
|
|
16
|
+
ModalBuilder,
|
|
17
|
+
LabelBuilder,
|
|
18
|
+
TextInputBuilder,
|
|
19
|
+
FileUploadBuilder,
|
|
20
|
+
ModalRoleSelectMenuBuilder,
|
|
21
|
+
} from '@minesa-org/mini-interaction';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Radio example
|
|
25
|
+
```ts
|
|
26
|
+
import { RadioBuilder } from '@minesa-org/mini-interaction';
|
|
27
|
+
|
|
28
|
+
const radio = new RadioBuilder()
|
|
29
|
+
.setCustomId('flavor')
|
|
30
|
+
.addOptions({ label: 'Vanilla', value: 'v', default: true }, { label: 'Chocolate', value: 'c' });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Error handling
|
|
34
|
+
Catch `ValidationError` from builders to surface actionable diagnostics.
|
|
@@ -1,37 +1,17 @@
|
|
|
1
|
-
import { type APIFileUploadComponent } from
|
|
2
|
-
import type
|
|
3
|
-
/** Shape describing initial file upload data accepted by the builder. */
|
|
1
|
+
import { type APIFileUploadComponent } from 'discord-api-types/v10';
|
|
2
|
+
import { type JSONEncodable } from './shared.js';
|
|
4
3
|
export type FileUploadBuilderData = {
|
|
5
4
|
customId?: string;
|
|
6
5
|
minValues?: number;
|
|
7
6
|
maxValues?: number;
|
|
8
7
|
required?: boolean;
|
|
9
8
|
};
|
|
10
|
-
/** Builder for Discord file upload components used in modals. */
|
|
11
9
|
export declare class FileUploadBuilder implements JSONEncodable<APIFileUploadComponent> {
|
|
12
|
-
private data;
|
|
13
|
-
/**
|
|
14
|
-
* Creates a new file upload builder with optional seed data.
|
|
15
|
-
*/
|
|
10
|
+
private readonly data;
|
|
16
11
|
constructor(data?: FileUploadBuilderData);
|
|
17
|
-
/**
|
|
18
|
-
* Sets the custom identifier for this file upload component.
|
|
19
|
-
*/
|
|
20
12
|
setCustomId(customId: string): this;
|
|
21
|
-
/**
|
|
22
|
-
* Sets the minimum number of files that must be uploaded (min 0, max 10).
|
|
23
|
-
*/
|
|
24
13
|
setMinValues(minValues: number): this;
|
|
25
|
-
/**
|
|
26
|
-
* Sets the maximum number of files that can be uploaded (max 10).
|
|
27
|
-
*/
|
|
28
14
|
setMaxValues(maxValues: number): this;
|
|
29
|
-
/**
|
|
30
|
-
* Sets whether files are required before submitting the modal.
|
|
31
|
-
*/
|
|
32
15
|
setRequired(required: boolean): this;
|
|
33
|
-
/**
|
|
34
|
-
* Serialises the builder into an API compatible file upload component payload.
|
|
35
|
-
*/
|
|
36
16
|
toJSON(): APIFileUploadComponent;
|
|
37
17
|
}
|
|
@@ -1,59 +1,22 @@
|
|
|
1
|
-
import { ComponentType
|
|
2
|
-
|
|
1
|
+
import { ComponentType } from 'discord-api-types/v10';
|
|
2
|
+
import { assertDefined, assertRange, assertStringLength, ValidationError } from '../types/validation.js';
|
|
3
3
|
export class FileUploadBuilder {
|
|
4
4
|
data;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
customId: data.customId,
|
|
11
|
-
minValues: data.minValues,
|
|
12
|
-
maxValues: data.maxValues,
|
|
13
|
-
required: data.required,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Sets the custom identifier for this file upload component.
|
|
18
|
-
*/
|
|
19
|
-
setCustomId(customId) {
|
|
20
|
-
this.data.customId = customId;
|
|
21
|
-
return this;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Sets the minimum number of files that must be uploaded (min 0, max 10).
|
|
25
|
-
*/
|
|
26
|
-
setMinValues(minValues) {
|
|
27
|
-
this.data.minValues = minValues;
|
|
28
|
-
return this;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Sets the maximum number of files that can be uploaded (max 10).
|
|
32
|
-
*/
|
|
33
|
-
setMaxValues(maxValues) {
|
|
34
|
-
this.data.maxValues = maxValues;
|
|
35
|
-
return this;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Sets whether files are required before submitting the modal.
|
|
39
|
-
*/
|
|
40
|
-
setRequired(required) {
|
|
41
|
-
this.data.required = required;
|
|
42
|
-
return this;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Serialises the builder into an API compatible file upload component payload.
|
|
46
|
-
*/
|
|
5
|
+
constructor(data = {}) { this.data = { ...data }; }
|
|
6
|
+
setCustomId(customId) { this.data.customId = customId; return this; }
|
|
7
|
+
setMinValues(minValues) { this.data.minValues = minValues; return this; }
|
|
8
|
+
setMaxValues(maxValues) { this.data.maxValues = maxValues; return this; }
|
|
9
|
+
setRequired(required) { this.data.required = required; return this; }
|
|
47
10
|
toJSON() {
|
|
48
|
-
|
|
49
|
-
|
|
11
|
+
const customId = assertDefined('FileUploadBuilder', 'custom_id', this.data.customId);
|
|
12
|
+
assertStringLength('FileUploadBuilder', 'custom_id', customId, 1, 100);
|
|
13
|
+
if (this.data.minValues !== undefined)
|
|
14
|
+
assertRange('FileUploadBuilder', 'min_values', this.data.minValues, 0, 10);
|
|
15
|
+
if (this.data.maxValues !== undefined)
|
|
16
|
+
assertRange('FileUploadBuilder', 'max_values', this.data.maxValues, 1, 10);
|
|
17
|
+
if (this.data.minValues !== undefined && this.data.maxValues !== undefined && this.data.minValues > this.data.maxValues) {
|
|
18
|
+
throw new ValidationError('FileUploadBuilder', 'min_values', 'cannot be greater than max_values');
|
|
50
19
|
}
|
|
51
|
-
return {
|
|
52
|
-
type: ComponentType.FileUpload,
|
|
53
|
-
custom_id: this.data.customId,
|
|
54
|
-
min_values: this.data.minValues,
|
|
55
|
-
max_values: this.data.maxValues,
|
|
56
|
-
required: this.data.required,
|
|
57
|
-
};
|
|
20
|
+
return { type: ComponentType.FileUpload, custom_id: customId, min_values: this.data.minValues, max_values: this.data.maxValues, required: this.data.required };
|
|
58
21
|
}
|
|
59
22
|
}
|
|
@@ -1,35 +1,16 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import type
|
|
3
|
-
/** Values accepted when composing label components. */
|
|
1
|
+
import { type APIComponentInLabel, type APILabelComponent } from 'discord-api-types/v10';
|
|
2
|
+
import { type JSONEncodable } from './shared.js';
|
|
4
3
|
export type LabelComponentLike = JSONEncodable<APIComponentInLabel> | APIComponentInLabel;
|
|
5
|
-
/** Shape describing initial label data accepted by the builder. */
|
|
6
4
|
export type LabelBuilderData = {
|
|
7
5
|
label?: string;
|
|
8
6
|
description?: string;
|
|
9
7
|
component?: LabelComponentLike;
|
|
10
8
|
};
|
|
11
|
-
/** Builder for Discord label components used in modals. */
|
|
12
9
|
export declare class LabelBuilder implements JSONEncodable<APILabelComponent> {
|
|
13
|
-
private data;
|
|
14
|
-
/**
|
|
15
|
-
* Creates a new label builder with optional seed data.
|
|
16
|
-
*/
|
|
10
|
+
private readonly data;
|
|
17
11
|
constructor(data?: LabelBuilderData);
|
|
18
|
-
/**
|
|
19
|
-
* Sets the label text (max 45 characters).
|
|
20
|
-
*/
|
|
21
12
|
setLabel(label: string): this;
|
|
22
|
-
/**
|
|
23
|
-
* Sets the optional description text (max 100 characters).
|
|
24
|
-
*/
|
|
25
13
|
setDescription(description: string): this;
|
|
26
|
-
/**
|
|
27
|
-
* Sets the component within the label.
|
|
28
|
-
* Can be a TextInput, SelectMenu, or FileUpload component.
|
|
29
|
-
*/
|
|
30
14
|
setComponent(component: LabelComponentLike): this;
|
|
31
|
-
/**
|
|
32
|
-
* Serialises the builder into an API compatible label component payload.
|
|
33
|
-
*/
|
|
34
15
|
toJSON(): APILabelComponent;
|
|
35
16
|
}
|
|
@@ -1,55 +1,21 @@
|
|
|
1
|
-
import { ComponentType
|
|
2
|
-
import { resolveJSONEncodable } from
|
|
3
|
-
|
|
1
|
+
import { ComponentType } from 'discord-api-types/v10';
|
|
2
|
+
import { resolveJSONEncodable } from './shared.js';
|
|
3
|
+
import { ValidationError, assertDefined, assertStringLength } from '../types/validation.js';
|
|
4
4
|
export class LabelBuilder {
|
|
5
5
|
data;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.data = {
|
|
11
|
-
label: data.label,
|
|
12
|
-
description: data.description,
|
|
13
|
-
component: data.component,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Sets the label text (max 45 characters).
|
|
18
|
-
*/
|
|
19
|
-
setLabel(label) {
|
|
20
|
-
this.data.label = label;
|
|
21
|
-
return this;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Sets the optional description text (max 100 characters).
|
|
25
|
-
*/
|
|
26
|
-
setDescription(description) {
|
|
27
|
-
this.data.description = description;
|
|
28
|
-
return this;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Sets the component within the label.
|
|
32
|
-
* Can be a TextInput, SelectMenu, or FileUpload component.
|
|
33
|
-
*/
|
|
34
|
-
setComponent(component) {
|
|
35
|
-
this.data.component = component;
|
|
36
|
-
return this;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Serialises the builder into an API compatible label component payload.
|
|
40
|
-
*/
|
|
6
|
+
constructor(data = {}) { this.data = { ...data }; }
|
|
7
|
+
setLabel(label) { this.data.label = label; return this; }
|
|
8
|
+
setDescription(description) { this.data.description = description; return this; }
|
|
9
|
+
setComponent(component) { this.data.component = component; return this; }
|
|
41
10
|
toJSON() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
description: this.data.description,
|
|
52
|
-
component: resolveJSONEncodable(this.data.component),
|
|
53
|
-
};
|
|
11
|
+
const label = assertDefined('LabelBuilder', 'label', this.data.label);
|
|
12
|
+
const component = assertDefined('LabelBuilder', 'component', this.data.component);
|
|
13
|
+
assertStringLength('LabelBuilder', 'label', label, 1, 45);
|
|
14
|
+
if (this.data.description)
|
|
15
|
+
assertStringLength('LabelBuilder', 'description', this.data.description, 1, 100);
|
|
16
|
+
const resolved = resolveJSONEncodable(component);
|
|
17
|
+
if (!('type' in resolved))
|
|
18
|
+
throw new ValidationError('LabelBuilder', 'component', 'must contain a valid component payload');
|
|
19
|
+
return { type: ComponentType.Label, label, description: this.data.description, component: resolved };
|
|
54
20
|
}
|
|
55
21
|
}
|
|
@@ -1,40 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type
|
|
3
|
-
|
|
4
|
-
export type ModalComponentLike = JSONEncodable<APIModalInteractionResponseCallbackComponent> | APIModalInteractionResponseCallbackComponent | JSONEncodable<APITextInputComponent> | APITextInputComponent | JSONEncodable<APIComponentInLabel> | APIComponentInLabel | JSONEncodable<APISelectMenuComponent> | APISelectMenuComponent | JSONEncodable<APIFileUploadComponent> | APIFileUploadComponent;
|
|
5
|
-
/** Shape describing initial modal data accepted by the builder. */
|
|
1
|
+
import { type APIModalInteractionResponseCallbackData, type APIModalInteractionResponseCallbackComponent } from 'discord-api-types/v10';
|
|
2
|
+
import { type JSONEncodable } from './shared.js';
|
|
3
|
+
export type ModalComponentLike = JSONEncodable<APIModalInteractionResponseCallbackComponent> | APIModalInteractionResponseCallbackComponent;
|
|
6
4
|
export type ModalBuilderData = {
|
|
7
5
|
customId?: string;
|
|
8
6
|
title?: string;
|
|
9
7
|
components?: Iterable<ModalComponentLike>;
|
|
10
8
|
};
|
|
11
|
-
/** Builder for Discord modal interaction responses. */
|
|
12
9
|
export declare class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCallbackData> {
|
|
13
|
-
private
|
|
14
|
-
private title?;
|
|
15
|
-
private components;
|
|
16
|
-
/**
|
|
17
|
-
* Creates a new modal builder with optional seed data.
|
|
18
|
-
*/
|
|
10
|
+
private readonly data;
|
|
19
11
|
constructor(data?: ModalBuilderData);
|
|
20
|
-
/**
|
|
21
|
-
* Sets the custom identifier returned when the modal is submitted.
|
|
22
|
-
*/
|
|
23
12
|
setCustomId(customId: string): this;
|
|
24
|
-
/**
|
|
25
|
-
* Sets the modal title displayed to users.
|
|
26
|
-
*/
|
|
27
13
|
setTitle(title: string): this;
|
|
28
|
-
/**
|
|
29
|
-
* Appends component rows to the modal body.
|
|
30
|
-
*/
|
|
31
14
|
addComponents(...components: ModalComponentLike[]): this;
|
|
32
|
-
/**
|
|
33
|
-
* Replaces all component rows for the modal.
|
|
34
|
-
*/
|
|
35
|
-
setComponents(components: Iterable<ModalComponentLike>): this;
|
|
36
|
-
/**
|
|
37
|
-
* Serialises the builder into an API compatible modal response payload.
|
|
38
|
-
*/
|
|
39
15
|
toJSON(): APIModalInteractionResponseCallbackData;
|
|
40
16
|
}
|
|
@@ -1,92 +1,31 @@
|
|
|
1
|
-
import { ComponentType } from
|
|
2
|
-
import { resolveJSONEncodable } from
|
|
3
|
-
|
|
1
|
+
import { ComponentType } from 'discord-api-types/v10';
|
|
2
|
+
import { resolveJSONEncodable } from './shared.js';
|
|
3
|
+
import { ValidationError, assertDefined, assertStringLength } from '../types/validation.js';
|
|
4
4
|
export class ModalBuilder {
|
|
5
|
-
|
|
6
|
-
title;
|
|
7
|
-
components;
|
|
8
|
-
/**
|
|
9
|
-
* Creates a new modal builder with optional seed data.
|
|
10
|
-
*/
|
|
5
|
+
data;
|
|
11
6
|
constructor(data = {}) {
|
|
12
|
-
this.
|
|
13
|
-
this.title = data.title;
|
|
14
|
-
this.components = data.components ? Array.from(data.components) : [];
|
|
7
|
+
this.data = { ...data, components: data.components ? Array.from(data.components) : [] };
|
|
15
8
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*/
|
|
19
|
-
setCustomId(customId) {
|
|
20
|
-
this.customId = customId;
|
|
21
|
-
return this;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Sets the modal title displayed to users.
|
|
25
|
-
*/
|
|
26
|
-
setTitle(title) {
|
|
27
|
-
this.title = title;
|
|
28
|
-
return this;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Appends component rows to the modal body.
|
|
32
|
-
*/
|
|
9
|
+
setCustomId(customId) { this.data.customId = customId; return this; }
|
|
10
|
+
setTitle(title) { this.data.title = title; return this; }
|
|
33
11
|
addComponents(...components) {
|
|
34
|
-
this.components.
|
|
35
|
-
return this;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Replaces all component rows for the modal.
|
|
39
|
-
*/
|
|
40
|
-
setComponents(components) {
|
|
41
|
-
this.components = Array.from(components);
|
|
12
|
+
this.data.components = [...(this.data.components ?? []), ...components];
|
|
42
13
|
return this;
|
|
43
14
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Serialises the builder into an API compatible modal response payload.
|
|
46
|
-
*/
|
|
47
15
|
toJSON() {
|
|
48
|
-
|
|
49
|
-
|
|
16
|
+
const customId = assertDefined('ModalBuilder', 'custom_id', this.data.customId);
|
|
17
|
+
const title = assertDefined('ModalBuilder', 'title', this.data.title);
|
|
18
|
+
assertStringLength('ModalBuilder', 'custom_id', customId, 1, 100);
|
|
19
|
+
assertStringLength('ModalBuilder', 'title', title, 1, 45);
|
|
20
|
+
const components = Array.from(this.data.components ?? []).map((c) => resolveJSONEncodable(c));
|
|
21
|
+
if (components.length === 0 || components.length > 5) {
|
|
22
|
+
throw new ValidationError('ModalBuilder', 'components', 'must contain between 1 and 5 components');
|
|
50
23
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (this.components.length === 0) {
|
|
55
|
-
throw new Error("[ModalBuilder] at least one component is required.");
|
|
56
|
-
}
|
|
57
|
-
if (this.components.length > 5) {
|
|
58
|
-
throw new Error("[ModalBuilder] no more than 5 components can be provided.");
|
|
59
|
-
}
|
|
60
|
-
// Auto-wrap components that need wrapping
|
|
61
|
-
const normalizedComponents = this.components.map((component) => {
|
|
62
|
-
const resolved = resolveJSONEncodable(component);
|
|
63
|
-
if (!resolved ||
|
|
64
|
-
typeof resolved !== "object" ||
|
|
65
|
-
!("type" in resolved)) {
|
|
66
|
-
return resolved;
|
|
24
|
+
for (const component of components) {
|
|
25
|
+
if (component.type !== ComponentType.ActionRow && component.type !== ComponentType.Label) {
|
|
26
|
+
throw new ValidationError('ModalBuilder', 'components', `invalid modal top-level component type ${component.type}`);
|
|
67
27
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// (ActionRows in modals are deprecated, but still supported for backwards compatibility)
|
|
71
|
-
if (componentType === ComponentType.TextInput ||
|
|
72
|
-
componentType === ComponentType.StringSelect ||
|
|
73
|
-
componentType === ComponentType.UserSelect ||
|
|
74
|
-
componentType === ComponentType.RoleSelect ||
|
|
75
|
-
componentType === ComponentType.MentionableSelect ||
|
|
76
|
-
componentType === ComponentType.ChannelSelect ||
|
|
77
|
-
componentType === ComponentType.FileUpload ||
|
|
78
|
-
componentType === ComponentType.Label) {
|
|
79
|
-
return {
|
|
80
|
-
type: ComponentType.ActionRow,
|
|
81
|
-
components: [resolved],
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return resolved;
|
|
85
|
-
});
|
|
86
|
-
return {
|
|
87
|
-
custom_id: this.customId,
|
|
88
|
-
title: this.title,
|
|
89
|
-
components: normalizedComponents,
|
|
90
|
-
};
|
|
28
|
+
}
|
|
29
|
+
return { custom_id: customId, title, components };
|
|
91
30
|
}
|
|
92
31
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { SelectMenuDefaultValueType, type APIChannelSelectComponent, type APISelectMenuDefaultValue
|
|
2
|
-
import type { JSONEncodable } from
|
|
3
|
-
/** Shape describing initial modal channel select menu data accepted by the builder. */
|
|
1
|
+
import { ChannelType, SelectMenuDefaultValueType, type APIChannelSelectComponent, type APISelectMenuDefaultValue } from 'discord-api-types/v10';
|
|
2
|
+
import type { JSONEncodable } from './shared.js';
|
|
4
3
|
export type ModalChannelSelectMenuBuilderData = {
|
|
5
4
|
customId?: string;
|
|
6
5
|
placeholder?: string;
|
|
@@ -11,51 +10,16 @@ export type ModalChannelSelectMenuBuilderData = {
|
|
|
11
10
|
channelTypes?: ChannelType[];
|
|
12
11
|
defaultValues?: APISelectMenuDefaultValue<SelectMenuDefaultValueType.Channel>[];
|
|
13
12
|
};
|
|
14
|
-
/** Builder for Discord channel select menu components in modals. */
|
|
15
13
|
export declare class ModalChannelSelectMenuBuilder implements JSONEncodable<APIChannelSelectComponent> {
|
|
16
|
-
private data;
|
|
17
|
-
/**
|
|
18
|
-
* Creates a new modal channel select menu builder with optional seed data.
|
|
19
|
-
*/
|
|
14
|
+
private readonly data;
|
|
20
15
|
constructor(data?: ModalChannelSelectMenuBuilderData);
|
|
21
|
-
/**
|
|
22
|
-
* Sets the unique custom identifier for the select menu interaction.
|
|
23
|
-
*/
|
|
24
16
|
setCustomId(customId: string): this;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
setPlaceholder(placeholder: string | null | undefined): this;
|
|
29
|
-
/**
|
|
30
|
-
* Sets the minimum number of channels that must be selected.
|
|
31
|
-
*/
|
|
32
|
-
setMinValues(minValues: number | null | undefined): this;
|
|
33
|
-
/**
|
|
34
|
-
* Sets the maximum number of channels that can be selected.
|
|
35
|
-
*/
|
|
36
|
-
setMaxValues(maxValues: number | null | undefined): this;
|
|
37
|
-
/**
|
|
38
|
-
* Toggles whether the select menu is disabled.
|
|
39
|
-
*/
|
|
17
|
+
setPlaceholder(placeholder?: string): this;
|
|
18
|
+
setMinValues(minValues?: number): this;
|
|
19
|
+
setMaxValues(maxValues?: number): this;
|
|
40
20
|
setDisabled(disabled: boolean): this;
|
|
41
|
-
/**
|
|
42
|
-
* Marks the select menu as required in the modal.
|
|
43
|
-
*/
|
|
44
21
|
setRequired(required: boolean): this;
|
|
45
|
-
|
|
46
|
-
* Replaces the default channel selections displayed when the menu renders.
|
|
47
|
-
*/
|
|
22
|
+
setChannelTypes(channelTypes: ChannelType[]): this;
|
|
48
23
|
setDefaultValues(defaultValues: Iterable<APISelectMenuDefaultValue<SelectMenuDefaultValueType.Channel>>): this;
|
|
49
|
-
/**
|
|
50
|
-
* Sets the channel types allowed in the select menu.
|
|
51
|
-
*/
|
|
52
|
-
setChannelTypes(...channelTypes: ChannelType[]): this;
|
|
53
|
-
/**
|
|
54
|
-
* Adds channel types allowed in the select menu.
|
|
55
|
-
*/
|
|
56
|
-
addChannelTypes(...channelTypes: ChannelType[]): this;
|
|
57
|
-
/**
|
|
58
|
-
* Serialises the builder into an API compatible channel select menu payload.
|
|
59
|
-
*/
|
|
60
24
|
toJSON(): APIChannelSelectComponent;
|
|
61
25
|
}
|
|
@@ -1,115 +1,30 @@
|
|
|
1
|
-
import { ComponentType, SelectMenuDefaultValueType
|
|
2
|
-
|
|
1
|
+
import { ComponentType, SelectMenuDefaultValueType } from 'discord-api-types/v10';
|
|
2
|
+
import { assertDefined, assertRange, assertStringLength, ValidationError } from '../types/validation.js';
|
|
3
3
|
export class ModalChannelSelectMenuBuilder {
|
|
4
4
|
data;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
maxValues: data.maxValues,
|
|
14
|
-
disabled: data.disabled,
|
|
15
|
-
required: data.required,
|
|
16
|
-
channelTypes: data.channelTypes,
|
|
17
|
-
defaultValues: data.defaultValues
|
|
18
|
-
? data.defaultValues.map((value) => ({
|
|
19
|
-
...value,
|
|
20
|
-
type: SelectMenuDefaultValueType.Channel,
|
|
21
|
-
}))
|
|
22
|
-
: undefined,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Sets the unique custom identifier for the select menu interaction.
|
|
27
|
-
*/
|
|
28
|
-
setCustomId(customId) {
|
|
29
|
-
this.data.customId = customId;
|
|
30
|
-
return this;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Sets or clears the placeholder text displayed when no channel is selected.
|
|
34
|
-
*/
|
|
35
|
-
setPlaceholder(placeholder) {
|
|
36
|
-
this.data.placeholder = placeholder ?? undefined;
|
|
37
|
-
return this;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Sets the minimum number of channels that must be selected.
|
|
41
|
-
*/
|
|
42
|
-
setMinValues(minValues) {
|
|
43
|
-
this.data.minValues = minValues ?? undefined;
|
|
44
|
-
return this;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Sets the maximum number of channels that can be selected.
|
|
48
|
-
*/
|
|
49
|
-
setMaxValues(maxValues) {
|
|
50
|
-
this.data.maxValues = maxValues ?? undefined;
|
|
51
|
-
return this;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Toggles whether the select menu is disabled.
|
|
55
|
-
*/
|
|
56
|
-
setDisabled(disabled) {
|
|
57
|
-
this.data.disabled = disabled;
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Marks the select menu as required in the modal.
|
|
62
|
-
*/
|
|
63
|
-
setRequired(required) {
|
|
64
|
-
this.data.required = required;
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Replaces the default channel selections displayed when the menu renders.
|
|
69
|
-
*/
|
|
5
|
+
constructor(data = {}) { this.data = { ...data }; }
|
|
6
|
+
setCustomId(customId) { this.data.customId = customId; return this; }
|
|
7
|
+
setPlaceholder(placeholder) { this.data.placeholder = placeholder; return this; }
|
|
8
|
+
setMinValues(minValues) { this.data.minValues = minValues; return this; }
|
|
9
|
+
setMaxValues(maxValues) { this.data.maxValues = maxValues; return this; }
|
|
10
|
+
setDisabled(disabled) { this.data.disabled = disabled; return this; }
|
|
11
|
+
setRequired(required) { this.data.required = required; return this; }
|
|
12
|
+
setChannelTypes(channelTypes) { this.data.channelTypes = [...channelTypes]; return this; }
|
|
70
13
|
setDefaultValues(defaultValues) {
|
|
71
|
-
this.data.defaultValues = Array.from(defaultValues, (
|
|
72
|
-
...value,
|
|
73
|
-
type: SelectMenuDefaultValueType.Channel,
|
|
74
|
-
}));
|
|
75
|
-
return this;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Sets the channel types allowed in the select menu.
|
|
79
|
-
*/
|
|
80
|
-
setChannelTypes(...channelTypes) {
|
|
81
|
-
this.data.channelTypes = channelTypes.flat();
|
|
82
|
-
return this;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Adds channel types allowed in the select menu.
|
|
86
|
-
*/
|
|
87
|
-
addChannelTypes(...channelTypes) {
|
|
88
|
-
this.data.channelTypes ??= [];
|
|
89
|
-
this.data.channelTypes.push(...channelTypes.flat());
|
|
14
|
+
this.data.defaultValues = Array.from(defaultValues, (v) => ({ ...v, type: SelectMenuDefaultValueType.Channel }));
|
|
90
15
|
return this;
|
|
91
16
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Serialises the builder into an API compatible channel select menu payload.
|
|
94
|
-
*/
|
|
95
17
|
toJSON() {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
required: this.data.required,
|
|
108
|
-
channel_types: this.data.channelTypes,
|
|
109
|
-
default_values: this.data.defaultValues?.map((value) => ({
|
|
110
|
-
...value,
|
|
111
|
-
type: SelectMenuDefaultValueType.Channel,
|
|
112
|
-
})),
|
|
113
|
-
};
|
|
18
|
+
const customId = assertDefined('ModalChannelSelectMenuBuilder', 'custom_id', this.data.customId);
|
|
19
|
+
assertStringLength('ModalChannelSelectMenuBuilder', 'custom_id', customId, 1, 100);
|
|
20
|
+
if (this.data.placeholder)
|
|
21
|
+
assertStringLength('ModalChannelSelectMenuBuilder', 'placeholder', this.data.placeholder, 1, 150);
|
|
22
|
+
if (this.data.minValues !== undefined)
|
|
23
|
+
assertRange('ModalChannelSelectMenuBuilder', 'min_values', this.data.minValues, 0, 25);
|
|
24
|
+
if (this.data.maxValues !== undefined)
|
|
25
|
+
assertRange('ModalChannelSelectMenuBuilder', 'max_values', this.data.maxValues, 1, 25);
|
|
26
|
+
if (this.data.minValues !== undefined && this.data.maxValues !== undefined && this.data.minValues > this.data.maxValues)
|
|
27
|
+
throw new ValidationError('ModalChannelSelectMenuBuilder', 'min_values', 'cannot exceed max_values');
|
|
28
|
+
return { type: ComponentType.ChannelSelect, custom_id: customId, placeholder: this.data.placeholder, min_values: this.data.minValues, max_values: this.data.maxValues, disabled: this.data.disabled, required: this.data.required, channel_types: this.data.channelTypes, default_values: this.data.defaultValues };
|
|
114
29
|
}
|
|
115
30
|
}
|