feat(transformers)!: Partial transformers (#4436)

* refactor(bot)!: setup desired properties for all transformers

SetupDesiredProps when is given an object that does not corrispond to a transformer object that supports desired properties will behave like TransformProperty on the entire object as when it tries to get the properties for said object it will find `never` as the props and for `IsKeyDesired` a props of `never` means that all props are desired.

* feat(transformers)!: Handle partials in transformers

* Add expiresAt to invite create event

* Use Equals helper, clean up a bit the code

* Explicit the IsKeyDesired TProps never behavior

* Fix type errors

* format

* Add all trasformer objects to bot.transformers.$inferredTypes

* Use transfromerInformations for bot.transformers.$inferredTypes

* code review

* code review

* fix component transformer

* fix

* Update packages/bot/src/transformers/types.ts

* Fixes from Tri (+ GPT sol) code review

---------

Co-authored-by: Link <link20050703@gmail.com>
This commit is contained in:
Fleny
2026-07-31 15:12:15 +05:30
committed by GitHub
co-authored by Link
parent eb4cdaa0f5
commit 4581222cf4
65 changed files with 747 additions and 487 deletions
+3 -3
View File
@@ -4,11 +4,11 @@ import type { CreateRestManagerOptions, RestManager } from '@discordeno/rest';
import { createRestManager } from '@discordeno/rest';
import type { BigString, GatewayDispatchEventNames, GatewayIntents, RecursivePartial } from '@discordeno/types';
import { createLogger, getBotIdFromToken, type logger } from '@discordeno/utils';
import type { CompleteDesiredProperties, DesiredPropertiesBehavior, TransformersDesiredProperties } from './desiredProperties.js';
import type { CompleteDesiredProperties, DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from './desiredProperties.js';
import type { EventHandlers } from './events.js';
import { type BotGatewayHandler, createBotGatewayHandlers, type GatewayHandlers } from './handlers.js';
import { type BotHelpers, createBotHelpers } from './helpers.js';
import { createTransformers, type TransformerFunctions, type Transformers } from './transformers.js';
import { createTransformers, type TransformerInformations, type Transformers } from './transformers.js';
/**
* Create a bot object that will maintain the rest and gateway connection.
@@ -160,7 +160,7 @@ export interface Bot<
/** The functions that should transform discord objects to discordeno shaped objects. */
transformers: Transformers<TProps, TBehavior> & {
$inferredTypes: {
[K in keyof TransformerFunctions<TProps, TBehavior>]: ReturnType<TransformerFunctions<TProps, TBehavior>[K]>;
[K in keyof TransformerInformations]: SetupDesiredProps<TransformerInformations[K]['transformed'], TProps, TBehavior>;
};
};
/** The handler functions that should handle incoming discord payloads from gateway and call an event. */
+22 -7
View File
@@ -1,7 +1,8 @@
import type { DiscordGatewayPayload, DiscordRateLimited, DiscordReady, DiscordVoiceChannelEffectAnimationType } from '@discordeno/types';
import type { DiscordGatewayPayload, DiscordRateLimited, DiscordReady, DiscordVoiceChannelEffectAnimationType, TargetTypes } from '@discordeno/types';
import type { Collection } from '@discordeno/utils';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from './desiredProperties.js';
import type {
Application,
AuditLogEntry,
AutoModerationActionExecution,
AutoModerationRule,
@@ -12,7 +13,6 @@ import type {
GuildApplicationCommandPermissions,
Integration,
Interaction,
Invite,
Member,
Message,
PresenceUpdate,
@@ -72,11 +72,26 @@ export type EventHandlers<TProps extends TransformersDesiredProperties, TBehavio
integrationCreate: (integration: SetupDesiredProps<Integration, TProps, TBehavior>) => unknown;
integrationDelete: (payload: { id: bigint; guildId: bigint; applicationId?: bigint }) => unknown;
integrationUpdate: (payload: { guildId: bigint }) => unknown;
inviteCreate: (invite: SetupDesiredProps<Invite, TProps, TBehavior>) => unknown;
inviteCreate: (invite: {
channelId: bigint;
code: string;
createdAt: number;
guildId?: bigint;
inviter?: SetupDesiredProps<User, TProps, TBehavior>;
maxAge: number;
maxUses: number;
targetType?: TargetTypes;
targetUser?: SetupDesiredProps<User, TProps, TBehavior>;
targetApplication?: Partial<SetupDesiredProps<Application, TProps, TBehavior>>;
temporary: boolean;
uses: number;
expiresAt?: number;
roleIds?: bigint[];
}) => unknown;
inviteDelete: (payload: { channelId: bigint; guildId?: bigint; code: string }) => unknown;
guildMemberAdd: (member: SetupDesiredProps<Member, TProps, TBehavior>, user: SetupDesiredProps<User, TProps, TBehavior>) => unknown;
guildMemberRemove: (user: SetupDesiredProps<User, TProps, TBehavior>, guildId: bigint) => unknown;
guildMemberUpdate: (member: SetupDesiredProps<Member, TProps, TBehavior>, user: SetupDesiredProps<User, TProps, TBehavior>) => unknown;
guildMemberUpdate: (member: Partial<SetupDesiredProps<Member, TProps, TBehavior>>, user: SetupDesiredProps<User, TProps, TBehavior>) => unknown;
guildStickersUpdate: (payload: { guildId: bigint; stickers: SetupDesiredProps<Sticker, TProps, TBehavior>[] }) => unknown;
messageCreate: (message: SetupDesiredProps<Message, TProps, TBehavior>) => unknown;
messageDelete: (payload: { id: bigint; channelId: bigint; guildId?: bigint }, message?: SetupDesiredProps<Message, TProps, TBehavior>) => unknown;
@@ -89,7 +104,7 @@ export type EventHandlers<TProps extends TransformersDesiredProperties, TBehavio
guildId?: bigint;
member?: SetupDesiredProps<Member, TProps, TBehavior>;
user?: SetupDesiredProps<User, TProps, TBehavior>;
emoji: SetupDesiredProps<Emoji, TProps, TBehavior>;
emoji: Partial<SetupDesiredProps<Emoji, TProps, TBehavior>>;
messageAuthorId?: bigint;
burst: boolean;
burstColors?: string[];
@@ -99,14 +114,14 @@ export type EventHandlers<TProps extends TransformersDesiredProperties, TBehavio
channelId: bigint;
messageId: bigint;
guildId?: bigint;
emoji: SetupDesiredProps<Emoji, TProps, TBehavior>;
emoji: Partial<SetupDesiredProps<Emoji, TProps, TBehavior>>;
burst: boolean;
}) => unknown;
reactionRemoveEmoji: (payload: {
channelId: bigint;
messageId: bigint;
guildId?: bigint;
emoji: SetupDesiredProps<Emoji, TProps, TBehavior>;
emoji: Partial<SetupDesiredProps<Emoji, TProps, TBehavior>>;
}) => unknown;
reactionRemoveAll: (payload: { channelId: bigint; messageId: bigint; guildId?: bigint }) => unknown;
presenceUpdate: (presence: SetupDesiredProps<PresenceUpdate, TProps, TBehavior>) => unknown;
@@ -6,6 +6,22 @@ export async function handleInviteCreate(bot: Bot, data: DiscordGatewayPayload,
const payload = data.d as DiscordInviteCreate;
// TODO: Add role_ids, the transformer should be kept for the Invite type, not for the gateway event
bot.events.inviteCreate(bot.transformers.invite(bot, payload, { shardId }));
bot.events.inviteCreate({
channelId: bot.transformers.snowflake(payload.channel_id),
code: payload.code,
createdAt: Date.parse(payload.created_at),
guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined,
inviter: payload.inviter ? bot.transformers.user(bot, payload.inviter) : undefined,
maxAge: payload.max_age,
maxUses: payload.max_uses,
targetType: payload.target_type,
targetUser: payload.target_user ? bot.transformers.user(bot, payload.target_user) : undefined,
targetApplication: payload.target_application
? bot.transformers.application(bot, payload.target_application, { shardId, partial: true })
: undefined,
temporary: payload.temporary,
uses: payload.uses,
expiresAt: payload.expires_at ? Date.parse(payload.expires_at) : undefined,
roleIds: payload.role_ids ? payload.role_ids.map((id) => bot.transformers.snowflake(id)) : undefined,
});
}
@@ -7,11 +7,8 @@ export async function handleInviteDelete(bot: Bot, data: DiscordGatewayPayload):
const payload = data.d as DiscordInviteDelete;
bot.events.inviteDelete({
/** The channel of the invite */
channelId: bot.transformers.snowflake(payload.channel_id),
/** The guild of the invite */
guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined,
/** The unique invite code */
code: payload.code,
});
}
@@ -7,9 +7,5 @@ export async function handleGuildMemberUpdate(bot: Bot, data: DiscordGatewayPayl
const payload = data.d as DiscordGuildMemberUpdate;
const user = bot.transformers.user(bot, payload.user);
bot.events.guildMemberUpdate(
// @ts-expect-error Flags in the update are nullable, while on the member they are be always present
bot.transformers.member(bot, payload, { guildId: payload.guild_id, userId: payload.user.id }),
user,
);
bot.events.guildMemberUpdate(bot.transformers.member(bot, payload, { guildId: payload.guild_id, userId: payload.user.id, partial: true }), user);
}
@@ -15,8 +15,7 @@ export async function handleMessageReactionAdd(bot: Bot, data: DiscordGatewayPay
guildId,
member: payload.member && guildId ? bot.transformers.member(bot, payload.member, { guildId, userId }) : undefined,
user: payload.member ? bot.transformers.user(bot, payload.member.user) : undefined,
// @ts-expect-error TODO: Deal with partials
emoji: bot.transformers.emoji(bot, payload.emoji),
emoji: bot.transformers.emoji(bot, payload.emoji, { partial: true }),
messageAuthorId: payload.message_author_id ? bot.transformers.snowflake(payload.message_author_id) : undefined,
burst: payload.burst,
burstColors: payload.burst_colors,
@@ -11,8 +11,7 @@ export async function handleMessageReactionRemove(bot: Bot, data: DiscordGateway
channelId: bot.transformers.snowflake(payload.channel_id),
messageId: bot.transformers.snowflake(payload.message_id),
guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined,
// @ts-expect-error TODO: Deal with partials
emoji: bot.transformers.emoji(bot, payload.emoji),
emoji: bot.transformers.emoji(bot, payload.emoji, { partial: true }),
burst: payload.burst,
});
}
@@ -10,7 +10,6 @@ export async function handleMessageReactionRemoveEmoji(bot: Bot, data: DiscordGa
channelId: bot.transformers.snowflake(payload.channel_id),
messageId: bot.transformers.snowflake(payload.message_id),
guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined,
// @ts-expect-error TODO: Deal with partials
emoji: bot.transformers.emoji(bot, payload.emoji),
emoji: bot.transformers.emoji(bot, payload.emoji, { partial: true }),
});
}
+2 -8
View File
@@ -383,10 +383,7 @@ export function createBotHelpers<TProps extends TransformersDesiredProperties, T
return bot.transformers.guild(bot, snakelize(await bot.rest.getGuild(guildId, options)));
},
getGuilds: async (bearerToken, options) => {
return (await bot.rest.getGuilds(bearerToken, options)).map<Partial<typeof bot.transformers.$inferredTypes.guild>>((res) =>
// @ts-expect-error getGuilds returns partial guilds
bot.transformers.guild(bot, snakelize(res)),
);
return (await bot.rest.getGuilds(bearerToken, options)).map((res) => bot.transformers.guild(bot, snakelize(res), { partial: true }));
},
getGuildApplicationCommand: async (commandId, guildId) => {
return bot.transformers.applicationCommand(bot, snakelize(await bot.rest.getGuildApplicationCommand(commandId, guildId)));
@@ -832,10 +829,7 @@ export function createBotHelpers<TProps extends TransformersDesiredProperties, T
return bot.transformers.entitlement(bot, snakelize(await bot.rest.getEntitlement(applicationId, entitlementId)));
},
createTestEntitlement: async (applicationId, body) => {
// @ts-expect-error createTestEntitlement gives a partial, and this method returns a partial
return bot.transformers.entitlement(bot, snakelize(await bot.rest.createTestEntitlement(applicationId, body))) as Partial<
typeof bot.transformers.$inferredTypes.entitlement
>;
return bot.transformers.entitlement(bot, snakelize(await bot.rest.createTestEntitlement(applicationId, body)), { partial: true });
},
deleteTestEntitlement: async (applicationId, entitlementId) => {
await bot.rest.deleteTestEntitlement(applicationId, entitlementId);
+212 -95
View File
@@ -38,7 +38,6 @@ import type {
DiscordInteractionDataOption,
DiscordInteractionDataResolved,
DiscordInteractionResource,
DiscordInviteCreate,
DiscordInviteMetadata,
DiscordInviteStageInstance,
DiscordLobby,
@@ -245,95 +244,88 @@ import { transformWelcomeScreen } from './transformers/welcomeScreen.js';
import { transformWidget } from './transformers/widget.js';
import { transformWidgetSettings } from './transformers/widgetSettings.js';
export type TransformerFunctions<TProps extends TransformersDesiredProperties, TBehavior extends DesiredPropertiesBehavior> = {
activity: TransformerFunction<TProps, TBehavior, DiscordActivity, Activity>;
activityAssets: TransformerFunction<TProps, TBehavior, DiscordActivityAssets, ActivityAssets>;
activityInstance: TransformerFunction<TProps, TBehavior, DiscordActivityInstance, ActivityInstance>;
activityLocation: TransformerFunction<TProps, TBehavior, DiscordActivityLocation, ActivityLocation>;
application: TransformerFunction<TProps, TBehavior, DiscordApplication, Application, { shardId?: number }>;
applicationCommand: TransformerFunction<TProps, TBehavior, DiscordApplicationCommand, ApplicationCommand>;
applicationCommandOption: TransformerFunction<TProps, TBehavior, DiscordApplicationCommandOption, ApplicationCommandOption>;
applicationCommandOptionChoice: TransformerFunction<TProps, TBehavior, DiscordApplicationCommandOptionChoice, ApplicationCommandOptionChoice>;
applicationCommandPermission: TransformerFunction<TProps, TBehavior, DiscordGuildApplicationCommandPermissions, GuildApplicationCommandPermissions>;
attachment: TransformerFunction<TProps, TBehavior, DiscordAttachment, Attachment>;
auditLogEntry: TransformerFunction<TProps, TBehavior, DiscordAuditLogEntry, AuditLogEntry>;
automodActionExecution: TransformerFunction<TProps, TBehavior, DiscordAutoModerationActionExecution, AutoModerationActionExecution>;
automodRule: TransformerFunction<TProps, TBehavior, DiscordAutoModerationRule, AutoModerationRule>;
avatarDecorationData: TransformerFunction<TProps, TBehavior, DiscordAvatarDecorationData, AvatarDecorationData>;
channel: TransformerFunction<TProps, TBehavior, DiscordChannel, Channel, { guildId?: BigString }>;
collectibles: TransformerFunction<TProps, TBehavior, DiscordCollectibles, Collectibles>;
component: TransformerFunction<TProps, TBehavior, DiscordMessageComponent | DiscordMessageComponentFromModalInteractionResponse, Component>;
defaultReactionEmoji: TransformerFunction<TProps, TBehavior, DiscordDefaultReactionEmoji, DefaultReactionEmoji>;
embed: TransformerFunction<TProps, TBehavior, DiscordEmbed, Embed>;
emoji: TransformerFunction<TProps, TBehavior, DiscordEmoji, Emoji>;
entitlement: TransformerFunction<TProps, TBehavior, DiscordEntitlement, Entitlement>;
forumTag: TransformerFunction<TProps, TBehavior, DiscordForumTag, ForumTag>;
gatewayBot: TransformerFunction<TProps, TBehavior, DiscordGetGatewayBot, GetGatewayBot>;
guild: TransformerFunction<TProps, TBehavior, DiscordGuild, Guild, { shardId?: number }>;
guildOnboarding: TransformerFunction<TProps, TBehavior, DiscordGuildOnboarding, GuildOnboarding>;
guildOnboardingPrompt: TransformerFunction<TProps, TBehavior, DiscordGuildOnboardingPrompt, GuildOnboardingPrompt>;
guildOnboardingPromptOption: TransformerFunction<TProps, TBehavior, DiscordGuildOnboardingPromptOption, GuildOnboardingPromptOption>;
incidentsData: TransformerFunction<TProps, TBehavior, DiscordIncidentsData, IncidentsData>;
integration: TransformerFunction<TProps, TBehavior, DiscordIntegrationCreateUpdate, Integration>;
interaction: TransformerFunction<TProps, TBehavior, DiscordInteraction, Interaction, { shardId?: number }>;
interactionCallback: TransformerFunction<TProps, TBehavior, DiscordInteractionCallback, InteractionCallback>;
interactionCallbackResponse: TransformerFunction<
TProps,
TBehavior,
DiscordInteractionCallbackResponse,
InteractionCallbackResponse,
{ shardId?: number }
>;
interactionDataOptions: TransformerFunction<TProps, TBehavior, DiscordInteractionDataOption, InteractionDataOption>;
interactionDataResolved: TransformerFunction<
TProps,
TBehavior,
export type TransformerInformations = {
activity: TransformerInformation<DiscordActivity, Activity, false>;
activityAssets: TransformerInformation<DiscordActivityAssets, ActivityAssets, false>;
activityInstance: TransformerInformation<DiscordActivityInstance, ActivityInstance, true>;
activityLocation: TransformerInformation<DiscordActivityLocation, ActivityLocation, true>;
application: TransformerInformation<DiscordApplication, Application, true, { shardId?: number }>;
applicationCommand: TransformerInformation<DiscordApplicationCommand, ApplicationCommand, false>;
applicationCommandOption: TransformerInformation<DiscordApplicationCommandOption, ApplicationCommandOption, false>;
applicationCommandOptionChoice: TransformerInformation<DiscordApplicationCommandOptionChoice, ApplicationCommandOptionChoice, false>;
applicationCommandPermission: TransformerInformation<DiscordGuildApplicationCommandPermissions, GuildApplicationCommandPermissions, false>;
attachment: TransformerInformation<DiscordAttachment, Attachment, true>;
auditLogEntry: TransformerInformation<DiscordAuditLogEntry, AuditLogEntry, false>;
automodActionExecution: TransformerInformation<DiscordAutoModerationActionExecution, AutoModerationActionExecution, false>;
automodRule: TransformerInformation<DiscordAutoModerationRule, AutoModerationRule, false>;
avatarDecorationData: TransformerInformation<DiscordAvatarDecorationData, AvatarDecorationData, true>;
channel: TransformerInformation<DiscordChannel, Channel, true, { guildId?: BigString }>;
collectibles: TransformerInformation<DiscordCollectibles, Collectibles, true>;
component: TransformerInformation<DiscordMessageComponent | DiscordMessageComponentFromModalInteractionResponse, Component, true>;
defaultReactionEmoji: TransformerInformation<DiscordDefaultReactionEmoji, DefaultReactionEmoji, true>;
embed: TransformerInformation<DiscordEmbed, Embed, false>;
emoji: TransformerInformation<DiscordEmoji, Emoji, true>;
entitlement: TransformerInformation<DiscordEntitlement, Entitlement, true>;
forumTag: TransformerInformation<DiscordForumTag, ForumTag, true>;
gatewayBot: TransformerInformation<DiscordGetGatewayBot, GetGatewayBot, false>;
guild: TransformerInformation<DiscordGuild, Guild, true, { shardId?: number }>;
guildOnboarding: TransformerInformation<DiscordGuildOnboarding, GuildOnboarding, true>;
guildOnboardingPrompt: TransformerInformation<DiscordGuildOnboardingPrompt, GuildOnboardingPrompt, true>;
guildOnboardingPromptOption: TransformerInformation<DiscordGuildOnboardingPromptOption, GuildOnboardingPromptOption, true>;
incidentsData: TransformerInformation<DiscordIncidentsData, IncidentsData, true>;
integration: TransformerInformation<DiscordIntegrationCreateUpdate, Integration, false>;
interaction: TransformerInformation<DiscordInteraction, Interaction, true, { shardId?: number }>;
interactionCallback: TransformerInformation<DiscordInteractionCallback, InteractionCallback, true>;
interactionCallbackResponse: TransformerInformation<DiscordInteractionCallbackResponse, InteractionCallbackResponse, true, { shardId?: number }>;
interactionDataOptions: TransformerInformation<DiscordInteractionDataOption, InteractionDataOption, false>;
interactionDataResolved: TransformerInformation<
DiscordInteractionDataResolved,
InteractionDataResolved,
true,
{ shardId?: number; guildId?: BigString }
>;
interactionResource: TransformerFunction<TProps, TBehavior, DiscordInteractionResource, InteractionResource, { shardId?: number }>;
invite: TransformerFunction<TProps, TBehavior, DiscordInviteCreate | DiscordInviteMetadata, Invite, { shardId?: number }>;
inviteStageInstance: TransformerFunction<TProps, TBehavior, DiscordInviteStageInstance, InviteStageInstance, { guildId?: BigString }>;
lobby: TransformerFunction<TProps, TBehavior, DiscordLobby, Lobby>;
lobbyMember: TransformerFunction<TProps, TBehavior, DiscordLobbyMember, LobbyMember>;
lobbyMessage: TransformerFunction<TProps, TBehavior, DiscordLobbyMessage, LobbyMessage>;
lobbyInvite: TransformerFunction<TProps, TBehavior, DiscordLobbyInvite, LobbyInvite>;
mediaGalleryItem: TransformerFunction<TProps, TBehavior, DiscordMediaGalleryItem, MediaGalleryItem>;
member: TransformerFunction<TProps, TBehavior, DiscordMember, Member, { guildId?: BigString; userId?: BigString }>;
message: TransformerFunction<TProps, TBehavior, DiscordMessage, Message, { shardId?: number }>;
messageCall: TransformerFunction<TProps, TBehavior, DiscordMessageCall, MessageCall>;
messageInteractionMetadata: TransformerFunction<TProps, TBehavior, DiscordMessageInteractionMetadata, MessageInteractionMetadata>;
messagePin: TransformerFunction<TProps, TBehavior, DiscordMessagePin, MessagePin, { shardId?: number }>;
messageSnapshot: TransformerFunction<TProps, TBehavior, DiscordMessageSnapshot, MessageSnapshot, { shardId?: number }>;
nameplate: TransformerFunction<TProps, TBehavior, DiscordNameplate, Nameplate>;
poll: TransformerFunction<TProps, TBehavior, DiscordPoll, Poll>;
pollMedia: TransformerFunction<TProps, TBehavior, DiscordPollMedia, PollMedia>;
presence: TransformerFunction<TProps, TBehavior, DiscordPresenceUpdate, PresenceUpdate>;
role: TransformerFunction<TProps, TBehavior, DiscordRole, Role, { guildId?: BigString }>;
roleColors: TransformerFunction<TProps, TBehavior, DiscordRoleColors, RoleColors>;
scheduledEvent: TransformerFunction<TProps, TBehavior, DiscordScheduledEvent, ScheduledEvent>;
scheduledEventRecurrenceRule: TransformerFunction<TProps, TBehavior, DiscordScheduledEventRecurrenceRule, ScheduledEventRecurrenceRule>;
sharedClientTheme: TransformerFunction<TProps, TBehavior, DiscordSharedClientTheme, SharedClientTheme>;
sku: TransformerFunction<TProps, TBehavior, DiscordSku, Sku>;
soundboardSound: TransformerFunction<TProps, TBehavior, DiscordSoundboardSound, SoundboardSound>;
stageInstance: TransformerFunction<TProps, TBehavior, DiscordStageInstance, StageInstance>;
sticker: TransformerFunction<TProps, TBehavior, DiscordSticker, Sticker>;
stickerPack: TransformerFunction<TProps, TBehavior, DiscordStickerPack, StickerPack>;
subscription: TransformerFunction<TProps, TBehavior, DiscordSubscription, Subscription>;
team: TransformerFunction<TProps, TBehavior, DiscordTeam, Team>;
template: TransformerFunction<TProps, TBehavior, DiscordTemplate, Template>;
threadMember: TransformerFunction<TProps, TBehavior, DiscordThreadMember, ThreadMember, ThreadMemberTransformerExtra>;
threadMemberGuildCreate: TransformerFunction<TProps, TBehavior, DiscordThreadMemberGuildCreate, ThreadMemberGuildCreate>;
unfurledMediaItem: TransformerFunction<TProps, TBehavior, DiscordUnfurledMediaItem, UnfurledMediaItem>;
user: TransformerFunction<TProps, TBehavior, DiscordUser, User>;
userPrimaryGuild: TransformerFunction<TProps, TBehavior, DiscordUserPrimaryGuild, UserPrimaryGuild>;
voiceRegion: TransformerFunction<TProps, TBehavior, DiscordVoiceRegion, VoiceRegion>;
voiceState: TransformerFunction<TProps, TBehavior, DiscordVoiceState, VoiceState, { guildId?: BigString }>;
webhook: TransformerFunction<TProps, TBehavior, DiscordWebhook, Webhook>;
welcomeScreen: TransformerFunction<TProps, TBehavior, DiscordWelcomeScreen, WelcomeScreen>;
widget: TransformerFunction<TProps, TBehavior, DiscordGuildWidget, GuildWidget>;
widgetSettings: TransformerFunction<TProps, TBehavior, DiscordGuildWidgetSettings, GuildWidgetSettings>;
interactionResource: TransformerInformation<DiscordInteractionResource, InteractionResource, true, { shardId?: number }>;
invite: TransformerInformation<DiscordInviteMetadata, Invite, true, { shardId?: number }>;
inviteStageInstance: TransformerInformation<DiscordInviteStageInstance, InviteStageInstance, true, { guildId?: BigString }>;
lobby: TransformerInformation<DiscordLobby, Lobby, true>;
lobbyMember: TransformerInformation<DiscordLobbyMember, LobbyMember, true>;
lobbyMessage: TransformerInformation<DiscordLobbyMessage, LobbyMessage, true>;
lobbyInvite: TransformerInformation<DiscordLobbyInvite, LobbyInvite, true>;
mediaGalleryItem: TransformerInformation<DiscordMediaGalleryItem, MediaGalleryItem, true>;
member: TransformerInformation<DiscordMember, Member, true, { guildId?: BigString; userId?: BigString }>;
message: TransformerInformation<DiscordMessage, Message, true, { shardId?: number }>;
messageCall: TransformerInformation<DiscordMessageCall, MessageCall, true>;
messageInteractionMetadata: TransformerInformation<DiscordMessageInteractionMetadata, MessageInteractionMetadata, true>;
messagePin: TransformerInformation<DiscordMessagePin, MessagePin, true, { shardId?: number }>;
messageSnapshot: TransformerInformation<DiscordMessageSnapshot, MessageSnapshot, true, { shardId?: number }>;
nameplate: TransformerInformation<DiscordNameplate, Nameplate, true>;
poll: TransformerInformation<DiscordPoll, Poll, true>;
pollMedia: TransformerInformation<DiscordPollMedia, PollMedia, true>;
presence: TransformerInformation<DiscordPresenceUpdate, PresenceUpdate, true>;
role: TransformerInformation<DiscordRole, Role, true, { guildId?: BigString }>;
roleColors: TransformerInformation<DiscordRoleColors, RoleColors, true>;
scheduledEvent: TransformerInformation<DiscordScheduledEvent, ScheduledEvent, true>;
scheduledEventRecurrenceRule: TransformerInformation<DiscordScheduledEventRecurrenceRule, ScheduledEventRecurrenceRule, true>;
sharedClientTheme: TransformerInformation<DiscordSharedClientTheme, SharedClientTheme, true>;
sku: TransformerInformation<DiscordSku, Sku, true>;
soundboardSound: TransformerInformation<DiscordSoundboardSound, SoundboardSound, true>;
stageInstance: TransformerInformation<DiscordStageInstance, StageInstance, true>;
sticker: TransformerInformation<DiscordSticker, Sticker, true>;
stickerPack: TransformerInformation<DiscordStickerPack, StickerPack, false>;
subscription: TransformerInformation<DiscordSubscription, Subscription, true>;
team: TransformerInformation<DiscordTeam, Team, false>;
template: TransformerInformation<DiscordTemplate, Template, false>;
threadMember: TransformerInformation<DiscordThreadMember, ThreadMember, false, ThreadMemberTransformerExtra>;
threadMemberGuildCreate: TransformerInformation<DiscordThreadMemberGuildCreate, ThreadMemberGuildCreate, false>;
unfurledMediaItem: TransformerInformation<DiscordUnfurledMediaItem, UnfurledMediaItem, true>;
user: TransformerInformation<DiscordUser, User, true>;
userPrimaryGuild: TransformerInformation<DiscordUserPrimaryGuild, UserPrimaryGuild, true>;
voiceRegion: TransformerInformation<DiscordVoiceRegion, VoiceRegion, false>;
voiceState: TransformerInformation<DiscordVoiceState, VoiceState, true, { guildId?: BigString }>;
webhook: TransformerInformation<DiscordWebhook, Webhook, true>;
welcomeScreen: TransformerInformation<DiscordWelcomeScreen, WelcomeScreen, false>;
widget: TransformerInformation<DiscordGuildWidget, GuildWidget, false>;
widgetSettings: TransformerInformation<DiscordGuildWidgetSettings, GuildWidgetSettings, false>;
};
export type Transformers<TProps extends TransformersDesiredProperties, TBehavior extends DesiredPropertiesBehavior> = TransformerFunctions<
@@ -553,34 +545,159 @@ export function createTransformers<TProps extends TransformersDesiredProperties,
} satisfies Transformers<TransformersDesiredProperties, DesiredPropertiesBehavior.RemoveKey> as unknown as Transformers<TProps, TBehavior>;
}
/**
* Information about a transformer including its payload and transformed types.
*
* @template TPayload - The type of the payload received from Discord.
* @template TTransformed - The type of the transformed object returned by the transformer.
* @template TPartial - Indicates if the transformer supports partial payloads.
* @template TExtra - Additional extra information that might be needed for transformation.
*/
export type TransformerInformation<TPayload, TTransformed, TPartial extends boolean, TExtra = {}> = {
payload: TPayload;
transformed: TTransformed;
extra: TExtra;
partial: TPartial;
};
/**
* A function that transforms a payload from Discord into a desired format.
*
* @template TProps - The desired properties for the transformer.
* @template TBehavior - The behavior for handling desired properties.
* @template TPayload - The type of the payload received from Discord.
* @template TTransformed - The type of the transformed object returned by the transformer.
* @template TExtra - Additional extra information that might be needed for transformation.
* @template TPartial - Indicates if the transformer supports partial payloads.
*/
export type TransformerFunction<
TProps extends TransformersDesiredProperties,
TBehavior extends DesiredPropertiesBehavior,
TPayload,
TTransformed,
TExtra = {},
> = (bot: Bot<TProps, TBehavior>, payload: TPayload, extra?: TExtra) => SetupDesiredProps<TTransformed, TProps, TBehavior>;
TPartial extends boolean = false,
> = TPartial extends true
? // We use the method syntax for functions (...Params): ReturnType instead of the arrow syntax because it allows us to have overloads in the type
{
(bot: Bot<TProps, TBehavior>, payload: TPayload, extra?: TExtra & { partial?: false }): SetupDesiredProps<TTransformed, TProps, TBehavior>;
(
bot: Bot<TProps, TBehavior>,
payload: Partial<TPayload>,
extra: TExtra & { partial: true },
): Partial<SetupDesiredProps<TTransformed, TProps, TBehavior>>;
}
: // Even if we don't need to overload, since the 2 syntaxes have slightly different semantics, we use the method syntax in here as well
{ (bot: Bot<TProps, TBehavior>, payload: TPayload, extra?: TExtra): SetupDesiredProps<TTransformed, TProps, TBehavior> };
/**
* A collection of transformer functions for various Discord entities.
*
* @template TProps - The desired properties for the transformers.
* @template TBehavior - The behavior for handling desired properties.
*/
export type TransformerFunctions<TProps extends TransformersDesiredProperties, TBehavior extends DesiredPropertiesBehavior> = {
[K in keyof TransformerInformations]: TransformerFunction<
TProps,
TBehavior,
TransformerInformations[K]['payload'],
TransformerInformations[K]['transformed'],
TransformerInformations[K]['extra'],
TransformerInformations[K]['partial']
>;
};
/**
* A function that customizes the transformed object after the initial transformation.
*
* @template TProps - The desired properties for the transformer.
* @template TBehavior - The behavior for handling desired properties.
* @template TPayload - The type of the payload received from Discord.
* @template TTransformed - The type of the transformed object returned by the transformer.
* @template TExtra - Additional extra information that might be needed for customization.
* @template TPartial - Indicates if the transformer supports partial payloads.
*/
export type TransformerCustomizerFunction<
TProps extends TransformersDesiredProperties,
TBehavior extends DesiredPropertiesBehavior,
TPayload,
TTransformed,
TExtra = {},
> = (bot: Bot<TProps, TBehavior>, payload: TPayload, transformed: TTransformed, extra?: TExtra) => any;
TPartial extends boolean = false,
> = TPartial extends true
? // We use the method syntax for functions (...Params): ReturnType instead of the arrow syntax because it allows us to have overloads in the type
{
(
bot: Bot<TProps, TBehavior>,
payload: TPayload,
transformed: SetupDesiredProps<TTransformed, TProps, TBehavior>,
extra: TExtra & { partial: false },
): any;
(
bot: Bot<TProps, TBehavior>,
payload: Partial<TPayload>,
transformed: Partial<SetupDesiredProps<TTransformed, TProps, TBehavior>>,
extra: TExtra & { partial: true },
): any;
}
: // Even if we don't need to overload, since the 2 syntaxes have slightly different semantics, we use the method syntax in here as well
{
(bot: Bot<TProps, TBehavior>, payload: TPayload, transformed: SetupDesiredProps<TTransformed, TProps, TBehavior>, extra?: TExtra): any;
};
/**
* A collection of transformer customizer functions for various Discord entities.
*
* @template TProps - The desired properties for the transformers.
* @template TBehavior - The behavior for handling desired properties.
*/
export type TransformerCustomizers<TProps extends TransformersDesiredProperties, TBehavior extends DesiredPropertiesBehavior> = {
[K in keyof TransformerFunctions<TProps, TBehavior>]: TransformerFunctions<TProps, TBehavior>[K] extends TransformerFunction<
[K in keyof TransformerInformations]: TransformerCustomizerFunction<
TProps,
TBehavior,
infer TPayload,
infer TTransformed,
infer TExtra
>
? TransformerCustomizerFunction<TProps, TBehavior, TPayload, SetupDesiredProps<TTransformed, TProps, TBehavior>, BigStringsToBigints<TExtra>>
: 'ERROR: Invalid transformer found';
TransformerInformations[K]['payload'],
TransformerInformations[K]['transformed'],
BigStringsToBigints<TransformerInformations[K]['extra']>,
TransformerInformations[K]['partial']
>;
};
/**
* Converting BigString properties in T to bigint, leaving other properties unchanged.
*
* @template T - The object type to transform.
*/
export type BigStringsToBigints<T> = {
[K in keyof T]: BigString extends T[K] ? bigint : T[K];
};
/**
* Calls a transformer customizer function with the provided parameters.
*
* @template TInfo - The key of the transformer information to use.
* @param customizer - The key of the transformer information to use.
* @param bot - The bot instance.
* @param payload - The original payload from Discord.
* @param transformed - The transformed object.
* @param extra - Additional extra information for the customizer.
* @returns The result of the customizer function.
*
* @remarks
* This function is used because it is hard to deal with the customizer overloads directly in the transformers.
*
* Since the overloads are present only with partial transformers, this function requires the partial extra, for non-partial transformers the normal function call can be used
*/
export function callCustomizer<TInfo extends keyof TransformerInformations>(
customizer: TInfo,
bot: Bot,
payload: TransformerInformations[TInfo]['payload'] | Partial<TransformerInformations[TInfo]['payload']>,
transformed:
| SetupDesiredProps<TransformerInformations[TInfo]['transformed'], TransformersDesiredProperties, DesiredPropertiesBehavior>
| Partial<SetupDesiredProps<TransformerInformations[TInfo]['transformed'], TransformersDesiredProperties, DesiredPropertiesBehavior>>,
extra: TransformerInformations[TInfo]['extra'] & { partial: boolean },
): any {
// The type of the customizer is not generalizable, so we use unknown, callCustomizer has the correct type and we cast it here
const customizerFn = bot.transformers.customizers[customizer] as (bot: Bot, payload: unknown, transformed: unknown, extra: unknown) => any;
return customizerFn(bot, payload, transformed, extra);
}
+10 -5
View File
@@ -1,9 +1,10 @@
import type { DiscordActivity, DiscordActivityAssets, DiscordActivityInstance, DiscordActivityLocation } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { Activity, ActivityAssets, ActivityInstance, ActivityLocation } from './types.js';
export function transformActivity(bot: Bot, payload: DiscordActivity): Activity {
export function transformActivity(bot: Bot, payload: DiscordActivity) {
const activity = {
name: payload.name,
type: payload.type,
@@ -40,7 +41,7 @@ export function transformActivity(bot: Bot, payload: DiscordActivity): Activity
return bot.transformers.customizers.activity(bot, payload, activity);
}
export function transformActivityInstance(bot: Bot, payload: DiscordActivityInstance): ActivityInstance {
export function transformActivityInstance(bot: Bot, payload: Partial<DiscordActivityInstance>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.activityInstance;
const activityInstance = {} as SetupDesiredProps<ActivityInstance, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -50,10 +51,12 @@ export function transformActivityInstance(bot: Bot, payload: DiscordActivityInst
if (props.location && payload.location) activityInstance.location = bot.transformers.activityLocation(bot, payload.location);
if (props.users && payload.users) activityInstance.users = payload.users.map((x) => bot.transformers.snowflake(x));
return bot.transformers.customizers.activityInstance(bot, payload, activityInstance);
return callCustomizer('activityInstance', bot, payload, activityInstance, {
partial: extra?.partial ?? false,
});
}
export function transformActivityLocation(bot: Bot, payload: DiscordActivityLocation): ActivityLocation {
export function transformActivityLocation(bot: Bot, payload: Partial<DiscordActivityLocation>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.activityLocation;
const activityLocation = {} as SetupDesiredProps<ActivityLocation, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -62,7 +65,9 @@ export function transformActivityLocation(bot: Bot, payload: DiscordActivityLoca
if (props.channelId && payload.channel_id) activityLocation.channelId = bot.transformers.snowflake(payload.channel_id);
if (props.guildId && payload.guild_id) activityLocation.guildId = bot.transformers.snowflake(payload.guild_id);
return bot.transformers.customizers.activityLocation(bot, payload, activityLocation);
return callCustomizer('activityLocation', bot, payload, activityLocation, {
partial: extra?.partial ?? false,
});
}
export function transformActivityAssets(bot: Bot, payload: DiscordActivityAssets): ActivityAssets {
+11 -11
View File
@@ -1,10 +1,11 @@
import { type DiscordApplication, DiscordApplicationIntegrationType, type DiscordUser } from '@discordeno/types';
import { type DiscordApplication, DiscordApplicationIntegrationType } from '@discordeno/types';
import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import { callCustomizer } from '../transformers.js';
import { ToggleBitfield } from './toggles/ToggleBitfield.js';
import type { Application } from './types.js';
export function transformApplication(bot: Bot, payload: DiscordApplication, extra?: { shardId?: number }): Application {
export function transformApplication(bot: Bot, payload: Partial<DiscordApplication>, extra?: { shardId?: number; partial?: boolean }) {
const application = {
name: payload.name,
description: payload.description,
@@ -21,21 +22,17 @@ export function transformApplication(bot: Bot, payload: DiscordApplication, extr
// flags_new is a string with the bitfield inside
flagsNew: payload.flags_new ? new ToggleBitfield(Number(payload.flags_new)) : undefined,
id: bot.transformers.snowflake(payload.id),
id: payload.id ? bot.transformers.snowflake(payload.id) : undefined,
icon: payload.icon ? iconHashToBigInt(payload.icon) : undefined,
owner: payload.owner
? // @ts-expect-error the partial here wont break anything
bot.transformers.user(bot, payload.owner)
: undefined,
owner: payload.owner ? bot.transformers.user(bot, payload.owner, { partial: true }) : undefined,
team: payload.team ? bot.transformers.team(bot, payload.team) : undefined,
guildId: payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined,
customInstallUrl: payload.custom_install_url,
// @ts-expect-error the partial here wont break anything
guild: payload.guild ? bot.transformers.guild(bot, payload.guild, { shardId: extra?.shardId }) : undefined,
guild: payload.guild ? bot.transformers.guild(bot, payload.guild, { shardId: extra?.shardId, partial: true }) : undefined,
approximateGuildCount: payload.approximate_guild_count,
approximateUserInstallCount: payload.approximate_user_install_count,
approximateUserAuthorizationCount: payload.approximate_user_authorization_count,
bot: payload.bot ? bot.transformers.user(bot, payload.bot as DiscordUser) : undefined,
bot: payload.bot ? bot.transformers.user(bot, payload.bot, { partial: true }) : undefined,
interactionsEndpointUrl: payload.interactions_endpoint_url ? payload.interactions_endpoint_url : undefined,
redirectUris: payload.redirect_uris,
roleConnectionsVerificationUrl: payload.role_connections_verification_url,
@@ -71,5 +68,8 @@ export function transformApplication(bot: Bot, payload: DiscordApplication, extr
eventWebhooksTypes: payload.event_webhooks_types,
} as Application;
return bot.transformers.customizers.application(bot, payload, application, extra);
return callCustomizer('application', bot, payload, application, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
@@ -2,7 +2,7 @@ import type { DiscordApplicationCommand } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { ApplicationCommand } from './types.js';
export function transformApplicationCommand(bot: Bot, payload: DiscordApplicationCommand): ApplicationCommand {
export function transformApplicationCommand(bot: Bot, payload: DiscordApplicationCommand) {
const applicationCommand = {
id: bot.transformers.snowflake(payload.id),
applicationId: bot.transformers.snowflake(payload.application_id),
@@ -2,7 +2,7 @@ import type { DiscordApplicationCommandOption } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { ApplicationCommandOption } from './types.js';
export function transformApplicationCommandOption(bot: Bot, payload: DiscordApplicationCommandOption): ApplicationCommandOption {
export function transformApplicationCommandOption(bot: Bot, payload: DiscordApplicationCommandOption) {
const applicationCommandOption = {
type: payload.type,
name: payload.name,
@@ -2,7 +2,7 @@ import type { DiscordApplicationCommandOptionChoice } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { ApplicationCommandOptionChoice } from './types.js';
export function transformApplicationCommandOptionChoice(bot: Bot, payload: DiscordApplicationCommandOptionChoice): ApplicationCommandOptionChoice {
export function transformApplicationCommandOptionChoice(bot: Bot, payload: DiscordApplicationCommandOptionChoice) {
const applicationCommandOptionChoice = {
name: payload.name,
nameLocalizations: payload.name_localizations ?? undefined,
@@ -2,10 +2,7 @@ import type { DiscordGuildApplicationCommandPermissions } from '@discordeno/type
import type { Bot } from '../bot.js';
import type { GuildApplicationCommandPermissions } from './types.js';
export function transformApplicationCommandPermission(
bot: Bot,
payload: DiscordGuildApplicationCommandPermissions,
): GuildApplicationCommandPermissions {
export function transformApplicationCommandPermission(bot: Bot, payload: DiscordGuildApplicationCommandPermissions) {
const applicationCommandPermission = {
id: bot.transformers.snowflake(payload.id),
applicationId: bot.transformers.snowflake(payload.application_id),
+6 -3
View File
@@ -1,9 +1,10 @@
import type { DiscordAttachment } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { Attachment } from './types.js';
export function transformAttachment(bot: Bot, payload: DiscordAttachment): typeof bot.transformers.$inferredTypes.attachment {
export function transformAttachment(bot: Bot, payload: Partial<DiscordAttachment>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.attachment;
const attachment = {} as SetupDesiredProps<Attachment, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -11,7 +12,7 @@ export function transformAttachment(bot: Bot, payload: DiscordAttachment): typeo
if (props.filename && payload.filename) attachment.filename = payload.filename;
if (props.title && payload.title) attachment.title = payload.title;
if (props.contentType && payload.content_type) attachment.contentType = payload.content_type;
if (props.size) attachment.size = payload.size;
if (props.size && payload.size !== undefined) attachment.size = payload.size;
if (props.url && payload.url) attachment.url = payload.url;
if (props.proxyUrl && payload.proxy_url) attachment.proxyUrl = payload.proxy_url;
if (props.height && payload.height) attachment.height = payload.height;
@@ -28,5 +29,7 @@ export function transformAttachment(bot: Bot, payload: DiscordAttachment): typeo
if (props.clipCreatedAt && payload.clip_created_at) attachment.clipCreatedAt = payload.clip_created_at;
if (props.application && payload.application) attachment.application = bot.transformers.application(bot, payload.application);
return bot.transformers.customizers.attachment(bot, payload, attachment);
return callCustomizer('attachment', bot, payload, attachment, {
partial: extra?.partial ?? false,
});
}
@@ -3,7 +3,7 @@ import { camelize } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { AuditLogEntry } from './types.js';
export function transformAuditLogEntry(bot: Bot, payload: DiscordAuditLogEntry): AuditLogEntry {
export function transformAuditLogEntry(bot: Bot, payload: DiscordAuditLogEntry) {
const auditLogEntry = {
id: bot.transformers.snowflake(payload.id),
changes: camelize(payload.changes),
@@ -2,7 +2,7 @@ import type { DiscordAutoModerationActionExecution } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { AutoModerationActionExecution } from './types.js';
export function transformAutoModerationActionExecution(bot: Bot, payload: DiscordAutoModerationActionExecution): AutoModerationActionExecution {
export function transformAutoModerationActionExecution(bot: Bot, payload: DiscordAutoModerationActionExecution) {
const rule = {
content: payload.content,
ruleTriggerType: payload.rule_trigger_type,
+1 -1
View File
@@ -2,7 +2,7 @@ import type { DiscordAutoModerationRule } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { AutoModerationRule } from './types.js';
export function transformAutoModerationRule(bot: Bot, payload: DiscordAutoModerationRule): AutoModerationRule {
export function transformAutoModerationRule(bot: Bot, payload: DiscordAutoModerationRule) {
const rule = {
name: payload.name,
eventType: payload.event_type,
@@ -1,14 +1,17 @@
import type { DiscordAvatarDecorationData } from '@discordeno/types';
import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import { callCustomizer } from '../transformers.js';
import type { AvatarDecorationData } from './types.js';
export function transformAvatarDecorationData(bot: Bot, payload: DiscordAvatarDecorationData): AvatarDecorationData {
export function transformAvatarDecorationData(bot: Bot, payload: Partial<DiscordAvatarDecorationData>, extra?: { partial?: boolean }) {
const data = {} as AvatarDecorationData;
const props = bot.transformers.desiredProperties.avatarDecorationData;
if (props.asset && payload.asset) data.asset = iconHashToBigInt(payload.asset);
if (props.skuId && payload.sku_id) data.skuId = bot.transformers.snowflake(payload.sku_id);
return data;
return callCustomizer('avatarDecorationData', bot, payload, data, {
partial: extra?.partial ?? false,
});
}
+9 -5
View File
@@ -2,6 +2,7 @@ import type { BigString, DiscordChannel, DiscordForumTag } from '@discordeno/typ
import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { ChannelToggles } from './toggles/channel.js';
import { Permissions } from './toggles/Permissions.js';
import type { Channel, ForumTag } from './types.js';
@@ -70,7 +71,7 @@ export const baseChannel: Channel = {
},
};
export function transformChannel(bot: Bot, payload: DiscordChannel, extra?: { guildId?: BigString }) {
export function transformChannel(bot: Bot, payload: Partial<DiscordChannel>, extra?: { guildId?: BigString; partial?: boolean }) {
const channel = Object.create(baseChannel) as SetupDesiredProps<Channel, TransformersDesiredProperties, DesiredPropertiesBehavior>;
const props = bot.transformers.desiredProperties.channel;
channel.toggles = new ChannelToggles(payload);
@@ -78,7 +79,7 @@ export function transformChannel(bot: Bot, payload: DiscordChannel, extra?: { gu
if (props.id && payload.id) channel.id = bot.transformers.snowflake(payload.id);
if (props.guildId && (extra?.guildId ?? payload.guild_id))
channel.guildId = extra?.guildId ? bot.transformers.snowflake(extra.guildId) : bot.transformers.snowflake(payload.guild_id!);
if (props.type) channel.type = payload.type;
if (props.type && payload.type !== undefined) channel.type = payload.type;
if (props.position) channel.position = payload.position;
if (props.name && payload.name) channel.name = payload.name;
if (props.topic && payload.topic) channel.topic = payload.topic;
@@ -123,12 +124,13 @@ export function transformChannel(bot: Bot, payload: DiscordChannel, extra?: { gu
if (props.defaultSortOrder && payload.default_sort_order !== undefined) channel.defaultSortOrder = payload.default_sort_order;
if (props.defaultForumLayout && payload.default_forum_layout !== undefined) channel.defaultForumLayout = payload.default_forum_layout;
return bot.transformers.customizers.channel(bot, payload, channel, {
return callCustomizer('channel', bot, payload, channel, {
guildId: extra?.guildId ? bot.transformers.snowflake(extra.guildId) : undefined,
partial: extra?.partial ?? false,
});
}
export function transformForumTag(bot: Bot, payload: DiscordForumTag): ForumTag {
export function transformForumTag(bot: Bot, payload: Partial<DiscordForumTag>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.forumTag;
const forumTag = {} as SetupDesiredProps<ForumTag, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -138,5 +140,7 @@ export function transformForumTag(bot: Bot, payload: DiscordForumTag): ForumTag
if (props.emojiId && payload.emoji_id) forumTag.emojiId = bot.transformers.snowflake(payload.emoji_id);
if (props.emojiName && payload.emoji_name) forumTag.emojiName = payload.emoji_name;
return bot.transformers.customizers.forumTag(bot, payload, forumTag);
return callCustomizer('forumTag', bot, payload, forumTag, {
partial: extra?.partial ?? false,
});
}
+112 -75
View File
@@ -39,12 +39,22 @@ import {
} from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { ToggleBitfield } from './toggles/ToggleBitfield.js';
import type { Component, MediaGalleryItem, UnfurledMediaItem } from './types.js';
export function transformComponent(bot: Bot, payload: DiscordMessageComponent | DiscordMessageComponentFromModalInteractionResponse): Component {
export function transformComponent(
bot: Bot,
payload: Partial<DiscordMessageComponent | DiscordMessageComponentFromModalInteractionResponse>,
extra?: { partial?: boolean },
) {
let component: SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
// There's nothing to do with a component without a type, as it means an issue on Discord, so we just throw an error
if (!payload.type) {
throw new Error(`[Discordeno] Received a component payload without a type in the component transformer.`);
}
// This switch is exhaustive, so we dont need the default case and TS does not error out for the un-initialized component variable
switch (payload.type) {
case MessageComponentTypes.ActionRow:
@@ -109,10 +119,12 @@ export function transformComponent(bot: Bot, payload: DiscordMessageComponent |
break;
}
return bot.transformers.customizers.component(bot, payload, component);
return callCustomizer('component', bot, payload, component, {
partial: extra?.partial ?? false,
});
}
export function transformUnfurledMediaItem(bot: Bot, payload: DiscordUnfurledMediaItem): UnfurledMediaItem {
export function transformUnfurledMediaItem(bot: Bot, payload: Partial<DiscordUnfurledMediaItem>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.unfurledMediaItem;
const mediaItem = {} as SetupDesiredProps<UnfurledMediaItem, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -126,10 +138,12 @@ export function transformUnfurledMediaItem(bot: Bot, payload: DiscordUnfurledMed
if (props.flags && payload.flags) mediaItem.flags = new ToggleBitfield(payload.flags);
if (props.attachmentId && payload.attachment_id) mediaItem.attachmentId = bot.transformers.snowflake(payload.attachment_id);
return bot.transformers.customizers.unfurledMediaItem(bot, payload, mediaItem);
return callCustomizer('unfurledMediaItem', bot, payload, mediaItem, {
partial: extra?.partial ?? false,
});
}
export function transformMediaGalleryItem(bot: Bot, payload: DiscordMediaGalleryItem): MediaGalleryItem {
export function transformMediaGalleryItem(bot: Bot, payload: Partial<DiscordMediaGalleryItem>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.mediaGalleryItem;
const galleryItem = {} as SetupDesiredProps<MediaGalleryItem, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -137,10 +151,12 @@ export function transformMediaGalleryItem(bot: Bot, payload: DiscordMediaGallery
if (props.description && payload.description) galleryItem.description = payload.description;
if (props.spoiler && payload.spoiler) galleryItem.spoiler = payload.spoiler;
return bot.transformers.customizers.mediaGalleryItem(bot, payload, galleryItem);
return callCustomizer('mediaGalleryItem', bot, payload, galleryItem, {
partial: extra?.partial ?? false,
});
}
function transformActionRow(bot: Bot, payload: DiscordActionRow) {
function transformActionRow(bot: Bot, payload: Partial<DiscordActionRow>) {
const props = bot.transformers.desiredProperties.component;
const actionRow = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -152,7 +168,7 @@ function transformActionRow(bot: Bot, payload: DiscordActionRow) {
return actionRow;
}
function transformContainerComponent(bot: Bot, payload: DiscordContainerComponent) {
function transformContainerComponent(bot: Bot, payload: Partial<DiscordContainerComponent>) {
const props = bot.transformers.desiredProperties.component;
const container = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -166,7 +182,7 @@ function transformContainerComponent(bot: Bot, payload: DiscordContainerComponen
return container;
}
function transformButtonComponent(bot: Bot, payload: DiscordButtonComponent) {
function transformButtonComponent(bot: Bot, payload: Partial<DiscordButtonComponent>) {
const props = bot.transformers.desiredProperties.component;
const button = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -175,8 +191,7 @@ function transformButtonComponent(bot: Bot, payload: DiscordButtonComponent) {
if (props.label && payload.label) button.label = payload.label;
if (props.customId && payload.custom_id) button.customId = payload.custom_id;
if (props.style && payload.style) button.style = payload.style;
// @ts-expect-error TODO: Deal with partials
if (props.emoji && payload.emoji) button.emoji = bot.transformers.emoji(bot, payload.emoji);
if (props.emoji && payload.emoji) button.emoji = bot.transformers.emoji(bot, payload.emoji, { partial: true });
if (props.url && payload.url) button.url = payload.url;
if (props.disabled && payload.disabled) button.disabled = payload.disabled;
if (props.skuId && payload.sku_id) button.skuId = bot.transformers.snowflake(payload.sku_id);
@@ -184,7 +199,7 @@ function transformButtonComponent(bot: Bot, payload: DiscordButtonComponent) {
return button;
}
function transformInputTextComponent(bot: Bot, payload: DiscordTextInputComponent | DiscordTextInputInteractionResponse) {
function transformInputTextComponent(bot: Bot, payload: Partial<DiscordTextInputComponent | DiscordTextInputInteractionResponse>) {
const props = bot.transformers.desiredProperties.component;
const input = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -193,7 +208,7 @@ function transformInputTextComponent(bot: Bot, payload: DiscordTextInputComponen
if (props.value && payload.value) input.value = payload.value;
if (props.customId && payload.custom_id) input.customId = payload.custom_id;
// Check if it is the component or the response
// We assume that if we find 'values' it is the interaction response
if ('style' in payload) {
if (props.style && payload.style) input.style = payload.style;
if (props.required && payload.required) input.required = payload.required;
@@ -206,7 +221,10 @@ function transformInputTextComponent(bot: Bot, payload: DiscordTextInputComponen
return input;
}
function transformStringSelectMenuComponent(bot: Bot, payload: DiscordStringSelectComponent | DiscordStringSelectInteractionResponseFromModal) {
function transformStringSelectMenuComponent(
bot: Bot,
payload: Partial<DiscordStringSelectComponent | DiscordStringSelectInteractionResponseFromModal>,
) {
const props = bot.transformers.desiredProperties.component;
const select = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -214,15 +232,17 @@ function transformStringSelectMenuComponent(bot: Bot, payload: DiscordStringSele
if (props.id && payload.id) select.id = payload.id;
if (props.customId && payload.custom_id) select.customId = payload.custom_id;
// Check if this is the string select response
// We assume that if we find 'values' it is the interaction response
if ('values' in payload) {
if (props.values && payload.values) select.values = payload.values;
} else {
if (props.placeholder && payload.placeholder) select.placeholder = payload.placeholder;
if (props.minValues && payload.min_values) select.minValues = payload.min_values;
if (props.maxValues && payload.max_values) select.maxValues = payload.max_values;
if (props.options && payload.options)
select.options = payload.options.map((option) => ({
const _payload = payload as Partial<DiscordStringSelectComponent>;
if (props.placeholder && _payload.placeholder) select.placeholder = _payload.placeholder;
if (props.minValues && _payload.min_values) select.minValues = _payload.min_values;
if (props.maxValues && _payload.max_values) select.maxValues = _payload.max_values;
if (props.options && _payload.options)
select.options = _payload.options.map((option) => ({
label: option.label,
value: option.value,
description: option.description,
@@ -235,13 +255,13 @@ function transformStringSelectMenuComponent(bot: Bot, payload: DiscordStringSele
: undefined,
default: option.default,
}));
if (props.disabled && payload.disabled) select.disabled = payload.disabled;
if (props.disabled && _payload.disabled) select.disabled = _payload.disabled;
}
return select;
}
function transformUserSelectMenuComponent(bot: Bot, payload: DiscordUserSelectComponent | DiscordUserSelectInteractionResponseFromModal) {
function transformUserSelectMenuComponent(bot: Bot, payload: Partial<DiscordUserSelectComponent | DiscordUserSelectInteractionResponseFromModal>) {
const props = bot.transformers.desiredProperties.component;
const select = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -249,26 +269,28 @@ function transformUserSelectMenuComponent(bot: Bot, payload: DiscordUserSelectCo
if (props.id && payload.id) select.id = payload.id;
if (props.customId && payload.custom_id) select.customId = payload.custom_id;
// Check if this is the user select response
// We assume that if we find 'values' it is the interaction response
if ('values' in payload) {
if (props.values && payload.values) select.values = payload.values;
if (props.resolved && payload.resolved) select.resolved = bot.transformers.interactionDataResolved(bot, payload.resolved);
} else {
if (props.placeholder && payload.placeholder) select.placeholder = payload.placeholder;
if (props.minValues && payload.min_values) select.minValues = payload.min_values;
if (props.maxValues && payload.max_values) select.maxValues = payload.max_values;
if (props.defaultValues && payload.default_values)
select.defaultValues = payload.default_values.map((defaultValue) => ({
const _payload = payload as Partial<DiscordUserSelectComponent>;
if (props.placeholder && _payload.placeholder) select.placeholder = _payload.placeholder;
if (props.minValues && _payload.min_values) select.minValues = _payload.min_values;
if (props.maxValues && _payload.max_values) select.maxValues = _payload.max_values;
if (props.defaultValues && _payload.default_values)
select.defaultValues = _payload.default_values.map((defaultValue) => ({
id: bot.transformers.snowflake(defaultValue.id),
type: defaultValue.type,
}));
if (props.disabled && payload.disabled) select.disabled = payload.disabled;
if (props.disabled && _payload.disabled) select.disabled = _payload.disabled;
}
return select;
}
function transformRoleSelectMenuComponent(bot: Bot, payload: DiscordRoleSelectComponent | DiscordRoleSelectInteractionResponseFromModal) {
function transformRoleSelectMenuComponent(bot: Bot, payload: Partial<DiscordRoleSelectComponent | DiscordRoleSelectInteractionResponseFromModal>) {
const props = bot.transformers.desiredProperties.component;
const select = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -276,20 +298,22 @@ function transformRoleSelectMenuComponent(bot: Bot, payload: DiscordRoleSelectCo
if (props.id && payload.id) select.id = payload.id;
if (props.customId && payload.custom_id) select.customId = payload.custom_id;
// Check if this is the role select response
// We assume that if we find 'values' it is the interaction response
if ('values' in payload) {
if (props.values && payload.values) select.values = payload.values;
if (props.resolved && payload.resolved) select.resolved = bot.transformers.interactionDataResolved(bot, payload.resolved);
} else {
if (props.placeholder && payload.placeholder) select.placeholder = payload.placeholder;
if (props.minValues && payload.min_values) select.minValues = payload.min_values;
if (props.maxValues && payload.max_values) select.maxValues = payload.max_values;
if (props.defaultValues && payload.default_values)
select.defaultValues = payload.default_values.map((defaultValue) => ({
const _payload = payload as Partial<DiscordRoleSelectComponent>;
if (props.placeholder && _payload.placeholder) select.placeholder = _payload.placeholder;
if (props.minValues && _payload.min_values) select.minValues = _payload.min_values;
if (props.maxValues && _payload.max_values) select.maxValues = _payload.max_values;
if (props.defaultValues && _payload.default_values)
select.defaultValues = _payload.default_values.map((defaultValue) => ({
id: bot.transformers.snowflake(defaultValue.id),
type: defaultValue.type,
}));
if (props.disabled && payload.disabled) select.disabled = payload.disabled;
if (props.disabled && _payload.disabled) select.disabled = _payload.disabled;
}
return select;
@@ -297,7 +321,7 @@ function transformRoleSelectMenuComponent(bot: Bot, payload: DiscordRoleSelectCo
function transformMentionableSelectMenuComponent(
bot: Bot,
payload: DiscordMentionableSelectComponent | DiscordMentionableSelectInteractionResponseFromModal,
payload: Partial<DiscordMentionableSelectComponent | DiscordMentionableSelectInteractionResponseFromModal>,
) {
const props = bot.transformers.desiredProperties.component;
const select = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -306,26 +330,31 @@ function transformMentionableSelectMenuComponent(
if (props.id && payload.id) select.id = payload.id;
if (props.customId && payload.custom_id) select.customId = payload.custom_id;
// Check if this is the mentionable select response
// We assume that if we find 'values' it is the interaction response
if ('values' in payload) {
if (props.values && payload.values) select.values = payload.values;
if (props.resolved && payload.resolved) select.resolved = bot.transformers.interactionDataResolved(bot, payload.resolved);
} else {
if (props.placeholder && payload.placeholder) select.placeholder = payload.placeholder;
if (props.minValues && payload.min_values) select.minValues = payload.min_values;
if (props.maxValues && payload.max_values) select.maxValues = payload.max_values;
if (props.defaultValues && payload.default_values)
select.defaultValues = payload.default_values.map((defaultValue) => ({
const _payload = payload as Partial<DiscordMentionableSelectComponent>;
if (props.placeholder && _payload.placeholder) select.placeholder = _payload.placeholder;
if (props.minValues && _payload.min_values) select.minValues = _payload.min_values;
if (props.maxValues && _payload.max_values) select.maxValues = _payload.max_values;
if (props.defaultValues && _payload.default_values)
select.defaultValues = _payload.default_values.map((defaultValue) => ({
id: bot.transformers.snowflake(defaultValue.id),
type: defaultValue.type,
}));
if (props.disabled && payload.disabled) select.disabled = payload.disabled;
if (props.disabled && _payload.disabled) select.disabled = _payload.disabled;
}
return select;
}
function transformChannelSelectMenuComponent(bot: Bot, payload: DiscordChannelSelectComponent | DiscordChannelSelectInteractionResponseFromModal) {
function transformChannelSelectMenuComponent(
bot: Bot,
payload: Partial<DiscordChannelSelectComponent | DiscordChannelSelectInteractionResponseFromModal>,
) {
const props = bot.transformers.desiredProperties.component;
const select = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -333,27 +362,29 @@ function transformChannelSelectMenuComponent(bot: Bot, payload: DiscordChannelSe
if (props.id && payload.id) select.id = payload.id;
if (props.customId && payload.custom_id) select.customId = payload.custom_id;
// Check if this is the channel select response
// We assume that if we find 'values' it is the interaction response
if ('values' in payload) {
if (props.values && payload.values) select.values = payload.values;
if (props.resolved && payload.resolved) select.resolved = bot.transformers.interactionDataResolved(bot, payload.resolved);
} else {
if (props.placeholder && payload.placeholder) select.placeholder = payload.placeholder;
if (props.minValues && payload.min_values) select.minValues = payload.min_values;
if (props.maxValues && payload.max_values) select.maxValues = payload.max_values;
if (props.defaultValues && payload.default_values)
select.defaultValues = payload.default_values.map((defaultValue) => ({
const _payload = payload as Partial<DiscordChannelSelectComponent>;
if (props.placeholder && _payload.placeholder) select.placeholder = _payload.placeholder;
if (props.minValues && _payload.min_values) select.minValues = _payload.min_values;
if (props.maxValues && _payload.max_values) select.maxValues = _payload.max_values;
if (props.defaultValues && _payload.default_values)
select.defaultValues = _payload.default_values.map((defaultValue) => ({
id: bot.transformers.snowflake(defaultValue.id),
type: defaultValue.type,
}));
if (props.channelTypes && payload.channel_types) select.channelTypes = payload.channel_types;
if (props.disabled && payload.disabled) select.disabled = payload.disabled;
if (props.channelTypes && _payload.channel_types) select.channelTypes = _payload.channel_types;
if (props.disabled && _payload.disabled) select.disabled = _payload.disabled;
}
return select;
}
function transformSectionComponent(bot: Bot, payload: DiscordSectionComponent) {
function transformSectionComponent(bot: Bot, payload: Partial<DiscordSectionComponent>) {
const props = bot.transformers.desiredProperties.component;
const section = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -365,7 +396,7 @@ function transformSectionComponent(bot: Bot, payload: DiscordSectionComponent) {
return section;
}
function transformThumbnailComponent(bot: Bot, payload: DiscordThumbnailComponent) {
function transformThumbnailComponent(bot: Bot, payload: Partial<DiscordThumbnailComponent>) {
const props = bot.transformers.desiredProperties.component;
const thumbnail = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -378,7 +409,7 @@ function transformThumbnailComponent(bot: Bot, payload: DiscordThumbnailComponen
return thumbnail;
}
function transformMediaGalleryComponent(bot: Bot, payload: DiscordMediaGalleryComponent) {
function transformMediaGalleryComponent(bot: Bot, payload: Partial<DiscordMediaGalleryComponent>) {
const props = bot.transformers.desiredProperties.component;
const mediaGallery = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -389,7 +420,7 @@ function transformMediaGalleryComponent(bot: Bot, payload: DiscordMediaGalleryCo
return mediaGallery;
}
function transformFileComponent(bot: Bot, payload: DiscordFileComponent) {
function transformFileComponent(bot: Bot, payload: Partial<DiscordFileComponent>) {
const props = bot.transformers.desiredProperties.component;
const file = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -403,13 +434,13 @@ function transformFileComponent(bot: Bot, payload: DiscordFileComponent) {
return file;
}
function transformTextDisplayComponent(bot: Bot, payload: DiscordTextDisplayComponent | DiscordTextDisplayInteractionResponse) {
function transformTextDisplayComponent(bot: Bot, payload: Partial<DiscordTextDisplayComponent | DiscordTextDisplayInteractionResponse>) {
const props = bot.transformers.desiredProperties.component;
const textDisplay = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.type && payload.type) textDisplay.type = payload.type;
if (props.id && payload.id) textDisplay.id = payload.id;
// That that this isn't a response
// We assume that if we find 'content' it is the component
if ('content' in payload) {
if (props.content && payload.content) textDisplay.content = payload.content;
}
@@ -417,7 +448,7 @@ function transformTextDisplayComponent(bot: Bot, payload: DiscordTextDisplayComp
return textDisplay;
}
function transformSeparatorComponent(bot: Bot, payload: DiscordSeparatorComponent) {
function transformSeparatorComponent(bot: Bot, payload: Partial<DiscordSeparatorComponent>) {
const props = bot.transformers.desiredProperties.component;
const separator = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -429,13 +460,13 @@ function transformSeparatorComponent(bot: Bot, payload: DiscordSeparatorComponen
return separator;
}
function transformLabelComponent(bot: Bot, payload: DiscordLabelComponent | DiscordLabelInteractionResponse) {
function transformLabelComponent(bot: Bot, payload: Partial<DiscordLabelComponent | DiscordLabelInteractionResponse>) {
const props = bot.transformers.desiredProperties.component;
const label = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.type && payload.type) label.type = payload.type;
if (props.id && payload.id) label.id = payload.id;
// Check that this isn't a response
// We assume that if we find 'label' it is the component
if ('label' in payload) {
if (props.label && payload.label) label.label = payload.label;
if (props.description && payload.description) label.description = payload.description;
@@ -445,7 +476,7 @@ function transformLabelComponent(bot: Bot, payload: DiscordLabelComponent | Disc
return label;
}
function transformFileUploadComponent(bot: Bot, payload: DiscordFileUploadComponent | DiscordFileUploadInteractionResponse) {
function transformFileUploadComponent(bot: Bot, payload: Partial<DiscordFileUploadComponent | DiscordFileUploadInteractionResponse>) {
const props = bot.transformers.desiredProperties.component;
const fileUpload = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -453,19 +484,21 @@ function transformFileUploadComponent(bot: Bot, payload: DiscordFileUploadCompon
if (props.id && payload.id) fileUpload.id = payload.id;
if (props.customId && payload.custom_id) fileUpload.customId = payload.custom_id;
// Check that this is a response
// We assume that if we find 'values' it is the interaction response
if ('values' in payload) {
if (props.values && payload.values) fileUpload.values = payload.values;
} else {
if (props.minValues && payload.min_values) fileUpload.minValues = payload.min_values;
if (props.maxValues && payload.max_values) fileUpload.maxValues = payload.max_values;
if (props.required && payload.required) fileUpload.required = payload.required;
const _payload = payload as Partial<DiscordFileUploadComponent>;
if (props.minValues && _payload.min_values) fileUpload.minValues = _payload.min_values;
if (props.maxValues && _payload.max_values) fileUpload.maxValues = _payload.max_values;
if (props.required && _payload.required) fileUpload.required = _payload.required;
}
return fileUpload;
}
function transformRadioGroupComponent(bot: Bot, payload: DiscordRadioGroupComponent | DiscordRadioGroupInteractionResponse) {
function transformRadioGroupComponent(bot: Bot, payload: Partial<DiscordRadioGroupComponent | DiscordRadioGroupInteractionResponse>) {
const props = bot.transformers.desiredProperties.component;
const radioGroup = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -473,18 +506,20 @@ function transformRadioGroupComponent(bot: Bot, payload: DiscordRadioGroupCompon
if (props.id && payload.id) radioGroup.id = payload.id;
if (props.customId && payload.custom_id) radioGroup.customId = payload.custom_id;
// Check if this is the component (has options) or the interaction response (modal submit, has value)
// We assume that if we find 'options' it is the component
if ('options' in payload) {
if (props.options && payload.options) radioGroup.options = payload.options;
if (props.required && payload.required !== undefined) radioGroup.required = payload.required;
} else {
if (props.value) radioGroup.value = payload.value ?? undefined;
const _payload = payload as Partial<DiscordRadioGroupInteractionResponse>;
if (props.value) radioGroup.value = _payload.value ?? undefined;
}
return radioGroup;
}
function transformCheckboxGroupComponent(bot: Bot, payload: DiscordCheckboxGroupComponent | DiscordCheckboxGroupInteractionResponse) {
function transformCheckboxGroupComponent(bot: Bot, payload: Partial<DiscordCheckboxGroupComponent | DiscordCheckboxGroupInteractionResponse>) {
const props = bot.transformers.desiredProperties.component;
const checkboxGroup = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -492,20 +527,22 @@ function transformCheckboxGroupComponent(bot: Bot, payload: DiscordCheckboxGroup
if (props.id && payload.id) checkboxGroup.id = payload.id;
if (props.customId && payload.custom_id) checkboxGroup.customId = payload.custom_id;
// Check if this is the component (has options) or the interaction response (modal submit, has values)
// We assume that if we find 'options' it is the component
if ('options' in payload) {
if (props.options && payload.options) checkboxGroup.options = payload.options;
if (props.minValues && payload.min_values !== undefined) checkboxGroup.minValues = payload.min_values;
if (props.maxValues && payload.max_values !== undefined) checkboxGroup.maxValues = payload.max_values;
if (props.required && payload.required !== undefined) checkboxGroup.required = payload.required;
} else {
if (props.values && payload.values) checkboxGroup.values = payload.values;
const _payload = payload as Partial<DiscordCheckboxGroupInteractionResponse>;
if (props.values && _payload.values) checkboxGroup.values = _payload.values;
}
return checkboxGroup;
}
function transformCheckboxComponent(bot: Bot, payload: DiscordCheckboxComponent | DiscordCheckboxInteractionResponse) {
function transformCheckboxComponent(bot: Bot, payload: Partial<DiscordCheckboxComponent | DiscordCheckboxInteractionResponse>) {
const props = bot.transformers.desiredProperties.component;
const checkbox = {} as SetupDesiredProps<Component, TransformersDesiredProperties, DesiredPropertiesBehavior>;
+1 -1
View File
@@ -3,7 +3,7 @@ import type { Bot } from '../bot.js';
import { ToggleBitfield } from './toggles/ToggleBitfield.js';
import type { Embed } from './types.js';
export function transformEmbed(bot: Bot, payload: DiscordEmbed): Embed {
export function transformEmbed(bot: Bot, payload: DiscordEmbed) {
const embed = {
title: payload.title,
type: payload.type,
+9 -4
View File
@@ -1,6 +1,7 @@
import type { DiscordDefaultReactionEmoji, DiscordEmoji } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { EmojiToggles } from './toggles/emoji.js';
import type { DefaultReactionEmoji, Emoji } from './types.js';
@@ -22,7 +23,7 @@ export const baseEmoji: Emoji = {
},
};
export function transformEmoji(bot: Bot, payload: DiscordEmoji): Emoji {
export function transformEmoji(bot: Bot, payload: Partial<DiscordEmoji>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.emoji;
const emoji = Object.create(baseEmoji) as SetupDesiredProps<Emoji, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -33,15 +34,19 @@ export function transformEmoji(bot: Bot, payload: DiscordEmoji): Emoji {
emoji.toggles = new EmojiToggles(payload);
return bot.transformers.customizers.emoji(bot, payload, emoji);
return callCustomizer('emoji', bot, payload, emoji, {
partial: extra?.partial ?? false,
});
}
export function transformDefaultReactionEmoji(bot: Bot, payload: DiscordDefaultReactionEmoji): DefaultReactionEmoji {
export function transformDefaultReactionEmoji(bot: Bot, payload: Partial<DiscordDefaultReactionEmoji>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.defaultReactionEmoji;
const defaultReactionEmoji = {} as SetupDesiredProps<DefaultReactionEmoji, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.emojiId && payload.emoji_id) defaultReactionEmoji.emojiId = bot.transformers.snowflake(payload.emoji_id);
if (props.emojiName && payload.emoji_name) defaultReactionEmoji.emojiName = payload.emoji_name;
return bot.transformers.customizers.defaultReactionEmoji(bot, payload, defaultReactionEmoji);
return callCustomizer('defaultReactionEmoji', bot, payload, defaultReactionEmoji, {
partial: extra?.partial ?? false,
});
}
+5 -2
View File
@@ -1,9 +1,10 @@
import type { DiscordEntitlement } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { Entitlement } from './types.js';
export function transformEntitlement(bot: Bot, payload: DiscordEntitlement): Entitlement {
export function transformEntitlement(bot: Bot, payload: Partial<DiscordEntitlement>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.entitlement;
const entitlement = {} as SetupDesiredProps<Entitlement, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -18,5 +19,7 @@ export function transformEntitlement(bot: Bot, payload: DiscordEntitlement): Ent
if (props.endsAt && payload.ends_at) entitlement.endsAt = Date.parse(payload.ends_at);
if (props.consumed && payload.consumed !== undefined) entitlement.consumed = payload.consumed;
return bot.transformers.customizers.entitlement(bot, payload, entitlement);
return callCustomizer('entitlement', bot, payload, entitlement, {
partial: extra?.partial ?? false,
});
}
+1 -1
View File
@@ -2,7 +2,7 @@ import type { DiscordGetGatewayBot } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { GetGatewayBot } from './types.js';
export function transformGatewayBot(bot: Bot, payload: DiscordGetGatewayBot): GetGatewayBot {
export function transformGatewayBot(bot: Bot, payload: DiscordGetGatewayBot) {
const gatewayBot = {
url: payload.url,
shards: payload.shards,
+26 -26
View File
@@ -1,7 +1,8 @@
import { ChannelTypes, type DiscordGuild, type DiscordPresenceUpdate } from '@discordeno/types';
import { ChannelTypes, type DiscordGuild } from '@discordeno/types';
import { Collection, iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { GuildToggles } from './toggles/guild.js';
import type { Channel, Guild } from './types.js';
@@ -23,30 +24,32 @@ export const baseGuild: Guild = {
},
};
export function transformGuild(bot: Bot, payload: DiscordGuild, extra?: { shardId?: number }): Guild {
const guildId = bot.transformers.snowflake(payload.id);
export function transformGuild(bot: Bot, payload: Partial<DiscordGuild>, extra?: { shardId?: number; partial?: boolean }) {
const props = bot.transformers.desiredProperties.guild;
const guild: SetupDesiredProps<Guild, TransformersDesiredProperties, DesiredPropertiesBehavior> = Object.create(baseGuild);
const guildId = payload.id ? bot.transformers.snowflake(payload.id) : undefined;
if (props.afkTimeout && payload.afk_timeout) guild.afkTimeout = payload.afk_timeout;
if (props.approximateMemberCount && payload.approximate_member_count) guild.approximateMemberCount = payload.approximate_member_count;
if (props.approximatePresenceCount && payload.approximate_presence_count) guild.approximatePresenceCount = payload.approximate_presence_count;
if (props.defaultMessageNotifications) guild.defaultMessageNotifications = payload.default_message_notifications;
if (props.defaultMessageNotifications && payload.default_message_notifications !== undefined)
guild.defaultMessageNotifications = payload.default_message_notifications;
if (props.description && payload.description) guild.description = payload.description;
if (props.toggles) guild.toggles = new GuildToggles(payload);
if (props.explicitContentFilter) guild.explicitContentFilter = payload.explicit_content_filter;
if (props.explicitContentFilter && payload.explicit_content_filter !== undefined) guild.explicitContentFilter = payload.explicit_content_filter;
if (props.maxMembers && payload.max_members) guild.maxMembers = payload.max_members;
if (props.maxPresences && payload.max_presences) guild.maxPresences = payload.max_presences ?? undefined;
if (props.maxVideoChannelUsers && payload.max_video_channel_users) guild.maxVideoChannelUsers = payload.max_video_channel_users;
if (props.maxStageVideoChannelUsers && payload.max_stage_video_channel_users)
guild.maxStageVideoChannelUsers = payload.max_stage_video_channel_users;
if (props.mfaLevel) guild.mfaLevel = payload.mfa_level;
if (props.mfaLevel && payload.mfa_level !== undefined) guild.mfaLevel = payload.mfa_level;
if (props.name && payload.name) guild.name = payload.name;
if (props.nsfwLevel) guild.nsfwLevel = payload.nsfw_level;
if (props.nsfwLevel && payload.nsfw_level !== undefined) guild.nsfwLevel = payload.nsfw_level;
if (props.preferredLocale && payload.preferred_locale) guild.preferredLocale = payload.preferred_locale;
if (props.premiumSubscriptionCount && payload.premium_subscription_count !== undefined)
guild.premiumSubscriptionCount = payload.premium_subscription_count;
if (props.premiumTier) guild.premiumTier = payload.premium_tier;
if (props.premiumTier && payload.premium_tier !== undefined) guild.premiumTier = payload.premium_tier;
if (props.stageInstances && payload.stage_instances)
guild.stageInstances = payload.stage_instances.map((si) => ({
/** The id of this Stage instance */
@@ -62,54 +65,48 @@ export function transformGuild(bot: Bot, payload: DiscordGuild, extra?: { shardI
guild.channels = new Collection(
[...(payload.channels ?? []), ...(payload.threads ?? [])].map((channel) => {
const result = bot.transformers.channel(bot, channel, { guildId });
// TODO: We should check that id exists, or else the collection will have undefined as it's key (This is valid for all the collections below as well)
// @ts-expect-error: See TODO above
return [result.id, result];
return [bot.transformers.snowflake(channel.id), result];
}),
);
if (props.members && payload.members)
guild.members = new Collection(
payload.members.map((member) => {
const result = bot.transformers.member(bot, member, { guildId, userId: bot.transformers.snowflake(member.user!.id) });
// @ts-expect-error: See TODO above
return [result.id, result];
const userId = bot.transformers.snowflake(member.user.id);
const result = bot.transformers.member(bot, member, { guildId, userId });
return [userId, result];
}),
);
if (props.roles && payload.roles)
guild.roles = new Collection(
payload.roles.map((role) => {
const result = bot.transformers.role(bot, role, { guildId });
// @ts-expect-error: See TODO above
return [result.id, result];
return [bot.transformers.snowflake(role.id), result];
}),
);
if (props.emojis && payload.emojis)
guild.emojis = new Collection(
payload.emojis.map((emoji) => {
const result = bot.transformers.emoji(bot, emoji);
// @ts-expect-error: See TODO above
return [result.id!, result];
return [bot.transformers.snowflake(emoji.id!), result];
}),
);
if (props.voiceStates && payload.voice_states)
guild.voiceStates = new Collection(
payload.voice_states.map((voiceState) => {
const result = bot.transformers.voiceState(bot, voiceState, { guildId });
// @ts-expect-error: See TODO above
return [result.userId, result];
return [bot.transformers.snowflake(voiceState.user_id), result];
}),
);
if (props.stickers && payload.stickers)
guild.stickers = new Collection(
payload.stickers?.map((sticker) => {
const result = bot.transformers.sticker(bot, sticker);
// @ts-expect-error: See TODO above
return [result.id, result];
return [bot.transformers.snowflake(sticker.id), result];
}),
);
if (props.systemChannelFlags && payload.system_channel_flags) guild.systemChannelFlags = payload.system_channel_flags;
if (props.vanityUrlCode && payload.vanity_url_code) guild.vanityUrlCode = payload.vanity_url_code;
if (props.verificationLevel) guild.verificationLevel = payload.verification_level;
if (props.verificationLevel && payload.verification_level !== undefined) guild.verificationLevel = payload.verification_level;
if (props.welcomeScreen && payload.welcome_screen)
guild.welcomeScreen = {
description: payload.welcome_screen.description ?? undefined,
@@ -127,7 +124,7 @@ export function transformGuild(bot: Bot, payload: DiscordGuild, extra?: { shardI
if (props.icon && payload.icon) guild.icon = iconHashToBigInt(payload.icon);
if (props.banner && payload.banner) guild.banner = iconHashToBigInt(payload.banner);
if (props.splash && payload.splash) guild.splash = iconHashToBigInt(payload.splash);
if (props.id && payload.id) guild.id = guildId;
if (props.id && guildId) guild.id = guildId;
if (props.ownerId && payload.owner_id) guild.ownerId = bot.transformers.snowflake(payload.owner_id);
if (props.permissions && payload.permissions) guild.permissions = bot.transformers.snowflake(payload.permissions);
if (props.afkChannelId && payload.afk_channel_id) guild.afkChannelId = bot.transformers.snowflake(payload.afk_channel_id);
@@ -144,10 +141,13 @@ export function transformGuild(bot: Bot, payload: DiscordGuild, extra?: { shardI
if (props.unavailable && payload.unavailable) guild.unavailable = payload.unavailable;
if (props.iconHash && payload.icon_hash) guild.iconHash = iconHashToBigInt(payload.icon_hash);
if (props.presences && payload.presences)
guild.presences = payload.presences?.map((presence) => bot.transformers.presence(bot, presence as DiscordPresenceUpdate));
guild.presences = payload.presences?.map((presence) => bot.transformers.presence(bot, presence, { partial: true }));
if (props.safetyAlertsChannelId && payload.safety_alerts_channel_id)
guild.safetyAlertsChannelId = bot.transformers.snowflake(payload.safety_alerts_channel_id);
if (props.incidentsData && payload.incidents_data) guild.incidentsData = bot.transformers.incidentsData(bot, payload.incidents_data);
return bot.transformers.customizers.guild(bot, payload, guild, extra);
return callCustomizer('guild', bot, payload, guild, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
@@ -1,9 +1,10 @@
import type { DiscordIncidentsData } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { IncidentsData } from './types.js';
export function transformIncidentsData(bot: Bot, payload: DiscordIncidentsData): IncidentsData {
export function transformIncidentsData(bot: Bot, payload: Partial<DiscordIncidentsData>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.incidentsData;
const incidentsData = {} as SetupDesiredProps<IncidentsData, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -12,5 +13,7 @@ export function transformIncidentsData(bot: Bot, payload: DiscordIncidentsData):
if (props.dmSpamDetectedAt && payload.dm_spam_detected_at) incidentsData.dmSpamDetectedAt = Date.parse(payload.dm_spam_detected_at);
if (props.raidDetectedAt && payload.raid_detected_at) incidentsData.raidDetectedAt = Date.parse(payload.raid_detected_at);
return bot.transformers.customizers.incidentsData(bot, payload, incidentsData);
return callCustomizer('incidentsData', bot, payload, incidentsData, {
partial: extra?.partial ?? false,
});
}
+1 -1
View File
@@ -3,7 +3,7 @@ import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { Integration } from './types.js';
export function transformIntegration(bot: Bot, payload: DiscordIntegrationCreateUpdate): Integration {
export function transformIntegration(bot: Bot, payload: DiscordIntegrationCreateUpdate) {
const integration = {
guildId: bot.transformers.snowflake(payload.guild_id),
id: bot.transformers.snowflake(payload.id),
+42 -41
View File
@@ -14,13 +14,8 @@ import {
import { Collection } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { InteractionResolvedDataChannel } from '../commandOptionsParser.js';
import type {
CompleteDesiredProperties,
DesiredPropertiesBehavior,
SetupDesiredProps,
TransformersDesiredProperties,
TransformProperty,
} from '../desiredProperties.js';
import type { CompleteDesiredProperties, DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type {
Interaction,
InteractionCallback,
@@ -144,12 +139,11 @@ export const baseInteraction: SetupDesiredProps<Interaction, CompleteDesiredProp
},
};
export function transformInteraction(bot: Bot, payload: DiscordInteraction, extra?: { shardId?: number }): Interaction {
const guildId = payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined;
const interaction: SetupDesiredProps<Interaction, TransformersDesiredProperties, DesiredPropertiesBehavior> = Object.create(baseInteraction);
export function transformInteraction(bot: Bot, payload: Partial<DiscordInteraction>, extra?: { shardId?: number; partial?: boolean }) {
const props = bot.transformers.desiredProperties.interaction;
const interaction: SetupDesiredProps<Interaction, TransformersDesiredProperties, DesiredPropertiesBehavior> = Object.create(baseInteraction);
const guildId = payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined;
interaction.bot = bot;
interaction.acknowledged = false;
@@ -160,9 +154,7 @@ export function transformInteraction(bot: Bot, payload: DiscordInteraction, extr
if (props.version && payload.version) interaction.version = payload.version;
if (props.locale && payload.locale) interaction.locale = payload.locale;
if (props.guildLocale && payload.guild_locale) interaction.guildLocale = payload.guild_locale;
if (props.guild && payload.guild)
// @ts-expect-error payload.guild is a Partial<DiscordGuild>
interaction.guild = bot.transformers.guild(bot, payload.guild, { shardId: extra?.shardId });
if (props.guild && payload.guild) interaction.guild = bot.transformers.guild(bot, payload.guild, { shardId: extra?.shardId, partial: true });
if (props.guildId && guildId) interaction.guildId = guildId;
if (props.user) {
if (payload.member?.user) interaction.user = bot.transformers.user(bot, payload.member?.user);
@@ -170,9 +162,7 @@ export function transformInteraction(bot: Bot, payload: DiscordInteraction, extr
}
if (props.appPermissions && payload.app_permissions) interaction.appPermissions = bot.transformers.snowflake(payload.app_permissions);
if (props.message && payload.message) interaction.message = bot.transformers.message(bot, payload.message, { shardId: extra?.shardId });
if (props.channel && payload.channel)
// @ts-expect-error payload.channel is a Partial<>
interaction.channel = bot.transformers.channel(bot, payload.channel, { guildId });
if (props.channel && payload.channel) interaction.channel = bot.transformers.channel(bot, payload.channel, { guildId, partial: true });
if (props.channelId && payload.channel_id) interaction.channelId = bot.transformers.snowflake(payload.channel_id);
if (props.member && guildId && payload.member)
interaction.member = bot.transformers.member(bot, payload.member, {
@@ -211,11 +201,13 @@ export function transformInteraction(bot: Bot, payload: DiscordInteraction, extr
};
}
// Typescript has an hard time with interaction.bot, so we need to tell him for sure this interaction is the of the correct type
return bot.transformers.customizers.interaction(bot, payload, interaction as unknown as typeof bot.transformers.$inferredTypes.interaction, extra);
return callCustomizer('interaction', bot, payload, interaction, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
export function transformInteractionDataOption(bot: Bot, option: DiscordInteractionDataOption): InteractionDataOption {
export function transformInteractionDataOption(bot: Bot, option: DiscordInteractionDataOption) {
const opt = {
name: option.name,
type: option.type,
@@ -229,16 +221,15 @@ export function transformInteractionDataOption(bot: Bot, option: DiscordInteract
export function transformInteractionDataResolved(
bot: Bot,
payload: DiscordInteractionDataResolved,
extra?: { shardId?: number; guildId?: BigString },
): TransformProperty<InteractionDataResolved, TransformersDesiredProperties, DesiredPropertiesBehavior.RemoveKey> {
const transformed: TransformProperty<InteractionDataResolved, TransformersDesiredProperties, DesiredPropertiesBehavior.RemoveKey> = {};
payload: Partial<DiscordInteractionDataResolved>,
extra?: { shardId?: number; guildId?: BigString; partial?: boolean },
) {
const transformed: SetupDesiredProps<InteractionDataResolved, TransformersDesiredProperties, DesiredPropertiesBehavior.RemoveKey> = {};
if (payload.messages) {
transformed.messages = new Collection(
Object.entries(payload.messages).map(([key, value]) => {
// @ts-expect-error TODO: Deal with partials
const message = bot.transformers.message(bot, value, { shardId: extra?.shardId });
const message = bot.transformers.message(bot, value, { shardId: extra?.shardId, partial: true });
const id = bot.transformers.snowflake(key);
return [id, message];
@@ -260,10 +251,10 @@ export function transformInteractionDataResolved(
if (extra?.guildId && payload.members) {
transformed.members = new Collection(
Object.entries(payload.members).map(([key, value]) => {
// @ts-expect-error TODO: Deal with partials, value is missing 2 values but the transformer can handle it, despite what the types says
const member = bot.transformers.member(bot, value, {
guildId: extra.guildId,
userId: bot.transformers.snowflake(key),
partial: true,
});
const id = bot.transformers.snowflake(key);
@@ -286,13 +277,10 @@ export function transformInteractionDataResolved(
if (payload.channels) {
transformed.channels = new Collection(
Object.entries(payload.channels).map(([key, value]) => {
const channel = bot.transformers.channel(bot, value) as InteractionResolvedDataChannel<
TransformersDesiredProperties,
DesiredPropertiesBehavior.RemoveKey
>;
const channel = bot.transformers.channel(bot, value, { partial: true });
const id = bot.transformers.snowflake(key);
return [id, channel];
return [id, channel as InteractionResolvedDataChannel<TransformersDesiredProperties, DesiredPropertiesBehavior.RemoveKey>];
}),
);
}
@@ -308,17 +296,18 @@ export function transformInteractionDataResolved(
);
}
return bot.transformers.customizers.interactionDataResolved(bot, payload, transformed, {
return callCustomizer('interactionDataResolved', bot, payload, transformed, {
shardId: extra?.shardId,
guildId: extra?.guildId ? bot.transformers.snowflake(extra.guildId) : undefined,
partial: extra?.partial ?? false,
});
}
export function transformInteractionCallbackResponse(
bot: Bot,
payload: DiscordInteractionCallbackResponse,
extra?: { shardId?: number },
): InteractionCallbackResponse {
payload: Partial<DiscordInteractionCallbackResponse>,
extra?: { shardId?: number; partial?: boolean },
) {
const props = bot.transformers.desiredProperties.interactionCallbackResponse;
const response = {} as SetupDesiredProps<InteractionCallbackResponse, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -326,10 +315,13 @@ export function transformInteractionCallbackResponse(
if (props.resource && payload.resource)
response.resource = bot.transformers.interactionResource(bot, payload.resource, { shardId: extra?.shardId });
return bot.transformers.customizers.interactionCallbackResponse(bot, payload, response, extra);
return callCustomizer('interactionCallbackResponse', bot, payload, response, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
export function transformInteractionCallback(bot: Bot, payload: DiscordInteractionCallback): InteractionCallback {
export function transformInteractionCallback(bot: Bot, payload: Partial<DiscordInteractionCallback>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.interactionCallback;
const callback = {} as SetupDesiredProps<InteractionCallback, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -340,10 +332,16 @@ export function transformInteractionCallback(bot: Bot, payload: DiscordInteracti
if (props.responseMessageEphemeral && payload.response_message_ephemeral) callback.responseMessageEphemeral = payload.response_message_ephemeral;
if (props.responseMessageLoading && payload.response_message_loading) callback.responseMessageLoading = payload.response_message_loading;
return bot.transformers.customizers.interactionCallback(bot, payload, callback);
return callCustomizer('interactionCallback', bot, payload, callback, {
partial: extra?.partial ?? false,
});
}
export function transformInteractionResource(bot: Bot, payload: DiscordInteractionResource, extra?: { shardId?: number }): InteractionResource {
export function transformInteractionResource(
bot: Bot,
payload: Partial<DiscordInteractionResource>,
extra?: { shardId?: number; partial?: boolean },
) {
const props = bot.transformers.desiredProperties.interactionResource;
const resource = {} as SetupDesiredProps<InteractionResource, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -351,5 +349,8 @@ export function transformInteractionResource(bot: Bot, payload: DiscordInteracti
if (props.activityInstance && payload.activity_instance) resource.activityInstance = payload.activity_instance;
if (props.message && payload.message) resource.message = bot.transformers.message(bot, payload.message, { shardId: extra?.shardId });
return bot.transformers.customizers.interactionResource(bot, payload, resource, extra);
return callCustomizer('interactionResource', bot, payload, resource, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
+24 -28
View File
@@ -1,46 +1,42 @@
import type { DiscordInviteCreate, DiscordInviteMetadata } from '@discordeno/types';
import { isInviteWithMetadata } from '@discordeno/utils';
import type { DiscordInviteMetadata } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { ToggleBitfield } from './toggles/ToggleBitfield.js';
import type { Invite } from './types.js';
export function transformInvite(bot: Bot, payload: DiscordInviteCreate | DiscordInviteMetadata, extra?: { shardId?: number }): Invite {
export function transformInvite(bot: Bot, payload: Partial<DiscordInviteMetadata>, extra?: { shardId?: number; partial?: boolean }) {
const props = bot.transformers.desiredProperties.invite;
const invite = {} as SetupDesiredProps<Invite, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.type && 'type' in payload) invite.type = payload.type;
if (props.type && payload.type !== undefined) invite.type = payload.type;
if (props.code && payload.code) invite.code = payload.code;
if (props.createdAt && payload.created_at) invite.createdAt = Date.parse(payload.created_at);
if (props.inviter && payload.inviter) invite.inviter = bot.transformers.user(bot, payload.inviter);
if (props.maxAge) invite.maxAge = payload.max_age;
if (props.maxUses) invite.maxUses = payload.max_uses;
if (props.maxAge && payload.max_age !== undefined) invite.maxAge = payload.max_age;
if (props.maxUses && payload.max_uses !== undefined) invite.maxUses = payload.max_uses;
if (props.targetType && payload.target_type) invite.targetType = payload.target_type;
if (props.targetUser && payload.target_user) invite.targetUser = bot.transformers.user(bot, payload.target_user);
if (props.targetApplication && payload.target_application)
// @ts-expect-error TODO: Partials
invite.targetApplication = bot.transformers.application(bot, payload.target_application, { shardId: extra?.shardId });
invite.targetApplication = bot.transformers.application(bot, payload.target_application, { shardId: extra?.shardId, partial: true });
if (props.temporary && payload.temporary) invite.temporary = payload.temporary;
if (props.uses && payload.uses) invite.uses = payload.uses;
if (isInviteWithMetadata(payload)) {
if (props.channelId && payload.channel?.id) invite.channelId = bot.transformers.snowflake(payload.channel.id);
if (props.guildId && payload.guild?.id) invite.guildId = bot.transformers.snowflake(payload.guild.id);
if (props.approximateMemberCount && payload.approximate_member_count) invite.approximateMemberCount = payload.approximate_member_count;
if (props.approximatePresenceCount && payload.approximate_presence_count !== undefined)
invite.approximatePresenceCount = payload.approximate_presence_count;
if (props.guildScheduledEvent && payload.guild_scheduled_event)
invite.guildScheduledEvent = bot.transformers.scheduledEvent(bot, payload.guild_scheduled_event);
if (props.expiresAt && payload.expires_at) {
invite.expiresAt = Date.parse(payload.expires_at);
}
if (props.flags && payload.flags) invite.flags = new ToggleBitfield(payload.flags);
// @ts-expect-error TODO: Partials
if (props.roles && payload.roles) invite.roles = payload.roles.map((role) => bot.transformers.role(bot, role));
} else {
if (props.channelId && payload.channel_id) invite.channelId = bot.transformers.snowflake(payload.channel_id);
if (props.guildId && payload.guild_id) invite.guildId = bot.transformers.snowflake(payload.guild_id);
if (props.uses && payload.uses !== undefined) invite.uses = payload.uses;
if (props.channelId && payload.channel?.id) invite.channelId = bot.transformers.snowflake(payload.channel.id);
if (props.guildId && payload.guild?.id) invite.guildId = bot.transformers.snowflake(payload.guild.id);
if (props.approximateMemberCount && payload.approximate_member_count) invite.approximateMemberCount = payload.approximate_member_count;
if (props.approximatePresenceCount && payload.approximate_presence_count !== undefined)
invite.approximatePresenceCount = payload.approximate_presence_count;
if (props.guildScheduledEvent && payload.guild_scheduled_event)
invite.guildScheduledEvent = bot.transformers.scheduledEvent(bot, payload.guild_scheduled_event);
if (props.expiresAt && payload.expires_at) invite.expiresAt = Date.parse(payload.expires_at);
if (props.flags && payload.flags) invite.flags = new ToggleBitfield(payload.flags);
if (props.roles && payload.roles) {
// Going from a Partial<T> To Pick<T, K> requires an as, as typescript can't guarantee that the properties exist on the partial
invite.roles = payload.roles.map((role) => bot.transformers.role(bot, role, { partial: true, guildId: payload.guild?.id })) as Invite['roles'];
}
return bot.transformers.customizers.invite(bot, payload, invite, extra);
return callCustomizer('invite', bot, payload, invite, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
+18 -9
View File
@@ -1,10 +1,11 @@
import type { DiscordLobby, DiscordLobbyInvite, DiscordLobbyMember, DiscordLobbyMessage } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { ToggleBitfield } from './toggles/ToggleBitfield.js';
import type { Lobby, LobbyInvite, LobbyMember, LobbyMessage } from './types.js';
export function transformLobby(bot: Bot, payload: DiscordLobby): Lobby {
export function transformLobby(bot: Bot, payload: Partial<DiscordLobby>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.lobby;
const lobby = {} as SetupDesiredProps<Lobby, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -14,10 +15,12 @@ export function transformLobby(bot: Bot, payload: DiscordLobby): Lobby {
if (props.members && payload.members) lobby.members = payload.members.map((member) => bot.transformers.lobbyMember(bot, member));
if (props.linkedChannel && payload.linked_channel) lobby.linkedChannel = bot.transformers.channel(bot, payload.linked_channel);
return bot.transformers.customizers.lobby(bot, payload, lobby);
return callCustomizer('lobby', bot, payload, lobby, {
partial: extra?.partial ?? false,
});
}
export function transformLobbyMember(bot: Bot, payload: DiscordLobbyMember): LobbyMember {
export function transformLobbyMember(bot: Bot, payload: Partial<DiscordLobbyMember>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.lobbyMember;
const lobbyMember = {} as SetupDesiredProps<LobbyMember, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -25,15 +28,17 @@ export function transformLobbyMember(bot: Bot, payload: DiscordLobbyMember): Lob
if (props.metadata && payload.metadata) lobbyMember.metadata = payload.metadata;
if (props.flags && payload.flags) lobbyMember.flags = new ToggleBitfield(payload.flags);
return bot.transformers.customizers.lobbyMember(bot, payload, lobbyMember);
return callCustomizer('lobbyMember', bot, payload, lobbyMember, {
partial: extra?.partial ?? false,
});
}
export function transformLobbyMessage(bot: Bot, payload: DiscordLobbyMessage): LobbyMessage {
export function transformLobbyMessage(bot: Bot, payload: Partial<DiscordLobbyMessage>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.lobbyMessage;
const lobbyMessage = {} as SetupDesiredProps<LobbyMessage, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.id && payload.id) lobbyMessage.id = bot.transformers.snowflake(payload.id);
if (props.type) lobbyMessage.type = payload.type;
if (props.type && payload.type !== undefined) lobbyMessage.type = payload.type;
if (props.content && payload.content) lobbyMessage.content = payload.content;
if (props.lobbyId && payload.lobby_id) lobbyMessage.lobbyId = bot.transformers.snowflake(payload.lobby_id);
if (props.channelId && payload.channel_id) lobbyMessage.channelId = bot.transformers.snowflake(payload.channel_id);
@@ -43,14 +48,18 @@ export function transformLobbyMessage(bot: Bot, payload: DiscordLobbyMessage): L
if (props.flags && payload.flags) lobbyMessage.flags = new ToggleBitfield(payload.flags);
if (props.applicationId && payload.application_id) lobbyMessage.applicationId = bot.transformers.snowflake(payload.application_id);
return bot.transformers.customizers.lobbyMessage(bot, payload, lobbyMessage);
return callCustomizer('lobbyMessage', bot, payload, lobbyMessage, {
partial: extra?.partial ?? false,
});
}
export function transformLobbyInvite(bot: Bot, payload: DiscordLobbyInvite): LobbyInvite {
export function transformLobbyInvite(bot: Bot, payload: Partial<DiscordLobbyInvite>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.lobbyInvite;
const lobbyInvite = {} as SetupDesiredProps<LobbyInvite, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.code && payload.code) lobbyInvite.code = payload.code;
return bot.transformers.customizers.lobbyInvite(bot, payload, lobbyInvite);
return callCustomizer('lobbyInvite', bot, payload, lobbyInvite, {
partial: extra?.partial ?? false,
});
}
+4 -2
View File
@@ -2,6 +2,7 @@ import type { BigString, DiscordMember } from '@discordeno/types';
import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { MemberToggles } from './toggles/member.js';
import { Permissions } from './toggles/Permissions.js';
import type { Member } from './types.js';
@@ -36,7 +37,7 @@ export const baseMember: Member = {
},
};
export function transformMember(bot: Bot, payload: DiscordMember, extra?: { guildId?: BigString; userId?: BigString }): Member {
export function transformMember(bot: Bot, payload: Partial<DiscordMember>, extra?: { guildId?: BigString; userId?: BigString; partial?: boolean }) {
const member: SetupDesiredProps<Member, TransformersDesiredProperties, DesiredPropertiesBehavior> = Object.create(baseMember);
const props = bot.transformers.desiredProperties.member;
@@ -57,8 +58,9 @@ export function transformMember(bot: Bot, payload: DiscordMember, extra?: { guil
member.avatarDecorationData = bot.transformers.avatarDecorationData(bot, payload.avatar_decoration_data);
if (props.collectibles && payload.collectibles) member.collectibles = bot.transformers.collectibles(bot, payload.collectibles);
return bot.transformers.customizers.member(bot, payload, member, {
return callCustomizer('member', bot, payload, member, {
guildId: extra?.guildId ? bot.transformers.snowflake(extra.guildId) : undefined,
userId: extra?.userId ? bot.transformers.snowflake(extra.userId) : undefined,
partial: extra?.partial ?? false,
});
}
+39 -28
View File
@@ -12,6 +12,7 @@ import { snowflakeToTimestamp } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import { CHANNEL_MENTION_REGEX } from '../constants.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { ToggleBitfield } from './toggles/ToggleBitfield.js';
import type {
Message,
@@ -141,7 +142,7 @@ export const baseMessage: Message = {
},
};
export function transformMessage(bot: Bot, payload: DiscordMessage, extra?: { shardId?: number }): Message {
export function transformMessage(bot: Bot, payload: Partial<DiscordMessage>, extra?: { shardId?: number; partial?: boolean }) {
const guildId = payload.guild_id ? bot.transformers.snowflake(payload.guild_id) : undefined;
const userId = payload.author?.id ? bot.transformers.snowflake(payload.author.id) : undefined;
@@ -153,8 +154,7 @@ export function transformMessage(bot: Bot, payload: DiscordMessage, extra?: { sh
if (props.author && payload.author) message.author = bot.transformers.user(bot, payload.author);
if (props.application && payload.application)
// @ts-expect-error TODO: Partials
message.application = bot.transformers.application(bot, payload.application, { shardId: extra?.shardId });
message.application = bot.transformers.application(bot, payload.application, { shardId: extra?.shardId, partial: true });
if (props.applicationId && payload.application_id) message.applicationId = bot.transformers.snowflake(payload.application_id);
if (props.attachments && payload.attachments?.length)
message.attachments = payload.attachments.map((attachment) => bot.transformers.attachment(bot, attachment));
@@ -175,8 +175,7 @@ export function transformMessage(bot: Bot, payload: DiscordMessage, extra?: { sh
interaction.id = bot.transformers.snowflake(payload.interaction.id);
}
if (messageInteractionProps.member && payload.interaction.member) {
// @ts-expect-error TODO: partial - check why this is partial and handle as needed
interaction.member = bot.transformers.member(bot, payload.interaction.member, { guildId, userId: payload.interaction.user.id });
interaction.member = bot.transformers.member(bot, payload.interaction.member, { guildId, userId: payload.interaction.user.id, partial: true });
}
if (messageInteractionProps.name) {
interaction.name = payload.interaction.name;
@@ -191,8 +190,7 @@ export function transformMessage(bot: Bot, payload: DiscordMessage, extra?: { sh
message.interaction = interaction;
}
if (props.member && guildId && userId && payload.member)
// @ts-expect-error TODO: partial
message.member = bot.transformers.member(bot, payload.member, { guildId, userId });
message.member = bot.transformers.member(bot, payload.member, { guildId, userId, partial: true });
if (payload.mention_everyone) message.mentionEveryone = true;
if (props.mentionedChannelIds && payload.mention_channels?.length) {
message.mentionedChannelIds = [
@@ -240,8 +238,7 @@ export function transformMessage(bot: Bot, payload: DiscordMessage, extra?: { sh
burst: reaction.count_details.burst,
normal: reaction.count_details.normal,
},
// @ts-expect-error TODO: Deal with partials
emoji: bot.transformers.emoji(bot, reaction.emoji),
emoji: bot.transformers.emoji(bot, reaction.emoji, { partial: true }),
burstColors: reaction.burst_colors,
}));
}
@@ -253,41 +250,49 @@ export function transformMessage(bot: Bot, payload: DiscordMessage, extra?: { sh
}));
if (payload.tts) message.tts = true;
if (props.thread && payload.thread) message.thread = bot.transformers.channel(bot, payload.thread, { guildId });
if (props.type) message.type = payload.type;
if (props.type && payload.type !== undefined) message.type = payload.type;
if (props.webhookId && payload.webhook_id) message.webhookId = bot.transformers.snowflake(payload.webhook_id);
if (props.poll && payload.poll) message.poll = bot.transformers.poll(bot, payload.poll);
if (props.call && payload.call) message.call = bot.transformers.messageCall(bot, payload.call);
return bot.transformers.customizers.message(bot, payload, message, extra);
return callCustomizer('message', bot, payload, message, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
export function transformMessagePin(bot: Bot, payload: DiscordMessagePin, extra?: { shardId?: number }): MessagePin {
export function transformMessagePin(bot: Bot, payload: Partial<DiscordMessagePin>, extra?: { shardId?: number; partial?: boolean }) {
const props = bot.transformers.desiredProperties.messagePin;
const messagePin = {} as SetupDesiredProps<MessagePin, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.pinnedAt && payload.pinned_at) messagePin.pinnedAt = Date.parse(payload.pinned_at);
if (props.message && payload.message) messagePin.message = bot.transformers.message(bot, payload.message, { shardId: extra?.shardId });
return bot.transformers.customizers.messagePin(bot, payload, messagePin, extra);
return callCustomizer('messagePin', bot, payload, messagePin, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
export function transformMessageSnapshot(bot: Bot, payload: DiscordMessageSnapshot, extra?: { shardId?: number }): MessageSnapshot {
export function transformMessageSnapshot(bot: Bot, payload: Partial<DiscordMessageSnapshot>, extra?: { shardId?: number; partial?: boolean }) {
const props = bot.transformers.desiredProperties.messageSnapshot;
const messageSnapshot = {} as SetupDesiredProps<MessageSnapshot, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.message && payload.message)
// @ts-expect-error TODO: Partials
messageSnapshot.message = bot.transformers.message(bot, payload.message, { shardId: extra?.shardId }) as Message;
messageSnapshot.message = bot.transformers.message(bot, payload.message, { shardId: extra?.shardId, partial: true }) as Message;
return bot.transformers.customizers.messageSnapshot(bot, payload, messageSnapshot, extra);
return callCustomizer('messageSnapshot', bot, payload, messageSnapshot, {
shardId: extra?.shardId,
partial: extra?.partial ?? false,
});
}
export function transformMessageInteractionMetadata(bot: Bot, payload: DiscordMessageInteractionMetadata): MessageInteractionMetadata {
export function transformMessageInteractionMetadata(bot: Bot, payload: Partial<DiscordMessageInteractionMetadata>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.messageInteractionMetadata;
const metadata = {} as SetupDesiredProps<MessageInteractionMetadata, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.id) metadata.id = bot.transformers.snowflake(payload.id);
if (props.authorizingIntegrationOwners) {
if (props.id && payload.id !== undefined) metadata.id = bot.transformers.snowflake(payload.id);
if (props.authorizingIntegrationOwners && payload.authorizing_integration_owners) {
metadata.authorizingIntegrationOwners = {};
if (payload.authorizing_integration_owners['0'])
metadata.authorizingIntegrationOwners[DiscordApplicationIntegrationType.GuildInstall] = bot.transformers.snowflake(
@@ -300,7 +305,7 @@ export function transformMessageInteractionMetadata(bot: Bot, payload: DiscordMe
}
if (props.originalResponseMessageId && payload.original_response_message_id)
metadata.originalResponseMessageId = bot.transformers.snowflake(payload.original_response_message_id);
if (props.type) metadata.type = payload.type;
if (props.type && payload.type !== undefined) metadata.type = payload.type;
if (props.user && payload.user) metadata.user = bot.transformers.user(bot, payload.user);
// Application command metadata
if ('target_user' in payload) {
@@ -318,27 +323,33 @@ export function transformMessageInteractionMetadata(bot: Bot, payload: DiscordMe
metadata.triggeringInteractionMetadata = bot.transformers.messageInteractionMetadata(bot, payload.triggering_interaction_metadata);
}
return bot.transformers.customizers.messageInteractionMetadata(bot, payload, metadata);
return callCustomizer('messageInteractionMetadata', bot, payload, metadata, {
partial: extra?.partial ?? false,
});
}
export function transformMessageCall(bot: Bot, payload: DiscordMessageCall): MessageCall {
export function transformMessageCall(bot: Bot, payload: Partial<DiscordMessageCall>, extra?: { partial?: boolean }) {
const call = {} as SetupDesiredProps<MessageCall, TransformersDesiredProperties, DesiredPropertiesBehavior>;
const props = bot.transformers.desiredProperties.messageCall;
if (props.participants && payload.participants) call.participants = payload.participants.map((x) => bot.transformers.snowflake(x));
if (props.endedTimestamp && payload.ended_timestamp) call.endedTimestamp = Date.parse(payload.ended_timestamp);
return bot.transformers.customizers.messageCall(bot, payload, call);
return callCustomizer('messageCall', bot, payload, call, {
partial: extra?.partial ?? false,
});
}
export function transformSharedClientTheme(bot: Bot, payload: DiscordSharedClientTheme): SharedClientTheme {
export function transformSharedClientTheme(bot: Bot, payload: Partial<DiscordSharedClientTheme>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.sharedClientTheme;
const theme = {} as SetupDesiredProps<SharedClientTheme, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.colors && payload.colors) theme.colors = payload.colors;
if (props.baseMix) theme.baseMix = payload.base_mix;
if (props.gradientAngle) theme.gradientAngle = payload.gradient_angle;
if (props.baseMix && payload.base_mix !== undefined) theme.baseMix = payload.base_mix;
if (props.gradientAngle && payload.gradient_angle !== undefined) theme.gradientAngle = payload.gradient_angle;
if (props.baseTheme && payload.base_theme !== undefined && payload.base_theme !== null) theme.baseTheme = payload.base_theme;
return bot.transformers.customizers.sharedClientTheme(bot, payload, theme);
return callCustomizer('sharedClientTheme', bot, payload, theme, {
partial: extra?.partial ?? false,
});
}
+16 -9
View File
@@ -1,24 +1,27 @@
import type { DiscordGuildOnboarding, DiscordGuildOnboardingPrompt, DiscordGuildOnboardingPromptOption } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { GuildOnboarding, GuildOnboardingPrompt, GuildOnboardingPromptOption } from './types.js';
export function transformGuildOnboarding(bot: Bot, payload: DiscordGuildOnboarding): GuildOnboarding {
export function transformGuildOnboarding(bot: Bot, payload: Partial<DiscordGuildOnboarding>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.guildOnboarding;
const guildOnboarding = {} as SetupDesiredProps<GuildOnboarding, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.guildId && payload.guild_id) guildOnboarding.guildId = bot.transformers.snowflake(payload.guild_id);
if (props.defaultChannelIds && payload.default_channel_ids)
guildOnboarding.defaultChannelIds = payload.default_channel_ids.map(bot.transformers.snowflake);
if (props.enabled) guildOnboarding.enabled = payload.enabled;
if (props.mode) guildOnboarding.mode = payload.mode;
if (props.enabled && payload.enabled !== undefined) guildOnboarding.enabled = payload.enabled;
if (props.mode && payload.mode !== undefined) guildOnboarding.mode = payload.mode;
if (props.prompts && payload.prompts)
guildOnboarding.prompts = payload.prompts.map((prompt) => bot.transformers.guildOnboardingPrompt(bot, prompt));
return bot.transformers.customizers.guildOnboarding(bot, payload, guildOnboarding);
return callCustomizer('guildOnboarding', bot, payload, guildOnboarding, {
partial: extra?.partial ?? false,
});
}
export function transformGuildOnboardingPrompt(bot: Bot, payload: DiscordGuildOnboardingPrompt): GuildOnboardingPrompt {
export function transformGuildOnboardingPrompt(bot: Bot, payload: Partial<DiscordGuildOnboardingPrompt>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.guildOnboardingPrompt;
const prompt = {} as SetupDesiredProps<GuildOnboardingPrompt, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -27,13 +30,15 @@ export function transformGuildOnboardingPrompt(bot: Bot, payload: DiscordGuildOn
if (props.required && payload.required) prompt.required = payload.required;
if (props.singleSelect && payload.single_select) prompt.singleSelect = payload.single_select;
if (props.title && payload.title) prompt.title = payload.title;
if (props.type) prompt.type = payload.type;
if (props.type && payload.type !== undefined) prompt.type = payload.type;
if (props.options && payload.options) prompt.options = payload.options.map((option) => bot.transformers.guildOnboardingPromptOption(bot, option));
return bot.transformers.customizers.guildOnboardingPrompt(bot, payload, prompt);
return callCustomizer('guildOnboardingPrompt', bot, payload, prompt, {
partial: extra?.partial ?? false,
});
}
export function transformGuildOnboardingPromptOption(bot: Bot, payload: DiscordGuildOnboardingPromptOption): GuildOnboardingPromptOption {
export function transformGuildOnboardingPromptOption(bot: Bot, payload: Partial<DiscordGuildOnboardingPromptOption>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.guildOnboardingPromptOption;
const option = {} as SetupDesiredProps<GuildOnboardingPromptOption, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -44,5 +49,7 @@ export function transformGuildOnboardingPromptOption(bot: Bot, payload: DiscordG
if (props.title && payload.title) option.title = payload.title;
if (props.description && payload.description) option.description = payload.description;
return bot.transformers.customizers.guildOnboardingPromptOption(bot, payload, option);
return callCustomizer('guildOnboardingPromptOption', bot, payload, option, {
partial: extra?.partial ?? false,
});
}
+12 -7
View File
@@ -1,9 +1,10 @@
import type { DiscordEmoji, DiscordPoll, DiscordPollMedia } from '@discordeno/types';
import type { DiscordPoll, DiscordPollMedia } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { Poll, PollMedia, PollResult } from './types.js';
export function transformPoll(bot: Bot, payload: DiscordPoll): Poll {
export function transformPoll(bot: Bot, payload: Partial<DiscordPoll>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.poll;
const poll = {} as SetupDesiredProps<Poll, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -12,7 +13,7 @@ export function transformPoll(bot: Bot, payload: DiscordPoll): Poll {
poll.answers = payload.answers.map((x) => ({ answerId: x.answer_id, pollMedia: bot.transformers.pollMedia(bot, x.poll_media) }));
if (props.expiry && payload.expiry) poll.expiry = Date.parse(payload.expiry);
if (props.allowMultiselect && payload.allow_multiselect) poll.allowMultiselect = payload.allow_multiselect;
if (props.layoutType) poll.layoutType = payload.layout_type;
if (props.layoutType && payload.layout_type !== undefined) poll.layoutType = payload.layout_type;
if (props.results && payload.results) {
const results = {} as SetupDesiredProps<PollResult, TransformersDesiredProperties, DesiredPropertiesBehavior>;
const pollResultProps = bot.transformers.desiredProperties.pollResult;
@@ -24,15 +25,19 @@ export function transformPoll(bot: Bot, payload: DiscordPoll): Poll {
poll.results = results;
}
return bot.transformers.customizers.poll(bot, payload, poll);
return callCustomizer('poll', bot, payload, poll, {
partial: extra?.partial ?? false,
});
}
export function transformPollMedia(bot: Bot, payload: DiscordPollMedia): PollMedia {
export function transformPollMedia(bot: Bot, payload: Partial<DiscordPollMedia>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.pollMedia;
const pollMedia = {} as SetupDesiredProps<PollMedia, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.text && payload.text) pollMedia.text = payload.text;
if (props.emoji && payload.emoji) pollMedia.emoji = bot.transformers.emoji(bot, payload.emoji as DiscordEmoji);
if (props.emoji && payload.emoji) pollMedia.emoji = bot.transformers.emoji(bot, payload.emoji, { partial: true });
return bot.transformers.customizers.pollMedia(bot, payload, pollMedia);
return callCustomizer('pollMedia', bot, payload, pollMedia, {
partial: extra?.partial ?? false,
});
}
+8 -5
View File
@@ -1,18 +1,21 @@
import { type DiscordPresenceUpdate, PresenceStatus } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { PresenceUpdate, User } from './types.js';
export function transformPresence(bot: Bot, payload: DiscordPresenceUpdate): PresenceUpdate {
export function transformPresence(bot: Bot, payload: Partial<DiscordPresenceUpdate>, extra?: { partial?: boolean }) {
const presence = {} as SetupDesiredProps<PresenceUpdate, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (payload.user) presence.user = bot.transformers.user(bot, payload.user) as User;
if (payload.guild_id) presence.guildId = bot.transformers.snowflake(payload.guild_id);
if (payload.status) presence.status = PresenceStatus[payload.status];
if (payload.activities) presence.activities = payload.activities.map((activity) => bot.transformers.activity(bot, activity));
if (payload.client_status.desktop) presence.desktop = payload.client_status.desktop;
if (payload.client_status.mobile) presence.mobile = payload.client_status.mobile;
if (payload.client_status.web) presence.web = payload.client_status.web;
if (payload.client_status?.desktop) presence.desktop = payload.client_status.desktop;
if (payload.client_status?.mobile) presence.mobile = payload.client_status.mobile;
if (payload.client_status?.web) presence.web = payload.client_status.web;
return bot.transformers.customizers.presence(bot, payload, presence);
return callCustomizer('presence', bot, payload, presence, {
partial: extra?.partial ?? false,
});
}
+10 -6
View File
@@ -2,6 +2,7 @@ import type { BigString, DiscordRole, DiscordRoleColors } from '@discordeno/type
import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { Permissions } from './toggles/Permissions.js';
import { RoleToggles } from './toggles/role.js';
import type { Role, RoleColors } from './types.js';
@@ -46,20 +47,20 @@ export const baseRole: Role = {
},
};
export function transformRole(bot: Bot, payload: DiscordRole, extra?: { guildId?: BigString }): Role {
export function transformRole(bot: Bot, payload: Partial<DiscordRole>, extra?: { guildId?: BigString; partial?: boolean }) {
const role: SetupDesiredProps<Role, TransformersDesiredProperties, DesiredPropertiesBehavior> = Object.create(baseRole);
const props = bot.transformers.desiredProperties.role;
if (props.id && payload.id) role.id = bot.transformers.snowflake(payload.id);
// Role name can be an empty string
if (props.name && payload.name !== undefined) role.name = payload.name;
if (props.position) role.position = payload.position;
if (props.position && payload.position !== undefined) role.position = payload.position;
if (props.guildId && extra?.guildId) role.guildId = bot.transformers.snowflake(extra?.guildId);
if (props.color && payload.color !== undefined) role.color = payload.color;
if (props.colors && payload.colors) role.colors = bot.transformers.roleColors(bot, payload.colors);
if (props.permissions && payload.permissions) role.permissions = new Permissions(payload.permissions);
if (props.icon && payload.icon) role.icon = iconHashToBigInt(payload.icon);
if (props.unicodeEmoji && payload.unicode_emoji) role.unicodeEmoji = payload.unicode_emoji;
if (props.flags) role.flags = payload.flags;
if (props.flags && payload.flags !== undefined) role.flags = payload.flags;
if (props.tags && payload.tags) {
role.internalTags = {};
if (payload.tags.bot_id) role.internalTags.botId = bot.transformers.snowflake(payload.tags.bot_id);
@@ -69,12 +70,13 @@ export function transformRole(bot: Bot, payload: DiscordRole, extra?: { guildId?
}
if (props.toggles) role.toggles = new RoleToggles(payload);
return bot.transformers.customizers.role(bot, payload, role, {
return callCustomizer('role', bot, payload, role, {
guildId: extra?.guildId ? bot.transformers.snowflake(extra.guildId) : undefined,
partial: extra?.partial ?? false,
});
}
export function transformRoleColors(bot: Bot, payload: DiscordRoleColors): RoleColors {
export function transformRoleColors(bot: Bot, payload: Partial<DiscordRoleColors>, extra?: { partial?: boolean }) {
const roleColors = {} as SetupDesiredProps<RoleColors, TransformersDesiredProperties, DesiredPropertiesBehavior>;
const props = bot.transformers.desiredProperties.roleColors;
@@ -84,5 +86,7 @@ export function transformRoleColors(bot: Bot, payload: DiscordRoleColors): RoleC
if (props.tertiaryColor && payload.tertiary_color !== undefined && payload.tertiary_color !== null)
roleColors.tertiaryColor = payload.tertiary_color;
return bot.transformers.customizers.roleColors(bot, payload, roleColors);
return callCustomizer('roleColors', bot, payload, roleColors, {
partial: extra?.partial ?? false,
});
}
@@ -2,9 +2,10 @@ import type { DiscordScheduledEvent, DiscordScheduledEventRecurrenceRule } from
import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { ScheduledEvent, ScheduledEventRecurrenceRule } from './types.js';
export function transformScheduledEvent(bot: Bot, payload: DiscordScheduledEvent): ScheduledEvent {
export function transformScheduledEvent(bot: Bot, payload: Partial<DiscordScheduledEvent>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.scheduledEvent;
const scheduledEvent = {} as SetupDesiredProps<ScheduledEvent, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -27,10 +28,16 @@ export function transformScheduledEvent(bot: Bot, payload: DiscordScheduledEvent
if (props.recurrenceRule && payload.recurrence_rule)
scheduledEvent.recurrenceRule = bot.transformers.scheduledEventRecurrenceRule(bot, payload.recurrence_rule);
return bot.transformers.customizers.scheduledEvent(bot, payload, scheduledEvent);
return callCustomizer('scheduledEvent', bot, payload, scheduledEvent, {
partial: extra?.partial ?? false,
});
}
export function transformScheduledEventRecurrenceRule(bot: Bot, payload: DiscordScheduledEventRecurrenceRule): ScheduledEventRecurrenceRule {
export function transformScheduledEventRecurrenceRule(
bot: Bot,
payload: Partial<DiscordScheduledEventRecurrenceRule>,
extra?: { partial?: boolean },
) {
const props = bot.transformers.desiredProperties.scheduledEventRecurrenceRule;
const recurrenceRule = {} as SetupDesiredProps<ScheduledEventRecurrenceRule, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -45,5 +52,7 @@ export function transformScheduledEventRecurrenceRule(bot: Bot, payload: Discord
if (props.byYearDay && payload.by_year_day) recurrenceRule.byYearDay = payload.by_year_day;
if (props.count && payload.count) recurrenceRule.count = payload.count;
return bot.transformers.customizers.scheduledEventRecurrenceRule(bot, payload, recurrenceRule);
return callCustomizer('scheduledEventRecurrenceRule', bot, payload, recurrenceRule, {
partial: extra?.partial ?? false,
});
}
+5 -2
View File
@@ -1,9 +1,10 @@
import type { DiscordSku } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { Sku } from './types.js';
export function transformSku(bot: Bot, payload: DiscordSku): Sku {
export function transformSku(bot: Bot, payload: Partial<DiscordSku>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.sku;
const sku = {} as SetupDesiredProps<Sku, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -14,5 +15,7 @@ export function transformSku(bot: Bot, payload: DiscordSku): Sku {
if (props.slug && payload.slug) sku.slug = payload.slug;
if (props.flags && payload.flags) sku.flags = payload.flags;
return bot.transformers.customizers.sku(bot, payload, sku);
return callCustomizer('sku', bot, payload, sku, {
partial: extra?.partial ?? false,
});
}
@@ -1,9 +1,10 @@
import type { DiscordSoundboardSound } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { SoundboardSound } from './types.js';
export function transformSoundboardSound(bot: Bot, payload: DiscordSoundboardSound): SoundboardSound {
export function transformSoundboardSound(bot: Bot, payload: Partial<DiscordSoundboardSound>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.soundboardSound;
const soundboardSound = {} as SetupDesiredProps<SoundboardSound, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -16,5 +17,7 @@ export function transformSoundboardSound(bot: Bot, payload: DiscordSoundboardSou
if (props.available && payload.available) soundboardSound.available = payload.available;
if (props.user && payload.user) soundboardSound.user = bot.transformers.user(bot, payload.user);
return bot.transformers.customizers.soundboardSound(bot, payload, soundboardSound);
return callCustomizer('soundboardSound', bot, payload, soundboardSound, {
partial: extra?.partial ?? false,
});
}
@@ -1,9 +1,10 @@
import type { DiscordStageInstance } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { StageInstance } from './types.js';
export function transformStageInstance(bot: Bot, payload: DiscordStageInstance): StageInstance {
export function transformStageInstance(bot: Bot, payload: Partial<DiscordStageInstance>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.stageInstance;
const stageInstance = {} as SetupDesiredProps<StageInstance, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -14,5 +15,7 @@ export function transformStageInstance(bot: Bot, payload: DiscordStageInstance):
if (props.guildScheduledEventId && payload.guild_scheduled_event_id)
stageInstance.guildScheduledEventId = bot.transformers.snowflake(payload.guild_scheduled_event_id);
return bot.transformers.customizers.stageInstance(bot, payload, stageInstance);
return callCustomizer('stageInstance', bot, payload, stageInstance, {
partial: extra?.partial ?? false,
});
}
@@ -1,26 +1,32 @@
import type { BigString, DiscordInviteStageInstance } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { InviteStageInstance } from './types.js';
export function transformInviteStageInstance(bot: Bot, payload: DiscordInviteStageInstance, extra?: { guildId?: BigString }): InviteStageInstance {
export function transformInviteStageInstance(
bot: Bot,
payload: Partial<DiscordInviteStageInstance>,
extra?: { guildId?: BigString; partial?: boolean },
) {
const props = bot.transformers.desiredProperties.inviteStageInstance;
const inviteStageInstance = {} as SetupDesiredProps<InviteStageInstance, TransformersDesiredProperties, DesiredPropertiesBehavior>;
if (props.members && payload.members) {
inviteStageInstance.members = payload.members.map((member) =>
// @ts-expect-error TODO: Partials
bot.transformers.member(bot, member, {
guildId: extra?.guildId,
userId: member.user?.id,
partial: true,
}),
);
}
if (props.participantCount) inviteStageInstance.participantCount = payload.participant_count;
if (props.speakerCount) inviteStageInstance.participantCount = payload.participant_count;
if (props.participantCount && payload.participant_count !== undefined) inviteStageInstance.participantCount = payload.participant_count;
if (props.speakerCount && payload.speaker_count !== undefined) inviteStageInstance.speakerCount = payload.speaker_count;
if (props.topic && payload.topic) inviteStageInstance.topic = payload.topic;
return bot.transformers.customizers.inviteStageInstance(bot, payload, inviteStageInstance, {
return callCustomizer('inviteStageInstance', bot, payload, inviteStageInstance, {
guildId: extra?.guildId ? bot.transformers.snowflake(extra.guildId) : undefined,
partial: extra?.partial ?? false,
});
}
+6 -3
View File
@@ -1,9 +1,10 @@
import type { DiscordSticker, DiscordStickerPack } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { Sticker, StickerPack } from './types.js';
export function transformSticker(bot: Bot, payload: DiscordSticker): Sticker {
export function transformSticker(bot: Bot, payload: Partial<DiscordSticker>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.sticker;
const sticker = {} as SetupDesiredProps<Sticker, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -19,10 +20,12 @@ export function transformSticker(bot: Bot, payload: DiscordSticker): Sticker {
if (props.user && payload.user) sticker.user = bot.transformers.user(bot, payload.user);
if (props.sortValue && payload.sort_value !== undefined) sticker.sortValue = payload.sort_value;
return bot.transformers.customizers.sticker(bot, payload, sticker);
return callCustomizer('sticker', bot, payload, sticker, {
partial: extra?.partial ?? false,
});
}
export function transformStickerPack(bot: Bot, payload: DiscordStickerPack): StickerPack {
export function transformStickerPack(bot: Bot, payload: DiscordStickerPack) {
const pack = {
id: bot.transformers.snowflake(payload.id),
stickers: payload.stickers.map((sticker) => bot.transformers.sticker(bot, sticker)),
@@ -1,9 +1,10 @@
import type { DiscordSubscription } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { Subscription } from './types.js';
export function transformSubscription(bot: Bot, payload: DiscordSubscription): Subscription {
export function transformSubscription(bot: Bot, payload: Partial<DiscordSubscription>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.subscription;
const subscription = {} as SetupDesiredProps<Subscription, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -20,5 +21,7 @@ export function transformSubscription(bot: Bot, payload: DiscordSubscription): S
if (props.canceledAt && payload.canceled_at) subscription.canceledAt = Date.parse(payload.canceled_at);
if (props.country && payload.country) subscription.country = payload.country;
return bot.transformers.customizers.subscription(bot, payload, subscription);
return callCustomizer('subscription', bot, payload, subscription, {
partial: extra?.partial ?? false,
});
}
+1 -1
View File
@@ -3,7 +3,7 @@ import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { Team } from './types.js';
export function transformTeam(bot: Bot, payload: DiscordTeam): Team {
export function transformTeam(bot: Bot, payload: DiscordTeam) {
const id = bot.transformers.snowflake(payload.id);
const team = {
+1 -1
View File
@@ -1,7 +1,7 @@
import type { DiscordTemplate } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { Template } from './types.js';
export function transformTemplate(bot: Bot, payload: DiscordTemplate): Template {
export function transformTemplate(bot: Bot, payload: DiscordTemplate) {
const template = {
code: payload.code,
name: payload.name,
@@ -2,7 +2,7 @@ import type { BigString, DiscordThreadMember, DiscordThreadMemberGuildCreate } f
import type { Bot } from '../bot.js';
import type { ThreadMember, ThreadMemberGuildCreate } from './types.js';
export function transformThreadMember(bot: Bot, payload: DiscordThreadMember, extra?: ThreadMemberTransformerExtra): ThreadMember {
export function transformThreadMember(bot: Bot, payload: DiscordThreadMember, extra?: ThreadMemberTransformerExtra) {
const threadMember = {
id: payload.id ? bot.transformers.snowflake(payload.id) : undefined,
userId: payload.user_id ? bot.transformers.snowflake(payload.user_id) : undefined,
@@ -31,7 +31,7 @@ export interface ThreadMemberTransformerExtra {
guildId?: BigString;
}
export function transformThreadMemberGuildCreate(bot: Bot, payload: DiscordThreadMemberGuildCreate): ThreadMemberGuildCreate {
export function transformThreadMemberGuildCreate(bot: Bot, payload: DiscordThreadMemberGuildCreate) {
const threadMember = {
joinTimestamp: Date.parse(payload.join_timestamp),
} as ThreadMemberGuildCreate;
@@ -17,7 +17,7 @@ export const ChannelToggle = {
};
export class ChannelToggles extends ToggleBitfield {
constructor(channelOrBitfield: DiscordChannel | number) {
constructor(channelOrBitfield: Partial<DiscordChannel> | number) {
super();
if (typeof channelOrBitfield === 'number') this.bitfield = channelOrBitfield;
@@ -13,7 +13,7 @@ export const EmojiToggle = {
};
export class EmojiToggles extends ToggleBitfield {
constructor(roleOrTogglesInt: DiscordEmoji | number) {
constructor(roleOrTogglesInt: Partial<DiscordEmoji> | number) {
super();
if (typeof roleOrTogglesInt === 'number') this.bitfield = roleOrTogglesInt;
@@ -117,7 +117,7 @@ export const GuildToggle = {
};
export class GuildToggles extends ToggleBitfieldBigint {
constructor(guildOrTogglesBigint: DiscordGuild | bigint) {
constructor(guildOrTogglesBigint: Partial<DiscordGuild> | bigint) {
super();
if (typeof guildOrTogglesBigint === 'bigint') this.bitfield = guildOrTogglesBigint;
@@ -17,7 +17,7 @@ export const RoleToggle = {
};
export class RoleToggles extends ToggleBitfield {
constructor(roleOrTogglesInt: DiscordRole | number) {
constructor(roleOrTogglesInt: Partial<DiscordRole> | number) {
super();
if (typeof roleOrTogglesInt === 'number') this.bitfield = roleOrTogglesInt;
@@ -13,7 +13,7 @@ export const UserToggle = {
};
export class UserToggles extends ToggleBitfield {
constructor(userOrTogglesInt: DiscordUser | number) {
constructor(userOrTogglesInt: Partial<DiscordUser> | number) {
super();
if (typeof userOrTogglesInt === 'number') this.bitfield = userOrTogglesInt;
@@ -19,7 +19,7 @@ export const VoiceStateToggle = {
};
export class VoiceStateToggles extends ToggleBitfield {
constructor(voiceOrTogglesInt: DiscordVoiceState | number) {
constructor(voiceOrTogglesInt: Partial<DiscordVoiceState> | number) {
super();
if (typeof voiceOrTogglesInt === 'number') this.bitfield = voiceOrTogglesInt;
+7 -6
View File
@@ -75,6 +75,7 @@ import type {
StickerFormatTypes,
StickerTypes,
SystemChannelFlags,
TargetTypes,
TeamMembershipStates,
TextStyles,
VerificationLevels,
@@ -966,7 +967,7 @@ export interface Guild {
/** All active threads in the guild that the current user has permission to view */
threads: Collection<bigint, Channel>;
/** Presences of the members in the guild, will only include non-offline members if the size is greater than large threshold */
presences?: PresenceUpdate[];
presences?: Partial<PresenceUpdate>[];
/** Banner hash */
banner?: bigint;
/** The preferred locale of a Community guild; used in server discovery and notices from Discord; defaults to "en-US" */
@@ -1173,7 +1174,7 @@ export interface InteractionData {
}
export interface InteractionDataResolved {
messages?: Collection<bigint, Message>;
messages?: Collection<bigint, Partial<Message>>;
users?: Collection<bigint, User>;
members?: Collection<bigint, InteractionResolvedDataMember<TransformersDesiredProperties, DesiredPropertiesBehavior>>;
roles?: Collection<bigint, Role>;
@@ -1207,14 +1208,14 @@ export interface Invite {
/** The maximum number of times the invite can be used */
maxUses: number;
/** The type of target for this voice channel invite */
targetType: number;
targetType?: TargetTypes;
/** The target user for this invite */
targetUser: User;
targetUser?: User;
/** The embedded application to open for this voice channel embedded application invite */
targetApplication?: Application;
targetApplication?: Partial<Application>;
/** Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) */
temporary: boolean;
/** How many times the invite has been used (always will be 0) */
/** How many times the invite has been used */
uses: number;
/** Approximate count of online members (only present when target_user is set) */
approximateMemberCount: number;
+17 -8
View File
@@ -2,6 +2,7 @@ import type { DiscordCollectibles, DiscordNameplate, DiscordUser, DiscordUserPri
import { avatarUrl, defaultAvatarUrl, displayAvatarUrl, iconHashToBigInt, snowflakeToTimestamp } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { ToggleBitfield } from './toggles/ToggleBitfield.js';
import { UserToggles } from './toggles/user.js';
import type { Collectibles, Nameplate, User, UserPrimaryGuild } from './types.js';
@@ -44,7 +45,7 @@ export const baseUser: User = {
},
};
export function transformUser(bot: Bot, payload: DiscordUser): User {
export function transformUser(bot: Bot, payload: Partial<DiscordUser>, extra?: { partial?: boolean }) {
const user: SetupDesiredProps<User, TransformersDesiredProperties, DesiredPropertiesBehavior> = Object.create(baseUser);
const props = bot.transformers.desiredProperties.user;
@@ -66,19 +67,23 @@ export function transformUser(bot: Bot, payload: DiscordUser): User {
if (props.collectibles && payload.collectibles) user.collectibles = bot.transformers.collectibles(bot, payload.collectibles);
if (props.primaryGuild && payload.primary_guild) user.primaryGuild = bot.transformers.userPrimaryGuild(bot, payload.primary_guild);
return bot.transformers.customizers.user(bot, payload, user);
return callCustomizer('user', bot, payload, user, {
partial: extra?.partial ?? false,
});
}
export function transformCollectibles(bot: Bot, payload: DiscordCollectibles): Collectibles {
export function transformCollectibles(bot: Bot, payload: Partial<DiscordCollectibles>, extra?: { partial?: boolean }) {
const collectibles = {} as SetupDesiredProps<Collectibles, TransformersDesiredProperties, DesiredPropertiesBehavior>;
const props = bot.transformers.desiredProperties.collectibles;
if (props.nameplate && payload.nameplate) collectibles.nameplate = bot.transformers.nameplate(bot, payload.nameplate);
return bot.transformers.customizers.collectibles(bot, payload, collectibles);
return callCustomizer('collectibles', bot, payload, collectibles, {
partial: extra?.partial ?? false,
});
}
export function transformNameplate(bot: Bot, payload: DiscordNameplate): Nameplate {
export function transformNameplate(bot: Bot, payload: Partial<DiscordNameplate>, extra?: { partial?: boolean }) {
const nameplate = {} as SetupDesiredProps<Nameplate, TransformersDesiredProperties, DesiredPropertiesBehavior>;
const props = bot.transformers.desiredProperties.nameplate;
@@ -87,10 +92,12 @@ export function transformNameplate(bot: Bot, payload: DiscordNameplate): Namepla
if (props.label && payload.label) nameplate.label = payload.label;
if (props.palette && payload.palette) nameplate.palette = payload.palette;
return bot.transformers.customizers.nameplate(bot, payload, nameplate);
return callCustomizer('nameplate', bot, payload, nameplate, {
partial: extra?.partial ?? false,
});
}
export function transformUserPrimaryGuild(bot: Bot, payload: DiscordUserPrimaryGuild): UserPrimaryGuild {
export function transformUserPrimaryGuild(bot: Bot, payload: Partial<DiscordUserPrimaryGuild>, extra?: { partial?: boolean }) {
const userPrimaryGuild = {} as SetupDesiredProps<UserPrimaryGuild, TransformersDesiredProperties, DesiredPropertiesBehavior>;
const props = bot.transformers.desiredProperties.userPrimaryGuild;
@@ -99,5 +106,7 @@ export function transformUserPrimaryGuild(bot: Bot, payload: DiscordUserPrimaryG
if (props.tag && payload.tag) userPrimaryGuild.tag = payload.tag;
if (props.badge && payload.badge) userPrimaryGuild.badge = iconHashToBigInt(payload.badge);
return bot.transformers.customizers.userPrimaryGuild(bot, payload, userPrimaryGuild);
return callCustomizer('userPrimaryGuild', bot, payload, userPrimaryGuild, {
partial: extra?.partial ?? false,
});
}
+1 -1
View File
@@ -2,7 +2,7 @@ import type { DiscordVoiceRegion } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { VoiceRegion } from './types.js';
export function transformVoiceRegion(bot: Bot, payload: DiscordVoiceRegion): VoiceRegion {
export function transformVoiceRegion(bot: Bot, payload: DiscordVoiceRegion) {
const voiceRegion = {
id: payload.id,
name: payload.name,
+5 -3
View File
@@ -1,10 +1,11 @@
import type { BigString, DiscordVoiceState } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import { VoiceStateToggles } from './toggles/voice.js';
import type { VoiceState } from './types.js';
export function transformVoiceState(bot: Bot, payload: DiscordVoiceState, extra?: { guildId?: BigString }): VoiceState {
export function transformVoiceState(bot: Bot, payload: Partial<DiscordVoiceState>, extra?: { guildId?: BigString; partial?: boolean }) {
const props = bot.transformers.desiredProperties.voiceState;
const voiceState = {} as SetupDesiredProps<VoiceState, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -13,10 +14,11 @@ export function transformVoiceState(bot: Bot, payload: DiscordVoiceState, extra?
if (props.channelId && payload.channel_id) voiceState.channelId = bot.transformers.snowflake(payload.channel_id);
if (props.guildId && extra?.guildId) voiceState.guildId = bot.transformers.snowflake(extra.guildId);
if (props.toggles) voiceState.toggles = new VoiceStateToggles(payload);
if (props.sessionId) voiceState.sessionId = payload.session_id;
if (props.sessionId && payload.session_id !== undefined) voiceState.sessionId = payload.session_id;
if (props.userId && payload.user_id) voiceState.userId = bot.transformers.snowflake(payload.user_id);
return bot.transformers.customizers.voiceState(bot, payload, voiceState, {
return callCustomizer('voiceState', bot, payload, voiceState, {
guildId: extra?.guildId ? bot.transformers.snowflake(extra.guildId) : undefined,
partial: extra?.partial ?? false,
});
}
+6 -4
View File
@@ -2,9 +2,10 @@ import type { DiscordWebhook } from '@discordeno/types';
import { iconHashToBigInt } from '@discordeno/utils';
import type { Bot } from '../bot.js';
import type { DesiredPropertiesBehavior, SetupDesiredProps, TransformersDesiredProperties } from '../desiredProperties.js';
import { callCustomizer } from '../transformers.js';
import type { Webhook } from './types.js';
export function transformWebhook(bot: Bot, payload: DiscordWebhook): typeof bot.transformers.$inferredTypes.webhook {
export function transformWebhook(bot: Bot, payload: Partial<DiscordWebhook>, extra?: { partial?: boolean }) {
const props = bot.transformers.desiredProperties.webhook;
const webhook = {} as SetupDesiredProps<Webhook, TransformersDesiredProperties, DesiredPropertiesBehavior>;
@@ -24,9 +25,10 @@ export function transformWebhook(bot: Bot, payload: DiscordWebhook): typeof bot.
icon: payload.source_guild.icon ? iconHashToBigInt(payload.source_guild.icon) : undefined,
};
if (props.sourceChannel && payload.source_channel)
// @ts-expect-error TODO: Partials
webhook.sourceChannel = bot.transformers.channel(bot, payload.source_channel, { guildId: payload.guild_id });
webhook.sourceChannel = bot.transformers.channel(bot, payload.source_channel, { guildId: payload.guild_id ?? undefined, partial: true });
if (props.url && payload.url) webhook.url = payload.url;
return bot.transformers.customizers.webhook(bot, payload, webhook);
return callCustomizer('webhook', bot, payload, webhook, {
partial: extra?.partial ?? false,
});
}
@@ -2,7 +2,7 @@ import type { DiscordWelcomeScreen } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { WelcomeScreen } from './types.js';
export function transformWelcomeScreen(bot: Bot, payload: DiscordWelcomeScreen): WelcomeScreen {
export function transformWelcomeScreen(bot: Bot, payload: DiscordWelcomeScreen) {
const welcomeScreen = {
description: payload.description ?? undefined,
welcomeChannels: payload.welcome_channels.map((channel) => ({
+3 -5
View File
@@ -2,15 +2,13 @@ import type { DiscordGuildWidget } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { GuildWidget } from './types.js';
export function transformWidget(bot: Bot, payload: DiscordGuildWidget): GuildWidget {
export function transformWidget(bot: Bot, payload: DiscordGuildWidget) {
const widget = {
id: bot.transformers.snowflake(payload.id),
name: payload.name,
instantInvite: payload.instant_invite ?? undefined,
// @ts-expect-error TODO: Deal with partials
channels: payload.channels.map((channel) => bot.transformers.channel(bot, channel)),
// @ts-expect-error TODO: Deal with partials
members: payload.members.map((user) => bot.transformers.user(bot, user)),
channels: payload.channels.map((channel) => bot.transformers.channel(bot, channel, { partial: true })),
members: payload.members.map((user) => bot.transformers.user(bot, user, { partial: true })),
presenceCount: payload.presence_count,
} as GuildWidget;
@@ -2,11 +2,11 @@ import type { DiscordGuildWidgetSettings } from '@discordeno/types';
import type { Bot } from '../bot.js';
import type { GuildWidgetSettings } from './types.js';
export function transformWidgetSettings(bot: Bot, payload: DiscordGuildWidgetSettings): GuildWidgetSettings {
export function transformWidgetSettings(bot: Bot, payload: DiscordGuildWidgetSettings) {
const widget = {
enabled: payload.enabled,
channelId: payload.channel_id ?? undefined,
};
} satisfies GuildWidgetSettings;
return bot.transformers.customizers.widgetSettings(bot, payload, widget);
}
+2 -2
View File
@@ -700,7 +700,7 @@ export interface DiscordInviteCreate {
/** The maximum number of times the invite can be used */
max_uses: number;
/** The type of target for this voice channel invite */
target_type: TargetTypes;
target_type?: TargetTypes;
/** The target user for this invite */
target_user?: DiscordUser;
/** The embedded application to open for this voice channel embedded application invite */
@@ -710,7 +710,7 @@ export interface DiscordInviteCreate {
/** How many times the invite has been used (always will be 0) */
uses: number;
/** The expiration date of this invite. */
expires_at: string;
expires_at: string | null;
/** the role ID(s) for roles in the guild given to the users that accept this invite */
role_ids?: string[];
}
+1 -13
View File
@@ -1,12 +1,4 @@
import type {
DiscordInviteCreate,
DiscordInviteMetadata,
GetMessagesAfter,
GetMessagesAround,
GetMessagesBefore,
GetMessagesLimit,
GetMessagesOptions,
} from '@discordeno/types';
import type { GetMessagesAfter, GetMessagesAround, GetMessagesBefore, GetMessagesLimit, GetMessagesOptions } from '@discordeno/types';
import { hasProperty } from './utils.js';
export function isGetMessagesAfter(options: GetMessagesOptions): options is GetMessagesAfter {
@@ -24,7 +16,3 @@ export function isGetMessagesAround(options: GetMessagesOptions): options is Get
export function isGetMessagesLimit(options: GetMessagesOptions): options is GetMessagesLimit {
return hasProperty(options, 'limit');
}
export function isInviteWithMetadata(options: DiscordInviteCreate | DiscordInviteMetadata): options is DiscordInviteMetadata {
return !hasProperty(options, 'channel_id');
}