diff --git a/packages/builders/__tests__/messages/fileBody.test.ts b/packages/builders/__tests__/messages/fileBody.test.ts index 2759ce610f8b..4af96a382e86 100644 --- a/packages/builders/__tests__/messages/fileBody.test.ts +++ b/packages/builders/__tests__/messages/fileBody.test.ts @@ -6,7 +6,7 @@ import { AttachmentBuilder, MessageBuilder } from '../../src/index.js'; test('AttachmentBuilder stores and exposes file data', () => { const data = Buffer.from('hello world'); const attachment = new AttachmentBuilder() - .setId('0') + .setId(1) .setFilename('greeting.txt') .setFileData(data) .setFileContentType('text/plain'); @@ -14,7 +14,7 @@ test('AttachmentBuilder stores and exposes file data', () => { expect(attachment.getRawFile()).toStrictEqual({ contentType: 'text/plain', data, - key: 'files[0]', + key: 'files[1]', name: 'greeting.txt', }); @@ -27,7 +27,7 @@ test('AttachmentBuilder stores and exposes file data', () => { test('MessageBuilder.toFileBody returns JSON body and files', () => { const msg = new MessageBuilder().setContent('here is a file').addAttachments( new AttachmentBuilder() - .setId('0') + .setId(0) .setFilename('file.bin') .setFileData(Buffer.from([1, 2, 3])) .setFileContentType('application/octet-stream'), @@ -47,7 +47,9 @@ test('MessageBuilder.toFileBody returns JSON body and files', () => { }); test('MessageBuilder.toFileBody returns empty files when attachments reference existing uploads', () => { - const msg = new MessageBuilder().addAttachments(new AttachmentBuilder().setId('123').setFilename('existing.png')); + const msg = new MessageBuilder().addAttachments( + new AttachmentBuilder().setId('1234567890123456789').setFilename('existing.png'), + ); const { body, files } = msg.toFileBody(); expect(body).toEqual(msg.toJSON()); diff --git a/packages/builders/__tests__/messages/message.test.ts b/packages/builders/__tests__/messages/message.test.ts index 3e05eecd08ac..963475b8280f 100644 --- a/packages/builders/__tests__/messages/message.test.ts +++ b/packages/builders/__tests__/messages/message.test.ts @@ -64,7 +64,7 @@ describe('Message', () => { row.addPrimaryButtonComponents((button) => button.setCustomId('abc').setLabel('def')), ) .setStickerIds('123', '456') - .addAttachments((attachment) => attachment.setId('hi!').setFilename('abc')) + .addAttachments((attachment) => attachment.setId(0).setFilename('abc')) .setFlags(MessageFlags.Ephemeral) .setEnforceNonce(false) .updatePoll((poll) => poll.addAnswers({ poll_media: { text: 'foo' } }).setQuestion({ text: 'foo' })); @@ -83,7 +83,7 @@ describe('Message', () => { }, ], sticker_ids: ['123', '456'], - attachments: [{ id: 'hi!', filename: 'abc' }], + attachments: [{ id: 0, filename: 'abc' }], flags: 64, enforce_nonce: false, poll: { diff --git a/packages/builders/src/Assertions.ts b/packages/builders/src/Assertions.ts index c672c95c08dc..50761780c245 100644 --- a/packages/builders/src/Assertions.ts +++ b/packages/builders/src/Assertions.ts @@ -3,6 +3,7 @@ import { z } from 'zod'; export const idPredicate = z.int().min(0).max(2_147_483_647).optional(); export const customIdPredicate = z.string().min(1).max(100); +export const snowflakePredicate = z.string().regex(/^[1-9]\d{16,18}$/); export const memberPermissionsPredicate = z.coerce.bigint(); diff --git a/packages/builders/src/components/Assertions.ts b/packages/builders/src/components/Assertions.ts index 67b9b44fc0c4..864a27d567a0 100644 --- a/packages/builders/src/components/Assertions.ts +++ b/packages/builders/src/components/Assertions.ts @@ -1,12 +1,12 @@ import { ButtonStyle, ChannelType, ComponentType, SelectMenuDefaultValueType } from 'discord-api-types/v10'; import { z } from 'zod'; -import { idPredicate, customIdPredicate } from '../Assertions.js'; +import { idPredicate, customIdPredicate, snowflakePredicate } from '../Assertions.js'; const labelPredicate = z.string().min(1).max(80); export const emojiPredicate = z .strictObject({ - id: z.string().optional(), + id: snowflakePredicate.optional(), name: z.string().min(2).max(32).optional(), animated: z.boolean().optional(), }) @@ -39,7 +39,7 @@ const buttonLinkPredicate = buttonPredicateBase.extend({ const buttonPremiumPredicate = buttonPredicateBase.extend({ style: z.literal(ButtonStyle.Premium), - sku_id: z.string(), + sku_id: snowflakePredicate, }); export const buttonPredicate = z.discriminatedUnion('style', [ @@ -64,7 +64,7 @@ export const selectMenuChannelPredicate = selectMenuBasePredicate.extend({ type: z.literal(ComponentType.ChannelSelect), channel_types: z.enum(ChannelType).array().optional(), default_values: z - .object({ id: z.string(), type: z.literal(SelectMenuDefaultValueType.Channel) }) + .object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.Channel) }) .array() .max(25) .optional(), @@ -74,7 +74,7 @@ export const selectMenuMentionablePredicate = selectMenuBasePredicate.extend({ type: z.literal(ComponentType.MentionableSelect), default_values: z .object({ - id: z.string(), + id: snowflakePredicate, type: z.literal([SelectMenuDefaultValueType.Role, SelectMenuDefaultValueType.User]), }) .array() @@ -85,7 +85,7 @@ export const selectMenuMentionablePredicate = selectMenuBasePredicate.extend({ export const selectMenuRolePredicate = selectMenuBasePredicate.extend({ type: z.literal(ComponentType.RoleSelect), default_values: z - .object({ id: z.string(), type: z.literal(SelectMenuDefaultValueType.Role) }) + .object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.Role) }) .array() .max(25) .optional(), @@ -142,7 +142,7 @@ export const selectMenuStringPredicate = selectMenuBasePredicate export const selectMenuUserPredicate = selectMenuBasePredicate.extend({ type: z.literal(ComponentType.UserSelect), default_values: z - .object({ id: z.string(), type: z.literal(SelectMenuDefaultValueType.User) }) + .object({ id: snowflakePredicate, type: z.literal(SelectMenuDefaultValueType.User) }) .array() .max(25) .optional(), diff --git a/packages/builders/src/messages/Assertions.ts b/packages/builders/src/messages/Assertions.ts index 32f97512ef18..a913deac8d6b 100644 --- a/packages/builders/src/messages/Assertions.ts +++ b/packages/builders/src/messages/Assertions.ts @@ -1,6 +1,7 @@ import { Buffer } from 'node:buffer'; import { AllowedMentionsTypes, ComponentType, MessageFlags, MessageReferenceType } from 'discord-api-types/v10'; import { z } from 'zod'; +import { snowflakePredicate } from '../Assertions.js'; import { embedPredicate } from './embed/Assertions.js'; import { pollPredicate } from './poll/Assertions.js'; @@ -15,7 +16,7 @@ export const rawFilePredicate = z.object({ export const attachmentPredicate = z.object({ // As a string it only makes sense for edits when we do have an attachment snowflake - id: z.union([z.string(), z.number()]), + id: z.union([snowflakePredicate, z.number()]), description: z.string().max(1_024).optional(), duration_secs: z .number()