diff --git a/packages/benchmarks/src/benchmarks/transformers.ts b/packages/benchmarks/src/benchmarks/transformers.ts index 94459d4f7..3abc4aed1 100644 --- a/packages/benchmarks/src/benchmarks/transformers.ts +++ b/packages/benchmarks/src/benchmarks/transformers.ts @@ -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, } diff --git a/packages/bot/src/events.ts b/packages/bot/src/events.ts index 9be74ba67..59e5d9df9 100644 --- a/packages/bot/src/events.ts +++ b/packages/bot/src/events.ts @@ -42,7 +42,15 @@ export type EventHandlers[] 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) => unknown scheduledEventCreate: (event: SetupDesiredProps) => unknown diff --git a/packages/bot/src/handlers/channels/THREAD_MEMBER_UPDATE.ts b/packages/bot/src/handlers/channels/THREAD_MEMBER_UPDATE.ts index 4e9c1a92d..ca1bf3a31 100644 --- a/packages/bot/src/handlers/channels/THREAD_MEMBER_UPDATE.ts +++ b/packages/bot/src/handlers/channels/THREAD_MEMBER_UPDATE.ts @@ -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, }) } diff --git a/packages/bot/src/transformers/message.ts b/packages/bot/src/transformers/message.ts index 3dd6e7d2e..2684866ea 100644 --- a/packages/bot/src/transformers/message.ts +++ b/packages/bot/src/transformers/message.ts @@ -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) { diff --git a/packages/types/src/discord/channel.ts b/packages/types/src/discord/channel.ts index 84f113c4a..e7d178e25 100644 --- a/packages/types/src/discord/channel.ts +++ b/packages/types/src/discord/channel.ts @@ -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 { /** 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 */ diff --git a/packages/types/src/discord/gateway.ts b/packages/types/src/discord/gateway.ts index 82fd22afa..b15fc82a8 100644 --- a/packages/types/src/discord/gateway.ts +++ b/packages/types/src/discord/gateway.ts @@ -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[] + /** 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[] + /** 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 + /** Users specifically mentioned in the message */ + mentions: Array }> +} + +/** 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 {} +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 + /** 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 {} +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 +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 +} /** https://discord.com/developers/docs/events/gateway-events#presence-update-presence-update-event-fields */ export interface DiscordPresenceUpdate { diff --git a/packages/types/src/discord/guild.ts b/packages/types/src/discord/guild.ts index d4601717f..b5382d814 100644 --- a/packages/types/src/discord/guild.ts +++ b/packages/types/src/discord/guild.ts @@ -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 { /** 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[] - /** 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[] /** 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 */ diff --git a/packages/types/src/discord/message.ts b/packages/types/src/discord/message.ts index 394d6a2fe..4396dd4c0 100644 --- a/packages/types/src/discord/message.ts +++ b/packages/types/src/discord/message.ts @@ -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 { /** 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 }> + // 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[] /**