Split extra fields into their own types (#4256)

* Split extra fields into their own types

For now they are added back to the types they belong to to avoid
breaking in this pr

* fix type errors

* Fix typo 1

Co-authored-by: Link <lts20050703@gmail.com>

* Fix typo 2

Co-authored-by: Link <lts20050703@gmail.com>

* Use omit and not partial on guild voice_states

Co-authored-by: Link <lts20050703@gmail.com>

* Remove unused `ts-expect-error`

---------

Co-authored-by: Link <lts20050703@gmail.com>
This commit is contained in:
Fleny
2025-07-20 18:47:30 +02:00
committed by GitHub
co-authored by Link
parent 08a47e6872
commit 1cb60c909c
8 changed files with 169 additions and 86 deletions
@@ -467,6 +467,7 @@ function oldtransformMessage(bot: Bot, payload: DiscordMessage): any {
bot.transformers.snowflake(text.substring(2, text.length - 1)),
),
],
// @ts-expect-error: partials
member: payload.member && guildId ? bot.transformers.member(bot, payload.member, guildId, userId) : undefined,
nonce: payload.nonce,
}
+9 -1
View File
@@ -42,7 +42,15 @@ export type EventHandlers<TProps extends TransformersDesiredProperties, TBehavio
threads: SetupDesiredProps<Channel, TProps, TBehavior>[]
members: ThreadMember[]
}) => unknown
threadMemberUpdate: (payload: { id: bigint; guildId: bigint; joinedAt: number; flags: number }) => unknown
threadMemberUpdate: (payload: {
id: bigint
guildId: bigint
// TODO: remove this in the next major version
/** @deprecated Use joinedTimestamp */
joinedAt: number
joinedTimestamp: number
flags: number
}) => unknown
threadMembersUpdate: (payload: { id: bigint; guildId: bigint; addedMembers?: ThreadMember[]; removedMemberIds?: bigint[] }) => unknown
threadUpdate: (thread: SetupDesiredProps<Channel, TProps, TBehavior>) => unknown
scheduledEventCreate: (event: SetupDesiredProps<ScheduledEvent, TProps, TBehavior>) => unknown
@@ -9,7 +9,9 @@ export async function handleThreadMemberUpdate(bot: Bot, data: DiscordGatewayPay
bot.events.threadMemberUpdate({
id: bot.transformers.snowflake(payload.id),
guildId: bot.transformers.snowflake(payload.guild_id),
joinedAt: Date.parse(payload.joined_at),
// TODO: remove this in the next major version
joinedAt: 'joined_at' in payload && typeof payload.joined_at === 'string' ? Date.parse(payload.joined_at) : 0,
joinedTimestamp: Date.parse(payload.join_timestamp),
flags: payload.flags,
})
}
+1
View File
@@ -194,6 +194,7 @@ export function transformMessage(
message.interaction = interaction
}
if (props.member && guildId && userId && payload.message.member)
// @ts-expect-error TODO: partial
message.member = bot.transformers.member(bot, payload.message.member, guildId, userId)
if (payload.message.mention_everyone) message.mentionEveryone = true
if (props.mentionedChannelIds && payload.message.mention_channels?.length) {
+2 -8
View File
@@ -1,12 +1,13 @@
/** Types for: https://discord.com/developers/docs/resources/channel */
import type { DiscordMessageComponents } from './components.js'
import type { DiscordThreadCreateExtra } from './gateway.js'
import type { DiscordMember } from './guild.js'
import type { DiscordAllowedMentions, DiscordAttachment, DiscordEmbed, MessageFlags } from './message.js'
import type { DiscordUser } from './user.js'
/** https://discord.com/developers/docs/resources/channel#channel-object-channel-structure */
export interface DiscordChannel {
export interface DiscordChannel extends Partial<DiscordThreadCreateExtra> {
/** The id of the channel */
id: string
/** The type of channel */
@@ -133,13 +134,6 @@ export interface DiscordChannel {
default_sort_order?: SortOrderTypes | null
/** the default forum layout view used to display posts in `GUILD_FORUM` channels. Defaults to `0`, which indicates a layout view has not been set by a channel admin */
default_forum_layout?: ForumLayout
/**
* When a thread is created this will be true on that channel payload for the thread.
*
* @remarks
* This will only exists on Thread Create gateway events.
*/
newly_created?: boolean
}
/** https://discord.com/developers/docs/resources/channel#channel-object-channel-types */
+145 -27
View File
@@ -9,12 +9,15 @@ import type { AutoModerationTriggerTypes, DiscordAutoModerationAction } from './
import type { DiscordChannel, DiscordThreadMember } from './channel.js'
import type { DiscordEmoji } from './emoji.js'
import type { DiscordIntegration, DiscordMember, DiscordMemberWithUser, DiscordUnavailableGuild } from './guild.js'
import type { DiscordScheduledEvent } from './guildScheduledEvent.js'
import type { TargetTypes } from './invite.js'
import type { DiscordReactionType } from './message.js'
import type { DiscordRole } from './permissions.js'
import type { DiscordSoundboardSound } from './soundboard.js'
import type { DiscordStageInstance } from './stageInstance.js'
import type { DiscordSticker } from './sticker.js'
import type { DiscordAvatarDecorationData, DiscordUser } from './user.js'
import type { DiscordVoiceState } from './voice.js'
/** https://discord.com/developers/docs/events/gateway#list-of-intents */
export enum GatewayIntents {
@@ -193,9 +196,9 @@ export interface DiscordSessionStartLimit {
}
/** https://discord.com/developers/docs/events/gateway-events#receive-events */
// TODO: Move 'RESUMED' from GatewayEventNames to GatewayDispatchEventNames
export type GatewayDispatchEventNames =
| 'READY'
| 'RESUMED'
| 'APPLICATION_COMMAND_PERMISSIONS_UPDATE'
| 'AUTO_MODERATION_RULE_CREATE'
| 'AUTO_MODERATION_RULE_UPDATE'
@@ -271,7 +274,7 @@ export type GatewayDispatchEventNames =
| 'MESSAGE_POLL_VOTE_REMOVE'
/** https://discord.com/developers/docs/events/gateway-events#receive-events */
export type GatewayEventNames = GatewayDispatchEventNames | 'RESUMED'
export type GatewayEventNames = GatewayDispatchEventNames
/** https://discord.com/developers/docs/events/gateway-events#payload-structure */
export interface DiscordGatewayPayload {
@@ -363,6 +366,17 @@ export interface DiscordAutoModerationActionExecution {
matched_content: string | null
}
/** https://discord.com/developers/docs/events/gateway-events#thread-create */
export interface DiscordThreadCreateExtra {
/**
* When a thread is created this will be true on that channel payload for the thread.
*
* @remarks
* The Thread Create event may fire for a few reasons, however this fields only exists when it is fired because a thread was created.
*/
newly_created?: boolean
}
/** https://discord.com/developers/docs/events/gateway-events#thread-list-sync-thread-list-sync-event-fields */
export interface DiscordThreadListSync {
/** The id of the guild */
@@ -376,17 +390,14 @@ export interface DiscordThreadListSync {
}
/** https://discord.com/developers/docs/events/gateway-events#thread-member-update-thread-member-update-event-extra-fields */
export interface DiscordThreadMemberUpdate {
/** The id of the thread */
id: string
/** The id of the guild */
export interface DiscordThreadMemberUpdateExtra {
/** Id of the guild */
guild_id: string
/** The timestamp when the bot joined this thread. */
joined_at: string
/** The flags this user has for this thread. Not useful for bots. */
flags: number
}
/** https://discord.com/developers/docs/events/gateway-events#thread-member-update-thread-member-update-event-extra-fields */
export interface DiscordThreadMemberUpdate extends DiscordThreadMember, DiscordThreadMemberUpdateExtra {}
/** https://discord.com/developers/docs/events/gateway-events#thread-members-update-thread-members-update-event-fields */
export interface DiscordThreadMembersUpdate {
/** The id of the thread */
@@ -411,21 +422,75 @@ export interface DiscordChannelPinsUpdate {
last_pin_timestamp?: string | null
}
// TODO: Add Guild Create: https://discord.com/developers/docs/events/gateway-events#guild-create-guild-create-extra-fields
// TODO: Add Create Guild Audit Log Entry: https://discord.com/developers/docs/events/gateway-events#guild-audit-log-entry-create-guild-audit-log-entry-create-event-extra-fields
/** https://discord.com/developers/docs/events/gateway-events#guild-create-guild-create-extra-fields */
export interface DiscordGuildCreateExtra {
/** When this guild was joined at */
joined_at: string
/** If this is considered a large guild */
large: boolean
/** If the guild is unavailable due to an outage */
unavailable?: boolean
/** Total number of member in this guild */
member_count: number
/**
* States of members currently in voice channels
*
* @remarks
* Lacks the `guild_id` key
*/
voice_states: Omit<DiscordVoiceState, "guild_id">[]
/** Users in the guild */
members: DiscordMemberWithUser[]
/** Channels in the guild */
channels: DiscordChannel[]
/** All active threads in the guild that the current user has permission to view */
threads: DiscordChannel[]
/**
* Presences of the members in the guild
*
* @remarks
* Will only include non-offline members if the size is greater than the large threshold.
*/
presences?: Partial<DiscordPresenceUpdate>[]
/** Stage instances in the guild */
stage_instances?: DiscordStageInstance[]
/** Scheduled events in the guild */
guild_scheduled_events: DiscordScheduledEvent[]
/** Soundboard sounds in the guild */
soundboard_sounds: DiscordSoundboardSound[]
}
// TODO: Give both a name: https://discord.com/developers/docs/events/gateway-events#guild-ban-add-guild-ban-add-event-fields, https://discord.com/developers/docs/events/gateway-events#guild-ban-remove-guild-ban-remove-event-fields
/**
* https://discord.com/developers/docs/events/gateway-events#guild-ban-add-guild-ban-add-event-fields
* https://discord.com/developers/docs/events/gateway-events#guild-ban-remove-guild-ban-remove-event-fields
*/
export interface DiscordGuildBanAddRemove {
/** https://discord.com/developers/docs/events/gateway-events#guild-audit-log-entry-create-guild-audit-log-entry-create-event-extra-fields */
export interface DiscordGuildAuditLogEntryCreateExtra {
/** The id of the guild */
guild_id: string
}
/** https://discord.com/developers/docs/events/gateway-events#guild-ban-add-guild-ban-add-event-fields */
export interface DiscordGuildBanAdd {
/** id of the guild */
guild_id: string
/** The banned user */
user: DiscordUser
}
/** https://discord.com/developers/docs/events/gateway-events#guild-ban-remove-guild-ban-remove-event-fields */
export interface DiscordGuildBanRemove {
/** id of the guild */
guild_id: string
/** The banned user */
user: DiscordUser
}
/**
* https://discord.com/developers/docs/events/gateway-events#guild-ban-add-guild-ban-add-event-fields
* https://discord.com/developers/docs/events/gateway-events#guild-ban-remove-guild-ban-remove-event-fields
*
* @deprecated
* Use {@link DiscordGuildBanAdd} and {@link DiscordGuildBanRemove} instead.
*/
export interface DiscordGuildBanAddRemove extends DiscordGuildBanAdd {}
/** https://discord.com/developers/docs/events/gateway-events#guild-emojis-update-guild-emojis-update-event-fields */
export interface DiscordGuildEmojisUpdate {
/** id of the guild */
@@ -449,11 +514,14 @@ export interface DiscordGuildIntegrationsUpdate {
}
/** https://discord.com/developers/docs/events/gateway-events#guild-member-add-guild-member-add-extra-fields */
export interface DiscordGuildMemberAdd extends DiscordMemberWithUser {
export interface DiscordGuildMemberAddExtra {
/** id of the guild */
guild_id: string
}
/** https://discord.com/developers/docs/events/gateway-events#guild-member-add-guild-member-add-extra-fields */
export interface DiscordGuildMemberAdd extends DiscordMemberWithUser, DiscordGuildMemberAddExtra {}
/** https://discord.com/developers/docs/events/gateway-events#guild-member-remove-guild-member-remove-event-fields */
export interface DiscordGuildMemberRemove {
/** The id of the guild */
@@ -580,7 +648,18 @@ export interface DiscordSoundboardSounds {
guild_id: string
}
// TODO: Add separate type for Integration Create and Integration Update: https://discord.com/developers/docs/events/gateway-events#integration-create-integration-create-event-additional-fields, https://discord.com/developers/docs/events/gateway-events#integration-update-integration-update-event-additional-fields
/** https://discord.com/developers/docs/events/gateway-events#integration-create-integration-create-event-additional-fields */
export interface DiscordIntegrationCreateExtra {
/** Id of the guild */
guild_id: string
}
/** https://discord.com/developers/docs/events/gateway-events#integration-update-integration-update-event-additional-fields */
export interface DiscordIntegrationUpdateExtra {
/** Id of the guild */
guild_id: string
}
/**
* https://discord.com/developers/docs/events/gateway-events#integration-create-integration-create-event-additional-fields
* https://discord.com/developers/docs/events/gateway-events#integration-update-integration-update-event-additional-fields
@@ -638,7 +717,18 @@ export interface DiscordInviteDelete {
code: string
}
// TODO: Add Message Create: https://discord.com/developers/docs/events/gateway-events#message-create-message-create-extra-fields
/** https://discord.com/developers/docs/events/gateway-events#message-create-message-create-extra-fields */
export interface DiscordMessageCreateExtra {
/** ID of the guild the message was sent in - unless it is an ephemeral message */
guild_id?: string
/** Member properties for this message's author. Missing for ephemeral messages and messages from webhooks */
member?: Partial<DiscordMemberWithUser>
/** Users specifically mentioned in the message */
mentions: Array<DiscordUser & { member?: Partial<DiscordMember> }>
}
/** https://discord.com/developers/docs/events/gateway-events#message-update */
export type DiscordMessageUpdateExtra = DiscordMessageCreateExtra
/** https://discord.com/developers/docs/events/gateway-events#message-delete-message-delete-event-fields */
export interface DiscordMessageDelete {
@@ -684,17 +774,45 @@ export interface DiscordMessageReactionAdd {
type: DiscordReactionType
}
// TODO: This should provably not depend on DiscordMessageReactionAdd
/** https://discord.com/developers/docs/events/gateway-events#message-reaction-remove-message-reaction-remove-event-fields */
export interface DiscordMessageReactionRemove extends Omit<DiscordMessageReactionAdd, 'member' | 'burst_colors'> {}
export interface DiscordMessageReactionRemove {
/** The id of the user */
user_id: string
/** The id of the channel */
channel_id: string
/** The id of the message */
message_id: string
/** The id of the guild */
guild_id?: string
/** The emoji used to react */
emoji: Partial<DiscordEmoji>
/** true if this is a super-reaction */
burst: boolean
/** The type of reaction */
type: DiscordReactionType
}
// TODO: This should provably not depend on DiscordMessageReactionAdd
/** https://discord.com/developers/docs/events/gateway-events#message-reaction-remove-all-message-reaction-remove-all-event-fields */
export interface DiscordMessageReactionRemoveAll extends Pick<DiscordMessageReactionAdd, 'channel_id' | 'message_id' | 'guild_id'> {}
export interface DiscordMessageReactionRemoveAll {
/** The id of the channel */
channel_id: string
/** The id of the message */
message_id: string
/** The id of the guild */
guild_id?: string
}
// TODO: This should provably not depend on DiscordMessageReactionAdd
/** https://discord.com/developers/docs/events/gateway-events#message-reaction-remove-emoji-message-reaction-remove-emoji-event-fields */
export type DiscordMessageReactionRemoveEmoji = Pick<DiscordMessageReactionAdd, 'channel_id' | 'guild_id' | 'message_id' | 'emoji'>
export interface DiscordMessageReactionRemoveEmoji {
/** The id of the channel */
channel_id: string
/** The id of the message */
message_id: string
/** The id of the guild */
guild_id?: string
/** The emoji used to react */
emoji: Partial<DiscordEmoji>
}
/** https://discord.com/developers/docs/events/gateway-events#presence-update-presence-update-event-fields */
export interface DiscordPresenceUpdate {
+2 -32
View File
@@ -2,17 +2,14 @@
import type { ChannelTypes, DiscordChannel, DiscordOverwrite, DiscordThreadMember, SortOrderTypes } from './channel.js'
import type { DiscordEmoji } from './emoji.js'
import type { DiscordPresenceUpdate } from './gateway.js'
import type { DiscordGuildCreateExtra } from './gateway.js'
import type { OAuth2Scope } from './oauth2.js'
import type { DiscordRole } from './permissions.js'
import type { DiscordSoundboardSound } from './soundboard.js'
import type { DiscordStageInstance } from './stageInstance.js'
import type { DiscordSticker } from './sticker.js'
import type { DiscordAvatarDecorationData, DiscordUser } from './user.js'
import type { DiscordVoiceState } from './voice.js'
/** https://discord.com/developers/docs/resources/guild#guild-object-guild-structure */
export interface DiscordGuild {
export interface DiscordGuild extends Partial<DiscordGuildCreateExtra> {
/** Guild name (2-100 characters, excluding trailing and leading whitespace) */
name: string
/** True if the user is the owner of the guild */
@@ -33,12 +30,6 @@ export interface DiscordGuild {
mfa_level: MfaLevels
/** System channel flags */
system_channel_flags: SystemChannelFlags
/** True if this is considered a large guild */
large?: boolean
/** True if this guild is unavailable due to an outage */
unavailable?: boolean
/** Total number of members in this guild */
member_count?: number
/** The maximum number of presences for the guild (the default value, currently 25000, is in effect when null is returned) */
max_presences?: number | null
/** The maximum number of members for the guild */
@@ -91,18 +82,6 @@ export interface DiscordGuild {
system_channel_id: string | null
/** The id of the channel where community guilds can display rules and/or guidelines */
rules_channel_id: string | null
/** When this guild was joined at */
joined_at?: string
/** States of members currently in voice channels; lacks the guild_id key */
voice_states?: Omit<DiscordVoiceState, 'guildId'>[]
/** Users in the guild */
members?: DiscordMember[]
/** Channels in the guild */
channels?: DiscordChannel[]
/** All active threads in the guild that the current user has permission to view */
threads?: DiscordChannel[]
/** Presences of the members in the guild, will only include non-offline members if the size is greater than large threshold */
presences?: Partial<DiscordPresenceUpdate>[]
/** Banner hash */
banner: string | null
/** The preferred locale of a Community guild; used in server discovery and notices from Discord; defaults to "en-US" */
@@ -111,21 +90,12 @@ export interface DiscordGuild {
public_updates_channel_id: string | null
/** The welcome screen of a Community guild, shown to new members, returned in an Invite's guild object */
welcome_screen?: DiscordWelcomeScreen
/** Stage instances in the guild */
stage_instances?: DiscordStageInstance[]
/** Custom guild stickers */
stickers?: DiscordSticker[]
/** The id of the channel where admins and moderators of Community guilds receive safety alerts from Discord */
safety_alerts_channel_id: string | null
/** The incidents data for this guild */
incidents_data: DiscordIncidentsData
/**
* Soundboard sounds in the guild
*
* @remarks
* Only sent by the gateway
*/
soundboard_sounds?: DiscordSoundboardSound[]
}
/** https://discord.com/developers/docs/resources/guild#guild-object-default-message-notification-level */
+6 -17
View File
@@ -4,33 +4,23 @@ import type { DiscordApplication } from './application.js'
import type { DiscordChannel, DiscordThreadMember } from './channel.js'
import type { DiscordMessageComponents } from './components.js'
import type { DiscordEmoji } from './emoji.js'
import type { DiscordMember } from './guild.js'
import type { DiscordMessageCreateExtra } from './gateway.js'
import type { DiscordAuthorizingIntegrationOwners, DiscordMessageInteraction, InteractionTypes } from './interactions.js'
import type { DiscordPoll } from './poll.js'
import type { DiscordSticker, DiscordStickerItem } from './sticker.js'
import type { DiscordUser } from './user.js'
/** https://discord.com/developers/docs/resources/message#message-object-message-structure */
export interface DiscordMessage {
export interface DiscordMessage extends Partial<DiscordMessageCreateExtra> {
/** id of the message */
id: string
/** id of the channel the message was sent in */
channel_id: string
/**
* id of the guild the message was sent in
* Note: For MESSAGE_CREATE and MESSAGE_UPDATE events, the message object may not contain a guild_id or member field since the events are sent directly to the receiving user and the bot who sent the message, rather than being sent through the guild like non-ephemeral messages.
*/
guild_id?: string
/**
* The author of this message (not guaranteed to be a valid user)
* Note: The author object follows the structure of the user object, but is only a valid user in the case where the message is generated by a user or bot user. If the message is generated by a webhook, the author object corresponds to the webhook's id, username, and avatar. You can tell if a message is generated by a webhook by checking for the webhook_id on the message object.
*/
author: DiscordUser
/**
* Member properties for this message's author
* Note: The member object exists in `MESSAGE_CREATE` and `MESSAGE_UPDATE` events from text-based guild channels. This allows bots to obtain real-time member data without requiring bots to store member state in memory.
*/
member?: DiscordMember
/** Contents of the message */
content?: string
/** When this message was sent */
@@ -41,11 +31,10 @@ export interface DiscordMessage {
tts: boolean
/** Whether this message mentions everyone */
mention_everyone: boolean
/**
* Users specifically mentioned in the message
* Note: The user objects in the mentions array will only have the partial member field present in `MESSAGE_CREATE` and `MESSAGE_UPDATE` events from text-based guild channels.
*/
mentions: Array<DiscordUser & { member?: Partial<DiscordMember> }>
// TODO: When we separate the types between with extra fields from gateway and non, we should add back the mentions field below
// For now it can remain from the gateway extra fields to avoid breaking changes for now.
// /** Users specifically mentioned in the message */
// mentions: DiscordUser[]
/** Roles specifically mentioned in this message */
mention_roles?: string[]
/**