diff --git a/src/handlers/voice/VOICE_STATE_UPDATE.ts b/src/handlers/voice/VOICE_STATE_UPDATE.ts index 8e2f3b514..3406114c1 100644 --- a/src/handlers/voice/VOICE_STATE_UPDATE.ts +++ b/src/handlers/voice/VOICE_STATE_UPDATE.ts @@ -9,6 +9,9 @@ export async function handleVoiceStateUpdate(data: DiscordGatewayPayload) { const payload = data.d as VoiceState; if (!payload.guildId) return; + // The "member" field in the payload is `null` if the user is in a live stage but not in the guild. + if (payload.member === null) return eventHandlers.voiceLurkerJoin?.(payload); + const guild = await cacheHandlers.get("guilds", snowflakeToBigint(payload.guildId)); if (!guild) return; diff --git a/src/types/discordeno/event_handlers.ts b/src/types/discordeno/event_handlers.ts index 60d59545c..176abee8e 100644 --- a/src/types/discordeno/event_handlers.ts +++ b/src/types/discordeno/event_handlers.ts @@ -9,8 +9,7 @@ import type { StageInstance } from "../channels/stage_instance.ts"; import type { ThreadMember } from "../channels/threads/thread_member.ts"; import type { ThreadMembersUpdate } from "../channels/threads/thread_members_update.ts"; import type { Emoji } from "../emojis/emoji.ts"; -import type { GatewayPayload } from "../gateway/gateway_payload.ts"; -import type { DiscordGatewayPayload } from "../gateway/gateway_payload.ts"; +import type { DiscordGatewayPayload, GatewayPayload } from "../gateway/gateway_payload.ts"; import type { IntegrationCreateUpdate } from "../integrations/integration_create_update.ts"; import type { IntegrationDelete } from "../integrations/integration_delete.ts"; import type { ApplicationCommandCreateUpdateDelete } from "../interactions/commands/application_command_create_update_delete.ts"; @@ -145,6 +144,8 @@ export type EventHandlersDefinitions = { voiceChannelLeave: [member: DiscordenoMember, channelId: bigint]; /** Sent when a user switches the voice channel */ voiceChannelSwitch: [member: DiscordenoMember, channelId: bigint, oldChannelId: bigint]; + /** Sent when a user/lurker joins a live stage, but they are not inside the guild. */ + voiceLurkerJoin: [voiceState: VoiceState]; /** Sent when a voice server is updated with information for making the bot connect to a voice channel. */ voiceServerUpdate: [payload: VoiceServerUpdate, guild: DiscordenoGuild]; /** Sent when someone joins/leaves/moves voice channels. */ diff --git a/src/types/voice/voice_state.ts b/src/types/voice/voice_state.ts index 156e15ad4..446bb122b 100644 --- a/src/types/voice/voice_state.ts +++ b/src/types/voice/voice_state.ts @@ -8,8 +8,8 @@ export interface VoiceState { channelId: string | null; /** The user id this voice state is for */ userId: string; - /** The guild member this voice state is for */ - member?: GuildMemberWithUser; + /** The guild member this voice state is for, or `null` if the user is in a live stage but not in the guild */ + member?: GuildMemberWithUser | null; /** The session id for this voice state */ sessionId: string; /** Whether this user is deafened by the server */