@magicyan/discord 1.5.4 → 1.6.1

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.
@@ -9,7 +9,7 @@ const row = require('./row.cjs');
9
9
  const text = require('./text.cjs');
10
10
 
11
11
  function createComponents(...data) {
12
- return data.flat().filter((value) => core.isDefined(value)).filter((value) => typeof value !== "boolean").map((component) => {
12
+ return data.flat().filter((value) => core.isDefined(value) && typeof value !== "boolean").map((component) => {
13
13
  if (typeof component === "string") {
14
14
  return text.createTextDisplay(component);
15
15
  }
@@ -7,7 +7,7 @@ import { createRow } from './row.mjs';
7
7
  import { createTextDisplay } from './text.mjs';
8
8
 
9
9
  function createComponents(...data) {
10
- return data.flat().filter((value) => isDefined(value)).filter((value) => typeof value !== "boolean").map((component) => {
10
+ return data.flat().filter((value) => isDefined(value) && typeof value !== "boolean").map((component) => {
11
11
  if (typeof component === "string") {
12
12
  return createTextDisplay(component);
13
13
  }
@@ -5,19 +5,17 @@ const attachment = require('../../guards/attachment.cjs');
5
5
  const core = require('@magicyan/core');
6
6
 
7
7
  function createMediaGallery(...items) {
8
- const gallery = new discord_js.MediaGalleryBuilder();
9
- for (const item of items.flat().filter(core.isDefined)) {
10
- if (typeof item === "string") {
11
- gallery.addItems({ media: { url: item } });
12
- continue;
13
- }
14
- if (attachment.isAttachment(item)) {
15
- gallery.addItems({ media: { url: `attachment://${item.name}` } });
16
- continue;
17
- }
18
- gallery.addItems(item);
19
- }
20
- return gallery;
8
+ return new discord_js.MediaGalleryBuilder({
9
+ items: items.flat().filter(core.isDefined).map((item) => {
10
+ if (typeof item === "string") {
11
+ return { media: { url: item } };
12
+ }
13
+ if (attachment.isAttachment(item)) {
14
+ return { media: { url: `attachment://${item.name}` } };
15
+ }
16
+ return item;
17
+ })
18
+ });
21
19
  }
22
20
 
23
21
  exports.createMediaGallery = createMediaGallery;
@@ -3,19 +3,17 @@ import { isAttachment } from '../../guards/attachment.mjs';
3
3
  import { isDefined } from '@magicyan/core';
4
4
 
5
5
  function createMediaGallery(...items) {
6
- const gallery = new MediaGalleryBuilder();
7
- for (const item of items.flat().filter(isDefined)) {
8
- if (typeof item === "string") {
9
- gallery.addItems({ media: { url: item } });
10
- continue;
11
- }
12
- if (isAttachment(item)) {
13
- gallery.addItems({ media: { url: `attachment://${item.name}` } });
14
- continue;
15
- }
16
- gallery.addItems(item);
17
- }
18
- return gallery;
6
+ return new MediaGalleryBuilder({
7
+ items: items.flat().filter(isDefined).map((item) => {
8
+ if (typeof item === "string") {
9
+ return { media: { url: item } };
10
+ }
11
+ if (isAttachment(item)) {
12
+ return { media: { url: `attachment://${item.name}` } };
13
+ }
14
+ return item;
15
+ })
16
+ });
19
17
  }
20
18
 
21
19
  export { createMediaGallery };
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ const discord_js = require('discord.js');
4
+ const row = require('./row.cjs');
5
+
6
+ function createTextInput(data) {
7
+ data.style ?? (data.style = discord_js.TextInputStyle.Short);
8
+ if ("label" in data)
9
+ delete data.label;
10
+ return new discord_js.TextInputBuilder(data);
11
+ }
12
+ function createModalInput(data) {
13
+ data.style ?? (data.style = discord_js.TextInputStyle.Short);
14
+ return row.createRow(new discord_js.TextInputBuilder(data));
15
+ }
16
+
17
+ exports.createModalInput = createModalInput;
18
+ exports.createTextInput = createTextInput;
@@ -0,0 +1,15 @@
1
+ import { TextInputStyle, TextInputBuilder } from 'discord.js';
2
+ import { createRow } from './row.mjs';
3
+
4
+ function createTextInput(data) {
5
+ data.style ?? (data.style = TextInputStyle.Short);
6
+ if ("label" in data)
7
+ delete data.label;
8
+ return new TextInputBuilder(data);
9
+ }
10
+ function createModalInput(data) {
11
+ data.style ?? (data.style = TextInputStyle.Short);
12
+ return createRow(new TextInputBuilder(data));
13
+ }
14
+
15
+ export { createModalInput, createTextInput };
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ const discord_js = require('discord.js');
4
+
5
+ function createLabel(argA, argB, argC, argD) {
6
+ if (typeof argA === "object") {
7
+ const { component, ...data } = argA;
8
+ return new discord_js.LabelBuilder({
9
+ ...data,
10
+ component: component?.toJSON()
11
+ });
12
+ }
13
+ if (typeof argB === "string") {
14
+ return new discord_js.LabelBuilder({
15
+ label: argA,
16
+ description: argB,
17
+ ...typeof argC === "number" ? { id: argC } : { component: argC?.toJSON(), id: argD }
18
+ });
19
+ }
20
+ return new discord_js.LabelBuilder({
21
+ label: argA,
22
+ component: argB?.toJSON(),
23
+ ...typeof argC === "number" ? { id: argC } : {}
24
+ });
25
+ }
26
+
27
+ exports.createLabel = createLabel;
@@ -0,0 +1,25 @@
1
+ import { LabelBuilder } from 'discord.js';
2
+
3
+ function createLabel(argA, argB, argC, argD) {
4
+ if (typeof argA === "object") {
5
+ const { component, ...data } = argA;
6
+ return new LabelBuilder({
7
+ ...data,
8
+ component: component?.toJSON()
9
+ });
10
+ }
11
+ if (typeof argB === "string") {
12
+ return new LabelBuilder({
13
+ label: argA,
14
+ description: argB,
15
+ ...typeof argC === "number" ? { id: argC } : { component: argC?.toJSON(), id: argD }
16
+ });
17
+ }
18
+ return new LabelBuilder({
19
+ label: argA,
20
+ component: argB?.toJSON(),
21
+ ...typeof argC === "number" ? { id: argC } : {}
22
+ });
23
+ }
24
+
25
+ export { createLabel };
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ const core = require('@magicyan/core');
4
+ const discord_js = require('discord.js');
5
+
6
+ function modalFieldsToRecord(data, parse) {
7
+ const collection = "fields" in data ? "fields" in data.fields ? data.fields.fields : data.fields : data;
8
+ const record = collection.reduce((acc, data2) => {
9
+ if (data2.type === discord_js.ComponentType.TextInput) {
10
+ acc[data2.customId] = data2.value;
11
+ return acc;
12
+ }
13
+ if (data2.type === discord_js.ComponentType.FileUpload) {
14
+ const attachments = data2.attachments;
15
+ acc[data2.customId] = Array.from(
16
+ attachments.mapValues((data3) => data3.url).values()
17
+ );
18
+ return acc;
19
+ }
20
+ acc[data2.customId] = Array.from(data2.values ?? []);
21
+ return acc;
22
+ }, {});
23
+ return parse ? parse(record) : record;
24
+ }
25
+ function createModalFields(...components) {
26
+ return components.filter((v) => core.isDefined(v) && typeof v !== "boolean").map((data) => {
27
+ if (typeof data === "string")
28
+ return {
29
+ type: discord_js.ComponentType.TextDisplay,
30
+ content: data
31
+ };
32
+ return data.data;
33
+ });
34
+ }
35
+
36
+ exports.createModalFields = createModalFields;
37
+ exports.modalFieldsToRecord = modalFieldsToRecord;
@@ -0,0 +1,34 @@
1
+ import { isDefined } from '@magicyan/core';
2
+ import { ComponentType } from 'discord.js';
3
+
4
+ function modalFieldsToRecord(data, parse) {
5
+ const collection = "fields" in data ? "fields" in data.fields ? data.fields.fields : data.fields : data;
6
+ const record = collection.reduce((acc, data2) => {
7
+ if (data2.type === ComponentType.TextInput) {
8
+ acc[data2.customId] = data2.value;
9
+ return acc;
10
+ }
11
+ if (data2.type === ComponentType.FileUpload) {
12
+ const attachments = data2.attachments;
13
+ acc[data2.customId] = Array.from(
14
+ attachments.mapValues((data3) => data3.url).values()
15
+ );
16
+ return acc;
17
+ }
18
+ acc[data2.customId] = Array.from(data2.values ?? []);
19
+ return acc;
20
+ }, {});
21
+ return parse ? parse(record) : record;
22
+ }
23
+ function createModalFields(...components) {
24
+ return components.filter((v) => isDefined(v) && typeof v !== "boolean").map((data) => {
25
+ if (typeof data === "string")
26
+ return {
27
+ type: ComponentType.TextDisplay,
28
+ content: data
29
+ };
30
+ return data.data;
31
+ });
32
+ }
33
+
34
+ export { createModalFields, modalFieldsToRecord };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const discord_js = require('discord.js');
4
+
5
+ function createFileUpload(arg) {
6
+ if (typeof arg === "string")
7
+ return new discord_js.FileUploadBuilder({
8
+ custom_id: arg
9
+ });
10
+ const { customId, maxValues, minValues, ...data } = arg;
11
+ return new discord_js.FileUploadBuilder({
12
+ custom_id: customId,
13
+ max_values: maxValues,
14
+ min_values: minValues,
15
+ ...data
16
+ });
17
+ }
18
+
19
+ exports.createFileUpload = createFileUpload;
@@ -0,0 +1,17 @@
1
+ import { FileUploadBuilder } from 'discord.js';
2
+
3
+ function createFileUpload(arg) {
4
+ if (typeof arg === "string")
5
+ return new FileUploadBuilder({
6
+ custom_id: arg
7
+ });
8
+ const { customId, maxValues, minValues, ...data } = arg;
9
+ return new FileUploadBuilder({
10
+ custom_id: customId,
11
+ max_values: maxValues,
12
+ min_values: minValues,
13
+ ...data
14
+ });
15
+ }
16
+
17
+ export { createFileUpload };
package/dist/index.cjs CHANGED
@@ -10,7 +10,6 @@ const emojis = require('./functions/emojis.cjs');
10
10
  const members = require('./functions/members.cjs');
11
11
  const message = require('./functions/message.cjs');
12
12
  const misc = require('./functions/misc.cjs');
13
- const modals = require('./functions/modals.cjs');
14
13
  const regex = require('./functions/regex.cjs');
15
14
  const roles = require('./functions/roles.cjs');
16
15
  const webhooks = require('./functions/webhooks.cjs');
@@ -19,29 +18,33 @@ const components = require('./functions/components/components.cjs');
19
18
  const container = require('./functions/components/container.cjs');
20
19
  const file = require('./functions/components/file.cjs');
21
20
  const gallery = require('./functions/components/gallery.cjs');
21
+ const input = require('./functions/components/input.cjs');
22
+ const label = require('./functions/components/label.cjs');
23
+ const modal = require('./functions/components/modal.cjs');
22
24
  const row = require('./functions/components/row.cjs');
23
25
  const section = require('./functions/components/section.cjs');
24
26
  const separator$1 = require('./functions/components/separator.cjs');
25
27
  const text = require('./functions/components/text.cjs');
26
28
  const thumbarea = require('./functions/components/thumbarea.cjs');
27
29
  const thumbnail = require('./functions/components/thumbnail.cjs');
30
+ const upload = require('./functions/components/upload.cjs');
28
31
  const assets = require('./functions/embeds/assets.cjs');
29
32
  const author = require('./functions/embeds/author.cjs');
30
33
  const embedplus = require('./functions/embeds/embedplus.cjs');
31
34
  const files = require('./functions/embeds/files.cjs');
32
35
  const footer = require('./functions/embeds/footer.cjs');
33
36
  const attachment = require('./guards/attachment.cjs');
34
- const message$1 = require('./guards/message.cjs');
35
37
  const button = require('./guards/components/button.cjs');
36
38
  const container$1 = require('./guards/components/container.cjs');
37
39
  const gallery$1 = require('./guards/components/gallery.cjs');
38
- const modal = require('./guards/components/modal.cjs');
40
+ const modal$1 = require('./guards/components/modal.cjs');
39
41
  const row$1 = require('./guards/components/row.cjs');
40
42
  const section$1 = require('./guards/components/section.cjs');
41
43
  const selectmenu = require('./guards/components/selectmenu.cjs');
42
44
  const separator$2 = require('./guards/components/separator.cjs');
43
45
  const textdisplay = require('./guards/components/textdisplay.cjs');
44
46
  const textinput = require('./guards/components/textinput.cjs');
47
+ const message$1 = require('./guards/message.cjs');
45
48
  const core = require('@magicyan/core');
46
49
 
47
50
 
@@ -61,10 +64,6 @@ exports.fetchMessageFromURL = message.fetchMessageFromURL;
61
64
  exports.findMessage = message.findMessage;
62
65
  exports.getMessageURLInfo = message.getMessageURLInfo;
63
66
  exports.setMobileStatus = misc.setMobileStatus;
64
- exports.createModal = modals.createModal;
65
- exports.createModalFields = modals.createModalFields;
66
- exports.createModalInput = modals.createModalInput;
67
- exports.modalFieldsToRecord = modals.modalFieldsToRecord;
68
67
  exports.extractMentionId = regex.extractMentionId;
69
68
  exports.findRole = roles.findRole;
70
69
  exports.createWebhookClient = webhooks.createWebhookClient;
@@ -75,12 +74,18 @@ exports.ContainerPlusBuilder = container.ContainerPlusBuilder;
75
74
  exports.createContainer = container.createContainer;
76
75
  exports.createFile = file.createFile;
77
76
  exports.createMediaGallery = gallery.createMediaGallery;
77
+ exports.createModalInput = input.createModalInput;
78
+ exports.createTextInput = input.createTextInput;
79
+ exports.createLabel = label.createLabel;
80
+ exports.createModalFields = modal.createModalFields;
81
+ exports.modalFieldsToRecord = modal.modalFieldsToRecord;
78
82
  exports.createRow = row.createRow;
79
83
  exports.createSection = section.createSection;
80
84
  exports.createSeparator = separator$1.createSeparator;
81
85
  exports.createTextDisplay = text.createTextDisplay;
82
86
  exports.createThumbArea = thumbarea.createThumbArea;
83
87
  exports.createThumbnail = thumbnail.createThumbnail;
88
+ exports.createFileUpload = upload.createFileUpload;
84
89
  exports.createEmbedAsset = assets.createEmbedAsset;
85
90
  exports.createEmbedAuthor = author.createEmbedAuthor;
86
91
  exports.EmbedPlusBuilder = embedplus.EmbedPlusBuilder;
@@ -88,12 +93,11 @@ exports.createEmbed = embedplus.createEmbed;
88
93
  exports.createEmbedFiles = files.createEmbedFiles;
89
94
  exports.createEmbedFooter = footer.createEmbedFooter;
90
95
  exports.isAttachment = attachment.isAttachment;
91
- exports.isMessage = message$1.isMessage;
92
96
  exports.isButtonBuilder = button.isButtonBuilder;
93
97
  exports.isContainerBuilder = container$1.isContainerBuilder;
94
98
  exports.isMediaGalleryBuilder = gallery$1.isMediaGalleryBuilder;
95
99
  exports.isMediaGalleryItemBuilder = gallery$1.isMediaGalleryItemBuilder;
96
- exports.isModalBuilder = modal.isModalBuilder;
100
+ exports.isModalBuilder = modal$1.isModalBuilder;
97
101
  exports.isActionRowBuilder = row$1.isActionRowBuilder;
98
102
  exports.isSectionBuilder = section$1.isSectionBuilder;
99
103
  exports.isAnySelectMenuBuilder = selectmenu.isAnySelectMenuBuilder;
@@ -105,6 +109,7 @@ exports.isUserSelectMenuBuilder = selectmenu.isUserSelectMenuBuilder;
105
109
  exports.isSeparatorBuilder = separator$2.isSeparatorBuilder;
106
110
  exports.isTextDisplayBuilder = textdisplay.isTextDisplayBuilder;
107
111
  exports.isTextInputBuilder = textinput.isTextInputBuilder;
112
+ exports.isMessage = message$1.isMessage;
108
113
  Object.keys(core).forEach(function (k) {
109
114
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = core[k];
110
115
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as discord_js from 'discord.js';
2
- import { GatewayIntentBits, Partials, ChannelType, Guild, CommandInteractionOption, Client, ApplicationCommand, GuildEmoji, GuildMember, GuildTextBasedChannel, Message, ActionRowBuilder, TextInputBuilder, ModalSubmitInteraction, ModalSubmitFields, Collection, TextInputComponent, ModalBuilder, TextInputStyle, TextInputComponentData, Role, WebhookClientOptions, WebhookClient, WebhookClientData, ComponentEmojiResolvable, ButtonBuilder, LinkButtonComponentData, Attachment, AttachmentBuilder, MessageActionRowComponentBuilder, TextDisplayBuilder, SeparatorBuilder, FileBuilder, SectionBuilder, MediaGalleryBuilder, ContainerBuilder, ContainerComponentBuilder as ContainerComponentBuilder$1, ColorResolvable, ComponentType, ContainerComponentData, ContainerComponent, APIUnfurledMediaItem, MediaGalleryItemData, AnyComponentBuilder, ThumbnailComponentData, ThumbnailBuilder, ButtonComponentData, EmbedAssetData, User, ClientUser, ImageURLOptions, EmbedFooterData, APIEmbed, Embed, EmbedData, EmbedBuilder, AttachmentData, MediaGalleryItemBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder } from 'discord.js';
2
+ import { GatewayIntentBits, Partials, ChannelType, Guild, CommandInteractionOption, Client, ApplicationCommand, GuildEmoji, GuildMember, GuildTextBasedChannel, Message, Role, WebhookClientOptions, WebhookClient, WebhookClientData, ComponentEmojiResolvable, ButtonBuilder, ActionRowBuilder, LinkButtonComponentData, Attachment, AttachmentBuilder, MessageActionRowComponentBuilder, TextDisplayBuilder, SeparatorBuilder, FileBuilder, SectionBuilder, MediaGalleryBuilder, ContainerBuilder, ContainerComponentBuilder as ContainerComponentBuilder$1, ColorResolvable, ComponentType, ContainerComponentData, ContainerComponent, APIUnfurledMediaItem, MediaGalleryItemData, TextInputBuilder, TextInputComponentData, TextInputStyle, LabelBuilderData, LabelBuilder, Collection, ModalData, LabelComponentData, TextDisplayComponentData, AnyComponentBuilder, ThumbnailComponentData, ThumbnailBuilder, ButtonComponentData, FileUploadBuilder, EmbedAssetData, User, ClientUser, ImageURLOptions, EmbedFooterData, APIEmbed, Embed, EmbedData, EmbedBuilder, AttachmentData, MediaGalleryItemBuilder, ModalBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder } from 'discord.js';
3
3
  export * from '@magicyan/core';
4
4
 
5
5
  declare const chars: {
@@ -144,22 +144,6 @@ declare function fetchMessageFromURL(client: Client | Client, url: string): Prom
144
144
 
145
145
  declare function setMobileStatus(): void;
146
146
 
147
- type TextInputData = Omit<TextInputComponentData, "type">;
148
- interface ModalFieldData extends Omit<TextInputData, "style"> {
149
- style?: TextInputStyle;
150
- }
151
- declare function createModalInput(data: ModalFieldData): ActionRowBuilder<TextInputBuilder>;
152
- type ModalFieldsData = Record<string, Omit<ModalFieldData, "customId">>;
153
- declare function createModalFields(data: ModalFieldsData): ActionRowBuilder<TextInputBuilder>[];
154
- type ModalFieldsRecord<K extends string> = Record<K, string>;
155
- declare function modalFieldsToRecord<K extends string = string>(fields: ModalSubmitInteraction | ModalSubmitFields | Collection<string, TextInputComponent>): ModalFieldsRecord<K>;
156
- interface CreateModalData {
157
- title?: string;
158
- customId: string;
159
- components: ModalFieldsData;
160
- }
161
- declare function createModal(data: CreateModalData): ModalBuilder;
162
-
163
147
  /**
164
148
  *
165
149
  * @param mention Discord mentionable string
@@ -429,6 +413,65 @@ type MediaGallerySource = MediaGalleryItemData | string | Attachment | Attachmen
429
413
  */
430
414
  declare function createMediaGallery(...items: (MediaGallerySource | MediaGallerySource[])[]): MediaGalleryBuilder;
431
415
 
416
+ type CreateInputData = Partial<Omit<TextInputComponentData, "type" | "label">>;
417
+ declare function createTextInput(data: CreateInputData): TextInputBuilder;
418
+ /**
419
+ * @deprecated Use {@link createTextInput} instead.
420
+ */
421
+ declare function createModalInput(data: Omit<TextInputComponentData, "type" | "style"> & {
422
+ style?: TextInputStyle;
423
+ }): ActionRowBuilder<TextInputBuilder>;
424
+
425
+ type CreateLabelData = Omit<LabelBuilderData, "type">;
426
+ type ComponentInLabelBuilder = NonNullable<CreateLabelData["component"]>;
427
+ declare function createLabel(data: CreateLabelData): LabelBuilder;
428
+ declare function createLabel(label: string, description?: string, component?: ComponentInLabelBuilder, id?: number): LabelBuilder;
429
+ declare function createLabel(label: string, component?: ComponentInLabelBuilder, id?: number): LabelBuilder;
430
+
431
+ type ModalComponents = LabelBuilder | TextDisplayBuilder | string | null | boolean | undefined;
432
+ type ResolveModalData = {
433
+ fields: {
434
+ fields: Collection<string, ModalData>;
435
+ };
436
+ } | {
437
+ fields: Collection<string, ModalData>;
438
+ } | Collection<string, ModalData>;
439
+ type ModalFieldsRecord = Record<string, string | string[]>;
440
+ /**
441
+ * Converts modal submitted fields into a plain record object, mapping each component `customId` to its value.
442
+ *
443
+ * This function supports multiple modal submission data shapes coming from Discord.js,
444
+ * automatically extracting and normalizing values from:
445
+ * - Text input fields (`ComponentType.TextInput`) → string
446
+ * - Select menus & other multi-value components → string[]
447
+ * - File upload fields (`ComponentType.FileUpload`) → string[] of attachment URLs
448
+ *
449
+ * Optionally, a parser callback may be provided to transform the resulting record before returning it.
450
+ *
451
+ * @template T The expected return type after optional parsing.
452
+ * @param {ResolveModalData} data - The modal submission data, usually from an interaction.
453
+ * @param {(record: ModalFieldsRecord) => T} [parse] - Optional transform function applied to the resulting record.
454
+ * @returns {ModalFieldsRecord | T} The processed fields as a record, or the parser result if provided.
455
+ *
456
+ * @example
457
+ * ```ts
458
+ * const fields = modalFieldsToRecord(interaction.fields);
459
+ * console.log(fields.username); // "JohnDoe"
460
+ * ```
461
+ *
462
+ * @example With parsing
463
+ * ```ts
464
+ * const result = modalFieldsToRecord(interaction.fields, fields => ({
465
+ * name: fields.username.trim(),
466
+ * age: Number.parseInt(fields.age),
467
+ * }));
468
+ * ```
469
+ */
470
+ declare function modalFieldsToRecord<const T = ModalFieldsRecord>(data: ResolveModalData): T;
471
+ declare function modalFieldsToRecord<const T = ModalFieldsRecord>(data: ResolveModalData, parse: (record: ModalFieldsRecord) => T): T;
472
+ type ModalFields = (LabelComponentData | TextDisplayComponentData)[];
473
+ declare function createModalFields(...components: ModalComponents[]): ModalFields;
474
+
432
475
  /**
433
476
  * Creates an {@link ActionRowBuilder} containing one or more UI components.
434
477
  *
@@ -675,6 +718,16 @@ type ThumbAreaData = {
675
718
  declare function createThumbArea(content: string, thumbnail?: ThumbAreaThumbnail): SectionBuilder | TextDisplayBuilder;
676
719
  declare function createThumbArea(data: ThumbAreaData): SectionBuilder | TextDisplayBuilder;
677
720
 
721
+ interface FileUploadData {
722
+ customId?: string;
723
+ minValues?: number;
724
+ maxValues?: number;
725
+ required?: boolean;
726
+ id?: number;
727
+ }
728
+ declare function createFileUpload(data: FileUploadData): FileUploadBuilder;
729
+ declare function createFileUpload(customId: string): FileUploadBuilder;
730
+
678
731
  type EmbedPlusAssetData = string | Attachment | AttachmentBuilder | EmbedAssetData | undefined | null;
679
732
  type EmbedAssetOptions = Omit<EmbedAssetData, "url">;
680
733
  declare function createEmbedAsset(source: EmbedPlusAssetData, options?: EmbedAssetOptions): EmbedAssetData | undefined;
@@ -841,26 +894,6 @@ declare function createEmbedFiles(embed: EmbedBuilder, options?: CreateEmbedFile
841
894
  */
842
895
  declare function isAttachment(value: unknown): value is Attachment | AttachmentBuilder;
843
896
 
844
- /**
845
- * Checks whether the given value is a {@link Message}.
846
- *
847
- * This function returns `true` if the value is an instance of `Message`
848
- * or if it structurally resembles a `Message` object.
849
- *
850
- * @param value - The value to check.
851
- * @returns `true` if the value is a `Message`, otherwise `false`.
852
- *
853
- * @example
854
- * import type { Message, Interaction } from "discord.js";
855
- *
856
- * function handle(input: Message | Interaction | unknown) {
857
- * if (isMessage(input)) {
858
- * console.log("This is a message with ID:", input.id);
859
- * }
860
- * }
861
- */
862
- declare function isMessage(value: unknown): value is Message;
863
-
864
897
  /**
865
898
  * Checks whether the given value is a {@link ButtonBuilder}.
866
899
  *
@@ -1082,5 +1115,25 @@ declare function isTextDisplayBuilder(value: unknown): value is TextDisplayBuild
1082
1115
  */
1083
1116
  declare function isTextInputBuilder(value: unknown): value is TextInputBuilder;
1084
1117
 
1085
- export { ContainerPlusBuilder, CustomItents, CustomPartials, EmbedLimit, EmbedPlusBuilder, Separator, chars, commandMention, createComponents, createContainer, createEmbed, createEmbedAsset, createEmbedAuthor, createEmbedFiles, createEmbedFooter, createFile, createLinkButton, createMediaGallery, createModal, createModalFields, createModalInput, createRow, createSection, createSeparator, createTextDisplay, createThumbArea, createThumbnail, createWebhookClient, extractMentionId, fetchMessageFromURL, findChannel, findCommand, findEmoji, findMember, findMessage, findRole, getChannelUrlInfo, getMessageURLInfo, isActionRowBuilder, isAnySelectMenuBuilder, isAttachment, isButtonBuilder, isChannelSelectMenuBuilder, isContainerBuilder, isMediaGalleryBuilder, isMediaGalleryItemBuilder, isMentionableSelectMenuBuilder, isMessage, isModalBuilder, isRoleSelectMenuBuilder, isSectionBuilder, isSeparatorBuilder, isStringSelectMenuBuilder, isTextDisplayBuilder, isTextInputBuilder, isUserSelectMenuBuilder, modalFieldsToRecord, setMobileStatus, wrapButtons };
1086
- export type { AnyEmbedData, AnySelectMenuBuilder, ComponentBuildersData, ComponentData, ContainerColor, ContainerComponentBuilder, ContainerData, ContainerInComponentType, CreateComponentData, CreateModalData, EmbedPlusAssetData, EmbedPlusAuthorData, EmbedPlusColorData, EmbedPlusData, EmbedPlusFooterData, EmbedPlusProperty, MagicComponentData, MediaGallerySource, ModalFieldsData, ModalFieldsRecord, SectionAccessory, SectionAccessoryData, SectionButtonAccessory, SectionData, SectionThumbnailAccessory, SeparatorData, ThumbAreaData, ThumbAreaThumbnail, ThumbnailData };
1118
+ /**
1119
+ * Checks whether the given value is a {@link Message}.
1120
+ *
1121
+ * This function returns `true` if the value is an instance of `Message`
1122
+ * or if it structurally resembles a `Message` object.
1123
+ *
1124
+ * @param value - The value to check.
1125
+ * @returns `true` if the value is a `Message`, otherwise `false`.
1126
+ *
1127
+ * @example
1128
+ * import type { Message, Interaction } from "discord.js";
1129
+ *
1130
+ * function handle(input: Message | Interaction | unknown) {
1131
+ * if (isMessage(input)) {
1132
+ * console.log("This is a message with ID:", input.id);
1133
+ * }
1134
+ * }
1135
+ */
1136
+ declare function isMessage(value: unknown): value is Message;
1137
+
1138
+ export { ContainerPlusBuilder, CustomItents, CustomPartials, EmbedLimit, EmbedPlusBuilder, Separator, chars, commandMention, createComponents, createContainer, createEmbed, createEmbedAsset, createEmbedAuthor, createEmbedFiles, createEmbedFooter, createFile, createFileUpload, createLabel, createLinkButton, createMediaGallery, createModalFields, createModalInput, createRow, createSection, createSeparator, createTextDisplay, createTextInput, createThumbArea, createThumbnail, createWebhookClient, extractMentionId, fetchMessageFromURL, findChannel, findCommand, findEmoji, findMember, findMessage, findRole, getChannelUrlInfo, getMessageURLInfo, isActionRowBuilder, isAnySelectMenuBuilder, isAttachment, isButtonBuilder, isChannelSelectMenuBuilder, isContainerBuilder, isMediaGalleryBuilder, isMediaGalleryItemBuilder, isMentionableSelectMenuBuilder, isMessage, isModalBuilder, isRoleSelectMenuBuilder, isSectionBuilder, isSeparatorBuilder, isStringSelectMenuBuilder, isTextDisplayBuilder, isTextInputBuilder, isUserSelectMenuBuilder, modalFieldsToRecord, setMobileStatus, wrapButtons };
1139
+ export type { AnyEmbedData, AnySelectMenuBuilder, ComponentBuildersData, ComponentData, ComponentInLabelBuilder, ContainerColor, ContainerComponentBuilder, ContainerData, ContainerInComponentType, CreateComponentData, CreateLabelData, EmbedPlusAssetData, EmbedPlusAuthorData, EmbedPlusColorData, EmbedPlusData, EmbedPlusFooterData, EmbedPlusProperty, FileUploadData, MagicComponentData, MediaGallerySource, SectionAccessory, SectionAccessoryData, SectionButtonAccessory, SectionData, SectionThumbnailAccessory, SeparatorData, ThumbAreaData, ThumbAreaThumbnail, ThumbnailData };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as discord_js from 'discord.js';
2
- import { GatewayIntentBits, Partials, ChannelType, Guild, CommandInteractionOption, Client, ApplicationCommand, GuildEmoji, GuildMember, GuildTextBasedChannel, Message, ActionRowBuilder, TextInputBuilder, ModalSubmitInteraction, ModalSubmitFields, Collection, TextInputComponent, ModalBuilder, TextInputStyle, TextInputComponentData, Role, WebhookClientOptions, WebhookClient, WebhookClientData, ComponentEmojiResolvable, ButtonBuilder, LinkButtonComponentData, Attachment, AttachmentBuilder, MessageActionRowComponentBuilder, TextDisplayBuilder, SeparatorBuilder, FileBuilder, SectionBuilder, MediaGalleryBuilder, ContainerBuilder, ContainerComponentBuilder as ContainerComponentBuilder$1, ColorResolvable, ComponentType, ContainerComponentData, ContainerComponent, APIUnfurledMediaItem, MediaGalleryItemData, AnyComponentBuilder, ThumbnailComponentData, ThumbnailBuilder, ButtonComponentData, EmbedAssetData, User, ClientUser, ImageURLOptions, EmbedFooterData, APIEmbed, Embed, EmbedData, EmbedBuilder, AttachmentData, MediaGalleryItemBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder } from 'discord.js';
2
+ import { GatewayIntentBits, Partials, ChannelType, Guild, CommandInteractionOption, Client, ApplicationCommand, GuildEmoji, GuildMember, GuildTextBasedChannel, Message, Role, WebhookClientOptions, WebhookClient, WebhookClientData, ComponentEmojiResolvable, ButtonBuilder, ActionRowBuilder, LinkButtonComponentData, Attachment, AttachmentBuilder, MessageActionRowComponentBuilder, TextDisplayBuilder, SeparatorBuilder, FileBuilder, SectionBuilder, MediaGalleryBuilder, ContainerBuilder, ContainerComponentBuilder as ContainerComponentBuilder$1, ColorResolvable, ComponentType, ContainerComponentData, ContainerComponent, APIUnfurledMediaItem, MediaGalleryItemData, TextInputBuilder, TextInputComponentData, TextInputStyle, LabelBuilderData, LabelBuilder, Collection, ModalData, LabelComponentData, TextDisplayComponentData, AnyComponentBuilder, ThumbnailComponentData, ThumbnailBuilder, ButtonComponentData, FileUploadBuilder, EmbedAssetData, User, ClientUser, ImageURLOptions, EmbedFooterData, APIEmbed, Embed, EmbedData, EmbedBuilder, AttachmentData, MediaGalleryItemBuilder, ModalBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder } from 'discord.js';
3
3
  export * from '@magicyan/core';
4
4
 
5
5
  declare const chars: {
@@ -144,22 +144,6 @@ declare function fetchMessageFromURL(client: Client | Client, url: string): Prom
144
144
 
145
145
  declare function setMobileStatus(): void;
146
146
 
147
- type TextInputData = Omit<TextInputComponentData, "type">;
148
- interface ModalFieldData extends Omit<TextInputData, "style"> {
149
- style?: TextInputStyle;
150
- }
151
- declare function createModalInput(data: ModalFieldData): ActionRowBuilder<TextInputBuilder>;
152
- type ModalFieldsData = Record<string, Omit<ModalFieldData, "customId">>;
153
- declare function createModalFields(data: ModalFieldsData): ActionRowBuilder<TextInputBuilder>[];
154
- type ModalFieldsRecord<K extends string> = Record<K, string>;
155
- declare function modalFieldsToRecord<K extends string = string>(fields: ModalSubmitInteraction | ModalSubmitFields | Collection<string, TextInputComponent>): ModalFieldsRecord<K>;
156
- interface CreateModalData {
157
- title?: string;
158
- customId: string;
159
- components: ModalFieldsData;
160
- }
161
- declare function createModal(data: CreateModalData): ModalBuilder;
162
-
163
147
  /**
164
148
  *
165
149
  * @param mention Discord mentionable string
@@ -429,6 +413,65 @@ type MediaGallerySource = MediaGalleryItemData | string | Attachment | Attachmen
429
413
  */
430
414
  declare function createMediaGallery(...items: (MediaGallerySource | MediaGallerySource[])[]): MediaGalleryBuilder;
431
415
 
416
+ type CreateInputData = Partial<Omit<TextInputComponentData, "type" | "label">>;
417
+ declare function createTextInput(data: CreateInputData): TextInputBuilder;
418
+ /**
419
+ * @deprecated Use {@link createTextInput} instead.
420
+ */
421
+ declare function createModalInput(data: Omit<TextInputComponentData, "type" | "style"> & {
422
+ style?: TextInputStyle;
423
+ }): ActionRowBuilder<TextInputBuilder>;
424
+
425
+ type CreateLabelData = Omit<LabelBuilderData, "type">;
426
+ type ComponentInLabelBuilder = NonNullable<CreateLabelData["component"]>;
427
+ declare function createLabel(data: CreateLabelData): LabelBuilder;
428
+ declare function createLabel(label: string, description?: string, component?: ComponentInLabelBuilder, id?: number): LabelBuilder;
429
+ declare function createLabel(label: string, component?: ComponentInLabelBuilder, id?: number): LabelBuilder;
430
+
431
+ type ModalComponents = LabelBuilder | TextDisplayBuilder | string | null | boolean | undefined;
432
+ type ResolveModalData = {
433
+ fields: {
434
+ fields: Collection<string, ModalData>;
435
+ };
436
+ } | {
437
+ fields: Collection<string, ModalData>;
438
+ } | Collection<string, ModalData>;
439
+ type ModalFieldsRecord = Record<string, string | string[]>;
440
+ /**
441
+ * Converts modal submitted fields into a plain record object, mapping each component `customId` to its value.
442
+ *
443
+ * This function supports multiple modal submission data shapes coming from Discord.js,
444
+ * automatically extracting and normalizing values from:
445
+ * - Text input fields (`ComponentType.TextInput`) → string
446
+ * - Select menus & other multi-value components → string[]
447
+ * - File upload fields (`ComponentType.FileUpload`) → string[] of attachment URLs
448
+ *
449
+ * Optionally, a parser callback may be provided to transform the resulting record before returning it.
450
+ *
451
+ * @template T The expected return type after optional parsing.
452
+ * @param {ResolveModalData} data - The modal submission data, usually from an interaction.
453
+ * @param {(record: ModalFieldsRecord) => T} [parse] - Optional transform function applied to the resulting record.
454
+ * @returns {ModalFieldsRecord | T} The processed fields as a record, or the parser result if provided.
455
+ *
456
+ * @example
457
+ * ```ts
458
+ * const fields = modalFieldsToRecord(interaction.fields);
459
+ * console.log(fields.username); // "JohnDoe"
460
+ * ```
461
+ *
462
+ * @example With parsing
463
+ * ```ts
464
+ * const result = modalFieldsToRecord(interaction.fields, fields => ({
465
+ * name: fields.username.trim(),
466
+ * age: Number.parseInt(fields.age),
467
+ * }));
468
+ * ```
469
+ */
470
+ declare function modalFieldsToRecord<const T = ModalFieldsRecord>(data: ResolveModalData): T;
471
+ declare function modalFieldsToRecord<const T = ModalFieldsRecord>(data: ResolveModalData, parse: (record: ModalFieldsRecord) => T): T;
472
+ type ModalFields = (LabelComponentData | TextDisplayComponentData)[];
473
+ declare function createModalFields(...components: ModalComponents[]): ModalFields;
474
+
432
475
  /**
433
476
  * Creates an {@link ActionRowBuilder} containing one or more UI components.
434
477
  *
@@ -675,6 +718,16 @@ type ThumbAreaData = {
675
718
  declare function createThumbArea(content: string, thumbnail?: ThumbAreaThumbnail): SectionBuilder | TextDisplayBuilder;
676
719
  declare function createThumbArea(data: ThumbAreaData): SectionBuilder | TextDisplayBuilder;
677
720
 
721
+ interface FileUploadData {
722
+ customId?: string;
723
+ minValues?: number;
724
+ maxValues?: number;
725
+ required?: boolean;
726
+ id?: number;
727
+ }
728
+ declare function createFileUpload(data: FileUploadData): FileUploadBuilder;
729
+ declare function createFileUpload(customId: string): FileUploadBuilder;
730
+
678
731
  type EmbedPlusAssetData = string | Attachment | AttachmentBuilder | EmbedAssetData | undefined | null;
679
732
  type EmbedAssetOptions = Omit<EmbedAssetData, "url">;
680
733
  declare function createEmbedAsset(source: EmbedPlusAssetData, options?: EmbedAssetOptions): EmbedAssetData | undefined;
@@ -841,26 +894,6 @@ declare function createEmbedFiles(embed: EmbedBuilder, options?: CreateEmbedFile
841
894
  */
842
895
  declare function isAttachment(value: unknown): value is Attachment | AttachmentBuilder;
843
896
 
844
- /**
845
- * Checks whether the given value is a {@link Message}.
846
- *
847
- * This function returns `true` if the value is an instance of `Message`
848
- * or if it structurally resembles a `Message` object.
849
- *
850
- * @param value - The value to check.
851
- * @returns `true` if the value is a `Message`, otherwise `false`.
852
- *
853
- * @example
854
- * import type { Message, Interaction } from "discord.js";
855
- *
856
- * function handle(input: Message | Interaction | unknown) {
857
- * if (isMessage(input)) {
858
- * console.log("This is a message with ID:", input.id);
859
- * }
860
- * }
861
- */
862
- declare function isMessage(value: unknown): value is Message;
863
-
864
897
  /**
865
898
  * Checks whether the given value is a {@link ButtonBuilder}.
866
899
  *
@@ -1082,5 +1115,25 @@ declare function isTextDisplayBuilder(value: unknown): value is TextDisplayBuild
1082
1115
  */
1083
1116
  declare function isTextInputBuilder(value: unknown): value is TextInputBuilder;
1084
1117
 
1085
- export { ContainerPlusBuilder, CustomItents, CustomPartials, EmbedLimit, EmbedPlusBuilder, Separator, chars, commandMention, createComponents, createContainer, createEmbed, createEmbedAsset, createEmbedAuthor, createEmbedFiles, createEmbedFooter, createFile, createLinkButton, createMediaGallery, createModal, createModalFields, createModalInput, createRow, createSection, createSeparator, createTextDisplay, createThumbArea, createThumbnail, createWebhookClient, extractMentionId, fetchMessageFromURL, findChannel, findCommand, findEmoji, findMember, findMessage, findRole, getChannelUrlInfo, getMessageURLInfo, isActionRowBuilder, isAnySelectMenuBuilder, isAttachment, isButtonBuilder, isChannelSelectMenuBuilder, isContainerBuilder, isMediaGalleryBuilder, isMediaGalleryItemBuilder, isMentionableSelectMenuBuilder, isMessage, isModalBuilder, isRoleSelectMenuBuilder, isSectionBuilder, isSeparatorBuilder, isStringSelectMenuBuilder, isTextDisplayBuilder, isTextInputBuilder, isUserSelectMenuBuilder, modalFieldsToRecord, setMobileStatus, wrapButtons };
1086
- export type { AnyEmbedData, AnySelectMenuBuilder, ComponentBuildersData, ComponentData, ContainerColor, ContainerComponentBuilder, ContainerData, ContainerInComponentType, CreateComponentData, CreateModalData, EmbedPlusAssetData, EmbedPlusAuthorData, EmbedPlusColorData, EmbedPlusData, EmbedPlusFooterData, EmbedPlusProperty, MagicComponentData, MediaGallerySource, ModalFieldsData, ModalFieldsRecord, SectionAccessory, SectionAccessoryData, SectionButtonAccessory, SectionData, SectionThumbnailAccessory, SeparatorData, ThumbAreaData, ThumbAreaThumbnail, ThumbnailData };
1118
+ /**
1119
+ * Checks whether the given value is a {@link Message}.
1120
+ *
1121
+ * This function returns `true` if the value is an instance of `Message`
1122
+ * or if it structurally resembles a `Message` object.
1123
+ *
1124
+ * @param value - The value to check.
1125
+ * @returns `true` if the value is a `Message`, otherwise `false`.
1126
+ *
1127
+ * @example
1128
+ * import type { Message, Interaction } from "discord.js";
1129
+ *
1130
+ * function handle(input: Message | Interaction | unknown) {
1131
+ * if (isMessage(input)) {
1132
+ * console.log("This is a message with ID:", input.id);
1133
+ * }
1134
+ * }
1135
+ */
1136
+ declare function isMessage(value: unknown): value is Message;
1137
+
1138
+ export { ContainerPlusBuilder, CustomItents, CustomPartials, EmbedLimit, EmbedPlusBuilder, Separator, chars, commandMention, createComponents, createContainer, createEmbed, createEmbedAsset, createEmbedAuthor, createEmbedFiles, createEmbedFooter, createFile, createFileUpload, createLabel, createLinkButton, createMediaGallery, createModalFields, createModalInput, createRow, createSection, createSeparator, createTextDisplay, createTextInput, createThumbArea, createThumbnail, createWebhookClient, extractMentionId, fetchMessageFromURL, findChannel, findCommand, findEmoji, findMember, findMessage, findRole, getChannelUrlInfo, getMessageURLInfo, isActionRowBuilder, isAnySelectMenuBuilder, isAttachment, isButtonBuilder, isChannelSelectMenuBuilder, isContainerBuilder, isMediaGalleryBuilder, isMediaGalleryItemBuilder, isMentionableSelectMenuBuilder, isMessage, isModalBuilder, isRoleSelectMenuBuilder, isSectionBuilder, isSeparatorBuilder, isStringSelectMenuBuilder, isTextDisplayBuilder, isTextInputBuilder, isUserSelectMenuBuilder, modalFieldsToRecord, setMobileStatus, wrapButtons };
1139
+ export type { AnyEmbedData, AnySelectMenuBuilder, ComponentBuildersData, ComponentData, ComponentInLabelBuilder, ContainerColor, ContainerComponentBuilder, ContainerData, ContainerInComponentType, CreateComponentData, CreateLabelData, EmbedPlusAssetData, EmbedPlusAuthorData, EmbedPlusColorData, EmbedPlusData, EmbedPlusFooterData, EmbedPlusProperty, FileUploadData, MagicComponentData, MediaGallerySource, SectionAccessory, SectionAccessoryData, SectionButtonAccessory, SectionData, SectionThumbnailAccessory, SeparatorData, ThumbAreaData, ThumbAreaThumbnail, ThumbnailData };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as discord_js from 'discord.js';
2
- import { GatewayIntentBits, Partials, ChannelType, Guild, CommandInteractionOption, Client, ApplicationCommand, GuildEmoji, GuildMember, GuildTextBasedChannel, Message, ActionRowBuilder, TextInputBuilder, ModalSubmitInteraction, ModalSubmitFields, Collection, TextInputComponent, ModalBuilder, TextInputStyle, TextInputComponentData, Role, WebhookClientOptions, WebhookClient, WebhookClientData, ComponentEmojiResolvable, ButtonBuilder, LinkButtonComponentData, Attachment, AttachmentBuilder, MessageActionRowComponentBuilder, TextDisplayBuilder, SeparatorBuilder, FileBuilder, SectionBuilder, MediaGalleryBuilder, ContainerBuilder, ContainerComponentBuilder as ContainerComponentBuilder$1, ColorResolvable, ComponentType, ContainerComponentData, ContainerComponent, APIUnfurledMediaItem, MediaGalleryItemData, AnyComponentBuilder, ThumbnailComponentData, ThumbnailBuilder, ButtonComponentData, EmbedAssetData, User, ClientUser, ImageURLOptions, EmbedFooterData, APIEmbed, Embed, EmbedData, EmbedBuilder, AttachmentData, MediaGalleryItemBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder } from 'discord.js';
2
+ import { GatewayIntentBits, Partials, ChannelType, Guild, CommandInteractionOption, Client, ApplicationCommand, GuildEmoji, GuildMember, GuildTextBasedChannel, Message, Role, WebhookClientOptions, WebhookClient, WebhookClientData, ComponentEmojiResolvable, ButtonBuilder, ActionRowBuilder, LinkButtonComponentData, Attachment, AttachmentBuilder, MessageActionRowComponentBuilder, TextDisplayBuilder, SeparatorBuilder, FileBuilder, SectionBuilder, MediaGalleryBuilder, ContainerBuilder, ContainerComponentBuilder as ContainerComponentBuilder$1, ColorResolvable, ComponentType, ContainerComponentData, ContainerComponent, APIUnfurledMediaItem, MediaGalleryItemData, TextInputBuilder, TextInputComponentData, TextInputStyle, LabelBuilderData, LabelBuilder, Collection, ModalData, LabelComponentData, TextDisplayComponentData, AnyComponentBuilder, ThumbnailComponentData, ThumbnailBuilder, ButtonComponentData, FileUploadBuilder, EmbedAssetData, User, ClientUser, ImageURLOptions, EmbedFooterData, APIEmbed, Embed, EmbedData, EmbedBuilder, AttachmentData, MediaGalleryItemBuilder, ModalBuilder, StringSelectMenuBuilder, UserSelectMenuBuilder, RoleSelectMenuBuilder, ChannelSelectMenuBuilder, MentionableSelectMenuBuilder } from 'discord.js';
3
3
  export * from '@magicyan/core';
4
4
 
5
5
  declare const chars: {
@@ -144,22 +144,6 @@ declare function fetchMessageFromURL(client: Client | Client, url: string): Prom
144
144
 
145
145
  declare function setMobileStatus(): void;
146
146
 
147
- type TextInputData = Omit<TextInputComponentData, "type">;
148
- interface ModalFieldData extends Omit<TextInputData, "style"> {
149
- style?: TextInputStyle;
150
- }
151
- declare function createModalInput(data: ModalFieldData): ActionRowBuilder<TextInputBuilder>;
152
- type ModalFieldsData = Record<string, Omit<ModalFieldData, "customId">>;
153
- declare function createModalFields(data: ModalFieldsData): ActionRowBuilder<TextInputBuilder>[];
154
- type ModalFieldsRecord<K extends string> = Record<K, string>;
155
- declare function modalFieldsToRecord<K extends string = string>(fields: ModalSubmitInteraction | ModalSubmitFields | Collection<string, TextInputComponent>): ModalFieldsRecord<K>;
156
- interface CreateModalData {
157
- title?: string;
158
- customId: string;
159
- components: ModalFieldsData;
160
- }
161
- declare function createModal(data: CreateModalData): ModalBuilder;
162
-
163
147
  /**
164
148
  *
165
149
  * @param mention Discord mentionable string
@@ -429,6 +413,65 @@ type MediaGallerySource = MediaGalleryItemData | string | Attachment | Attachmen
429
413
  */
430
414
  declare function createMediaGallery(...items: (MediaGallerySource | MediaGallerySource[])[]): MediaGalleryBuilder;
431
415
 
416
+ type CreateInputData = Partial<Omit<TextInputComponentData, "type" | "label">>;
417
+ declare function createTextInput(data: CreateInputData): TextInputBuilder;
418
+ /**
419
+ * @deprecated Use {@link createTextInput} instead.
420
+ */
421
+ declare function createModalInput(data: Omit<TextInputComponentData, "type" | "style"> & {
422
+ style?: TextInputStyle;
423
+ }): ActionRowBuilder<TextInputBuilder>;
424
+
425
+ type CreateLabelData = Omit<LabelBuilderData, "type">;
426
+ type ComponentInLabelBuilder = NonNullable<CreateLabelData["component"]>;
427
+ declare function createLabel(data: CreateLabelData): LabelBuilder;
428
+ declare function createLabel(label: string, description?: string, component?: ComponentInLabelBuilder, id?: number): LabelBuilder;
429
+ declare function createLabel(label: string, component?: ComponentInLabelBuilder, id?: number): LabelBuilder;
430
+
431
+ type ModalComponents = LabelBuilder | TextDisplayBuilder | string | null | boolean | undefined;
432
+ type ResolveModalData = {
433
+ fields: {
434
+ fields: Collection<string, ModalData>;
435
+ };
436
+ } | {
437
+ fields: Collection<string, ModalData>;
438
+ } | Collection<string, ModalData>;
439
+ type ModalFieldsRecord = Record<string, string | string[]>;
440
+ /**
441
+ * Converts modal submitted fields into a plain record object, mapping each component `customId` to its value.
442
+ *
443
+ * This function supports multiple modal submission data shapes coming from Discord.js,
444
+ * automatically extracting and normalizing values from:
445
+ * - Text input fields (`ComponentType.TextInput`) → string
446
+ * - Select menus & other multi-value components → string[]
447
+ * - File upload fields (`ComponentType.FileUpload`) → string[] of attachment URLs
448
+ *
449
+ * Optionally, a parser callback may be provided to transform the resulting record before returning it.
450
+ *
451
+ * @template T The expected return type after optional parsing.
452
+ * @param {ResolveModalData} data - The modal submission data, usually from an interaction.
453
+ * @param {(record: ModalFieldsRecord) => T} [parse] - Optional transform function applied to the resulting record.
454
+ * @returns {ModalFieldsRecord | T} The processed fields as a record, or the parser result if provided.
455
+ *
456
+ * @example
457
+ * ```ts
458
+ * const fields = modalFieldsToRecord(interaction.fields);
459
+ * console.log(fields.username); // "JohnDoe"
460
+ * ```
461
+ *
462
+ * @example With parsing
463
+ * ```ts
464
+ * const result = modalFieldsToRecord(interaction.fields, fields => ({
465
+ * name: fields.username.trim(),
466
+ * age: Number.parseInt(fields.age),
467
+ * }));
468
+ * ```
469
+ */
470
+ declare function modalFieldsToRecord<const T = ModalFieldsRecord>(data: ResolveModalData): T;
471
+ declare function modalFieldsToRecord<const T = ModalFieldsRecord>(data: ResolveModalData, parse: (record: ModalFieldsRecord) => T): T;
472
+ type ModalFields = (LabelComponentData | TextDisplayComponentData)[];
473
+ declare function createModalFields(...components: ModalComponents[]): ModalFields;
474
+
432
475
  /**
433
476
  * Creates an {@link ActionRowBuilder} containing one or more UI components.
434
477
  *
@@ -675,6 +718,16 @@ type ThumbAreaData = {
675
718
  declare function createThumbArea(content: string, thumbnail?: ThumbAreaThumbnail): SectionBuilder | TextDisplayBuilder;
676
719
  declare function createThumbArea(data: ThumbAreaData): SectionBuilder | TextDisplayBuilder;
677
720
 
721
+ interface FileUploadData {
722
+ customId?: string;
723
+ minValues?: number;
724
+ maxValues?: number;
725
+ required?: boolean;
726
+ id?: number;
727
+ }
728
+ declare function createFileUpload(data: FileUploadData): FileUploadBuilder;
729
+ declare function createFileUpload(customId: string): FileUploadBuilder;
730
+
678
731
  type EmbedPlusAssetData = string | Attachment | AttachmentBuilder | EmbedAssetData | undefined | null;
679
732
  type EmbedAssetOptions = Omit<EmbedAssetData, "url">;
680
733
  declare function createEmbedAsset(source: EmbedPlusAssetData, options?: EmbedAssetOptions): EmbedAssetData | undefined;
@@ -841,26 +894,6 @@ declare function createEmbedFiles(embed: EmbedBuilder, options?: CreateEmbedFile
841
894
  */
842
895
  declare function isAttachment(value: unknown): value is Attachment | AttachmentBuilder;
843
896
 
844
- /**
845
- * Checks whether the given value is a {@link Message}.
846
- *
847
- * This function returns `true` if the value is an instance of `Message`
848
- * or if it structurally resembles a `Message` object.
849
- *
850
- * @param value - The value to check.
851
- * @returns `true` if the value is a `Message`, otherwise `false`.
852
- *
853
- * @example
854
- * import type { Message, Interaction } from "discord.js";
855
- *
856
- * function handle(input: Message | Interaction | unknown) {
857
- * if (isMessage(input)) {
858
- * console.log("This is a message with ID:", input.id);
859
- * }
860
- * }
861
- */
862
- declare function isMessage(value: unknown): value is Message;
863
-
864
897
  /**
865
898
  * Checks whether the given value is a {@link ButtonBuilder}.
866
899
  *
@@ -1082,5 +1115,25 @@ declare function isTextDisplayBuilder(value: unknown): value is TextDisplayBuild
1082
1115
  */
1083
1116
  declare function isTextInputBuilder(value: unknown): value is TextInputBuilder;
1084
1117
 
1085
- export { ContainerPlusBuilder, CustomItents, CustomPartials, EmbedLimit, EmbedPlusBuilder, Separator, chars, commandMention, createComponents, createContainer, createEmbed, createEmbedAsset, createEmbedAuthor, createEmbedFiles, createEmbedFooter, createFile, createLinkButton, createMediaGallery, createModal, createModalFields, createModalInput, createRow, createSection, createSeparator, createTextDisplay, createThumbArea, createThumbnail, createWebhookClient, extractMentionId, fetchMessageFromURL, findChannel, findCommand, findEmoji, findMember, findMessage, findRole, getChannelUrlInfo, getMessageURLInfo, isActionRowBuilder, isAnySelectMenuBuilder, isAttachment, isButtonBuilder, isChannelSelectMenuBuilder, isContainerBuilder, isMediaGalleryBuilder, isMediaGalleryItemBuilder, isMentionableSelectMenuBuilder, isMessage, isModalBuilder, isRoleSelectMenuBuilder, isSectionBuilder, isSeparatorBuilder, isStringSelectMenuBuilder, isTextDisplayBuilder, isTextInputBuilder, isUserSelectMenuBuilder, modalFieldsToRecord, setMobileStatus, wrapButtons };
1086
- export type { AnyEmbedData, AnySelectMenuBuilder, ComponentBuildersData, ComponentData, ContainerColor, ContainerComponentBuilder, ContainerData, ContainerInComponentType, CreateComponentData, CreateModalData, EmbedPlusAssetData, EmbedPlusAuthorData, EmbedPlusColorData, EmbedPlusData, EmbedPlusFooterData, EmbedPlusProperty, MagicComponentData, MediaGallerySource, ModalFieldsData, ModalFieldsRecord, SectionAccessory, SectionAccessoryData, SectionButtonAccessory, SectionData, SectionThumbnailAccessory, SeparatorData, ThumbAreaData, ThumbAreaThumbnail, ThumbnailData };
1118
+ /**
1119
+ * Checks whether the given value is a {@link Message}.
1120
+ *
1121
+ * This function returns `true` if the value is an instance of `Message`
1122
+ * or if it structurally resembles a `Message` object.
1123
+ *
1124
+ * @param value - The value to check.
1125
+ * @returns `true` if the value is a `Message`, otherwise `false`.
1126
+ *
1127
+ * @example
1128
+ * import type { Message, Interaction } from "discord.js";
1129
+ *
1130
+ * function handle(input: Message | Interaction | unknown) {
1131
+ * if (isMessage(input)) {
1132
+ * console.log("This is a message with ID:", input.id);
1133
+ * }
1134
+ * }
1135
+ */
1136
+ declare function isMessage(value: unknown): value is Message;
1137
+
1138
+ export { ContainerPlusBuilder, CustomItents, CustomPartials, EmbedLimit, EmbedPlusBuilder, Separator, chars, commandMention, createComponents, createContainer, createEmbed, createEmbedAsset, createEmbedAuthor, createEmbedFiles, createEmbedFooter, createFile, createFileUpload, createLabel, createLinkButton, createMediaGallery, createModalFields, createModalInput, createRow, createSection, createSeparator, createTextDisplay, createTextInput, createThumbArea, createThumbnail, createWebhookClient, extractMentionId, fetchMessageFromURL, findChannel, findCommand, findEmoji, findMember, findMessage, findRole, getChannelUrlInfo, getMessageURLInfo, isActionRowBuilder, isAnySelectMenuBuilder, isAttachment, isButtonBuilder, isChannelSelectMenuBuilder, isContainerBuilder, isMediaGalleryBuilder, isMediaGalleryItemBuilder, isMentionableSelectMenuBuilder, isMessage, isModalBuilder, isRoleSelectMenuBuilder, isSectionBuilder, isSeparatorBuilder, isStringSelectMenuBuilder, isTextDisplayBuilder, isTextInputBuilder, isUserSelectMenuBuilder, modalFieldsToRecord, setMobileStatus, wrapButtons };
1139
+ export type { AnyEmbedData, AnySelectMenuBuilder, ComponentBuildersData, ComponentData, ComponentInLabelBuilder, ContainerColor, ContainerComponentBuilder, ContainerData, ContainerInComponentType, CreateComponentData, CreateLabelData, EmbedPlusAssetData, EmbedPlusAuthorData, EmbedPlusColorData, EmbedPlusData, EmbedPlusFooterData, EmbedPlusProperty, FileUploadData, MagicComponentData, MediaGallerySource, SectionAccessory, SectionAccessoryData, SectionButtonAccessory, SectionData, SectionThumbnailAccessory, SeparatorData, ThumbAreaData, ThumbAreaThumbnail, ThumbnailData };
package/dist/index.mjs CHANGED
@@ -8,7 +8,6 @@ export { findEmoji } from './functions/emojis.mjs';
8
8
  export { findMember } from './functions/members.mjs';
9
9
  export { fetchMessageFromURL, findMessage, getMessageURLInfo } from './functions/message.mjs';
10
10
  export { setMobileStatus } from './functions/misc.mjs';
11
- export { createModal, createModalFields, createModalInput, modalFieldsToRecord } from './functions/modals.mjs';
12
11
  export { extractMentionId } from './functions/regex.mjs';
13
12
  export { findRole } from './functions/roles.mjs';
14
13
  export { createWebhookClient } from './functions/webhooks.mjs';
@@ -17,19 +16,22 @@ export { createComponents } from './functions/components/components.mjs';
17
16
  export { ContainerPlusBuilder, createContainer } from './functions/components/container.mjs';
18
17
  export { createFile } from './functions/components/file.mjs';
19
18
  export { createMediaGallery } from './functions/components/gallery.mjs';
19
+ export { createModalInput, createTextInput } from './functions/components/input.mjs';
20
+ export { createLabel } from './functions/components/label.mjs';
21
+ export { createModalFields, modalFieldsToRecord } from './functions/components/modal.mjs';
20
22
  export { createRow } from './functions/components/row.mjs';
21
23
  export { createSection } from './functions/components/section.mjs';
22
24
  export { createSeparator } from './functions/components/separator.mjs';
23
25
  export { createTextDisplay } from './functions/components/text.mjs';
24
26
  export { createThumbArea } from './functions/components/thumbarea.mjs';
25
27
  export { createThumbnail } from './functions/components/thumbnail.mjs';
28
+ export { createFileUpload } from './functions/components/upload.mjs';
26
29
  export { createEmbedAsset } from './functions/embeds/assets.mjs';
27
30
  export { createEmbedAuthor } from './functions/embeds/author.mjs';
28
31
  export { EmbedPlusBuilder, createEmbed } from './functions/embeds/embedplus.mjs';
29
32
  export { createEmbedFiles } from './functions/embeds/files.mjs';
30
33
  export { createEmbedFooter } from './functions/embeds/footer.mjs';
31
34
  export { isAttachment } from './guards/attachment.mjs';
32
- export { isMessage } from './guards/message.mjs';
33
35
  export { isButtonBuilder } from './guards/components/button.mjs';
34
36
  export { isContainerBuilder } from './guards/components/container.mjs';
35
37
  export { isMediaGalleryBuilder, isMediaGalleryItemBuilder } from './guards/components/gallery.mjs';
@@ -40,4 +42,5 @@ export { isAnySelectMenuBuilder, isChannelSelectMenuBuilder, isMentionableSelect
40
42
  export { isSeparatorBuilder } from './guards/components/separator.mjs';
41
43
  export { isTextDisplayBuilder } from './guards/components/textdisplay.mjs';
42
44
  export { isTextInputBuilder } from './guards/components/textinput.mjs';
45
+ export { isMessage } from './guards/message.mjs';
43
46
  export * from '@magicyan/core';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magicyan/discord",
3
- "version": "1.5.4",
3
+ "version": "1.6.1",
4
4
  "description": "Simple functions to facilitate discord bot development",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,7 +39,7 @@
39
39
  "test": "vitest"
40
40
  },
41
41
  "peerDependencies": {
42
- "discord.js": "^14.22.1"
42
+ "discord.js": "^14.24.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@magicyan/config": "*",
@@ -49,6 +49,7 @@
49
49
  "vitest": "^2.1.9"
50
50
  },
51
51
  "dependencies": {
52
- "@magicyan/core": "^1.1.5"
52
+ "@magicyan/core": "^1.1.5",
53
+ "zod": "^4.1.12"
53
54
  }
54
55
  }
@@ -1,33 +0,0 @@
1
- 'use strict';
2
-
3
- const discord_js = require('discord.js');
4
- const row = require('./components/row.cjs');
5
-
6
- function createModalInput(data) {
7
- data.style ?? (data.style = discord_js.TextInputStyle.Short);
8
- return row.createRow(new discord_js.TextInputBuilder(data));
9
- }
10
- function createModalFields(data) {
11
- return Object.entries(data).map(
12
- ([customId, data2]) => createModalInput({ customId, ...data2 })
13
- );
14
- }
15
- function modalFieldsToRecord(fields) {
16
- const modalFields = fields instanceof discord_js.ModalSubmitInteraction ? fields.fields.fields : "fields" in fields ? fields.fields : fields;
17
- return modalFields.reduce((acc, { customId, value }) => {
18
- acc[customId] = value;
19
- return acc;
20
- }, {});
21
- }
22
- function createModal(data) {
23
- return new discord_js.ModalBuilder({
24
- title: data.title ?? data.customId,
25
- customId: data.customId,
26
- components: createModalFields(data.components)
27
- });
28
- }
29
-
30
- exports.createModal = createModal;
31
- exports.createModalFields = createModalFields;
32
- exports.createModalInput = createModalInput;
33
- exports.modalFieldsToRecord = modalFieldsToRecord;
@@ -1,28 +0,0 @@
1
- import { TextInputStyle, TextInputBuilder, ModalSubmitInteraction, ModalBuilder } from 'discord.js';
2
- import { createRow } from './components/row.mjs';
3
-
4
- function createModalInput(data) {
5
- data.style ?? (data.style = TextInputStyle.Short);
6
- return createRow(new TextInputBuilder(data));
7
- }
8
- function createModalFields(data) {
9
- return Object.entries(data).map(
10
- ([customId, data2]) => createModalInput({ customId, ...data2 })
11
- );
12
- }
13
- function modalFieldsToRecord(fields) {
14
- const modalFields = fields instanceof ModalSubmitInteraction ? fields.fields.fields : "fields" in fields ? fields.fields : fields;
15
- return modalFields.reduce((acc, { customId, value }) => {
16
- acc[customId] = value;
17
- return acc;
18
- }, {});
19
- }
20
- function createModal(data) {
21
- return new ModalBuilder({
22
- title: data.title ?? data.customId,
23
- customId: data.customId,
24
- components: createModalFields(data.components)
25
- });
26
- }
27
-
28
- export { createModal, createModalFields, createModalInput, modalFieldsToRecord };