@minesa-org/mini-interaction 0.0.3 → 0.0.5

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.
@@ -0,0 +1,45 @@
1
+ import { type APIAutoModerationAction, type APIAutoModerationRuleTriggerMetadata, AutoModerationRuleEventType, AutoModerationRuleTriggerType } from "discord-api-types/v10";
2
+ import type { RESTPostAPIAutoModerationRuleJSONBody } from "discord-api-types/v10";
3
+ import type { JSONEncodable } from "./shared.js";
4
+ /**
5
+ * Shape describing initial data used to seed the auto moderation rule builder.
6
+ */
7
+ export type AutomodRuleBuilderData = Partial<RESTPostAPIAutoModerationRuleJSONBody> & {
8
+ name?: string;
9
+ event_type?: AutoModerationRuleEventType;
10
+ trigger_type?: AutoModerationRuleTriggerType;
11
+ actions?: APIAutoModerationAction[];
12
+ };
13
+ /** Builder that produces Discord auto moderation rule payloads. */
14
+ export declare class AutomodRuleBuilder implements JSONEncodable<RESTPostAPIAutoModerationRuleJSONBody> {
15
+ private data;
16
+ constructor(data?: AutomodRuleBuilderData);
17
+ /** Sets the name of the rule. */
18
+ setName(name: string): this;
19
+ /** Sets the event type that will trigger the rule. */
20
+ setEventType(eventType: AutoModerationRuleEventType): this;
21
+ /** Sets the trigger type for the rule. */
22
+ setTriggerType(triggerType: AutoModerationRuleTriggerType): this;
23
+ /**
24
+ * Assigns trigger metadata describing how the rule should match content.
25
+ */
26
+ setTriggerMetadata(metadata: APIAutoModerationRuleTriggerMetadata | null | undefined): this;
27
+ /**
28
+ * Replaces the actions executed when the rule triggers.
29
+ */
30
+ setActions(actions: Array<APIAutoModerationAction | JSONEncodable<APIAutoModerationAction>>): this;
31
+ /** Adds an action to execute when the rule triggers. */
32
+ addAction(action: APIAutoModerationAction | JSONEncodable<APIAutoModerationAction>): this;
33
+ /** Enables or disables the rule. */
34
+ setEnabled(enabled: boolean): this;
35
+ /** Replaces the list of exempt role ids. */
36
+ setExemptRoles(roleIds: string[]): this;
37
+ /** Adds an exempt role id if not already present. */
38
+ addExemptRole(roleId: string): this;
39
+ /** Replaces the list of exempt channel ids. */
40
+ setExemptChannels(channelIds: string[]): this;
41
+ /** Adds an exempt channel id if not already present. */
42
+ addExemptChannel(channelId: string): this;
43
+ /** Serialises the builder into a REST auto moderation rule payload. */
44
+ toJSON(): RESTPostAPIAutoModerationRuleJSONBody;
45
+ }
@@ -0,0 +1,151 @@
1
+ import { resolveJSONEncodable } from "./shared.js";
2
+ /** Builder that produces Discord auto moderation rule payloads. */
3
+ export class AutomodRuleBuilder {
4
+ data;
5
+ constructor(data = {}) {
6
+ this.data = {
7
+ ...data,
8
+ actions: data.actions?.map(cloneAutoModerationAction) ?? [],
9
+ trigger_metadata: data.trigger_metadata
10
+ ? cloneTriggerMetadata(data.trigger_metadata)
11
+ : undefined,
12
+ exempt_roles: data.exempt_roles ? [...data.exempt_roles] : undefined,
13
+ exempt_channels: data.exempt_channels
14
+ ? [...data.exempt_channels]
15
+ : undefined,
16
+ };
17
+ }
18
+ /** Sets the name of the rule. */
19
+ setName(name) {
20
+ this.data.name = name;
21
+ return this;
22
+ }
23
+ /** Sets the event type that will trigger the rule. */
24
+ setEventType(eventType) {
25
+ this.data.event_type = eventType;
26
+ return this;
27
+ }
28
+ /** Sets the trigger type for the rule. */
29
+ setTriggerType(triggerType) {
30
+ this.data.trigger_type = triggerType;
31
+ return this;
32
+ }
33
+ /**
34
+ * Assigns trigger metadata describing how the rule should match content.
35
+ */
36
+ setTriggerMetadata(metadata) {
37
+ this.data.trigger_metadata = metadata
38
+ ? cloneTriggerMetadata(metadata)
39
+ : undefined;
40
+ return this;
41
+ }
42
+ /**
43
+ * Replaces the actions executed when the rule triggers.
44
+ */
45
+ setActions(actions) {
46
+ this.data.actions = actions.map((action) => cloneAutoModerationAction(resolveJSONEncodable(action)));
47
+ return this;
48
+ }
49
+ /** Adds an action to execute when the rule triggers. */
50
+ addAction(action) {
51
+ if (!this.data.actions) {
52
+ this.data.actions = [];
53
+ }
54
+ this.data.actions.push(cloneAutoModerationAction(resolveJSONEncodable(action)));
55
+ return this;
56
+ }
57
+ /** Enables or disables the rule. */
58
+ setEnabled(enabled) {
59
+ this.data.enabled = enabled;
60
+ return this;
61
+ }
62
+ /** Replaces the list of exempt role ids. */
63
+ setExemptRoles(roleIds) {
64
+ this.data.exempt_roles = [...roleIds];
65
+ return this;
66
+ }
67
+ /** Adds an exempt role id if not already present. */
68
+ addExemptRole(roleId) {
69
+ if (!this.data.exempt_roles) {
70
+ this.data.exempt_roles = [];
71
+ }
72
+ if (!this.data.exempt_roles.includes(roleId)) {
73
+ this.data.exempt_roles.push(roleId);
74
+ }
75
+ return this;
76
+ }
77
+ /** Replaces the list of exempt channel ids. */
78
+ setExemptChannels(channelIds) {
79
+ this.data.exempt_channels = [...channelIds];
80
+ return this;
81
+ }
82
+ /** Adds an exempt channel id if not already present. */
83
+ addExemptChannel(channelId) {
84
+ if (!this.data.exempt_channels) {
85
+ this.data.exempt_channels = [];
86
+ }
87
+ if (!this.data.exempt_channels.includes(channelId)) {
88
+ this.data.exempt_channels.push(channelId);
89
+ }
90
+ return this;
91
+ }
92
+ /** Serialises the builder into a REST auto moderation rule payload. */
93
+ toJSON() {
94
+ const { name, event_type, trigger_type } = this.data;
95
+ const actions = this.data.actions ?? [];
96
+ if (!name) {
97
+ throw new Error("[AutomodRuleBuilder] name is required");
98
+ }
99
+ if (event_type === undefined) {
100
+ throw new Error("[AutomodRuleBuilder] event type is required");
101
+ }
102
+ if (trigger_type === undefined) {
103
+ throw new Error("[AutomodRuleBuilder] trigger type is required");
104
+ }
105
+ if (actions.length === 0) {
106
+ throw new Error("[AutomodRuleBuilder] at least one action is required");
107
+ }
108
+ return {
109
+ name,
110
+ event_type,
111
+ trigger_type,
112
+ trigger_metadata: this.data.trigger_metadata
113
+ ? cloneTriggerMetadata(this.data.trigger_metadata)
114
+ : undefined,
115
+ actions: actions.map(cloneAutoModerationAction),
116
+ enabled: this.data.enabled,
117
+ exempt_roles: this.data.exempt_roles
118
+ ? [...this.data.exempt_roles]
119
+ : undefined,
120
+ exempt_channels: this.data.exempt_channels
121
+ ? [...this.data.exempt_channels]
122
+ : undefined,
123
+ };
124
+ }
125
+ }
126
+ function cloneTriggerMetadata(metadata) {
127
+ return {
128
+ keyword_filter: metadata.keyword_filter
129
+ ? [...metadata.keyword_filter]
130
+ : undefined,
131
+ presets: metadata.presets ? [...metadata.presets] : undefined,
132
+ allow_list: metadata.allow_list ? [...metadata.allow_list] : undefined,
133
+ regex_patterns: metadata.regex_patterns
134
+ ? [...metadata.regex_patterns]
135
+ : undefined,
136
+ mention_total_limit: metadata.mention_total_limit,
137
+ mention_raid_protection_enabled: metadata.mention_raid_protection_enabled,
138
+ };
139
+ }
140
+ function cloneAutoModerationAction(action) {
141
+ return {
142
+ type: action.type,
143
+ metadata: action.metadata
144
+ ? {
145
+ channel_id: action.metadata.channel_id,
146
+ duration_seconds: action.metadata.duration_seconds,
147
+ custom_message: action.metadata.custom_message,
148
+ }
149
+ : undefined,
150
+ };
151
+ }
@@ -0,0 +1,33 @@
1
+ import { type APIEmbed, type APIEmbedAuthor, type APIEmbedField, type APIEmbedFooter, type APIEmbedImage, type APIEmbedThumbnail } from "discord-api-types/v10";
2
+ import type { JSONEncodable } from "./shared.js";
3
+ /** Shape describing data used to seed an embed builder instance. */
4
+ export type EmbedBuilderData = Partial<APIEmbed>;
5
+ /** Builder for Discord embed payloads. */
6
+ export declare class EmbedBuilder implements JSONEncodable<APIEmbed> {
7
+ private data;
8
+ constructor(data?: EmbedBuilderData);
9
+ /** Sets or clears the embed title. */
10
+ setTitle(title: string | null | undefined): this;
11
+ /** Sets or clears the embed description. */
12
+ setDescription(description: string | null | undefined): this;
13
+ /** Sets or clears the embed URL. */
14
+ setURL(url: string | null | undefined): this;
15
+ /** Sets or clears the embed color. */
16
+ setColor(color: number | null | undefined): this;
17
+ /** Sets the timestamp value using either a Date or ISO string. */
18
+ setTimestamp(timestamp: Date | number | string | null | undefined): this;
19
+ /** Sets or clears the footer. */
20
+ setFooter(footer: APIEmbedFooter | null | undefined): this;
21
+ /** Sets or clears the image. */
22
+ setImage(image: APIEmbedImage | null | undefined): this;
23
+ /** Sets or clears the thumbnail. */
24
+ setThumbnail(thumbnail: APIEmbedThumbnail | null | undefined): this;
25
+ /** Sets or clears the author. */
26
+ setAuthor(author: APIEmbedAuthor | null | undefined): this;
27
+ /** Replaces the embed fields with the provided values. */
28
+ setFields(fields: APIEmbedField[]): this;
29
+ /** Appends fields to the embed. */
30
+ addFields(...fields: APIEmbedField[]): this;
31
+ /** Serialises the builder into an API compatible embed payload. */
32
+ toJSON(): APIEmbed;
33
+ }
@@ -0,0 +1,94 @@
1
+ /** Builder for Discord embed payloads. */
2
+ export class EmbedBuilder {
3
+ data;
4
+ constructor(data = {}) {
5
+ this.data = {
6
+ ...data,
7
+ footer: data.footer ? { ...data.footer } : undefined,
8
+ image: data.image ? { ...data.image } : undefined,
9
+ thumbnail: data.thumbnail ? { ...data.thumbnail } : undefined,
10
+ author: data.author ? { ...data.author } : undefined,
11
+ fields: data.fields ? data.fields.map((field) => ({ ...field })) : [],
12
+ };
13
+ }
14
+ /** Sets or clears the embed title. */
15
+ setTitle(title) {
16
+ this.data.title = title ?? undefined;
17
+ return this;
18
+ }
19
+ /** Sets or clears the embed description. */
20
+ setDescription(description) {
21
+ this.data.description = description ?? undefined;
22
+ return this;
23
+ }
24
+ /** Sets or clears the embed URL. */
25
+ setURL(url) {
26
+ this.data.url = url ?? undefined;
27
+ return this;
28
+ }
29
+ /** Sets or clears the embed color. */
30
+ setColor(color) {
31
+ this.data.color = color ?? undefined;
32
+ return this;
33
+ }
34
+ /** Sets the timestamp value using either a Date or ISO string. */
35
+ setTimestamp(timestamp) {
36
+ if (timestamp === null || timestamp === undefined) {
37
+ this.data.timestamp = undefined;
38
+ }
39
+ else if (timestamp instanceof Date) {
40
+ this.data.timestamp = timestamp.toISOString();
41
+ }
42
+ else if (typeof timestamp === "number") {
43
+ this.data.timestamp = new Date(timestamp).toISOString();
44
+ }
45
+ else {
46
+ this.data.timestamp = new Date(timestamp).toISOString();
47
+ }
48
+ return this;
49
+ }
50
+ /** Sets or clears the footer. */
51
+ setFooter(footer) {
52
+ this.data.footer = footer ? { ...footer } : undefined;
53
+ return this;
54
+ }
55
+ /** Sets or clears the image. */
56
+ setImage(image) {
57
+ this.data.image = image ? { ...image } : undefined;
58
+ return this;
59
+ }
60
+ /** Sets or clears the thumbnail. */
61
+ setThumbnail(thumbnail) {
62
+ this.data.thumbnail = thumbnail ? { ...thumbnail } : undefined;
63
+ return this;
64
+ }
65
+ /** Sets or clears the author. */
66
+ setAuthor(author) {
67
+ this.data.author = author ? { ...author } : undefined;
68
+ return this;
69
+ }
70
+ /** Replaces the embed fields with the provided values. */
71
+ setFields(fields) {
72
+ this.data.fields = fields.map((field) => ({ ...field }));
73
+ return this;
74
+ }
75
+ /** Appends fields to the embed. */
76
+ addFields(...fields) {
77
+ if (!this.data.fields) {
78
+ this.data.fields = [];
79
+ }
80
+ this.data.fields.push(...fields.map((field) => ({ ...field })));
81
+ return this;
82
+ }
83
+ /** Serialises the builder into an API compatible embed payload. */
84
+ toJSON() {
85
+ return {
86
+ ...this.data,
87
+ footer: this.data.footer ? { ...this.data.footer } : undefined,
88
+ image: this.data.image ? { ...this.data.image } : undefined,
89
+ thumbnail: this.data.thumbnail ? { ...this.data.thumbnail } : undefined,
90
+ author: this.data.author ? { ...this.data.author } : undefined,
91
+ fields: this.data.fields?.map((field) => ({ ...field })),
92
+ };
93
+ }
94
+ }
@@ -0,0 +1,131 @@
1
+ import type { APIComponentInContainer, APIContainerComponent, APIMediaGalleryComponent, APIMediaGalleryItem, APISectionAccessoryComponent, APISectionComponent, APISeparatorComponent, APITextDisplayComponent, APIThumbnailComponent, APIUnfurledMediaItem } from "discord-api-types/v10";
2
+ import { SeparatorSpacingSize } from "discord-api-types/v10";
3
+ import type { JSONEncodable } from "./shared.js";
4
+ /** Structured message container payload. */
5
+ export type MiniContainerComponent = APIContainerComponent;
6
+ /** Section within a structured message container. */
7
+ export type MiniSectionComponent = APISectionComponent;
8
+ /** Text display structured message component. */
9
+ export type MiniTextDisplayComponent = APITextDisplayComponent;
10
+ /** Separator structured message component. */
11
+ export type MiniSeparatorComponent = APISeparatorComponent;
12
+ /** Thumbnail payload used within sections. */
13
+ export type MiniThumbnailComponent = APIThumbnailComponent;
14
+ /** Gallery item payload. */
15
+ export type MiniGalleryItemComponent = APIMediaGalleryItem;
16
+ /** Gallery structured message component. */
17
+ export type MiniGalleryComponent = APIMediaGalleryComponent;
18
+ /** Union of supported components within a container. */
19
+ export type MiniContentComponent = APIComponentInContainer;
20
+ /** Union of supported section accessory components. */
21
+ export type MiniSectionAccessoryComponent = APISectionAccessoryComponent;
22
+ /** Data accepted by the container builder constructor. */
23
+ export type MiniContainerBuilderData = Partial<Omit<MiniContainerComponent, "components">> & {
24
+ components?: Array<MiniContentComponent | JSONEncodable<MiniContentComponent>>;
25
+ };
26
+ /** Data accepted by the section builder constructor. */
27
+ export type MiniSectionBuilderData = Partial<Omit<MiniSectionComponent, "components" | "accessory">> & {
28
+ components?: Array<MiniTextDisplayComponent | JSONEncodable<MiniTextDisplayComponent>>;
29
+ accessory?: MiniSectionAccessoryComponent | JSONEncodable<MiniSectionAccessoryComponent>;
30
+ };
31
+ /** Data accepted by the text display builder constructor. */
32
+ export type MiniTextDisplayBuilderData = Partial<MiniTextDisplayComponent>;
33
+ /** Data accepted by the separator builder constructor. */
34
+ export type MiniSeparatorBuilderData = Partial<MiniSeparatorComponent>;
35
+ /** Data accepted by the gallery builder constructor. */
36
+ export type MiniGalleryBuilderData = Partial<Omit<MiniGalleryComponent, "items">> & {
37
+ items?: Array<MiniGalleryItemComponent | JSONEncodable<MiniGalleryItemComponent>>;
38
+ };
39
+ /** Data accepted by the gallery item builder constructor. */
40
+ export type MiniGalleryItemBuilderData = Partial<MiniGalleryItemComponent>;
41
+ /** Data accepted by the thumbnail builder constructor. */
42
+ export type MiniThumbnailBuilderData = Partial<MiniThumbnailComponent>;
43
+ /** Builder for structured message container payloads. */
44
+ export declare class MiniContainerBuilder implements JSONEncodable<MiniContainerComponent> {
45
+ private data;
46
+ constructor(data?: MiniContainerBuilderData);
47
+ /** Replaces the components contained in the container. */
48
+ setComponents(components: Array<MiniContentComponent | JSONEncodable<MiniContentComponent>>): this;
49
+ /** Replaces the sections contained in the container. */
50
+ setSections(sections: Array<MiniSectionComponent | JSONEncodable<MiniSectionComponent>>): this;
51
+ /** Adds a component to the container. */
52
+ addComponent(component: MiniContentComponent | JSONEncodable<MiniContentComponent>): this;
53
+ /** Adds a section to the container. */
54
+ addSection(section: MiniSectionComponent | JSONEncodable<MiniSectionComponent>): this;
55
+ /** Sets or clears the accent colour on the container. */
56
+ setAccentColor(color: number | null | undefined): this;
57
+ /** Marks the container as a spoiler. */
58
+ setSpoiler(spoiler: boolean | null | undefined): this;
59
+ /** Serialises the container into a structured message payload. */
60
+ toJSON(): MiniContainerComponent;
61
+ }
62
+ /** Builder for structured message sections. */
63
+ export declare class MiniSectionBuilder implements JSONEncodable<MiniSectionComponent> {
64
+ private data;
65
+ constructor(data?: MiniSectionBuilderData);
66
+ /** Replaces the text components within the section. */
67
+ setComponents(components: Array<MiniTextDisplayComponent | JSONEncodable<MiniTextDisplayComponent>>): this;
68
+ /** Adds a text component to the section. */
69
+ addComponent(component: MiniTextDisplayComponent | JSONEncodable<MiniTextDisplayComponent>): this;
70
+ /** Sets or clears the accessory component. */
71
+ setAccessory(accessory: MiniSectionAccessoryComponent | JSONEncodable<MiniSectionAccessoryComponent> | null | undefined): this;
72
+ /** Serialises the section into a structured message payload. */
73
+ toJSON(): MiniSectionComponent;
74
+ }
75
+ /** Builder for structured message text display components. */
76
+ export declare class MiniTextDisplayBuilder implements JSONEncodable<MiniTextDisplayComponent> {
77
+ private data;
78
+ constructor(data?: MiniTextDisplayBuilderData);
79
+ /** Sets or clears the primary text content. */
80
+ setContent(content: string | null | undefined): this;
81
+ /** Serialises the component into a structured message payload. */
82
+ toJSON(): MiniTextDisplayComponent;
83
+ }
84
+ /** Builder for structured message separator components. */
85
+ export declare class MiniSeparatorBuilder implements JSONEncodable<MiniSeparatorComponent> {
86
+ private data;
87
+ constructor(data?: MiniSeparatorBuilderData);
88
+ /** Sets whether the separator should render a divider. */
89
+ setDivider(divider: boolean | null | undefined): this;
90
+ /** Sets the separator spacing value. */
91
+ setSpacing(spacing: SeparatorSpacingSize | null | undefined): this;
92
+ /** Serialises the separator payload. */
93
+ toJSON(): MiniSeparatorComponent;
94
+ }
95
+ /** Builder for structured message thumbnails. */
96
+ export declare class MiniThumbnailBuilder implements JSONEncodable<MiniThumbnailComponent> {
97
+ private data;
98
+ constructor(data?: MiniThumbnailBuilderData);
99
+ /** Sets the media payload for the thumbnail. */
100
+ setMedia(media: APIUnfurledMediaItem | null | undefined): this;
101
+ /** Sets the thumbnail description. */
102
+ setDescription(description: string | null | undefined): this;
103
+ /** Marks the thumbnail as a spoiler. */
104
+ setSpoiler(spoiler: boolean | null | undefined): this;
105
+ /** Serialises the thumbnail payload. */
106
+ toJSON(): MiniThumbnailComponent;
107
+ }
108
+ /** Builder for structured message gallery items. */
109
+ export declare class MiniGalleryItemBuilder implements JSONEncodable<MiniGalleryItemComponent> {
110
+ private data;
111
+ constructor(data?: MiniGalleryItemBuilderData);
112
+ /** Sets the media item payload. */
113
+ setMedia(media: APIUnfurledMediaItem | null | undefined): this;
114
+ /** Sets the media description. */
115
+ setDescription(description: string | null | undefined): this;
116
+ /** Marks the media item as a spoiler. */
117
+ setSpoiler(spoiler: boolean | null | undefined): this;
118
+ /** Serialises the gallery item payload. */
119
+ toJSON(): MiniGalleryItemComponent;
120
+ }
121
+ /** Builder for structured message gallery components. */
122
+ export declare class MiniGalleryBuilder implements JSONEncodable<MiniGalleryComponent> {
123
+ private data;
124
+ constructor(data?: MiniGalleryBuilderData);
125
+ /** Replaces the gallery items. */
126
+ setItems(items: Array<MiniGalleryItemComponent | JSONEncodable<MiniGalleryItemComponent>>): this;
127
+ /** Adds a gallery item to the payload. */
128
+ addItem(item: MiniGalleryItemComponent | JSONEncodable<MiniGalleryItemComponent>): this;
129
+ /** Serialises the gallery payload. */
130
+ toJSON(): MiniGalleryComponent;
131
+ }
@@ -0,0 +1,345 @@
1
+ import { ComponentType } from "discord-api-types/v10";
2
+ import { resolveJSONEncodable } from "./shared.js";
3
+ function cloneContainerComponent(component) {
4
+ const resolved = resolveJSONEncodable(component);
5
+ return { ...resolved };
6
+ }
7
+ function cloneAccessory(accessory) {
8
+ const resolved = resolveJSONEncodable(accessory);
9
+ return { ...resolved };
10
+ }
11
+ /** Builder for structured message container payloads. */
12
+ export class MiniContainerBuilder {
13
+ data;
14
+ constructor(data = {}) {
15
+ const components = data.components
16
+ ? data.components.map((component) => cloneContainerComponent(component))
17
+ : [];
18
+ this.data = {
19
+ ...data,
20
+ type: ComponentType.Container,
21
+ components,
22
+ };
23
+ }
24
+ /** Replaces the components contained in the container. */
25
+ setComponents(components) {
26
+ this.data.components = components.map((component) => cloneContainerComponent(component));
27
+ return this;
28
+ }
29
+ /** Replaces the sections contained in the container. */
30
+ setSections(sections) {
31
+ return this.setComponents(sections);
32
+ }
33
+ /** Adds a component to the container. */
34
+ addComponent(component) {
35
+ this.data.components ??= [];
36
+ this.data.components.push(cloneContainerComponent(component));
37
+ return this;
38
+ }
39
+ /** Adds a section to the container. */
40
+ addSection(section) {
41
+ return this.addComponent(section);
42
+ }
43
+ /** Sets or clears the accent colour on the container. */
44
+ setAccentColor(color) {
45
+ if (color === undefined) {
46
+ delete this.data.accent_color;
47
+ }
48
+ else {
49
+ this.data.accent_color = color ?? null;
50
+ }
51
+ return this;
52
+ }
53
+ /** Marks the container as a spoiler. */
54
+ setSpoiler(spoiler) {
55
+ if (spoiler === null || spoiler === undefined) {
56
+ delete this.data.spoiler;
57
+ }
58
+ else {
59
+ this.data.spoiler = spoiler;
60
+ }
61
+ return this;
62
+ }
63
+ /** Serialises the container into a structured message payload. */
64
+ toJSON() {
65
+ const components = this.data.components ?? [];
66
+ return {
67
+ ...this.data,
68
+ type: ComponentType.Container,
69
+ components: components.map((component) => ({ ...component })),
70
+ };
71
+ }
72
+ }
73
+ /** Builder for structured message sections. */
74
+ export class MiniSectionBuilder {
75
+ data;
76
+ constructor(data = {}) {
77
+ const components = data.components
78
+ ? data.components.map((component) => cloneTextDisplayComponent(component))
79
+ : [];
80
+ const rawAccessory = data.accessory;
81
+ const accessory = rawAccessory === undefined || rawAccessory === null
82
+ ? undefined
83
+ : cloneAccessory(rawAccessory);
84
+ this.data = {
85
+ ...data,
86
+ type: ComponentType.Section,
87
+ components,
88
+ accessory,
89
+ };
90
+ }
91
+ /** Replaces the text components within the section. */
92
+ setComponents(components) {
93
+ this.data.components = components.map((component) => cloneTextDisplayComponent(component));
94
+ return this;
95
+ }
96
+ /** Adds a text component to the section. */
97
+ addComponent(component) {
98
+ this.data.components ??= [];
99
+ this.data.components.push(cloneTextDisplayComponent(component));
100
+ return this;
101
+ }
102
+ /** Sets or clears the accessory component. */
103
+ setAccessory(accessory) {
104
+ if (accessory === null || accessory === undefined) {
105
+ this.data.accessory = undefined;
106
+ }
107
+ else {
108
+ this.data.accessory = cloneAccessory(accessory);
109
+ }
110
+ return this;
111
+ }
112
+ /** Serialises the section into a structured message payload. */
113
+ toJSON() {
114
+ const components = this.data.components ?? [];
115
+ const accessory = this.data.accessory ? { ...this.data.accessory } : undefined;
116
+ if (!accessory) {
117
+ throw new Error("[MiniSectionBuilder] accessory is required for sections");
118
+ }
119
+ return {
120
+ ...this.data,
121
+ type: ComponentType.Section,
122
+ components: components.map((component) => ({ ...component })),
123
+ accessory,
124
+ };
125
+ }
126
+ }
127
+ function cloneTextDisplayComponent(component) {
128
+ const resolved = resolveJSONEncodable(component);
129
+ return { ...resolved };
130
+ }
131
+ /** Builder for structured message text display components. */
132
+ export class MiniTextDisplayBuilder {
133
+ data;
134
+ constructor(data = {}) {
135
+ this.data = {
136
+ ...data,
137
+ type: ComponentType.TextDisplay,
138
+ };
139
+ }
140
+ /** Sets or clears the primary text content. */
141
+ setContent(content) {
142
+ if (content === null || content === undefined) {
143
+ delete this.data.content;
144
+ }
145
+ else {
146
+ this.data.content = content;
147
+ }
148
+ return this;
149
+ }
150
+ /** Serialises the component into a structured message payload. */
151
+ toJSON() {
152
+ if (!this.data.content) {
153
+ throw new Error("[MiniTextDisplayBuilder] content is required for text displays");
154
+ }
155
+ return {
156
+ ...this.data,
157
+ type: ComponentType.TextDisplay,
158
+ };
159
+ }
160
+ }
161
+ /** Builder for structured message separator components. */
162
+ export class MiniSeparatorBuilder {
163
+ data;
164
+ constructor(data = {}) {
165
+ this.data = {
166
+ ...data,
167
+ type: ComponentType.Separator,
168
+ };
169
+ }
170
+ /** Sets whether the separator should render a divider. */
171
+ setDivider(divider) {
172
+ if (divider === null || divider === undefined) {
173
+ delete this.data.divider;
174
+ }
175
+ else {
176
+ this.data.divider = divider;
177
+ }
178
+ return this;
179
+ }
180
+ /** Sets the separator spacing value. */
181
+ setSpacing(spacing) {
182
+ if (spacing === null || spacing === undefined) {
183
+ delete this.data.spacing;
184
+ }
185
+ else {
186
+ this.data.spacing = spacing;
187
+ }
188
+ return this;
189
+ }
190
+ /** Serialises the separator payload. */
191
+ toJSON() {
192
+ return {
193
+ ...this.data,
194
+ type: ComponentType.Separator,
195
+ };
196
+ }
197
+ }
198
+ /** Builder for structured message thumbnails. */
199
+ export class MiniThumbnailBuilder {
200
+ data;
201
+ constructor(data = {}) {
202
+ this.data = {
203
+ ...data,
204
+ type: ComponentType.Thumbnail,
205
+ media: data.media ? { ...data.media } : undefined,
206
+ };
207
+ }
208
+ /** Sets the media payload for the thumbnail. */
209
+ setMedia(media) {
210
+ if (media === null || media === undefined) {
211
+ delete this.data.media;
212
+ }
213
+ else {
214
+ this.data.media = { ...media };
215
+ }
216
+ return this;
217
+ }
218
+ /** Sets the thumbnail description. */
219
+ setDescription(description) {
220
+ if (description === null || description === undefined) {
221
+ delete this.data.description;
222
+ }
223
+ else {
224
+ this.data.description = description;
225
+ }
226
+ return this;
227
+ }
228
+ /** Marks the thumbnail as a spoiler. */
229
+ setSpoiler(spoiler) {
230
+ if (spoiler === null || spoiler === undefined) {
231
+ delete this.data.spoiler;
232
+ }
233
+ else {
234
+ this.data.spoiler = spoiler;
235
+ }
236
+ return this;
237
+ }
238
+ /** Serialises the thumbnail payload. */
239
+ toJSON() {
240
+ if (!this.data.media) {
241
+ throw new Error("[MiniThumbnailBuilder] media is required for thumbnails");
242
+ }
243
+ return {
244
+ ...this.data,
245
+ type: ComponentType.Thumbnail,
246
+ };
247
+ }
248
+ }
249
+ /** Builder for structured message gallery items. */
250
+ export class MiniGalleryItemBuilder {
251
+ data;
252
+ constructor(data = {}) {
253
+ this.data = {
254
+ ...data,
255
+ media: data.media ? { ...data.media } : undefined,
256
+ };
257
+ }
258
+ /** Sets the media item payload. */
259
+ setMedia(media) {
260
+ if (media === null || media === undefined) {
261
+ delete this.data.media;
262
+ }
263
+ else {
264
+ this.data.media = { ...media };
265
+ }
266
+ return this;
267
+ }
268
+ /** Sets the media description. */
269
+ setDescription(description) {
270
+ if (description === null || description === undefined) {
271
+ delete this.data.description;
272
+ }
273
+ else {
274
+ this.data.description = description;
275
+ }
276
+ return this;
277
+ }
278
+ /** Marks the media item as a spoiler. */
279
+ setSpoiler(spoiler) {
280
+ if (spoiler === null || spoiler === undefined) {
281
+ delete this.data.spoiler;
282
+ }
283
+ else {
284
+ this.data.spoiler = spoiler;
285
+ }
286
+ return this;
287
+ }
288
+ /** Serialises the gallery item payload. */
289
+ toJSON() {
290
+ if (!this.data.media) {
291
+ throw new Error("[MiniGalleryItemBuilder] media is required for gallery items");
292
+ }
293
+ return {
294
+ ...this.data,
295
+ media: { ...this.data.media },
296
+ };
297
+ }
298
+ }
299
+ /** Builder for structured message gallery components. */
300
+ export class MiniGalleryBuilder {
301
+ data;
302
+ constructor(data = {}) {
303
+ const items = data.items
304
+ ? data.items.map((item) => resolveGalleryItem(item))
305
+ : [];
306
+ this.data = {
307
+ ...data,
308
+ type: ComponentType.MediaGallery,
309
+ items,
310
+ };
311
+ }
312
+ /** Replaces the gallery items. */
313
+ setItems(items) {
314
+ this.data.items = items.map((item) => resolveGalleryItem(item));
315
+ return this;
316
+ }
317
+ /** Adds a gallery item to the payload. */
318
+ addItem(item) {
319
+ this.data.items ??= [];
320
+ this.data.items.push(resolveGalleryItem(item));
321
+ return this;
322
+ }
323
+ /** Serialises the gallery payload. */
324
+ toJSON() {
325
+ const items = this.data.items ?? [];
326
+ if (items.length === 0) {
327
+ throw new Error("[MiniGalleryBuilder] at least one gallery item is required");
328
+ }
329
+ return {
330
+ ...this.data,
331
+ type: ComponentType.MediaGallery,
332
+ items: items.map((item) => ({ ...item })),
333
+ };
334
+ }
335
+ }
336
+ function resolveGalleryItem(item) {
337
+ const resolved = resolveJSONEncodable(item);
338
+ if (!resolved.media) {
339
+ throw new Error("[MiniGalleryBuilder] gallery items require media");
340
+ }
341
+ return {
342
+ ...resolved,
343
+ media: { ...resolved.media },
344
+ };
345
+ }
@@ -11,4 +11,10 @@ export { ChannelSelectMenuBuilder } from "./ChannelSelectMenuBuilder.js";
11
11
  export type { ChannelSelectMenuBuilderData } from "./ChannelSelectMenuBuilder.js";
12
12
  export { ModalBuilder } from "./ModalBuilder.js";
13
13
  export type { ModalBuilderData, ModalComponentLike } from "./ModalBuilder.js";
14
+ export { AutomodRuleBuilder } from "./AutomodRuleBuilder.js";
15
+ export type { AutomodRuleBuilderData } from "./AutomodRuleBuilder.js";
16
+ export { EmbedBuilder } from "./EmbedBuilder.js";
17
+ export type { EmbedBuilderData } from "./EmbedBuilder.js";
18
+ export { MiniContainerBuilder, MiniSectionBuilder, MiniTextDisplayBuilder, MiniSeparatorBuilder, MiniGalleryBuilder, MiniGalleryItemBuilder, MiniThumbnailBuilder, } from "./MiniContainerBuilder.js";
19
+ export type { MiniContainerBuilderData, MiniSectionBuilderData, MiniTextDisplayBuilderData, MiniSeparatorBuilderData, MiniGalleryBuilderData, MiniGalleryItemBuilderData, MiniThumbnailBuilderData, MiniContainerComponent, MiniSectionComponent, MiniTextDisplayComponent, MiniSeparatorComponent, MiniGalleryComponent, MiniGalleryItemComponent, MiniThumbnailComponent, MiniContentComponent, MiniSectionAccessoryComponent, } from "./MiniContainerBuilder.js";
14
20
  export type { JSONEncodable } from "./shared.js";
@@ -5,3 +5,6 @@ export { StringSelectMenuBuilder } from "./StringSelectMenuBuilder.js";
5
5
  export { RoleSelectMenuBuilder } from "./RoleSelectMenuBuilder.js";
6
6
  export { ChannelSelectMenuBuilder } from "./ChannelSelectMenuBuilder.js";
7
7
  export { ModalBuilder } from "./ModalBuilder.js";
8
+ export { AutomodRuleBuilder } from "./AutomodRuleBuilder.js";
9
+ export { EmbedBuilder } from "./EmbedBuilder.js";
10
+ export { MiniContainerBuilder, MiniSectionBuilder, MiniTextDisplayBuilder, MiniSeparatorBuilder, MiniGalleryBuilder, MiniGalleryItemBuilder, MiniThumbnailBuilder, } from "./MiniContainerBuilder.js";
@@ -8,6 +8,7 @@ export type MiniInteractionOptions = {
8
8
  applicationId: string;
9
9
  publicKey: string;
10
10
  commandsDirectory?: string | false;
11
+ componentsDirectory?: string | false;
11
12
  fetchImplementation?: typeof fetch;
12
13
  verifyKeyImplementation?: VerifyKeyFunction;
13
14
  };
@@ -58,14 +59,17 @@ export declare class MiniInteraction {
58
59
  private readonly fetchImpl;
59
60
  private readonly verifyKeyImpl;
60
61
  private readonly commandsDirectory;
62
+ private readonly componentsDirectory;
61
63
  private readonly commands;
62
64
  private readonly componentHandlers;
63
65
  private commandsLoaded;
64
66
  private loadCommandsPromise;
67
+ private componentsLoaded;
68
+ private loadComponentsPromise;
65
69
  /**
66
70
  * Creates a new MiniInteraction client with optional command auto-loading and custom runtime hooks.
67
71
  */
68
- constructor({ applicationId, publicKey, commandsDirectory, fetchImplementation, verifyKeyImplementation, }: MiniInteractionOptions);
72
+ constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, fetchImplementation, verifyKeyImplementation, }: MiniInteractionOptions);
69
73
  /**
70
74
  * Registers a single command handler with the client.
71
75
  *
@@ -90,6 +94,12 @@ export declare class MiniInteraction {
90
94
  * @param components - The component definitions to register.
91
95
  */
92
96
  useComponents(components: MiniInteractionComponent[]): this;
97
+ /**
98
+ * Recursively loads components from the configured components directory.
99
+ *
100
+ * @param directory - Optional directory override for component discovery.
101
+ */
102
+ loadComponentsFromDirectory(directory?: string): Promise<this>;
93
103
  /**
94
104
  * Recursively loads commands from the configured commands directory.
95
105
  *
@@ -143,19 +153,27 @@ export declare class MiniInteraction {
143
153
  /**
144
154
  * Recursively collects all command module file paths from the target directory.
145
155
  */
146
- private collectCommandFiles;
156
+ private collectModuleFiles;
147
157
  /**
148
158
  * Determines whether the provided file path matches a supported command file extension.
149
159
  */
150
- private isSupportedCommandFile;
160
+ private isSupportedModuleFile;
151
161
  /**
152
162
  * Dynamically imports and validates a command module from disk.
153
163
  */
154
164
  private importCommandModule;
165
+ /**
166
+ * Dynamically imports and validates a component module from disk.
167
+ */
168
+ private importComponentModule;
155
169
  /**
156
170
  * Normalises the request body into a UTF-8 string for signature validation and parsing.
157
171
  */
158
172
  private normalizeBody;
173
+ /**
174
+ * Ensures components have been loaded from disk once before being accessed.
175
+ */
176
+ private ensureComponentsLoaded;
159
177
  /**
160
178
  * Ensures commands have been loaded from disk once before being accessed.
161
179
  */
@@ -7,8 +7,8 @@ import { verifyKey } from "discord-interactions";
7
7
  import { DISCORD_BASE_URL } from "../utils/constants.js";
8
8
  import { createCommandInteraction } from "../utils/CommandInteractionOptions.js";
9
9
  import { createMessageComponentInteraction, } from "../utils/MessageComponentInteraction.js";
10
- /** File extensions that are treated as command modules when auto-loading. */
11
- const SUPPORTED_COMMAND_EXTENSIONS = new Set([
10
+ /** File extensions that are treated as loadable modules when auto-loading. */
11
+ const SUPPORTED_MODULE_EXTENSIONS = new Set([
12
12
  ".js",
13
13
  ".mjs",
14
14
  ".cjs",
@@ -26,14 +26,17 @@ export class MiniInteraction {
26
26
  fetchImpl;
27
27
  verifyKeyImpl;
28
28
  commandsDirectory;
29
+ componentsDirectory;
29
30
  commands = new Map();
30
31
  componentHandlers = new Map();
31
32
  commandsLoaded = false;
32
33
  loadCommandsPromise = null;
34
+ componentsLoaded = false;
35
+ loadComponentsPromise = null;
33
36
  /**
34
37
  * Creates a new MiniInteraction client with optional command auto-loading and custom runtime hooks.
35
38
  */
36
- constructor({ applicationId, publicKey, commandsDirectory, fetchImplementation, verifyKeyImplementation, }) {
39
+ constructor({ applicationId, publicKey, commandsDirectory, componentsDirectory, fetchImplementation, verifyKeyImplementation, }) {
37
40
  if (!applicationId) {
38
41
  throw new Error("[MiniInteraction] applicationId is required");
39
42
  }
@@ -53,6 +56,10 @@ export class MiniInteraction {
53
56
  commandsDirectory === false
54
57
  ? null
55
58
  : this.resolveCommandsDirectory(commandsDirectory);
59
+ this.componentsDirectory =
60
+ componentsDirectory === false
61
+ ? null
62
+ : this.resolveComponentsDirectory(componentsDirectory);
56
63
  }
57
64
  /**
58
65
  * Registers a single command handler with the client.
@@ -111,6 +118,42 @@ export class MiniInteraction {
111
118
  }
112
119
  return this;
113
120
  }
121
+ /**
122
+ * Recursively loads components from the configured components directory.
123
+ *
124
+ * @param directory - Optional directory override for component discovery.
125
+ */
126
+ async loadComponentsFromDirectory(directory) {
127
+ const targetDirectory = directory !== undefined
128
+ ? this.resolveComponentsDirectory(directory)
129
+ : this.componentsDirectory;
130
+ if (!targetDirectory) {
131
+ throw new Error("[MiniInteraction] Components directory support disabled. Provide a directory path.");
132
+ }
133
+ const exists = await this.pathExists(targetDirectory);
134
+ if (!exists) {
135
+ this.componentsLoaded = true;
136
+ console.warn(`[MiniInteraction] Components directory "${targetDirectory}" does not exist. Skipping component auto-load.`);
137
+ return this;
138
+ }
139
+ const files = await this.collectModuleFiles(targetDirectory);
140
+ if (files.length === 0) {
141
+ this.componentsLoaded = true;
142
+ console.warn(`[MiniInteraction] No component files found under "${targetDirectory}".`);
143
+ return this;
144
+ }
145
+ for (const file of files) {
146
+ const components = await this.importComponentModule(file);
147
+ if (components.length === 0) {
148
+ continue;
149
+ }
150
+ for (const component of components) {
151
+ this.useComponent(component);
152
+ }
153
+ }
154
+ this.componentsLoaded = true;
155
+ return this;
156
+ }
114
157
  /**
115
158
  * Recursively loads commands from the configured commands directory.
116
159
  *
@@ -129,7 +172,7 @@ export class MiniInteraction {
129
172
  console.warn(`[MiniInteraction] Commands directory "${targetDirectory}" does not exist. Skipping command auto-load.`);
130
173
  return this;
131
174
  }
132
- const files = await this.collectCommandFiles(targetDirectory);
175
+ const files = await this.collectModuleFiles(targetDirectory);
133
176
  if (files.length === 0) {
134
177
  this.commandsLoaded = true;
135
178
  console.warn(`[MiniInteraction] No command files found under "${targetDirectory}".`);
@@ -385,7 +428,7 @@ export class MiniInteraction {
385
428
  /**
386
429
  * Recursively collects all command module file paths from the target directory.
387
430
  */
388
- async collectCommandFiles(directory) {
431
+ async collectModuleFiles(directory) {
389
432
  const entries = await readdir(directory, { withFileTypes: true });
390
433
  const files = [];
391
434
  for (const entry of entries) {
@@ -394,11 +437,11 @@ export class MiniInteraction {
394
437
  }
395
438
  const fullPath = path.join(directory, entry.name);
396
439
  if (entry.isDirectory()) {
397
- const nestedFiles = await this.collectCommandFiles(fullPath);
440
+ const nestedFiles = await this.collectModuleFiles(fullPath);
398
441
  files.push(...nestedFiles);
399
442
  continue;
400
443
  }
401
- if (entry.isFile() && this.isSupportedCommandFile(fullPath)) {
444
+ if (entry.isFile() && this.isSupportedModuleFile(fullPath)) {
402
445
  files.push(fullPath);
403
446
  }
404
447
  }
@@ -407,8 +450,8 @@ export class MiniInteraction {
407
450
  /**
408
451
  * Determines whether the provided file path matches a supported command file extension.
409
452
  */
410
- isSupportedCommandFile(filePath) {
411
- return SUPPORTED_COMMAND_EXTENSIONS.has(path.extname(filePath).toLowerCase());
453
+ isSupportedModuleFile(filePath) {
454
+ return SUPPORTED_MODULE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
412
455
  }
413
456
  /**
414
457
  * Dynamically imports and validates a command module from disk.
@@ -441,6 +484,47 @@ export class MiniInteraction {
441
484
  return null;
442
485
  }
443
486
  }
487
+ /**
488
+ * Dynamically imports and validates a component module from disk.
489
+ */
490
+ async importComponentModule(absolutePath) {
491
+ try {
492
+ const moduleUrl = pathToFileURL(absolutePath).href;
493
+ const imported = await import(moduleUrl);
494
+ const candidate = imported.default ??
495
+ imported.component ??
496
+ imported.components ??
497
+ imported.componentDefinition ??
498
+ imported;
499
+ const candidates = Array.isArray(candidate)
500
+ ? candidate
501
+ : [candidate];
502
+ const components = [];
503
+ for (const item of candidates) {
504
+ if (!item || typeof item !== "object") {
505
+ continue;
506
+ }
507
+ const { customId, handler } = item;
508
+ if (typeof customId !== "string") {
509
+ console.warn(`[MiniInteraction] Component module "${absolutePath}" is missing "customId". Skipping.`);
510
+ continue;
511
+ }
512
+ if (typeof handler !== "function") {
513
+ console.warn(`[MiniInteraction] Component module "${absolutePath}" is missing a "handler" function. Skipping.`);
514
+ continue;
515
+ }
516
+ components.push({ customId, handler });
517
+ }
518
+ if (components.length === 0) {
519
+ console.warn(`[MiniInteraction] Component module "${absolutePath}" did not export any valid components. Skipping.`);
520
+ }
521
+ return components;
522
+ }
523
+ catch (error) {
524
+ console.error(`[MiniInteraction] Failed to load component module "${absolutePath}":`, error);
525
+ return [];
526
+ }
527
+ }
444
528
  /**
445
529
  * Normalises the request body into a UTF-8 string for signature validation and parsing.
446
530
  */
@@ -450,6 +534,20 @@ export class MiniInteraction {
450
534
  }
451
535
  return Buffer.from(body).toString("utf8");
452
536
  }
537
+ /**
538
+ * Ensures components have been loaded from disk once before being accessed.
539
+ */
540
+ async ensureComponentsLoaded() {
541
+ if (this.componentsLoaded || this.componentsDirectory === null) {
542
+ return;
543
+ }
544
+ if (!this.loadComponentsPromise) {
545
+ this.loadComponentsPromise = this.loadComponentsFromDirectory().then(() => {
546
+ this.loadComponentsPromise = null;
547
+ });
548
+ }
549
+ await this.loadComponentsPromise;
550
+ }
453
551
  /**
454
552
  * Ensures commands have been loaded from disk once before being accessed.
455
553
  */
@@ -546,6 +644,7 @@ export class MiniInteraction {
546
644
  },
547
645
  };
548
646
  }
647
+ await this.ensureComponentsLoaded();
549
648
  const handler = this.componentHandlers.get(customId);
550
649
  if (!handler) {
551
650
  return {
package/dist/index.d.ts CHANGED
@@ -12,5 +12,6 @@ export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes
12
12
  export { ChannelType } from "./types/ChannelType.js";
13
13
  export { InteractionFollowUpFlags, InteractionReplyFlags, } from "./types/InteractionFlags.js";
14
14
  export { ButtonStyle } from "./types/ButtonStyle.js";
15
+ export { MiniPermFlags } from "./types/PermissionFlags.js";
15
16
  export type { MiniComponentActionRow, MiniComponentMessageActionRow, } from "./types/ComponentTypes.js";
16
17
  export * from "./builders/index.js";
package/dist/index.js CHANGED
@@ -6,4 +6,5 @@ export { RoleConnectionMetadataTypes } from "./types/RoleConnectionMetadataTypes
6
6
  export { ChannelType } from "./types/ChannelType.js";
7
7
  export { InteractionFollowUpFlags, InteractionReplyFlags, } from "./types/InteractionFlags.js";
8
8
  export { ButtonStyle } from "./types/ButtonStyle.js";
9
+ export { MiniPermFlags } from "./types/PermissionFlags.js";
9
10
  export * from "./builders/index.js";
@@ -0,0 +1,55 @@
1
+ /** Convenience mapping of Discord permission bit flags for MiniInteraction usage. */
2
+ export declare const MiniPermFlags: {
3
+ readonly CreateInstantInvite: bigint;
4
+ readonly KickMembers: bigint;
5
+ readonly BanMembers: bigint;
6
+ readonly Administrator: bigint;
7
+ readonly ManageChannels: bigint;
8
+ readonly ManageGuild: bigint;
9
+ readonly AddReactions: bigint;
10
+ readonly ViewAuditLog: bigint;
11
+ readonly PrioritySpeaker: bigint;
12
+ readonly Stream: bigint;
13
+ readonly ViewChannel: bigint;
14
+ readonly SendMessages: bigint;
15
+ readonly SendTTSMessages: bigint;
16
+ readonly ManageMessages: bigint;
17
+ readonly EmbedLinks: bigint;
18
+ readonly AttachFiles: bigint;
19
+ readonly ReadMessageHistory: bigint;
20
+ readonly MentionEveryone: bigint;
21
+ readonly UseExternalEmojis: bigint;
22
+ readonly ViewGuildInsights: bigint;
23
+ readonly Connect: bigint;
24
+ readonly Speak: bigint;
25
+ readonly MuteMembers: bigint;
26
+ readonly DeafenMembers: bigint;
27
+ readonly MoveMembers: bigint;
28
+ readonly UseVAD: bigint;
29
+ readonly ChangeNickname: bigint;
30
+ readonly ManageNicknames: bigint;
31
+ readonly ManageRoles: bigint;
32
+ readonly ManageWebhooks: bigint;
33
+ readonly ManageEmojisAndStickers: bigint;
34
+ readonly ManageGuildExpressions: bigint;
35
+ readonly UseApplicationCommands: bigint;
36
+ readonly RequestToSpeak: bigint;
37
+ readonly ManageEvents: bigint;
38
+ readonly ManageThreads: bigint;
39
+ readonly CreatePublicThreads: bigint;
40
+ readonly CreatePrivateThreads: bigint;
41
+ readonly UseExternalStickers: bigint;
42
+ readonly SendMessagesInThreads: bigint;
43
+ readonly UseEmbeddedActivities: bigint;
44
+ readonly ModerateMembers: bigint;
45
+ readonly ViewCreatorMonetizationAnalytics: bigint;
46
+ readonly UseSoundboard: bigint;
47
+ readonly CreateGuildExpressions: bigint;
48
+ readonly CreateEvents: bigint;
49
+ readonly UseExternalSounds: bigint;
50
+ readonly SendVoiceMessages: bigint;
51
+ readonly SendPolls: bigint;
52
+ readonly UseExternalApps: bigint;
53
+ readonly PinMessages: bigint;
54
+ };
55
+ export type MiniPermFlag = (typeof MiniPermFlags)[keyof typeof MiniPermFlags];
@@ -0,0 +1,5 @@
1
+ import { PermissionFlagsBits } from "discord-api-types/v10";
2
+ /** Convenience mapping of Discord permission bit flags for MiniInteraction usage. */
3
+ export const MiniPermFlags = {
4
+ ...PermissionFlagsBits,
5
+ };
@@ -1,3 +1,13 @@
1
+ import { ComponentType, MessageFlags as RawMessageFlags } from "discord-api-types/v10";
2
+ const COMPONENTS_V2_TYPES = new Set([
3
+ ComponentType.Container,
4
+ ComponentType.Section,
5
+ ComponentType.TextDisplay,
6
+ ComponentType.MediaGallery,
7
+ ComponentType.File,
8
+ ComponentType.Separator,
9
+ ComponentType.Thumbnail,
10
+ ]);
1
11
  /**
2
12
  * Normalises helper flag enums into the raw Discord `MessageFlags` bitfield.
3
13
  *
@@ -17,16 +27,51 @@ export function normaliseInteractionMessageData(data) {
17
27
  if (!data) {
18
28
  return undefined;
19
29
  }
20
- if (data.flags === undefined) {
21
- return data;
22
- }
23
- const { flags, ...rest } = data;
24
- const normalisedFlags = normaliseMessageFlags(flags);
25
- if (normalisedFlags === flags) {
30
+ const usesComponentsV2 = Array.isArray(data.components)
31
+ ? containsComponentsV2(data.components)
32
+ : false;
33
+ const normalisedFlags = normaliseMessageFlags(data.flags);
34
+ const finalFlags = usesComponentsV2
35
+ ? ((normalisedFlags ?? 0) | RawMessageFlags.IsComponentsV2)
36
+ : normalisedFlags;
37
+ if (finalFlags === data.flags) {
26
38
  return data;
27
39
  }
40
+ const { flags: _flags, ...rest } = data;
28
41
  return {
29
42
  ...rest,
30
- flags: normalisedFlags,
43
+ ...(finalFlags !== undefined ? { flags: finalFlags } : {}),
31
44
  };
32
45
  }
46
+ function containsComponentsV2(components) {
47
+ return components.some((component) => componentUsesComponentsV2(component));
48
+ }
49
+ function componentUsesComponentsV2(component) {
50
+ const resolved = resolveComponentLike(component);
51
+ if (!resolved || typeof resolved !== "object") {
52
+ return false;
53
+ }
54
+ const type = resolved.type;
55
+ if (typeof type !== "number") {
56
+ return false;
57
+ }
58
+ if (COMPONENTS_V2_TYPES.has(type)) {
59
+ return true;
60
+ }
61
+ if (type === ComponentType.ActionRow) {
62
+ const rowComponents = resolved.components;
63
+ if (Array.isArray(rowComponents)) {
64
+ return containsComponentsV2(rowComponents);
65
+ }
66
+ }
67
+ return false;
68
+ }
69
+ function resolveComponentLike(component) {
70
+ if (component && typeof component === "object" && "toJSON" in component) {
71
+ const encoder = component;
72
+ if (typeof encoder.toJSON === "function") {
73
+ return encoder.toJSON();
74
+ }
75
+ }
76
+ return component;
77
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minesa-org/mini-interaction",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Mini interaction, connecting your app with Discord via HTTP-interaction (Vercel support).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",