mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
feat: message structures (#10982)
* feat: message structures * fix: docs * chore: components and more * feat: embed and more * feat: more substructures and code review suggestions * chore: tests and date conversions * chore: jsdoc strings * fix: tests * fix: tests * feat: hexColor getters * chore: remove getters for nested data * chore: apply suggestions from code review * fix: burst_colors in toJSON * docs: rephrase SectionBuilder remark * chore: add LabelComponent * fix: add name and size to file component * chore: move resolved interaction data to interactions dir * fix: code review * chore: bump discord-api-types * chore: apply code review suggestions * fix: lockfile * chore: update remark * fix: missing export * chore: code review and tests * build: fix file * fix: typo * fix: missing toJSON * fix: remove redundant patch overrides * chore: missing component suffix * chore: better name * chore: add comment explaining timestamp conversion --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
@@ -38,7 +38,7 @@ import {
|
||||
TextChannel,
|
||||
ThreadMetadata,
|
||||
VoiceChannel,
|
||||
} from '../src/index.js';
|
||||
} from '../src/channels/index.js';
|
||||
import { kData } from '../src/utils/symbols.js';
|
||||
|
||||
describe('text channel', () => {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { APIExtendedInvite, APIInvite } from 'discord-api-types/v10';
|
||||
import { InviteTargetType, InviteType } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { Invite } from '../src/index.js';
|
||||
import { Invite } from '../src/invites/Invite.js';
|
||||
import { dateToDiscordISOTimestamp } from '../src/utils/optimization.js';
|
||||
import { kPatch } from '../src/utils/symbols.js';
|
||||
|
||||
describe('Invite', () => {
|
||||
@@ -22,7 +23,7 @@ describe('Invite', () => {
|
||||
|
||||
const dataExtended: Omit<APIExtendedInvite, 'expires_at'> = {
|
||||
...data,
|
||||
created_at: '2020-10-10T13:50:17.209Z',
|
||||
created_at: '2020-10-10T13:50:17.209000+00:00',
|
||||
max_age: 12,
|
||||
max_uses: 34,
|
||||
temporary: false,
|
||||
@@ -54,7 +55,7 @@ describe('Invite', () => {
|
||||
const instance = new Invite(dataExtended);
|
||||
expect(instance.type).toBe(data.type);
|
||||
expect(instance.code).toBe(dataExtended.code);
|
||||
expect(instance.createdAt?.toISOString()).toBe(dataExtended.created_at);
|
||||
expect(dateToDiscordISOTimestamp(instance.createdAt!)).toBe(dataExtended.created_at);
|
||||
expect(instance.createdTimestamp).toBe(Date.parse(dataExtended.created_at));
|
||||
expect(instance.maxAge).toBe(dataExtended.max_age);
|
||||
expect(instance.maxUses).toBe(dataExtended.max_uses);
|
||||
@@ -63,10 +64,10 @@ describe('Invite', () => {
|
||||
expect(instance.targetType).toBe(dataExtended.target_type);
|
||||
expect(instance.temporary).toBe(dataExtended.temporary);
|
||||
expect(instance.uses).toBe(dataExtended.uses);
|
||||
expect(instance.expiresTimestamp).toStrictEqual(Date.parse('2020-10-10T13:50:29.209Z'));
|
||||
expect(instance.expiresAt).toStrictEqual(new Date('2020-10-10T13:50:29.209Z'));
|
||||
expect(instance.expiresTimestamp).toStrictEqual(Date.parse('2020-10-10T13:50:29.209000+00:00'));
|
||||
expect(instance.expiresAt).toStrictEqual(new Date('2020-10-10T13:50:29.209000+00:00'));
|
||||
expect(instance.url).toBe('https://discord.gg/123');
|
||||
expect(instance.toJSON()).toEqual({ ...dataExtended, expires_at: '2020-10-10T13:50:29.209Z' });
|
||||
expect(instance.toJSON()).toEqual({ ...dataExtended, expires_at: '2020-10-10T13:50:29.209000+00:00' });
|
||||
});
|
||||
|
||||
test('Invite with omitted properties', () => {
|
||||
@@ -79,8 +80,8 @@ describe('Invite', () => {
|
||||
});
|
||||
|
||||
test('Invite with expiration', () => {
|
||||
const instance = new Invite({ ...dataExtended, expires_at: '2020-10-10T13:50:29.209Z' });
|
||||
expect(instance.toJSON()).toEqual({ ...dataExtended, expires_at: '2020-10-10T13:50:29.209Z' });
|
||||
const instance = new Invite({ ...dataExtended, expires_at: '2020-10-10T13:50:29.209000+00:00' });
|
||||
expect(instance.toJSON()).toEqual({ ...dataExtended, expires_at: '2020-10-10T13:50:29.209000+00:00' });
|
||||
});
|
||||
|
||||
test('Patching Invite works in place', () => {
|
||||
|
||||
478
packages/structures/__tests__/message.test.ts
Normal file
478
packages/structures/__tests__/message.test.ts
Normal file
@@ -0,0 +1,478 @@
|
||||
import { DiscordSnowflake } from '@sapphire/snowflake';
|
||||
import type {
|
||||
APIActionRowComponent,
|
||||
APIButtonComponent,
|
||||
APIChannelSelectComponent,
|
||||
APIContainerComponent,
|
||||
APIFileComponent,
|
||||
APIMediaGalleryComponent,
|
||||
APIMentionableSelectComponent,
|
||||
APIMessage,
|
||||
APIRoleSelectComponent,
|
||||
APISectionComponent,
|
||||
APISeparatorComponent,
|
||||
APIStringSelectComponent,
|
||||
APIUser,
|
||||
APIUserSelectComponent,
|
||||
} from 'discord-api-types/v10';
|
||||
import {
|
||||
MessageReferenceType,
|
||||
MessageType,
|
||||
MessageFlags,
|
||||
ComponentType,
|
||||
ButtonStyle,
|
||||
SeparatorSpacingSize,
|
||||
ChannelType,
|
||||
SelectMenuDefaultValueType,
|
||||
} from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { Attachment } from '../src/messages/Attachment.js';
|
||||
import { Message } from '../src/messages/Message.js';
|
||||
import { ContainerComponent } from '../src/messages/components/ContainerComponent.js';
|
||||
import { Embed } from '../src/messages/embeds/Embed.js';
|
||||
import { User } from '../src/users/User.js';
|
||||
import { dateToDiscordISOTimestamp } from '../src/utils/optimization.js';
|
||||
|
||||
const user: APIUser = {
|
||||
username: 'user',
|
||||
avatar: 'abcd123',
|
||||
global_name: 'User',
|
||||
discriminator: '0',
|
||||
id: '3',
|
||||
};
|
||||
|
||||
describe('message with embeds and attachments', () => {
|
||||
const timestamp = '2025-10-09T17:48:20.192000+00:00';
|
||||
const data: APIMessage = {
|
||||
id: DiscordSnowflake.generate({ timestamp: Date.parse(timestamp) }).toString(),
|
||||
type: MessageType.Default,
|
||||
position: 10,
|
||||
channel_id: '2',
|
||||
author: user,
|
||||
attachments: [
|
||||
{
|
||||
filename: 'file.txt',
|
||||
description: 'describe attachment',
|
||||
id: '0',
|
||||
proxy_url: 'https://media.example.com/attachment/123.txt',
|
||||
size: 5,
|
||||
url: 'https://example.com/attachment/123.txt',
|
||||
},
|
||||
],
|
||||
content: 'something <&5> <&6>',
|
||||
edited_timestamp: '2025-10-09T17:50:20.292000+00:00',
|
||||
embeds: [
|
||||
{
|
||||
author: {
|
||||
name: 'embed author',
|
||||
icon_url: 'https://discord.js.org/static/logo.svg',
|
||||
},
|
||||
color: 255,
|
||||
description: 'describe me',
|
||||
fields: [
|
||||
{
|
||||
name: 'field name',
|
||||
value: 'field value',
|
||||
inline: false,
|
||||
},
|
||||
],
|
||||
footer: {
|
||||
text: 'footer',
|
||||
},
|
||||
image: {
|
||||
url: 'https://discord.js.org/static/logo.svg',
|
||||
},
|
||||
thumbnail: {
|
||||
url: 'https://discord.js.org/static/logo.svg',
|
||||
},
|
||||
title: 'Title',
|
||||
timestamp: '2025-10-19T21:39:40.193000+00:00',
|
||||
},
|
||||
],
|
||||
mention_everyone: false,
|
||||
mention_roles: ['5', '6'],
|
||||
mentions: [user],
|
||||
pinned: false,
|
||||
timestamp,
|
||||
tts: false,
|
||||
flags: MessageFlags.SuppressNotifications,
|
||||
};
|
||||
|
||||
test('Message has all properties', () => {
|
||||
const instance = new Message(data);
|
||||
expect(instance.id).toBe(data.id);
|
||||
expect(instance.channelId).toBe(data.channel_id);
|
||||
expect(instance.position).toBe(data.position);
|
||||
expect(instance.content).toBe(data.content);
|
||||
expect(instance.createdTimestamp).toBe(Date.parse(data.timestamp));
|
||||
expect(dateToDiscordISOTimestamp(instance.createdAt!)).toBe(data.timestamp);
|
||||
expect(instance.flags?.toJSON()).toBe(data.flags);
|
||||
expect(instance.editedTimestamp).toBe(Date.parse(data.edited_timestamp!));
|
||||
expect(dateToDiscordISOTimestamp(instance.editedAt!)).toBe(data.edited_timestamp);
|
||||
expect(instance.nonce).toBe(data.nonce);
|
||||
expect(instance.pinned).toBe(data.pinned);
|
||||
expect(instance.tts).toBe(data.tts);
|
||||
expect(instance.webhookId).toBe(data.webhook_id);
|
||||
expect(instance.type).toBe(MessageType.Default);
|
||||
expect(instance.toJSON()).toEqual(data);
|
||||
});
|
||||
|
||||
test('Attachment sub-structure', () => {
|
||||
const instances = data.attachments?.map((attachment) => new Attachment(attachment));
|
||||
expect(instances?.map((attachment) => attachment.toJSON())).toEqual(data.attachments);
|
||||
expect(instances?.[0]?.description).toBe(data.attachments?.[0]?.description);
|
||||
expect(instances?.[0]?.filename).toBe(data.attachments?.[0]?.filename);
|
||||
expect(instances?.[0]?.id).toBe(data.attachments?.[0]?.id);
|
||||
expect(instances?.[0]?.size).toBe(data.attachments?.[0]?.size);
|
||||
expect(instances?.[0]?.url).toBe(data.attachments?.[0]?.url);
|
||||
expect(instances?.[0]?.proxyURL).toBe(data.attachments?.[0]?.proxy_url);
|
||||
});
|
||||
|
||||
test('Embed sub-structure', () => {
|
||||
const instances = data.embeds?.map((embed) => new Embed(embed));
|
||||
expect(instances?.map((embed) => embed.toJSON())).toEqual(data.embeds);
|
||||
expect(instances?.[0]?.description).toBe(data.embeds?.[0]?.description);
|
||||
expect(instances?.[0]?.color).toBe(data.embeds?.[0]?.color);
|
||||
expect(instances?.[0]?.timestamp).toBe(Date.parse(data.embeds![0]!.timestamp!));
|
||||
expect(instances?.[0]?.title).toBe(data.embeds?.[0]?.title);
|
||||
expect(instances?.[0]?.url).toBe(data.embeds?.[0]?.url);
|
||||
expect(instances?.[0]?.type).toBe(data.embeds?.[0]?.type);
|
||||
});
|
||||
|
||||
test('User sub-structure', () => {
|
||||
const instance = new User(data.author);
|
||||
const instances = data.mentions.map((user) => new User(user));
|
||||
expect(instance.toJSON()).toEqual(data.author);
|
||||
expect(instances.map((user) => user.toJSON())).toEqual(data.mentions);
|
||||
expect(instance.avatar).toBe(data.author.avatar);
|
||||
expect(instance.discriminator).toBe(data.author.discriminator);
|
||||
expect(instance.displayName).toBe(data.author.global_name);
|
||||
expect(instance.globalName).toBe(data.author.global_name);
|
||||
expect(instance.id).toBe(data.author.id);
|
||||
expect(instance.username).toBe(data.author.username);
|
||||
});
|
||||
});
|
||||
|
||||
describe('message with components', () => {
|
||||
const timestamp = '2025-10-10T15:48:20.192000+00:00';
|
||||
const buttonRow: APIActionRowComponent<APIButtonComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
id: 5,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.Button,
|
||||
style: ButtonStyle.Danger,
|
||||
custom_id: 'danger',
|
||||
disabled: false,
|
||||
emoji: {
|
||||
animated: false,
|
||||
id: '12345',
|
||||
name: 'emoji',
|
||||
},
|
||||
id: 6,
|
||||
label: 'Danger button',
|
||||
},
|
||||
{
|
||||
type: ComponentType.Button,
|
||||
style: ButtonStyle.Link,
|
||||
url: 'https://discord.js.org/',
|
||||
disabled: false,
|
||||
id: 7,
|
||||
label: 'DJS',
|
||||
},
|
||||
{
|
||||
type: ComponentType.Button,
|
||||
style: ButtonStyle.Premium,
|
||||
sku_id: '9876',
|
||||
disabled: false,
|
||||
id: 8,
|
||||
},
|
||||
],
|
||||
};
|
||||
const file: APIFileComponent = {
|
||||
type: ComponentType.File,
|
||||
file: {
|
||||
url: 'attachment://file.txt',
|
||||
attachment_id: '0',
|
||||
content_type: 'text/plain',
|
||||
flags: 0,
|
||||
},
|
||||
id: 9,
|
||||
spoiler: true,
|
||||
};
|
||||
const mediaGallery: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
media: {
|
||||
url: 'https://discord.js.org/static/logo.svg',
|
||||
content_type: 'image/svg+xml',
|
||||
height: 50,
|
||||
width: 50,
|
||||
},
|
||||
description: 'Logo',
|
||||
spoiler: false,
|
||||
},
|
||||
],
|
||||
id: 10,
|
||||
};
|
||||
const section: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
accessory: {
|
||||
type: ComponentType.Thumbnail,
|
||||
media: {
|
||||
url: 'https://discord.js.org/static/logo.svg',
|
||||
},
|
||||
description: 'Logo thumbnail',
|
||||
id: 13,
|
||||
spoiler: false,
|
||||
},
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'Text',
|
||||
id: 14,
|
||||
},
|
||||
],
|
||||
id: 12,
|
||||
};
|
||||
const separator: APISeparatorComponent = {
|
||||
type: ComponentType.Separator,
|
||||
divider: true,
|
||||
id: 15,
|
||||
spacing: SeparatorSpacingSize.Large,
|
||||
};
|
||||
const channelRow: APIActionRowComponent<APIChannelSelectComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
id: 16,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.ChannelSelect,
|
||||
custom_id: 'channel',
|
||||
channel_types: [ChannelType.GuildCategory, ChannelType.GuildText],
|
||||
default_values: [
|
||||
{
|
||||
id: '123456789012345678',
|
||||
type: SelectMenuDefaultValueType.Channel,
|
||||
},
|
||||
{
|
||||
id: '123456789012345679',
|
||||
type: SelectMenuDefaultValueType.Channel,
|
||||
},
|
||||
],
|
||||
disabled: false,
|
||||
id: 17,
|
||||
max_values: 2,
|
||||
min_values: 0,
|
||||
placeholder: '(none)',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
const mentionRow: APIActionRowComponent<APIMentionableSelectComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
id: 18,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.MentionableSelect,
|
||||
custom_id: 'mention',
|
||||
default_values: [
|
||||
{
|
||||
id: '123456789012345678',
|
||||
type: SelectMenuDefaultValueType.User,
|
||||
},
|
||||
{
|
||||
id: '123456789012345679',
|
||||
type: SelectMenuDefaultValueType.Role,
|
||||
},
|
||||
],
|
||||
disabled: false,
|
||||
id: 19,
|
||||
max_values: 2,
|
||||
min_values: 0,
|
||||
placeholder: '(none)',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
const roleRow: APIActionRowComponent<APIRoleSelectComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
id: 20,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.RoleSelect,
|
||||
custom_id: 'role',
|
||||
default_values: [
|
||||
{
|
||||
id: '123456789012345678',
|
||||
type: SelectMenuDefaultValueType.Role,
|
||||
},
|
||||
{
|
||||
id: '123456789012345679',
|
||||
type: SelectMenuDefaultValueType.Role,
|
||||
},
|
||||
],
|
||||
disabled: false,
|
||||
id: 21,
|
||||
max_values: 2,
|
||||
min_values: 0,
|
||||
placeholder: '(none)',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
const userRow: APIActionRowComponent<APIUserSelectComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
id: 22,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.UserSelect,
|
||||
custom_id: 'user',
|
||||
default_values: [
|
||||
{
|
||||
id: '123456789012345678',
|
||||
type: SelectMenuDefaultValueType.User,
|
||||
},
|
||||
{
|
||||
id: '123456789012345679',
|
||||
type: SelectMenuDefaultValueType.User,
|
||||
},
|
||||
],
|
||||
disabled: false,
|
||||
id: 23,
|
||||
max_values: 2,
|
||||
min_values: 0,
|
||||
placeholder: '(none)',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
const stringRow: APIActionRowComponent<APIStringSelectComponent> = {
|
||||
type: ComponentType.ActionRow,
|
||||
id: 24,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.StringSelect,
|
||||
custom_id: 'string',
|
||||
options: [
|
||||
{
|
||||
label: 'one',
|
||||
value: '1',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
label: 'two',
|
||||
value: '2',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
label: 'three',
|
||||
value: '3',
|
||||
description: 'third',
|
||||
emoji: {
|
||||
id: '3333333333333333333',
|
||||
name: '3',
|
||||
animated: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
disabled: false,
|
||||
id: 25,
|
||||
max_values: 2,
|
||||
min_values: 0,
|
||||
placeholder: '(none)',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
const container: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
accent_color: 255,
|
||||
id: 4,
|
||||
components: [
|
||||
buttonRow,
|
||||
file,
|
||||
mediaGallery,
|
||||
section,
|
||||
separator,
|
||||
channelRow,
|
||||
mentionRow,
|
||||
roleRow,
|
||||
userRow,
|
||||
stringRow,
|
||||
],
|
||||
spoiler: true,
|
||||
};
|
||||
const data: APIMessage = {
|
||||
id: DiscordSnowflake.generate({ timestamp: Date.parse(timestamp) }).toString(),
|
||||
type: MessageType.Reply,
|
||||
position: 15,
|
||||
channel_id: '2',
|
||||
author: user,
|
||||
attachments: [
|
||||
{
|
||||
filename: 'file.txt',
|
||||
description: 'describe attachment',
|
||||
id: '0',
|
||||
proxy_url: 'https://media.example.com/attachment/123.txt',
|
||||
size: 5,
|
||||
url: 'https://example.com/attachment/123.txt',
|
||||
},
|
||||
],
|
||||
content: '',
|
||||
edited_timestamp: '2025-10-10T15:50:20.292000+00:00',
|
||||
embeds: [],
|
||||
components: [container],
|
||||
message_reference: {
|
||||
channel_id: '505050505050505050',
|
||||
message_id: '606060606060606060',
|
||||
guild_id: '707070707070707070',
|
||||
type: MessageReferenceType.Default,
|
||||
},
|
||||
mention_everyone: false,
|
||||
mention_roles: ['5', '6'],
|
||||
mentions: [user],
|
||||
pinned: false,
|
||||
timestamp,
|
||||
tts: false,
|
||||
flags: MessageFlags.IsComponentsV2 | MessageFlags.Ephemeral,
|
||||
};
|
||||
|
||||
test('Message has all properties', () => {
|
||||
const instance = new Message(data);
|
||||
expect(instance.id).toBe(data.id);
|
||||
expect(instance.channelId).toBe(data.channel_id);
|
||||
expect(instance.position).toBe(data.position);
|
||||
expect(instance.content).toBe(data.content);
|
||||
expect(instance.createdTimestamp).toBe(Date.parse(data.timestamp));
|
||||
expect(dateToDiscordISOTimestamp(instance.createdAt!)).toBe(data.timestamp);
|
||||
expect(instance.flags?.toJSON()).toBe(data.flags);
|
||||
expect(instance.editedTimestamp).toBe(Date.parse(data.edited_timestamp!));
|
||||
expect(dateToDiscordISOTimestamp(instance.editedAt!)).toBe(data.edited_timestamp);
|
||||
expect(instance.nonce).toBe(data.nonce);
|
||||
expect(instance.pinned).toBe(data.pinned);
|
||||
expect(instance.tts).toBe(data.tts);
|
||||
expect(instance.webhookId).toBe(data.webhook_id);
|
||||
expect(instance.type).toBe(MessageType.Reply);
|
||||
expect(instance.toJSON()).toEqual(data);
|
||||
});
|
||||
|
||||
test('Attachment sub-structure', () => {
|
||||
const instances = data.attachments?.map((attachment) => new Attachment(attachment));
|
||||
expect(instances?.map((attachment) => attachment.toJSON())).toEqual(data.attachments);
|
||||
expect(instances?.[0]?.description).toBe(data.attachments?.[0]?.description);
|
||||
expect(instances?.[0]?.filename).toBe(data.attachments?.[0]?.filename);
|
||||
expect(instances?.[0]?.id).toBe(data.attachments?.[0]?.id);
|
||||
expect(instances?.[0]?.size).toBe(data.attachments?.[0]?.size);
|
||||
expect(instances?.[0]?.url).toBe(data.attachments?.[0]?.url);
|
||||
expect(instances?.[0]?.proxyURL).toBe(data.attachments?.[0]?.proxy_url);
|
||||
});
|
||||
|
||||
test('Component sub-structures', () => {
|
||||
const containerInstance = new ContainerComponent(data.components?.[0] as APIContainerComponent);
|
||||
expect(containerInstance.toJSON()).toEqual(container);
|
||||
expect(containerInstance.type).toBe(container.type);
|
||||
expect(containerInstance.id).toBe(container.id);
|
||||
expect(containerInstance.spoiler).toBe(container.spoiler);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import { expectNotType, expectType } from 'tsd';
|
||||
import { expectTypeOf } from 'vitest';
|
||||
import type { MixinTypes } from '../../src/MixinTypes.d.ts';
|
||||
import type { kMixinConstruct } from '../../src/utils/symbols.js';
|
||||
@@ -16,22 +15,26 @@ declare const extendsBothOmitBoth: Omit<
|
||||
keyof Base | typeof kMixinConstruct
|
||||
>;
|
||||
|
||||
expectType<MixinTypes<Base, [MixinProperty1]>>(extendsNoOmit);
|
||||
expectType<MixinTypes<Base<'property1'>, [MixinProperty1<'property1'>]>>(extendsOmitProperty1);
|
||||
expectNotType<MixinTypes<Base, [MixinProperty1]>>(extendsOmitProperty1);
|
||||
expectNotType<MixinTypes<Base<'property1'>, [MixinProperty1<'property1'>]>>(extendsNoOmit);
|
||||
expectTypeOf(extendsNoOmit).toEqualTypeOf<MixinTypes<Base, [MixinProperty1]>>();
|
||||
expectTypeOf(extendsOmitProperty1).toEqualTypeOf<MixinTypes<Base<'property1'>, [MixinProperty1<'property1'>]>>();
|
||||
expectTypeOf(extendsOmitProperty1).not.toEqualTypeOf<MixinTypes<Base, [MixinProperty1]>>();
|
||||
expectTypeOf(extendsNoOmit).not.toEqualTypeOf<MixinTypes<Base<'property1'>, [MixinProperty1<'property1'>]>>();
|
||||
|
||||
expectType<MixinTypes<Base, [MixinProperty1, MixinProperty2]>>(extendsBothNoOmit);
|
||||
expectTypeOf(extendsBothNoOmit).toEqualTypeOf<MixinTypes<Base, [MixinProperty1, MixinProperty2]>>();
|
||||
// Since MixinProperty2 doesn't utilize the type of property1 in kData, this works and is ok
|
||||
expectType<MixinTypes<Base<'property1'>, [MixinProperty1<'property1'>, MixinProperty2]>>(extendsBothOmitProperty1);
|
||||
expectNotType<MixinTypes<Base, [MixinProperty1, MixinProperty2]>>(extendsBothOmitProperty1);
|
||||
expectTypeOf(extendsBothOmitProperty1).toEqualTypeOf<
|
||||
MixinTypes<Base<'property1'>, [MixinProperty1<'property1'>, MixinProperty2]>
|
||||
>();
|
||||
expectTypeOf(extendsBothOmitProperty1).not.toEqualTypeOf<MixinTypes<Base, [MixinProperty1, MixinProperty2]>>();
|
||||
// Since MixinProperty2 doesn't utilize the type of property1 in kData, this works and is ok
|
||||
expectNotType<MixinTypes<Base<'property1'>, [MixinProperty1<'property1'>, MixinProperty2]>>(extendsBothNoOmit);
|
||||
expectTypeOf(extendsBothNoOmit).not.toEqualTypeOf<
|
||||
MixinTypes<Base<'property1'>, [MixinProperty1<'property1'>, MixinProperty2]>
|
||||
>();
|
||||
|
||||
// Earlier mixins in the list must specify all properties because of the way merging works
|
||||
expectType<
|
||||
expectTypeOf(extendsBothOmitBoth).toEqualTypeOf<
|
||||
MixinTypes<Base<'property1' | 'property2'>, [MixinProperty1<'property1' | 'property2'>, MixinProperty2<'property2'>]>
|
||||
>(extendsBothOmitBoth);
|
||||
>();
|
||||
|
||||
expectTypeOf<MixinTypes<Base<'property1'>, [MixinProperty1]>>().toBeNever();
|
||||
// @ts-expect-error Shouldn't be able to assign non identical omits
|
||||
|
||||
@@ -1,79 +1,81 @@
|
||||
import type { ChannelType, GuildChannelType, GuildTextChannelType, ThreadChannelType } from 'discord-api-types/v10';
|
||||
import { expectNever, expectType } from 'tsd';
|
||||
import type { Channel } from '../../src/index.js';
|
||||
import { expectTypeOf } from 'vitest';
|
||||
import type { Channel } from '../../src/channels/Channel.js';
|
||||
|
||||
declare const channel: Channel;
|
||||
|
||||
if (channel.isGuildBased()) {
|
||||
expectType<string>(channel.guildId);
|
||||
expectType<GuildChannelType>(channel.type);
|
||||
expectTypeOf(channel.guildId).toBeString();
|
||||
expectTypeOf(channel.type).toEqualTypeOf<GuildChannelType>();
|
||||
|
||||
if (channel.isDMBased()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
|
||||
if (channel.isPermissionCapable()) {
|
||||
expectType<Exclude<GuildChannelType, ChannelType.GuildDirectory | ThreadChannelType>>(channel.type);
|
||||
expectTypeOf(channel.type).toEqualTypeOf<
|
||||
Exclude<GuildChannelType, ChannelType.GuildDirectory | ThreadChannelType>
|
||||
>();
|
||||
}
|
||||
|
||||
if (channel.isTextBased()) {
|
||||
expectType<GuildTextChannelType>(channel.type);
|
||||
expectTypeOf(channel.type).toEqualTypeOf<GuildTextChannelType>();
|
||||
}
|
||||
|
||||
if (channel.isWebhookCapable()) {
|
||||
expectType<ChannelType.GuildForum | ChannelType.GuildMedia | Exclude<GuildTextChannelType, ThreadChannelType>>(
|
||||
channel.type,
|
||||
);
|
||||
expectTypeOf(channel.type).toEqualTypeOf<
|
||||
ChannelType.GuildForum | ChannelType.GuildMedia | Exclude<GuildTextChannelType, ThreadChannelType>
|
||||
>();
|
||||
}
|
||||
|
||||
if (channel.isThread()) {
|
||||
expectType<ThreadChannelType>(channel.type);
|
||||
expectTypeOf(channel.type).toEqualTypeOf<ThreadChannelType>();
|
||||
}
|
||||
|
||||
if (channel.isThreadOnly()) {
|
||||
expectType<ChannelType.GuildForum | ChannelType.GuildMedia>(channel.type);
|
||||
expectTypeOf(channel.type).toEqualTypeOf<ChannelType.GuildForum | ChannelType.GuildMedia>();
|
||||
}
|
||||
|
||||
if (channel.isVoiceBased()) {
|
||||
expectType<ChannelType.GuildStageVoice | ChannelType.GuildVoice>(channel.type);
|
||||
expectTypeOf(channel.type).toEqualTypeOf<ChannelType.GuildStageVoice | ChannelType.GuildVoice>();
|
||||
if (!channel.isTextBased()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
|
||||
if (!channel.isWebhookCapable()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channel.isDMBased()) {
|
||||
expectType<ChannelType.DM | ChannelType.GroupDM>(channel.type);
|
||||
expectTypeOf(channel.type).toEqualTypeOf<ChannelType.DM | ChannelType.GroupDM>();
|
||||
|
||||
if (channel.isGuildBased()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
|
||||
if (channel.isPermissionCapable()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
|
||||
if (channel.isWebhookCapable()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
|
||||
if (channel.isVoiceBased()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
|
||||
if (channel.isThread()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
|
||||
if (channel.isThreadOnly()) {
|
||||
expectNever(channel);
|
||||
expectTypeOf(channel).toBeNever();
|
||||
}
|
||||
|
||||
if (channel.isTextBased()) {
|
||||
expectType<ChannelType.DM | ChannelType.GroupDM>(channel.type);
|
||||
expectTypeOf(channel.type).toEqualTypeOf<ChannelType.DM | ChannelType.GroupDM>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
"eslint-formatter-compact": "^9.0.1",
|
||||
"eslint-formatter-pretty": "^7.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"tsd": "^0.33.0",
|
||||
"tsup": "^8.5.0",
|
||||
"turbo": "^2.5.8",
|
||||
"typescript": "~5.9.3",
|
||||
@@ -91,8 +90,5 @@
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "__tests__/types"
|
||||
}
|
||||
}
|
||||
|
||||
16
packages/structures/src/bitfields/AttachmentFlagsBitField.ts
Normal file
16
packages/structures/src/bitfields/AttachmentFlagsBitField.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { AttachmentFlags } from 'discord-api-types/v10';
|
||||
import { BitField } from './BitField.js';
|
||||
|
||||
/**
|
||||
* Data structure that makes it easy to interact with a {@link Attachment#flags} bitfield.
|
||||
*/
|
||||
export class AttachmentFlagsBitField extends BitField<keyof AttachmentFlags> {
|
||||
/**
|
||||
* Numeric attachment flags.
|
||||
*/
|
||||
public static override readonly Flags = AttachmentFlags;
|
||||
|
||||
public override toJSON() {
|
||||
return super.toJSON(true);
|
||||
}
|
||||
}
|
||||
16
packages/structures/src/bitfields/MessageFlagsBitField.ts
Normal file
16
packages/structures/src/bitfields/MessageFlagsBitField.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { MessageFlags } from 'discord-api-types/v10';
|
||||
import { BitField } from './BitField.js';
|
||||
|
||||
/**
|
||||
* Data structure that makes it easy to interact with a {@link Message#flags} bitfield.
|
||||
*/
|
||||
export class MessageFlagsBitField extends BitField<keyof MessageFlags> {
|
||||
/**
|
||||
* Numeric message flags.
|
||||
*/
|
||||
public static override readonly Flags = MessageFlags;
|
||||
|
||||
public override toJSON() {
|
||||
return super.toJSON(true);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
export * from './BitField.js';
|
||||
|
||||
export * from './AttachmentFlagsBitField.js';
|
||||
export * from './ChannelFlagsBitField.js';
|
||||
export * from './MessageFlagsBitField.js';
|
||||
export * from './PermissionsBitField.js';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { DiscordSnowflake } from '@sapphire/snowflake';
|
||||
import type { APIChannel, APIPartialChannel, ChannelType, ChannelFlags } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { ChannelFlagsBitField } from '../bitfields/ChannelFlagsBitField.js';
|
||||
import { kData, kPatch } from '../utils/symbols.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import { isIdSet } from '../utils/type-guards.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import type { ChannelPermissionMixin } from './mixins/ChannelPermissionMixin.js';
|
||||
@@ -50,15 +50,6 @@ export class Channel<
|
||||
super(data as ChannelDataType<Type>);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.[kPatch]}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public override [kPatch](data: Partial<ChannelDataType<Type>>) {
|
||||
return super[kPatch](data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the channel
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
export * from './bitfields/index.js';
|
||||
export * from './channels/index.js';
|
||||
export * from './interactions/index.js';
|
||||
export * from './invites/index.js';
|
||||
export * from './messages/index.js';
|
||||
export * from './polls/index.js';
|
||||
export * from './stickers/index.js';
|
||||
export * from './users/index.js';
|
||||
export * from './Structure.js';
|
||||
export * from './Mixin.js';
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { APIInteractionDataResolved } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents data for users, members, channels, and roles in the message's auto-populated select menus.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has substructures `User`, `Channel`, `Role`, `Message`, `GuildMember`, `Attachment`, which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export abstract class ResolvedInteractionData<
|
||||
Omitted extends keyof APIInteractionDataResolved | '' = '',
|
||||
> extends Structure<APIInteractionDataResolved, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIInteractionDataResolved, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
1
packages/structures/src/interactions/index.ts
Normal file
1
packages/structures/src/interactions/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './ResolvedInteractionData.js';
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type APIInvite, type APIExtendedInvite, RouteBases } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData, kExpiresTimestamp, kCreatedTimestamp, kPatch } from '../utils/symbols.js';
|
||||
import { dateToDiscordISOTimestamp } from '../utils/optimization.js';
|
||||
import { kData, kExpiresTimestamp, kCreatedTimestamp } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
export interface APIActualInvite extends APIInvite, Partial<Omit<APIExtendedInvite, keyof APIInvite>> {}
|
||||
@@ -47,16 +48,6 @@ export class Invite<Omitted extends keyof APIActualInvite | '' = 'created_at' |
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.[kPatch]}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public override [kPatch](data: Partial<APIActualInvite>) {
|
||||
super[kPatch](data);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*
|
||||
@@ -201,11 +192,11 @@ export class Invite<Omitted extends keyof APIActualInvite | '' = 'created_at' |
|
||||
public override toJSON() {
|
||||
const clone = super.toJSON();
|
||||
if (this[kExpiresTimestamp]) {
|
||||
clone.expires_at = new Date(this[kExpiresTimestamp]).toISOString();
|
||||
clone.expires_at = dateToDiscordISOTimestamp(new Date(this[kExpiresTimestamp]));
|
||||
}
|
||||
|
||||
if (this[kCreatedTimestamp]) {
|
||||
clone.created_at = new Date(this[kCreatedTimestamp]).toISOString();
|
||||
clone.created_at = dateToDiscordISOTimestamp(new Date(this[kCreatedTimestamp]));
|
||||
}
|
||||
|
||||
return clone;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { APIApplicationCommandInteractionMetadata, InteractionType } from 'discord-api-types/v10';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { InteractionMetadata } from './InteractionMetadata.js';
|
||||
|
||||
/**
|
||||
* Represents metadata about the application command interaction causing a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `User` which needs to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class ApplicationCommandInteractionMetadata<
|
||||
Omitted extends keyof APIApplicationCommandInteractionMetadata | '' = '',
|
||||
> extends InteractionMetadata<InteractionType.ApplicationCommand, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ApplicationCommandInteractionMetadata.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIApplicationCommandInteractionMetadata> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIApplicationCommandInteractionMetadata, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the message the command was run on
|
||||
*/
|
||||
public get targetMessageId() {
|
||||
return this[kData].target_message_id;
|
||||
}
|
||||
}
|
||||
118
packages/structures/src/messages/Attachment.ts
Normal file
118
packages/structures/src/messages/Attachment.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type { APIAttachment, AttachmentFlags } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { AttachmentFlagsBitField } from '../bitfields/AttachmentFlagsBitField.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
export class Attachment<Omitted extends keyof APIAttachment | '' = ''> extends Structure<APIAttachment, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each Attachment.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIAttachment> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIAttachment, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the attachment
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the attached file
|
||||
*/
|
||||
public get filename() {
|
||||
return this[kData].filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* The title of the file
|
||||
*/
|
||||
public get title() {
|
||||
return this[kData].title;
|
||||
}
|
||||
|
||||
/**
|
||||
* The description for the file
|
||||
*/
|
||||
public get description() {
|
||||
return this[kData].description;
|
||||
}
|
||||
|
||||
/**
|
||||
* The attachment's media type
|
||||
*/
|
||||
public get contentType() {
|
||||
return this[kData].content_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the file in bytes
|
||||
*/
|
||||
public get size() {
|
||||
return this[kData].size;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source URL of the file
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
|
||||
/**
|
||||
* A proxied URL of the file
|
||||
*/
|
||||
public get proxyURL() {
|
||||
return this[kData].proxy_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the file (if image)
|
||||
*/
|
||||
public get height() {
|
||||
return this[kData].height;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the file (if image)
|
||||
*/
|
||||
public get width() {
|
||||
return this[kData].width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this attachment is ephemeral
|
||||
*/
|
||||
public get ephemeral() {
|
||||
return this[kData].ephemeral;
|
||||
}
|
||||
|
||||
/**
|
||||
* The duration of the audio file
|
||||
*/
|
||||
public get durationSecs() {
|
||||
return this[kData].duration_secs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 encoded bytearray representing a sampled waveform
|
||||
*/
|
||||
public get waveform() {
|
||||
return this[kData].waveform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachment flags combined as a bitfield
|
||||
*/
|
||||
public get flags() {
|
||||
const flags = this[kData].flags;
|
||||
return flags ? new AttachmentFlagsBitField(this[kData].flags as AttachmentFlags) : null;
|
||||
}
|
||||
}
|
||||
54
packages/structures/src/messages/ChannelMention.ts
Normal file
54
packages/structures/src/messages/ChannelMention.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { APIChannelMention } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents the mention of a channel on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class ChannelMention<Omitted extends keyof APIChannelMention | '' = ''> extends Structure<
|
||||
APIChannelMention,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ChannelMention.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIChannelMention> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the channel mention
|
||||
*/
|
||||
public constructor(data: Partialize<APIChannelMention, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the mentioned channel
|
||||
*/
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the mentioned channel
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the mentioned channel
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the guild the mentioned channel is in
|
||||
*/
|
||||
public get guildId() {
|
||||
return this[kData].guild_id;
|
||||
}
|
||||
}
|
||||
48
packages/structures/src/messages/InteractionMetadata.ts
Normal file
48
packages/structures/src/messages/InteractionMetadata.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { APIMessageInteractionMetadata, InteractionType } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
export type InteractionMetadataType<Type extends InteractionType> = Extract<
|
||||
APIMessageInteractionMetadata,
|
||||
{ type: Type }
|
||||
>;
|
||||
|
||||
/**
|
||||
* Represents metadata about the interaction causing a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `User` which needs to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export abstract class InteractionMetadata<
|
||||
Type extends InteractionType,
|
||||
Omitted extends keyof InteractionMetadataType<Type> | '' = '',
|
||||
> extends Structure<InteractionMetadataType<Type>, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<InteractionMetadataType<Type>, Omitted>) {
|
||||
super(data as InteractionMetadataType<Type>);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the interaction
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the original response message, present only on follow-up messages
|
||||
*/
|
||||
public get originalResponseMessageId() {
|
||||
return this[kData].original_response_message_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of interaction
|
||||
*/
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
}
|
||||
178
packages/structures/src/messages/Message.ts
Normal file
178
packages/structures/src/messages/Message.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import { DiscordSnowflake } from '@sapphire/snowflake';
|
||||
import type { APIMessage, MessageFlags } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { MessageFlagsBitField } from '../bitfields/MessageFlagsBitField.js';
|
||||
import { dateToDiscordISOTimestamp } from '../utils/optimization.js';
|
||||
import { kData, kEditedTimestamp } from '../utils/symbols.js';
|
||||
import { isIdSet } from '../utils/type-guards.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
// TODO: missing substructures: application
|
||||
|
||||
/**
|
||||
* Represents a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has substructures `Message`, `Channel`, `MessageActivity`, `MessageCall`, `MessageReference`, `Attachment`, `Application`, `ChannelMention`, `Reaction`, `Poll`, `ResolvedInteractionData`, `RoleSubscriptionData`, `Sticker`, all the different `Component`s, ... which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class Message<Omitted extends keyof APIMessage | '' = 'edited_timestamp' | 'timestamp'> extends Structure<
|
||||
APIMessage,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each Message
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIMessage> = {
|
||||
set timestamp(_: string) {},
|
||||
set edited_timestamp(_: string) {},
|
||||
};
|
||||
|
||||
protected [kEditedTimestamp]: number | null = null;
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the message
|
||||
*/
|
||||
public constructor(data: Partialize<APIMessage, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected override optimizeData(data: Partial<APIMessage>) {
|
||||
if (data.edited_timestamp) {
|
||||
this[kEditedTimestamp] = Date.parse(data.edited_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The message's id
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the interaction's application, if this message is a reply to an interaction
|
||||
*/
|
||||
public get applicationId() {
|
||||
return this[kData].application_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The channel's id this message was sent in
|
||||
*/
|
||||
public get channelId() {
|
||||
return this[kData].channel_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp this message was created at
|
||||
*/
|
||||
public get createdTimestamp() {
|
||||
return isIdSet(this.id) ? DiscordSnowflake.timestampFrom(this.id) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the message was created at
|
||||
*/
|
||||
public get createdAt() {
|
||||
const createdTimestamp = this.createdTimestamp;
|
||||
return createdTimestamp ? new Date(createdTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The content of the message
|
||||
*/
|
||||
public get content() {
|
||||
return this[kData].content;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp this message was last edited at, or `null` if it never was edited
|
||||
*/
|
||||
public get editedTimestamp() {
|
||||
return this[kEditedTimestamp];
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the message was last edited at, or `null` if it never was edited
|
||||
*/
|
||||
public get editedAt() {
|
||||
const editedTimestamp = this.editedTimestamp;
|
||||
return editedTimestamp ? new Date(editedTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The flags of this message as a bit field
|
||||
*/
|
||||
public get flags() {
|
||||
const flags = this[kData].flags;
|
||||
return flags ? new MessageFlagsBitField(this[kData].flags as MessageFlags) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The nonce used when sending this message.
|
||||
*
|
||||
* @remarks This is only present in MESSAGE_CREATE event, if a nonce was provided when sending
|
||||
*/
|
||||
public get nonce() {
|
||||
return this[kData].nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this message is pinned in its channel
|
||||
*/
|
||||
public get pinned() {
|
||||
return this[kData].pinned;
|
||||
}
|
||||
|
||||
/**
|
||||
* A generally increasing integer (there may be gaps or duplicates) that represents the approximate position of the message in a thread
|
||||
* It can be used to estimate the relative position of the message in a thread in company with `totalMessageSent` on parent thread
|
||||
*/
|
||||
public get position() {
|
||||
return this[kData].position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this message was a TTS message
|
||||
*/
|
||||
public get tts() {
|
||||
return this[kData].tts;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of message
|
||||
*/
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the message is generated by a webhook, this is the webhook's id
|
||||
*/
|
||||
public get webhookId() {
|
||||
return this[kData].webhook_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.toJSON}
|
||||
*/
|
||||
public override toJSON() {
|
||||
const clone = super.toJSON();
|
||||
if (this[kEditedTimestamp]) {
|
||||
clone.edited_timestamp = dateToDiscordISOTimestamp(new Date(this[kEditedTimestamp]));
|
||||
}
|
||||
|
||||
const createdAt = this.createdAt;
|
||||
if (createdAt) {
|
||||
clone.timestamp = dateToDiscordISOTimestamp(createdAt);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
35
packages/structures/src/messages/MessageActivity.ts
Normal file
35
packages/structures/src/messages/MessageActivity.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { APIMessageActivity } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
export class MessageActivity<Omitted extends keyof APIMessageActivity | '' = ''> extends Structure<
|
||||
APIMessageActivity,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each MessageActivity.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIMessageActivity> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIMessageActivity, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The party id from a Rich Presence event
|
||||
*/
|
||||
public get partyId() {
|
||||
return this[kData].party_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of message activity
|
||||
*/
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
}
|
||||
65
packages/structures/src/messages/MessageCall.ts
Normal file
65
packages/structures/src/messages/MessageCall.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import type { APIMessageCall } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { dateToDiscordISOTimestamp } from '../utils/optimization.js';
|
||||
import { kEndedTimestamp } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
export class MessageCall<Omitted extends keyof APIMessageCall | '' = 'ended_timestamp'> extends Structure<
|
||||
APIMessageCall,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each MessageCall
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIMessageCall> = {
|
||||
set ended_timestamp(_: string) {},
|
||||
};
|
||||
|
||||
protected [kEndedTimestamp]: number | null = null;
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the message call
|
||||
*/
|
||||
public constructor(data: Partialize<APIMessageCall, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected override optimizeData(data: Partial<APIMessageCall>) {
|
||||
if (data.ended_timestamp) {
|
||||
this[kEndedTimestamp] = Date.parse(data.ended_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp this call ended at, or `null` if it didn't end yet
|
||||
*/
|
||||
public get endedTimestamp() {
|
||||
return this[kEndedTimestamp];
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the call ended at, or `null` if it didn't end yet
|
||||
*/
|
||||
public get endedAt() {
|
||||
const endedTimestamp = this.endedTimestamp;
|
||||
return endedTimestamp ? new Date(endedTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.toJSON}
|
||||
*/
|
||||
public override toJSON() {
|
||||
const clone = super.toJSON();
|
||||
if (this[kEndedTimestamp]) {
|
||||
clone.ended_timestamp = dateToDiscordISOTimestamp(new Date(this[kEndedTimestamp]));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { APIMessageComponentInteractionMetadata, InteractionType } from 'discord-api-types/v10';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { InteractionMetadata } from './InteractionMetadata.js';
|
||||
|
||||
/**
|
||||
* Represents metadata about the message component interaction causing a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class MessageComponentInteractionMetadata<
|
||||
Omitted extends keyof APIMessageComponentInteractionMetadata | '' = '',
|
||||
> extends InteractionMetadata<InteractionType.MessageComponent, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each MessageComponentInteractionMetadata.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIMessageComponentInteractionMetadata> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIMessageComponentInteractionMetadata, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the message that contained the interactive component
|
||||
*/
|
||||
public get interactedMessageId() {
|
||||
return this[kData].interacted_message_id;
|
||||
}
|
||||
}
|
||||
54
packages/structures/src/messages/MessageReference.ts
Normal file
54
packages/structures/src/messages/MessageReference.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { MessageReferenceType, type APIMessageReference } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents the reference to another message on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class MessageReference<Omitted extends keyof APIMessageReference | '' = ''> extends Structure<
|
||||
APIMessageReference,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each MessageReference.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIMessageReference> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the message reference
|
||||
*/
|
||||
public constructor(data: Partialize<APIMessageReference, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this reference
|
||||
*/
|
||||
public get type() {
|
||||
return 'type' in this[kData] ? (this[kData].type as MessageReferenceType) : MessageReferenceType.Default;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the referenced message
|
||||
*/
|
||||
public get messageId() {
|
||||
return this[kData].message_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the channel the referenced message was sent in
|
||||
*/
|
||||
public get channelId() {
|
||||
return this[kData].channel_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the guild the referenced message was sent in
|
||||
*/
|
||||
public get guildId() {
|
||||
return this[kData].guild_id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { APIModalSubmitInteractionMetadata, InteractionType } from 'discord-api-types/v10';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
import { InteractionMetadata } from './InteractionMetadata.js';
|
||||
|
||||
/**
|
||||
* Represents metadata about the modal submit interaction causing a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `InteractionMetadata` which needs to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class ModalSubmitInteractionMetadata<
|
||||
Omitted extends keyof APIModalSubmitInteractionMetadata | '' = '',
|
||||
> extends InteractionMetadata<InteractionType.ModalSubmit, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ModalSubmitInteractionMetadata.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIModalSubmitInteractionMetadata> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIModalSubmitInteractionMetadata, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
80
packages/structures/src/messages/Reaction.ts
Normal file
80
packages/structures/src/messages/Reaction.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { APIReaction } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kBurstColors, kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents a reaction on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has substructures `Emoji`, `ReactionCountDetails` which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class Reaction<Omitted extends keyof APIReaction | '' = ''> extends Structure<APIReaction, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each Reaction.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIReaction> = {
|
||||
set burst_colors(_: string[]) {},
|
||||
};
|
||||
|
||||
protected [kBurstColors]: number[] | null = null;
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the reaction
|
||||
*/
|
||||
public constructor(data: Partialize<APIReaction, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected override optimizeData(data: Partial<APIReaction>) {
|
||||
if (data.burst_colors) {
|
||||
this[kBurstColors] = data.burst_colors.map((color) => Number.parseInt(color, 16));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount how often this emoji has been used to react (including super reacts)
|
||||
*/
|
||||
public get count() {
|
||||
return this[kData].count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current user has reacted using this emoji
|
||||
*/
|
||||
public get me() {
|
||||
return this[kData].me;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current user has super-reacted using this emoji
|
||||
*/
|
||||
public get meBurst() {
|
||||
return this[kData].me_burst;
|
||||
}
|
||||
|
||||
/**
|
||||
* The colors used for super reaction
|
||||
*/
|
||||
public get burstColors() {
|
||||
return this[kBurstColors];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.toJSON}
|
||||
*/
|
||||
public override toJSON() {
|
||||
const clone = super.toJSON();
|
||||
if (this[kBurstColors]) {
|
||||
clone.burst_colors = this[kBurstColors].map((color) => `#${color.toString(16).padStart(6, '0')}`);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
40
packages/structures/src/messages/ReactionCountDetails.ts
Normal file
40
packages/structures/src/messages/ReactionCountDetails.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { APIReactionCountDetails } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents the usage count of a reaction on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class ReactionCountDetails<Omitted extends keyof APIReactionCountDetails | '' = ''> extends Structure<
|
||||
APIReactionCountDetails,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ReactionCountDetails.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIReactionCountDetails> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the reaction count details
|
||||
*/
|
||||
public constructor(data: Partialize<APIReactionCountDetails, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount how often this emoji has been used to react (excluding super reacts)
|
||||
*/
|
||||
public get normal() {
|
||||
return this[kData].normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount how often this emoji has been used to super-react
|
||||
*/
|
||||
public get burst() {
|
||||
return this[kData].burst;
|
||||
}
|
||||
}
|
||||
48
packages/structures/src/messages/RoleSubscriptionData.ts
Normal file
48
packages/structures/src/messages/RoleSubscriptionData.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { APIMessageRoleSubscriptionData } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents metadata about the role subscription causing a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export abstract class RoleSubscriptionData<
|
||||
Omitted extends keyof APIMessageRoleSubscriptionData | '' = '',
|
||||
> extends Structure<APIMessageRoleSubscriptionData, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIMessageRoleSubscriptionData, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the SKU and listing the user is subscribed to
|
||||
*/
|
||||
public get roleSubscriptionListingId() {
|
||||
return this[kData].role_subscription_listing_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the tier the user is subscribed to
|
||||
*/
|
||||
public get tierName() {
|
||||
return this[kData].tier_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of months the user has been subscribed for
|
||||
*/
|
||||
public get totalMonthsSubscribed() {
|
||||
return this[kData].total_months_subscribed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this notification is for a renewal
|
||||
*/
|
||||
public get isRenewal() {
|
||||
return this[kData].is_renewal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { APIActionRowComponent, APIComponentInActionRow, ComponentType } from 'discord-api-types/v10';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import type { ComponentDataType } from './Component.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents an action row component on a message or modal.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `Component`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class ActionRowComponent<
|
||||
Type extends APIComponentInActionRow,
|
||||
Omitted extends keyof APIActionRowComponent<Type> | '' = '',
|
||||
> extends Component<ComponentDataType<ComponentType.ActionRow>, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ActionRowComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<ComponentDataType<ComponentType.ActionRow>> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the action row
|
||||
*/
|
||||
public constructor(data: Partialize<ComponentDataType<ComponentType.ActionRow>, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { APIButtonComponent, APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* The data stored by a {@link ButtonComponent} structure based on its {@link (ButtonComponent:class)."style"} property.
|
||||
*/
|
||||
export type ButtonDataType<Style extends ButtonStyle> = Style extends
|
||||
| ButtonStyle.Danger
|
||||
| ButtonStyle.Primary
|
||||
| ButtonStyle.Secondary
|
||||
| ButtonStyle.Success
|
||||
? APIButtonComponentWithCustomId
|
||||
: Extract<APIButtonComponent, { style: Style }>;
|
||||
|
||||
export abstract class ButtonComponent<
|
||||
Style extends ButtonStyle,
|
||||
Omitted extends keyof ButtonDataType<Style> | '' = '',
|
||||
> extends Component<ButtonDataType<Style>, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the button
|
||||
*/
|
||||
public constructor(data: Partialize<ButtonDataType<Style>, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The style of the button
|
||||
*/
|
||||
public get style() {
|
||||
return this[kData].style;
|
||||
}
|
||||
|
||||
/**
|
||||
* The status of the button
|
||||
*/
|
||||
public get disabled() {
|
||||
return typeof this[kData].disabled === 'boolean' ? this[kData].disabled : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { APIChannelSelectComponent, ChannelType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { SelectMenuComponent } from './SelectMenuComponent.js';
|
||||
|
||||
/**
|
||||
* Represents a channel select menu component.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `SelectMenuDefaultValue`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class ChannelSelectMenuComponent<
|
||||
Omitted extends keyof APIChannelSelectComponent | '' = '',
|
||||
> extends SelectMenuComponent<APIChannelSelectComponent, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ChannelSelectMenuComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIChannelSelectComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the channel select menu
|
||||
*/
|
||||
public constructor(data: Partialize<APIChannelSelectComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of channel types to include in the channel select component
|
||||
*/
|
||||
public get channelTypes() {
|
||||
return Array.isArray(this[kData].channel_types) ? (this[kData].channel_types as readonly ChannelType[]) : null;
|
||||
}
|
||||
}
|
||||
36
packages/structures/src/messages/components/Component.ts
Normal file
36
packages/structures/src/messages/components/Component.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { APIBaseComponent, APIMessageComponent, APIModalComponent, ComponentType } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* The data stored by a {@link Component} structure based on its {@link (Component:class)."type"} property.
|
||||
*/
|
||||
export type ComponentDataType<Type extends ComponentType | 'unknown'> = Type extends ComponentType
|
||||
? Extract<APIMessageComponent | APIModalComponent, { type: Type }>
|
||||
: APIBaseComponent<ComponentType>;
|
||||
export abstract class Component<
|
||||
Type extends APIMessageComponent | APIModalComponent,
|
||||
Omitted extends keyof Type | '' = '',
|
||||
> extends Structure<Type, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the component
|
||||
*/
|
||||
public constructor(data: Partialize<Type, Omitted>) {
|
||||
super(data as Type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 32 bit integer used as an optional identifier for component
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the component
|
||||
*/
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { APIMessageComponentEmoji } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
export class ComponentEmoji<Omitted extends keyof APIMessageComponentEmoji | '' = ''> extends Structure<
|
||||
APIMessageComponentEmoji,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ComponentEmoji.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIMessageComponentEmoji> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the component emoji
|
||||
*/
|
||||
public constructor(data: Partialize<APIMessageComponentEmoji, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the emoji
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the emoji
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this emoji is animated
|
||||
*/
|
||||
public get animated() {
|
||||
return this[kData].animated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { APIContainerComponent } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a container component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `Component`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class ContainerComponent<Omitted extends keyof APIContainerComponent | '' = ''> extends Component<
|
||||
APIContainerComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ContainerComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIContainerComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the container
|
||||
*/
|
||||
public constructor(data: Partialize<APIContainerComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Color for the accent on the container as RGB
|
||||
*/
|
||||
public get accentColor() {
|
||||
return this[kData].accent_color;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hexadecimal version of the accent color, with a leading hash
|
||||
*/
|
||||
public get hexAccentColor() {
|
||||
const accentColor = this.accentColor;
|
||||
if (typeof accentColor !== 'number') return accentColor;
|
||||
return `#${accentColor.toString(16).padStart(6, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the container should be a spoiler (or blurred out)
|
||||
*/
|
||||
public get spoiler() {
|
||||
return this[kData].spoiler;
|
||||
}
|
||||
}
|
||||
48
packages/structures/src/messages/components/FileComponent.ts
Normal file
48
packages/structures/src/messages/components/FileComponent.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { APIFileComponent } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a file component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `UnfurledMediaItem` which needs to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class FileComponent<Omitted extends keyof APIFileComponent | '' = ''> extends Component<
|
||||
APIFileComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each FileComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIFileComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the file component
|
||||
*/
|
||||
public constructor(data: Partialize<APIFileComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the media should be a spoiler (or blurred out)
|
||||
*/
|
||||
public get spoiler() {
|
||||
return this[kData].spoiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the file
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the file in bytes
|
||||
*/
|
||||
public get size() {
|
||||
return this[kData].size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { APIFileUploadComponent } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a file upload component on a modal.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class FileUploadComponent<Omitted extends keyof APIFileUploadComponent | '' = ''> extends Component<
|
||||
APIFileUploadComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each FileUploadComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIFileUploadComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the file upload component
|
||||
*/
|
||||
public constructor(data: Partialize<APIFileUploadComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The custom id to be sent in the interaction when the modal gets submitted
|
||||
*/
|
||||
public get customId() {
|
||||
return this[kData].custom_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum number of items that can be uploaded
|
||||
*/
|
||||
public get maxValues() {
|
||||
return this[kData].max_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum number of items that must be uploaded
|
||||
*/
|
||||
public get minValues() {
|
||||
return this[kData].min_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the file upload requires files to be uploaded before submitting the modal
|
||||
*/
|
||||
public get required() {
|
||||
return this[kData].required;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { APIButtonComponentWithCustomId, ButtonStyle } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import type { ButtonDataType } from './ButtonComponent.js';
|
||||
import { LabeledButtonComponent } from './LabeledButtonComponent.js';
|
||||
|
||||
/**
|
||||
* Represents a button causing a message component interaction on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class InteractiveButtonComponent<
|
||||
Style extends ButtonStyle.Danger | ButtonStyle.Primary | ButtonStyle.Secondary | ButtonStyle.Success,
|
||||
Omitted extends keyof APIButtonComponentWithCustomId | '' = '',
|
||||
> extends LabeledButtonComponent<Style, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each InteractiveButtonComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIButtonComponentWithCustomId> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the interactive button
|
||||
*/
|
||||
public constructor(data: Partialize<APIButtonComponentWithCustomId, Omitted>) {
|
||||
super(data as ButtonDataType<Style>);
|
||||
}
|
||||
|
||||
/**
|
||||
* The custom id to be sent in the interaction when clicked
|
||||
*/
|
||||
public get customId() {
|
||||
return this[kData].custom_id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { APILabelComponent, ComponentType } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import type { ComponentDataType } from './Component.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a label component on a modal.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `Component`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class LabelComponent<Omitted extends keyof APILabelComponent | '' = ''> extends Component<
|
||||
ComponentDataType<ComponentType.Label>,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each LabelComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<ComponentDataType<ComponentType.Label>> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the label
|
||||
*/
|
||||
public constructor(data: Partialize<ComponentDataType<ComponentType.Label>, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The label text
|
||||
*/
|
||||
public get label() {
|
||||
return this[kData].label;
|
||||
}
|
||||
|
||||
/**
|
||||
* An optional description text for the label
|
||||
*/
|
||||
public get description() {
|
||||
return this[kData].description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { ButtonStyle } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import type { ButtonDataType } from './ButtonComponent.js';
|
||||
import { ButtonComponent } from './ButtonComponent.js';
|
||||
|
||||
/**
|
||||
* Base class for all buttons that can have a label on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `ComponentEmoji` which needs to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export abstract class LabeledButtonComponent<
|
||||
Style extends Exclude<ButtonStyle, ButtonStyle.Premium>,
|
||||
Omitted extends keyof ButtonDataType<Style> | '' = '',
|
||||
> extends ButtonComponent<Style, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the button
|
||||
*/
|
||||
public constructor(data: Partialize<ButtonDataType<Style>, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The label to be displayed on the button
|
||||
*/
|
||||
public get label() {
|
||||
return this[kData].label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { APIButtonComponentWithURL, ButtonStyle } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { LabeledButtonComponent } from './LabeledButtonComponent.js';
|
||||
|
||||
/**
|
||||
* Represents a button linking to an URL on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class LinkButtonComponent<
|
||||
Omitted extends keyof APIButtonComponentWithURL | '' = '',
|
||||
> extends LabeledButtonComponent<ButtonStyle.Link, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each LinkButtonComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIButtonComponentWithURL> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the link button
|
||||
*/
|
||||
public constructor(data: Partialize<APIButtonComponentWithURL, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL to direct users to when clicked
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { APIMediaGalleryComponent } from 'discord-api-types/v10';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a media gallery component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `MediaGalleryItem`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class MediaGalleryComponent<Omitted extends keyof APIMediaGalleryComponent | '' = ''> extends Component<
|
||||
APIMediaGalleryComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each MediaGalleryComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIMediaGalleryComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the media gallery
|
||||
*/
|
||||
public constructor(data: Partialize<APIMediaGalleryComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { APIMediaGalleryItem } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents an item in a media gallery on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `UnfurledMediaItem` which needs to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class MediaGalleryItem<Omitted extends keyof APIMediaGalleryItem | '' = ''> extends Structure<
|
||||
APIMediaGalleryItem,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each MediaGalleryItem.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIMediaGalleryItem> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the media gallery item
|
||||
*/
|
||||
public constructor(data: Partialize<APIMediaGalleryItem, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alt text for the media
|
||||
*/
|
||||
public get description() {
|
||||
return this[kData].description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the media should be a spoiler (or blurred out)
|
||||
*/
|
||||
public get spoiler() {
|
||||
return this[kData].spoiler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { APIMentionableSelectComponent } from 'discord-api-types/v10';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { SelectMenuComponent } from './SelectMenuComponent.js';
|
||||
|
||||
/**
|
||||
* Represents a mentionable select menu component.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `SelectMenuDefaultValue`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class MentionableSelectMenuComponent<
|
||||
Omitted extends keyof APIMentionableSelectComponent | '' = '',
|
||||
> extends SelectMenuComponent<APIMentionableSelectComponent, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each MentionableSelectMenuComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIMentionableSelectComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the mentionable select menu
|
||||
*/
|
||||
public constructor(data: Partialize<APIMentionableSelectComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { APIButtonComponentWithSKUId, ButtonStyle } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { ButtonComponent } from './ButtonComponent.js';
|
||||
|
||||
/**
|
||||
* Represents a button used to buy an SKU from a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class PremiumButtonComponent<
|
||||
Omitted extends keyof APIButtonComponentWithSKUId | '' = '',
|
||||
> extends ButtonComponent<ButtonStyle.Premium, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each PremiumButtonComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIButtonComponentWithSKUId> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the premium button
|
||||
*/
|
||||
public constructor(data: Partialize<APIButtonComponentWithSKUId, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id for a purchasable SKU
|
||||
*/
|
||||
public get skuId() {
|
||||
return this[kData].sku_id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { APIRoleSelectComponent } from 'discord-api-types/v10';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { SelectMenuComponent } from './SelectMenuComponent.js';
|
||||
|
||||
/**
|
||||
* Represents a role select menu component.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `SelectMenuDefaultValue`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class RoleSelectMenuComponent<
|
||||
Omitted extends keyof APIRoleSelectComponent | '' = '',
|
||||
> extends SelectMenuComponent<APIRoleSelectComponent, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each RoleSelectMenuComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIRoleSelectComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the role select menu
|
||||
*/
|
||||
public constructor(data: Partialize<APIRoleSelectComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { APISectionComponent } from 'discord-api-types/v10';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a section component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `Component`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class SectionComponent<Omitted extends keyof APISectionComponent | '' = ''> extends Component<
|
||||
APISectionComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each SectionComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APISectionComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the section
|
||||
*/
|
||||
public constructor(data: Partialize<APISectionComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { APISelectMenuComponent } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
export abstract class SelectMenuComponent<
|
||||
Type extends APISelectMenuComponent,
|
||||
Omitted extends keyof Type | '' = '',
|
||||
> extends Component<Type, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the select menu
|
||||
*/
|
||||
public constructor(data: Partialize<Type, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The customId to be sent in the interaction when a selection is made
|
||||
*/
|
||||
public get customId() {
|
||||
return this[kData].custom_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the select menu is disabled
|
||||
*/
|
||||
public get disabled() {
|
||||
return this[kData].disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum number of items that can be chosen
|
||||
*/
|
||||
public get maxValues() {
|
||||
return this[kData].max_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum number of items that must be chosen
|
||||
*/
|
||||
public get minValues() {
|
||||
return this[kData].min_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom placeholder text if nothing is selected
|
||||
*/
|
||||
public get placeholder() {
|
||||
return this[kData].placeholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a selection is required
|
||||
*/
|
||||
public get required() {
|
||||
return this[kData].required;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { APISelectMenuDefaultValue, SelectMenuDefaultValueType } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
export class SelectMenuDefaultValue<
|
||||
Type extends SelectMenuDefaultValueType,
|
||||
Omitted extends keyof APISelectMenuDefaultValue<Type> | '' = '',
|
||||
> extends Structure<APISelectMenuDefaultValue<Type>, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each SelectMenuDefaultValue.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APISelectMenuDefaultValue<SelectMenuDefaultValueType>> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the select menu default value
|
||||
*/
|
||||
public constructor(data: Partialize<APISelectMenuDefaultValue<Type>, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { APISeparatorComponent } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a separator component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class SeparatorComponent<Omitted extends keyof APISeparatorComponent | '' = ''> extends Component<
|
||||
APISeparatorComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each SeparatorComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APISeparatorComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the separator
|
||||
*/
|
||||
public constructor(data: Partialize<APISeparatorComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a visual divider should be displayed in the component
|
||||
*/
|
||||
public get divider() {
|
||||
return this[kData].divider;
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the separator padding
|
||||
*/
|
||||
public get spacing() {
|
||||
return this[kData].spacing;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { APIStringSelectComponent } from 'discord-api-types/v10';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { SelectMenuComponent } from './SelectMenuComponent.js';
|
||||
|
||||
/**
|
||||
* Represents a string select menu component.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `StringSelectMenuOption`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class StringSelectMenuComponent<
|
||||
Omitted extends keyof APIStringSelectComponent | '' = '',
|
||||
> extends SelectMenuComponent<APIStringSelectComponent, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each StringSelectMenuComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIStringSelectComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the string select menu
|
||||
*/
|
||||
public constructor(data: Partialize<APIStringSelectComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { APISelectMenuOption } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents an option in a string select menu component.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `ComponentEmoji` which needs to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class StringSelectMenuOption<Omitted extends keyof APISelectMenuOption | '' = ''> extends Structure<
|
||||
APISelectMenuOption,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each StringSelectMenuOption.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APISelectMenuOption> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the string select menu option
|
||||
*/
|
||||
public constructor(data: Partialize<APISelectMenuOption, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this option should be already-selected by default
|
||||
*/
|
||||
public get default() {
|
||||
return this[kData].default;
|
||||
}
|
||||
|
||||
/**
|
||||
* An additional description of the option
|
||||
*/
|
||||
public get description() {
|
||||
return this[kData].description;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user-facing name of the option
|
||||
*/
|
||||
public get label() {
|
||||
return this[kData].label;
|
||||
}
|
||||
|
||||
/**
|
||||
* The dev-defined value of the option
|
||||
*/
|
||||
public get value() {
|
||||
return this[kData].value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { APITextDisplayComponent } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a text display component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class TextDisplayComponent<Omitted extends keyof APITextDisplayComponent | '' = ''> extends Component<
|
||||
APITextDisplayComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each TextDisplayComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APITextDisplayComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the text display
|
||||
*/
|
||||
public constructor(data: Partialize<APITextDisplayComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Text that will be displayed similar to a message
|
||||
*/
|
||||
public get content() {
|
||||
return this[kData].content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import type { APITextInputComponent } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a text input component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class TextInputComponent<Omitted extends keyof APITextInputComponent | '' = ''> extends Component<
|
||||
APITextInputComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each TextInputComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APITextInputComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the text input
|
||||
*/
|
||||
public constructor(data: Partialize<APITextInputComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The custom id for the text input
|
||||
*/
|
||||
public get customId() {
|
||||
return this[kData].custom_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text that appears on top of the text input field
|
||||
*/
|
||||
public get label() {
|
||||
return this[kData].label;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximal length of text input
|
||||
*/
|
||||
public get maxLength() {
|
||||
return this[kData].max_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimal length of text input
|
||||
*/
|
||||
public get minLength() {
|
||||
return this[kData].min_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The placeholder for the text input
|
||||
*/
|
||||
public get placeholder() {
|
||||
return this[kData].placeholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this text input is required
|
||||
*/
|
||||
public get required() {
|
||||
return this[kData].required;
|
||||
}
|
||||
|
||||
/**
|
||||
* One of text input styles
|
||||
*/
|
||||
public get style() {
|
||||
return this[kData].style;
|
||||
}
|
||||
|
||||
/**
|
||||
* The pre-filled text in the text input
|
||||
*/
|
||||
public get value() {
|
||||
return this[kData].value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { APIThumbnailComponent } from 'discord-api-types/v10';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { Component } from './Component.js';
|
||||
|
||||
/**
|
||||
* Represents a thumbnail component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `UnfurledMediaItem` which needs to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class ThumbnailComponent<Omitted extends keyof APIThumbnailComponent | '' = ''> extends Component<
|
||||
APIThumbnailComponent,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each ThumbnailComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIThumbnailComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the thumbnail
|
||||
*/
|
||||
public constructor(data: Partialize<APIThumbnailComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alt text for the media
|
||||
*/
|
||||
public get description() {
|
||||
return this[kData].description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the thumbnail should be a spoiler (or blurred out)
|
||||
*/
|
||||
public get spoiler() {
|
||||
return this[kData].spoiler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import type { APIUnfurledMediaItem } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
// TODO: add `flags` as a BitField class and appropriate getter, once it gets properly documented
|
||||
|
||||
/**
|
||||
* Represents a media item in a component on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class UnfurledMediaItem<Omitted extends keyof APIUnfurledMediaItem | '' = ''> extends Structure<
|
||||
APIUnfurledMediaItem,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each UnfurledMediaItem.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIUnfurledMediaItem> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the unfurled media item
|
||||
*/
|
||||
public constructor(data: Partialize<APIUnfurledMediaItem, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the uploaded attachment
|
||||
*/
|
||||
public get attachmentId() {
|
||||
return this[kData].attachment_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The media type of the content
|
||||
*/
|
||||
public get contentType() {
|
||||
return this[kData].content_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the media item (if image)
|
||||
*/
|
||||
public get height() {
|
||||
return this[kData].height;
|
||||
}
|
||||
|
||||
/**
|
||||
* The proxied URL of the media item
|
||||
*/
|
||||
public get proxyURL() {
|
||||
return this[kData].proxy_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports arbitrary URLs and attachment:// references
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the media item (if image)
|
||||
*/
|
||||
public get width() {
|
||||
return this[kData].width;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { APIUserSelectComponent } from 'discord-api-types/v10';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
import { SelectMenuComponent } from './SelectMenuComponent.js';
|
||||
|
||||
/**
|
||||
* Represents a user select menu component.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has `SelectMenuDefaultValue`s as substructures which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class UserSelectMenuComponent<
|
||||
Omitted extends keyof APIUserSelectComponent | '' = '',
|
||||
> extends SelectMenuComponent<APIUserSelectComponent, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each UserSelectMenuComponent.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIUserSelectComponent> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the user select menu
|
||||
*/
|
||||
public constructor(data: Partialize<APIUserSelectComponent, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
28
packages/structures/src/messages/components/index.ts
Normal file
28
packages/structures/src/messages/components/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export * from './ActionRowComponent.js';
|
||||
export * from './ButtonComponent.js';
|
||||
export * from './ChannelSelectMenuComponent.js';
|
||||
export * from './Component.js';
|
||||
export * from './ContainerComponent.js';
|
||||
export * from './FileComponent.js';
|
||||
export * from './FileUploadComponent.js';
|
||||
export * from './InteractiveButtonComponent.js';
|
||||
export * from './LinkButtonComponent.js';
|
||||
export * from './MediaGalleryComponent.js';
|
||||
export * from './MentionableSelectMenuComponent.js';
|
||||
export * from './LabeledButtonComponent.js';
|
||||
export * from './PremiumButtonComponent.js';
|
||||
export * from './RoleSelectMenuComponent.js';
|
||||
export * from './SectionComponent.js';
|
||||
export * from './SelectMenuComponent.js';
|
||||
export * from './SeparatorComponent.js';
|
||||
export * from './StringSelectMenuComponent.js';
|
||||
export * from './TextDisplayComponent.js';
|
||||
export * from './TextInputComponent.js';
|
||||
export * from './ThumbnailComponent.js';
|
||||
export * from './UserSelectMenuComponent.js';
|
||||
|
||||
export * from './ComponentEmoji.js';
|
||||
export * from './MediaGalleryItem.js';
|
||||
export * from './SelectMenuDefaultValue.js';
|
||||
export * from './StringSelectMenuOption.js';
|
||||
export * from './UnfurledMediaItem.js';
|
||||
104
packages/structures/src/messages/embeds/Embed.ts
Normal file
104
packages/structures/src/messages/embeds/Embed.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import type { APIEmbed } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { dateToDiscordISOTimestamp } from '../../utils/optimization.js';
|
||||
import { kCreatedTimestamp, kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents an embed on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has substructures `EmbedAuthor`, `EmbedFooter`, `EmbedField`, `EmbedImage`, `EmbedThumbnail`, `EmbedProvider`, `EmbedVideo` which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class Embed<Omitted extends keyof APIEmbed | '' = ''> extends Structure<APIEmbed, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each Embed.
|
||||
*/
|
||||
public static override readonly DataTemplate: Partial<APIEmbed> = {
|
||||
set timestamp(_: string) {},
|
||||
};
|
||||
|
||||
protected [kCreatedTimestamp]: number | null = null;
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIEmbed, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected override optimizeData(data: Partial<APIEmbed>) {
|
||||
if (data.timestamp) {
|
||||
this[kCreatedTimestamp] = Date.parse(data.timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The color code of the embed
|
||||
*/
|
||||
public get color() {
|
||||
return this[kData].color;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hexadecimal version of the embed color, with a leading hash
|
||||
*/
|
||||
public get hexColor() {
|
||||
const color = this.color;
|
||||
if (typeof color !== 'number') return color;
|
||||
return `#${color.toString(16).padStart(6, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The description of the embed
|
||||
*/
|
||||
public get description() {
|
||||
return this[kData].description;
|
||||
}
|
||||
|
||||
/**
|
||||
* THe title of the embed
|
||||
*/
|
||||
public get title() {
|
||||
return this[kData].title;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp of the embed content
|
||||
*/
|
||||
public get timestamp() {
|
||||
return this[kCreatedTimestamp];
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of embed (always "rich" for webhook embeds)
|
||||
*/
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL of the embed
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.toJSON}
|
||||
*/
|
||||
public override toJSON() {
|
||||
const clone = super.toJSON();
|
||||
if (this[kCreatedTimestamp]) {
|
||||
clone.timestamp = dateToDiscordISOTimestamp(new Date(this[kCreatedTimestamp]));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
46
packages/structures/src/messages/embeds/EmbedAuthor.ts
Normal file
46
packages/structures/src/messages/embeds/EmbedAuthor.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { APIEmbedAuthor } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents author data in an embed on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class EmbedAuthor<Omitted extends keyof APIEmbedAuthor | '' = ''> extends Structure<APIEmbedAuthor, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIEmbedAuthor, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the author
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL of author icon
|
||||
*/
|
||||
public get iconURL() {
|
||||
return this[kData].icon_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* A proxied URL of author icon
|
||||
*/
|
||||
public get proxyIconURL() {
|
||||
return this[kData].proxy_icon_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL of the author
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
}
|
||||
39
packages/structures/src/messages/embeds/EmbedField.ts
Normal file
39
packages/structures/src/messages/embeds/EmbedField.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { APIEmbedField } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents a field's data in an embed on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class EmbedField<Omitted extends keyof APIEmbedField | '' = ''> extends Structure<APIEmbedField, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIEmbedField, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the field
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of the field
|
||||
*/
|
||||
public get value() {
|
||||
return this[kData].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this field should display inline
|
||||
*/
|
||||
public get inline() {
|
||||
return this[kData].inline;
|
||||
}
|
||||
}
|
||||
39
packages/structures/src/messages/embeds/EmbedFooter.ts
Normal file
39
packages/structures/src/messages/embeds/EmbedFooter.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { APIEmbedFooter } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents footer data in an embed on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class EmbedFooter<Omitted extends keyof APIEmbedFooter | '' = ''> extends Structure<APIEmbedFooter, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIEmbedFooter, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The footer text
|
||||
*/
|
||||
public get text() {
|
||||
return this[kData].text;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL of the footer icon
|
||||
*/
|
||||
public get iconURL() {
|
||||
return this[kData].icon_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* A proxied URL of the footer icon
|
||||
*/
|
||||
public get proxyIconURL() {
|
||||
return this[kData].proxy_icon_url;
|
||||
}
|
||||
}
|
||||
46
packages/structures/src/messages/embeds/EmbedImage.ts
Normal file
46
packages/structures/src/messages/embeds/EmbedImage.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { APIEmbedImage } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents image data in an embed on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class EmbedImage<Omitted extends keyof APIEmbedImage | '' = ''> extends Structure<APIEmbedImage, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIEmbedImage, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the image
|
||||
*/
|
||||
public get height() {
|
||||
return this[kData].height;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the image
|
||||
*/
|
||||
public get width() {
|
||||
return this[kData].width;
|
||||
}
|
||||
|
||||
/**
|
||||
* A proxied URL of the image
|
||||
*/
|
||||
public get proxyURL() {
|
||||
return this[kData].proxy_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source URL of the image
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
}
|
||||
35
packages/structures/src/messages/embeds/EmbedProvider.ts
Normal file
35
packages/structures/src/messages/embeds/EmbedProvider.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { APIEmbedProvider } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents provider data in an embed on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class EmbedProvider<Omitted extends keyof APIEmbedProvider | '' = ''> extends Structure<
|
||||
APIEmbedProvider,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIEmbedProvider, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the provider
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL of the provider
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
}
|
||||
49
packages/structures/src/messages/embeds/EmbedThumbnail.ts
Normal file
49
packages/structures/src/messages/embeds/EmbedThumbnail.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { APIEmbedThumbnail } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents thumbnail data in an embed on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class EmbedThumbnail<Omitted extends keyof APIEmbedThumbnail | '' = ''> extends Structure<
|
||||
APIEmbedThumbnail,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIEmbedThumbnail, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the thumbnail
|
||||
*/
|
||||
public get height() {
|
||||
return this[kData].height;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the thumbnail
|
||||
*/
|
||||
public get width() {
|
||||
return this[kData].width;
|
||||
}
|
||||
|
||||
/**
|
||||
* A proxied URL of the thumbnail
|
||||
*/
|
||||
public get proxyURL() {
|
||||
return this[kData].proxy_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source URL of the thumbnail
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
}
|
||||
46
packages/structures/src/messages/embeds/EmbedVideo.ts
Normal file
46
packages/structures/src/messages/embeds/EmbedVideo.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { APIEmbedVideo } from 'discord-api-types/v10';
|
||||
import { Structure } from '../../Structure.js';
|
||||
import { kData } from '../../utils/symbols.js';
|
||||
import type { Partialize } from '../../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents video data in an embed on a message.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class EmbedVideo<Omitted extends keyof APIEmbedVideo | '' = ''> extends Structure<APIEmbedVideo, Omitted> {
|
||||
/**
|
||||
* @param data - The raw data received from the API for the connection
|
||||
*/
|
||||
public constructor(data: Partialize<APIEmbedVideo, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The height of the video
|
||||
*/
|
||||
public get height() {
|
||||
return this[kData].height;
|
||||
}
|
||||
|
||||
/**
|
||||
* The width of the video
|
||||
*/
|
||||
public get width() {
|
||||
return this[kData].width;
|
||||
}
|
||||
|
||||
/**
|
||||
* A proxied URL of the video
|
||||
*/
|
||||
public get proxyURL() {
|
||||
return this[kData].proxy_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source URL of the video
|
||||
*/
|
||||
public get url() {
|
||||
return this[kData].url;
|
||||
}
|
||||
}
|
||||
8
packages/structures/src/messages/embeds/index.ts
Normal file
8
packages/structures/src/messages/embeds/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './Embed.js';
|
||||
export * from './EmbedAuthor.js';
|
||||
export * from './EmbedField.js';
|
||||
export * from './EmbedFooter.js';
|
||||
export * from './EmbedImage.js';
|
||||
export * from './EmbedProvider.js';
|
||||
export * from './EmbedThumbnail.js';
|
||||
export * from './EmbedVideo.js';
|
||||
16
packages/structures/src/messages/index.ts
Normal file
16
packages/structures/src/messages/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export * from './components/index.js';
|
||||
export * from './embeds/index.js';
|
||||
|
||||
export * from './ApplicationCommandInteractionMetadata.js';
|
||||
export * from './Attachment.js';
|
||||
export * from './ChannelMention.js';
|
||||
export * from './InteractionMetadata.js';
|
||||
export * from './Message.js';
|
||||
export * from './MessageActivity.js';
|
||||
export * from './MessageCall.js';
|
||||
export * from './MessageComponentInteractionMetadata.js';
|
||||
export * from './MessageReference.js';
|
||||
export * from './ModalSubmitInteractionMetadata.js';
|
||||
export * from './Reaction.js';
|
||||
export * from './ReactionCountDetails.js';
|
||||
export * from './RoleSubscriptionData.js';
|
||||
87
packages/structures/src/polls/Poll.ts
Normal file
87
packages/structures/src/polls/Poll.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { APIPoll } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { dateToDiscordISOTimestamp } from '../utils/optimization.js';
|
||||
import { kData, kExpiresTimestamp } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents a poll on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has substructures `PollMedia`, `PollAnswer`, `PollResults` which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class Poll<Omitted extends keyof APIPoll | '' = ''> extends Structure<APIPoll, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each Poll.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIPoll> = {
|
||||
set expiry(_: string) {},
|
||||
};
|
||||
|
||||
/**
|
||||
* Optimized storage of {@link discord-api-types/v10#(APIPoll:interface).expiry}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected [kExpiresTimestamp]: number | null = null;
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the poll
|
||||
*/
|
||||
public constructor(data: Partialize<APIPoll, Omitted>) {
|
||||
super(data);
|
||||
this.optimizeData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.optimizeData}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected override optimizeData(data: Partial<APIPoll>) {
|
||||
if (data.expiry) {
|
||||
this[kExpiresTimestamp] = Date.parse(data.expiry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a user can select multiple answers
|
||||
*/
|
||||
public get allowMultiselect() {
|
||||
return this[kData].allow_multiselect;
|
||||
}
|
||||
|
||||
/**
|
||||
* The layout type of the poll
|
||||
*/
|
||||
public get layoutType() {
|
||||
return this[kData].layout_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timestamp this poll will expire at
|
||||
*/
|
||||
public get expiresTimestamp() {
|
||||
return this[kExpiresTimestamp];
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the poll will expire at
|
||||
*/
|
||||
public get expiresAt() {
|
||||
const expiresTimestamp = this.expiresTimestamp;
|
||||
return expiresTimestamp ? new Date(expiresTimestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.toJSON}
|
||||
*/
|
||||
public override toJSON() {
|
||||
const clone = super.toJSON();
|
||||
if (this[kExpiresTimestamp]) {
|
||||
clone.expiry = dateToDiscordISOTimestamp(new Date(this[kExpiresTimestamp]));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
31
packages/structures/src/polls/PollAnswer.ts
Normal file
31
packages/structures/src/polls/PollAnswer.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { APIPollAnswer } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents an answer to a poll on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `PollMedia` which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class PollAnswer<Omitted extends keyof APIPollAnswer | '' = ''> extends Structure<APIPollAnswer, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each PollAnswer.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIPollAnswer> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the poll answer
|
||||
*/
|
||||
public constructor(data: Partialize<APIPollAnswer, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the poll answer
|
||||
*/
|
||||
public get answerId() {
|
||||
return this[kData].answer_id;
|
||||
}
|
||||
}
|
||||
47
packages/structures/src/polls/PollAnswerCount.ts
Normal file
47
packages/structures/src/polls/PollAnswerCount.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { APIPollAnswerCount } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents the counts of answers to a poll on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class PollAnswerCount<Omitted extends keyof APIPollAnswerCount | '' = ''> extends Structure<
|
||||
APIPollAnswerCount,
|
||||
Omitted
|
||||
> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each PollAnswerCount.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIPollAnswerCount> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the poll answer count
|
||||
*/
|
||||
public constructor(data: Partialize<APIPollAnswerCount, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the poll answer
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of votes for this answer
|
||||
*/
|
||||
public get count() {
|
||||
return this[kData].count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current user voted for this answer
|
||||
*/
|
||||
public get meVoted() {
|
||||
return this[kData].me_voted;
|
||||
}
|
||||
}
|
||||
31
packages/structures/src/polls/PollMedia.ts
Normal file
31
packages/structures/src/polls/PollMedia.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { APIPollMedia } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents a field of a poll on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `Emoji` which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class PollMedia<Omitted extends keyof APIPollMedia | '' = ''> extends Structure<APIPollMedia, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each PollMedia.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIPollMedia> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the poll media
|
||||
*/
|
||||
public constructor(data: Partialize<APIPollMedia, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The text of the poll field
|
||||
*/
|
||||
public get text() {
|
||||
return this[kData].text;
|
||||
}
|
||||
}
|
||||
31
packages/structures/src/polls/PollResults.ts
Normal file
31
packages/structures/src/polls/PollResults.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { APIPollResults } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents the results of a poll on a message on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
* @remarks has a substructure `PollAnswerCount` which need to be instantiated and stored by an extending class using it
|
||||
*/
|
||||
export class PollResults<Omitted extends keyof APIPollResults | '' = ''> extends Structure<APIPollResults, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each PollResults.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APIPollResults> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the poll results
|
||||
*/
|
||||
public constructor(data: Partialize<APIPollResults, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the votes have been precisely counted
|
||||
*/
|
||||
public get isFinalized() {
|
||||
return this[kData].is_finalized;
|
||||
}
|
||||
}
|
||||
5
packages/structures/src/polls/index.ts
Normal file
5
packages/structures/src/polls/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './Poll.js';
|
||||
export * from './PollAnswer.js';
|
||||
export * from './PollAnswerCount.js';
|
||||
export * from './PollMedia.js';
|
||||
export * from './PollResults.js';
|
||||
72
packages/structures/src/stickers/Sticker.ts
Normal file
72
packages/structures/src/stickers/Sticker.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { APISticker } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
* Represents a sticker on Discord.
|
||||
*
|
||||
* @typeParam Omitted - Specify the properties that will not be stored in the raw data field as a union, implement via `DataTemplate`
|
||||
*/
|
||||
export class Sticker<Omitted extends keyof APISticker | '' = ''> extends Structure<APISticker, Omitted> {
|
||||
/**
|
||||
* The template used for removing data from the raw data stored for each SitckerItem.
|
||||
*/
|
||||
public static override DataTemplate: Partial<APISticker> = {};
|
||||
|
||||
/**
|
||||
* @param data - The raw data received from the API for the sticker item
|
||||
*/
|
||||
public constructor(data: Partialize<APISticker, Omitted>) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the sticker
|
||||
*/
|
||||
public get id() {
|
||||
return this[kData].id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the sticker
|
||||
*/
|
||||
public get name() {
|
||||
return this[kData].name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The format type of the sticker
|
||||
*/
|
||||
public get formatType() {
|
||||
return this[kData].format_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this guild sticker can be used, may be false due to loss of Server Boosts
|
||||
*/
|
||||
public get available() {
|
||||
return this[kData].available;
|
||||
}
|
||||
|
||||
/**
|
||||
* The description of the sticker
|
||||
*/
|
||||
public get description() {
|
||||
return this[kData].description;
|
||||
}
|
||||
|
||||
/**
|
||||
* The autocomplete/suggestion tags for the sticker
|
||||
*/
|
||||
public get tags() {
|
||||
return this[kData].tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this sticker
|
||||
*/
|
||||
public get type() {
|
||||
return this[kData].type;
|
||||
}
|
||||
}
|
||||
1
packages/structures/src/stickers/index.ts
Normal file
1
packages/structures/src/stickers/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Sticker.js';
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { APIConnection } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData, kPatch } from '../utils/symbols.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
/**
|
||||
@@ -21,15 +21,6 @@ export class Connection<Omitted extends keyof APIConnection | '' = ''> extends S
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.[kPatch]}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public override [kPatch](data: Partial<APIConnection>) {
|
||||
return super[kPatch](data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the connection account
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DiscordSnowflake } from '@sapphire/snowflake';
|
||||
import type { APIUser } from 'discord-api-types/v10';
|
||||
import { Structure } from '../Structure.js';
|
||||
import { kData, kPatch } from '../utils/symbols.js';
|
||||
import { kData } from '../utils/symbols.js';
|
||||
import { isIdSet } from '../utils/type-guards.js';
|
||||
import type { Partialize } from '../utils/types.js';
|
||||
|
||||
@@ -24,15 +24,6 @@ export class User<Omitted extends keyof APIUser | '' = ''> extends Structure<API
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc Structure.[kPatch]}
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public override [kPatch](data: Partial<APIUser>) {
|
||||
return super[kPatch](data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user's id
|
||||
*/
|
||||
|
||||
@@ -8,3 +8,15 @@ export function extendTemplate<SuperTemplate extends Record<string, unknown>>(
|
||||
> &
|
||||
SuperTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a JavaScript Date object into the timestamp format used by Discord in payloads.
|
||||
* E.g. `2025-11-16T14:09:25.239000+00:00`
|
||||
*
|
||||
* @private
|
||||
* @param date a Date instance
|
||||
* @returns an ISO8601 timestamp with microseconds precision and explicit +00:00 timezone
|
||||
*/
|
||||
export function dateToDiscordISOTimestamp(date: Date) {
|
||||
return `${date.getUTCFullYear()}-${(date.getUTCMonth() + 1).toString().padStart(2, '0')}-${date.getUTCDate().toString().padStart(2, '0')}T${date.getUTCHours().toString().padStart(2, '0')}:${date.getUTCMinutes().toString().padStart(2, '0')}:${date.getUTCSeconds().toString().padStart(2, '0')}.${date.getUTCMilliseconds().toString().padEnd(6, '0')}+00:00`;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ export const kData = Symbol.for('djs.structures.data');
|
||||
export const kClone = Symbol.for('djs.structures.clone');
|
||||
export const kPatch = Symbol.for('djs.structures.patch');
|
||||
export const kExpiresTimestamp = Symbol.for('djs.structures.expiresTimestamp');
|
||||
export const kEndedTimestamp = Symbol.for('djs.structures.endedTimestamp');
|
||||
export const kCreatedTimestamp = Symbol.for('djs.structures.createdTimestamp');
|
||||
export const kEditedTimestamp = Symbol.for('djs.structures.editedTimestamp');
|
||||
export const kArchiveTimestamp = Symbol.for('djs.structures.archiveTimestamp');
|
||||
@@ -9,6 +10,8 @@ export const kArchiveTimestamp = Symbol.for('djs.structures.archiveTimestamp');
|
||||
export const kAllow = Symbol.for('djs.structures.allow');
|
||||
export const kDeny = Symbol.for('djs.structures.deny');
|
||||
|
||||
export const kBurstColors = Symbol.for('djs.structures.burstColors');
|
||||
|
||||
export const kLastPinTimestamp = Symbol.for('djs.structures.lastPinTimestamp');
|
||||
|
||||
export const kMixinConstruct = Symbol.for('djs.structures.mixin.construct');
|
||||
|
||||
Reference in New Issue
Block a user