lunibee 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +225 -225
  3. package/dist/builders/attachment.d.ts +10 -2
  4. package/dist/builders/commands.d.ts +161 -101
  5. package/dist/builders/components.d.ts +54 -75
  6. package/dist/builders/embed.d.ts +68 -35
  7. package/dist/builders/index.d.ts +1 -1
  8. package/dist/builders/index.js +108 -133
  9. package/dist/builders/index.js.map +8 -8
  10. package/dist/collection/cache.d.ts +13 -6
  11. package/dist/collection/index.d.ts +90 -17
  12. package/dist/collection/index.js +442 -266
  13. package/dist/collection/index.js.map +4 -4
  14. package/dist/core/index.d.ts +9 -0
  15. package/dist/core/index.js +791 -436
  16. package/dist/core/index.js.map +47 -47
  17. package/dist/core/permissions.d.ts +9 -5
  18. package/dist/formatters/index.js +9 -5
  19. package/dist/formatters/index.js.map +3 -3
  20. package/dist/handlers/index.js +5 -10
  21. package/dist/handlers/index.js.map +3 -3
  22. package/dist/index.js +1010 -679
  23. package/dist/index.js.map +66 -65
  24. package/dist/managers/base.d.ts +26 -14
  25. package/dist/managers/emoji.d.ts +7 -1
  26. package/dist/managers/guild-resources.d.ts +11 -3
  27. package/dist/managers/guild.d.ts +13 -5
  28. package/dist/managers/index.d.ts +28 -14
  29. package/dist/managers/index.js +580 -340
  30. package/dist/managers/index.js.map +26 -26
  31. package/dist/managers/member.d.ts +5 -3
  32. package/dist/managers/message.d.ts +7 -3
  33. package/dist/managers/role.d.ts +13 -1
  34. package/dist/rest/error-codes.d.ts +48 -0
  35. package/dist/rest/errors.d.ts +14 -6
  36. package/dist/rest/index.d.ts +25 -12
  37. package/dist/rest/index.js +105 -18
  38. package/dist/rest/index.js.map +15 -14
  39. package/dist/rest/redis.d.ts +1 -1
  40. package/dist/rest/route.d.ts +8 -4
  41. package/dist/rest/routes.d.ts +102 -46
  42. package/dist/rest/transport.d.ts +10 -5
  43. package/dist/rest/webhook.d.ts +9 -5
  44. package/dist/sharding/bus.d.ts +34 -17
  45. package/dist/sharding/index.d.ts +34 -17
  46. package/dist/sharding/index.js +68 -36
  47. package/dist/sharding/index.js.map +17 -17
  48. package/dist/structures/base.d.ts +28 -10
  49. package/dist/structures/channels.d.ts +28 -14
  50. package/dist/structures/index.d.ts +6 -6
  51. package/dist/structures/index.js +81 -99
  52. package/dist/structures/index.js.map +19 -18
  53. package/dist/structures/interactions.d.ts +98 -50
  54. package/dist/structures/resources.d.ts +90 -46
  55. package/dist/types/gateway.d.ts +1 -1
  56. package/dist/types/index.d.ts +43 -18
  57. package/dist/types/index.js +20 -22
  58. package/dist/types/index.js.map +4 -4
  59. package/dist/utils/index.js.map +2 -2
  60. package/dist/voice/index.d.ts +12 -4
  61. package/dist/voice/index.js +17 -20
  62. package/dist/voice/index.js.map +3 -3
  63. package/dist/ws/decoder.d.ts +6 -2
  64. package/dist/ws/index.d.ts +25 -13
  65. package/dist/ws/index.js +63 -31
  66. package/dist/ws/index.js.map +14 -14
  67. package/dist/ws/protocol.d.ts +10 -2
  68. package/dist/ws/session.d.ts +14 -4
  69. package/dist/ws/state.d.ts +22 -11
  70. package/package.json +16 -15
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../packages/voice/src/index.ts"],
4
4
  "sourcesContent": [
5
- "/** Voice session lifecycle states. */\nexport enum VoiceConnectionState {\n Disconnected = \"disconnected\",\n Connecting = \"connecting\",\n Connected = \"connected\",\n Destroyed = \"destroyed\",\n}\n\n/** Voice protocol transport errors. */\nexport class VoiceError extends Error {\n /** Creates a voice error. @param message Error message. @param options Optional native error options. */\n public constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"VoiceError\";\n }\n}\n\n/** Options used to establish a voice session. */\nexport interface VoiceConnectionOptions {\n /** Guild containing the voice channel. */\n guildId: string;\n /** Voice channel to join. */\n channelId?: string;\n /** Whether the self user should be muted. */\n selfMute?: boolean;\n /** Whether the self user should be deafened. */\n selfDeaf?: boolean;\n}\n\n/** UDP transport supplied by the runtime-specific voice implementation. */\nexport interface VoiceUdpTransport {\n /** Sends an RTP/RTCP packet. @param packet Packet bytes. @param host Destination host. @param port Destination port. @returns Optional completion promise. */\n send(packet: Uint8Array, host: string, port: number): Promise<void> | void;\n /** Closes the UDP socket. @returns Nothing. */\n close(): void;\n /** Optional listener for incoming UDP packets. */\n onMessage?(listener: (packet: Uint8Array) => void): void;\n}\n\n/** Voice Gateway transport abstraction. */\nexport interface VoiceGatewayTransport {\n /** Opens the voice gateway. @param endpoint Voice gateway endpoint. @param sessionId Voice session identifier. @returns Promise fulfilled when connected. */\n connect(endpoint: string, sessionId: string): Promise<void>;\n /** Sends a voice gateway payload. @param payload Gateway payload. @returns Nothing. */\n send(payload: unknown): void;\n /** Closes the voice gateway. @param code Optional close code. @param reason Optional close reason. @returns Nothing. */\n close(code?: number, reason?: string): void;\n}\n\n/** Speaking flags used by the voice gateway. */\nexport const SpeakingFlags = {\n Microphone: 1,\n Soundshare: 2,\n Priority: 4,\n} as const;\n\n/** Voice session event payloads. */\nexport interface VoiceEvents {\n /** State transition event. */\n stateChange: [VoiceConnectionState, VoiceConnectionState];\n /** Speaking flag event. */\n speaking: [number, number];\n /** Voice transport error event. */\n error: [VoiceError];\n}\n\ntype VoiceListener = (...args: any[]) => unknown;\n\n/** Tracks a Discord voice session and exposes transport-independent lifecycle controls. */\nexport class VoiceConnection {\n /** Current voice connection state. */\n public state: VoiceConnectionState = VoiceConnectionState.Disconnected;\n /** Guild associated with this voice session. */\n public readonly guildId: string;\n /** Voice channel associated with this session. */\n public channelId?: string;\n /** Whether the client is self-muted. */\n public selfMute: boolean;\n /** Whether the client is self-deafened. */\n public selfDeaf: boolean;\n /** Active voice gateway transport, when attached. */\n public gateway?: VoiceGatewayTransport;\n /** Active UDP transport, when attached. */\n public udp?: VoiceUdpTransport;\n /** Registered lifecycle listeners. */\n readonly #listeners = new Map<keyof VoiceEvents, Set<VoiceListener>>();\n /** Receives incoming audio. */\n public readonly receiver: VoiceReceiver;\n\n /** Creates a voice connection state manager. @param options Guild ID or connection options. @throws {TypeError} If the guild ID is empty. */\n public constructor(options: string | VoiceConnectionOptions) {\n const resolved =\n typeof options === \"string\" ? { guildId: options } : options;\n if (!resolved.guildId?.trim()) {\n throw new TypeError(\n \"A guild ID is required for a voice connection.\",\n );\n }\n this.guildId = resolved.guildId;\n this.channelId = resolved.channelId;\n this.selfMute = resolved.selfMute ?? false;\n this.selfDeaf = resolved.selfDeaf ?? false;\n this.receiver = new VoiceReceiver(this);\n }\n\n /** Registers a voice lifecycle listener. @param event Event name. @param listener Listener callback. @returns This connection. @throws {TypeError} If listener is not callable. */\n public on<K extends keyof VoiceEvents>(\n event: K,\n listener: (...args: VoiceEvents[K]) => unknown,\n ): this {\n if (typeof listener !== \"function\")\n throw new TypeError(\"Voice listener must be a function.\");\n let listeners = this.#listeners.get(event);\n if (!listeners) this.#listeners.set(event, (listeners = new Set()));\n listeners.add(listener);\n return this;\n }\n\n /** Removes a voice lifecycle listener. @param event Event name. @param listener Listener callback. @returns This connection. */\n public off<K extends keyof VoiceEvents>(\n event: K,\n listener: (...args: VoiceEvents[K]) => unknown,\n ): this {\n this.#listeners.get(event)?.delete(listener);\n return this;\n }\n\n /** Attaches protocol transports, replacing and cleaning up the old pair when present. @param gateway Voice gateway transport. @param udp UDP transport. @returns This connection. @throws {VoiceError} If the connection is destroyed. @throws {TypeError} If a transport is missing. */\n public attachTransports(\n gateway: VoiceGatewayTransport,\n udp: VoiceUdpTransport,\n ): this {\n this.assertUsable();\n if (!gateway || !udp)\n throw new TypeError(\n \"Both voice gateway and UDP transports are required.\",\n );\n if (this.gateway || this.udp) this.#closeTransports();\n this.gateway = gateway;\n this.udp = udp;\n // Capture the transport this listener belongs to so that packets from a\n // later-replaced transport (whose listener cannot be unregistered — the\n // VoiceUdpTransport interface exposes no removal API) are ignored.\n const attachedUdp = udp;\n if (attachedUdp.onMessage) {\n attachedUdp.onMessage((packet) => {\n if (this.udp === attachedUdp) this.receiver.onPacket(packet);\n });\n }\n return this;\n }\n\n /** Begins the connection lifecycle. @returns Nothing. @throws {VoiceError} If the connection is destroyed. */\n public connect(): void {\n this.assertUsable();\n if (this.state === VoiceConnectionState.Connected) return;\n this.#transition(VoiceConnectionState.Connecting);\n if (this.state !== VoiceConnectionState.Connecting) return;\n this.#transition(VoiceConnectionState.Connected);\n }\n\n /** Disconnects the current voice session. @returns Nothing. */\n public disconnect(): void {\n if (this.state === VoiceConnectionState.Destroyed) return;\n this.#closeTransports();\n this.receiver.close();\n this.#transition(VoiceConnectionState.Disconnected);\n }\n\n /** Permanently destroys this voice connection. @returns Nothing. */\n public destroy(): void {\n if (this.state === VoiceConnectionState.Destroyed) return;\n this.#closeTransports();\n this.receiver.close();\n this.channelId = undefined;\n this.#transition(VoiceConnectionState.Destroyed);\n this.#listeners.clear();\n }\n\n /** Updates the voice channel and connection state. @param channelId Voice channel identifier. @returns Nothing. @throws {VoiceError} If the connection is destroyed. @throws {TypeError} If channelId is empty. */\n public setChannel(channelId: string): void {\n this.assertUsable();\n if (!channelId?.trim())\n throw new TypeError(\"A voice channel ID is required.\");\n this.channelId = channelId;\n }\n\n /** Updates self mute/deaf state. @param options Suppression flags. @returns Nothing. @throws {VoiceError} If the connection is destroyed. */\n public setSuppression(options: {\n selfMute?: boolean;\n selfDeaf?: boolean;\n }): void {\n this.assertUsable();\n if (options.selfMute !== undefined) this.selfMute = options.selfMute;\n if (options.selfDeaf !== undefined) this.selfDeaf = options.selfDeaf;\n }\n\n /** Publishes a speaking state through the attached voice gateway. @param flags Speaking flags. @returns Nothing. @throws {VoiceError} If the connection is destroyed or has no gateway transport. */\n public setSpeaking(flags: number = SpeakingFlags.Microphone): void {\n this.assertUsable();\n if (!this.gateway) {\n throw new VoiceError(\n \"A voice gateway transport is required to change speaking state.\",\n );\n }\n this.gateway.send({ op: 5, d: { speaking: flags, delay: 0, ssrc: 0 } });\n this.#emit(\"speaking\", flags, 0);\n }\n\n /** Ensures the connection is still usable. @returns Nothing. @throws {VoiceError} If destroyed. */\n #assertUsable(): void {\n if (this.state === VoiceConnectionState.Destroyed) {\n throw new VoiceError(\"Voice connection has been destroyed.\");\n }\n }\n\n /** Closes both active transports while isolating cleanup failures. @returns Nothing. */\n #closeTransports(): void {\n try {\n this.gateway?.close(1000, \"Voice connection replaced or closed\");\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice gateway cleanup failed.\", {\n cause: error,\n }),\n );\n }\n try {\n this.udp?.close();\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice UDP cleanup failed.\", {\n cause: error,\n }),\n );\n }\n this.gateway = undefined;\n this.udp = undefined;\n }\n\n /** Transitions to a new lifecycle state and emits the transition event. @param next Next state. @returns Nothing. */\n #transition(next: VoiceConnectionState): void {\n const previous = this.state;\n this.state = next;\n if (previous === next) return;\n for (const listener of this.#listeners.get(\"stateChange\") ?? []) {\n try {\n void listener(next, previous);\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof VoiceError\n ? error\n : new VoiceError(\"Voice event listener failed.\", {\n cause: error,\n }),\n );\n }\n if (this.state !== next && next !== VoiceConnectionState.Destroyed)\n return;\n }\n }\n\n /** Emits a voice event while isolating consumer failures. @param event Event name. @param args Event arguments. @returns Nothing. */\n #emit<K extends keyof VoiceEvents>(\n event: K,\n ...args: VoiceEvents[K]\n ): void {\n for (const listener of this.#listeners.get(event) ?? []) {\n try {\n void listener(...args);\n } catch {\n /* Consumer failures are isolated. */\n }\n }\n }\n\n /** Ensures the connection is still usable. @returns Nothing. @throws {VoiceError} If destroyed. */\n private assertUsable(): void {\n this.#assertUsable();\n }\n}\n\n// ─── Audio Pipeline ───────────────────────────────────────────────────────────\n\n/** Audio player lifecycle states. */\nexport type AudioPlayerState = \"idle\" | \"playing\" | \"paused\" | \"stopped\";\n\n/** An audio stream — wraps a `ReadableStream<Uint8Array>` with optional metadata. */\nexport class AudioStream {\n /** Underlying binary stream. */ public readonly stream: ReadableStream<Uint8Array>;\n /** Optional human-readable title for the stream. */ public readonly title?: string;\n /** Optional duration hint in seconds. */ public readonly duration?: number;\n\n /** Creates an audio stream.\n * @param stream Binary audio data stream.\n * @param metadata Optional metadata: title and duration in seconds.\n */\n public constructor(\n stream: ReadableStream<Uint8Array>,\n metadata?: { title?: string; duration?: number },\n ) {\n this.stream = stream;\n this.title = metadata?.title;\n this.duration = metadata?.duration;\n }\n}\n\ntype AudioPlayerListener<K extends keyof AudioPlayerEvents> = (\n ...args: AudioPlayerEvents[K]\n) => unknown;\n\ninterface AudioPlayerEvents {\n stateChange: [from: AudioPlayerState, to: AudioPlayerState];\n error: [error: Error];\n finish: [];\n}\n\n/**\n * Lightweight audio player with a simple state machine.\n *\n * States:\n * - `idle` — no stream loaded.\n * - `playing` — actively feeding audio data.\n * - `paused` — temporarily suspended; can be resumed.\n * - `stopped` — stream was stopped and discarded; load a new stream to play again.\n *\n * Events: `stateChange(from, to)`, `error(err)`, `finish()`.\n */\nexport class AudioPlayer {\n #state: AudioPlayerState = \"idle\";\n #stream?: AudioStream;\n #reader?: ReadableStreamDefaultReader<Uint8Array>;\n /** Resolves the pump's pause await when the player is resumed or stopped. */\n #resumeSignal?: () => void;\n readonly #listeners = new Map<\n string,\n Set<AudioPlayerListener<keyof AudioPlayerEvents>>\n >();\n\n /** Current player state. */\n public get state(): AudioPlayerState {\n return this.#state;\n }\n\n /** The audio stream currently loaded, if any. */\n public get current(): AudioStream | undefined {\n return this.#stream;\n }\n\n /**\n * Begins playing an audio stream.\n * If a stream is already playing, it is stopped first; the previous\n * stream's reader cancellation is awaited before the new stream starts so\n * the old and new pump loops cannot run concurrently.\n * @param audioStream Stream to play.\n */\n public async play(audioStream: AudioStream): Promise<void> {\n if (this.#state === \"playing\" || this.#state === \"paused\")\n await this.stop();\n this.#stream = audioStream;\n this.#reader = audioStream.stream.getReader();\n this.#transition(\"playing\");\n this.#pump().catch((err) => this.#emitError(err));\n }\n\n /** Pauses a currently-playing stream. No-op if not playing. */\n public pause(): void {\n if (this.#state !== \"playing\") return;\n this.#transition(\"paused\");\n }\n\n /** Resumes a paused stream. No-op if not paused. */\n public resume(): void {\n if (this.#state !== \"paused\") return;\n this.#transition(\"playing\");\n // Wake the existing pump loop rather than starting a second one.\n const signal = this.#resumeSignal;\n this.#resumeSignal = undefined;\n signal?.();\n }\n\n /**\n * Stops and discards the current stream. The returned promise resolves once\n * the underlying reader's cancellation has settled, so a subsequent `play`\n * never overlaps with the previous pump loop.\n */\n public async stop(): Promise<void> {\n if (this.#state === \"idle\" || this.#state === \"stopped\") return;\n const reader = this.#reader;\n this.#reader = undefined;\n this.#stream = undefined;\n this.#transition(\"stopped\");\n // Release a pump loop that is currently awaiting a resume.\n const signal = this.#resumeSignal;\n this.#resumeSignal = undefined;\n signal?.();\n if (reader) {\n try {\n await reader.cancel();\n } catch {\n /* Cancellation failures are non-fatal. */\n }\n }\n }\n\n /** Registers an event listener. @returns `this` for chaining. */\n public on<K extends keyof AudioPlayerEvents>(\n event: K,\n listener: AudioPlayerListener<K>,\n ): this {\n let set = this.#listeners.get(event) as\n Set<AudioPlayerListener<K>> | undefined;\n if (!set) {\n this.#listeners.set(\n event,\n (set = new Set() as unknown as Set<\n AudioPlayerListener<keyof AudioPlayerEvents>\n >),\n );\n }\n (set as unknown as Set<AudioPlayerListener<K>>).add(listener);\n return this;\n }\n\n /** Removes an event listener. @returns `this` for chaining. */\n public off<K extends keyof AudioPlayerEvents>(\n event: K,\n listener: AudioPlayerListener<K>,\n ): this {\n (\n this.#listeners.get(event) as\n Set<AudioPlayerListener<K>> | undefined\n )?.delete(listener);\n return this;\n }\n\n /**\n * Handles a decoded audio chunk. The base implementation is a no-op;\n * subclasses override this to pipe audio to a UDP socket or other sink.\n * @param chunk A chunk of audio bytes read from the current stream.\n */\n protected onChunk(chunk: Uint8Array): void {\n void chunk;\n }\n\n /** Reads chunks from the reader while the player is in a playing state. */\n async #pump(): Promise<void> {\n const reader = this.#reader;\n if (!reader) return;\n while (true) {\n // Suspend without polling: park until resume()/stop() signals us.\n if (this.#state === \"paused\") {\n await new Promise<void>((resolve) => {\n this.#resumeSignal = resolve;\n });\n }\n // Abort if stopped/idle (e.g. resolved by stop()).\n if (this.#state !== \"playing\") return;\n const { done, value } = await reader.read();\n if (done) {\n if (this.#state === \"playing\" || this.#state === \"paused\") {\n this.#transition(\"idle\");\n this.#stream = undefined;\n this.#reader = undefined;\n this.#emit(\"finish\");\n }\n return;\n }\n // A stop() during the read wins — drop the chunk and exit.\n if (this.#state !== \"playing\" && this.#state !== \"paused\") return;\n // Deliver the chunk to the sink hook; loop back to honour pause state.\n this.onChunk(value);\n }\n }\n\n #transition(next: AudioPlayerState): void {\n if (this.#state === next) return;\n const from = this.#state;\n this.#state = next;\n this.#emit(\"stateChange\", from, next);\n }\n\n #emitError(err: unknown): void {\n this.#emit(\n \"error\",\n err instanceof Error ? err : new VoiceError(String(err)),\n );\n }\n\n #emit<K extends keyof AudioPlayerEvents>(\n event: K,\n ...args: AudioPlayerEvents[K]\n ): void {\n for (const listener of this.#listeners.get(event) ?? []) {\n try {\n void (listener as (...a: unknown[]) => unknown)(...args);\n } catch {\n /* Listener failures are isolated. */\n }\n }\n }\n}\n\n// ─── Audio Receive Pipeline ───────────────────────────────────────────────────\n\n/** Tracks incoming audio and maps SSRCs to readable streams per user. */\nexport class VoiceReceiver {\n /** The connection this receiver is attached to. */\n public readonly connection: VoiceConnection;\n readonly #ssrcMap = new Map<number, string>();\n /** Active stream controllers per user; a Set so multiple subscribers to the\n * same user all receive audio instead of the last one overwriting earlier ones. */\n readonly #streams = new Map<\n string,\n Set<ReadableStreamDefaultController<Uint8Array>>\n >();\n\n /** Creates a VoiceReceiver. @param connection Voice connection. */\n public constructor(connection: VoiceConnection) {\n this.connection = connection;\n }\n\n /** Maps an SSRC to a User ID (usually from gateway speaking events). */\n public mapSsrc(ssrc: number, userId: string): void {\n this.#ssrcMap.set(ssrc, userId);\n }\n\n /** Subscribes to incoming audio from a specific user. @param userId User identifier. @returns A binary readable stream of the audio. */\n public subscribe(userId: string): AudioStream {\n // A destroyed connection never closes its receiver again, so a late\n // subscriber would wait forever.\n if (this.connection.state === VoiceConnectionState.Destroyed)\n throw new VoiceError(\"Voice connection has been destroyed.\");\n let controllerRef: ReadableStreamDefaultController<Uint8Array>;\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n let set = this.#streams.get(userId);\n if (!set) this.#streams.set(userId, (set = new Set()));\n set.add(controller);\n },\n cancel: () => {\n const set = this.#streams.get(userId);\n if (set) {\n set.delete(controllerRef);\n if (set.size === 0) this.#streams.delete(userId);\n }\n },\n });\n return new AudioStream(stream, { title: `User ${userId} Audio` });\n }\n\n /** Ends every subscriber stream and forgets SSRC mappings. Called when the\n * connection disconnects or is destroyed, so readers do not wait forever. */\n public close(): void {\n for (const controllers of this.#streams.values())\n for (const controller of controllers) {\n try {\n controller.close();\n } catch {\n // Already closed or errored by the consumer.\n }\n }\n this.#streams.clear();\n this.#ssrcMap.clear();\n }\n\n /** Processes an incoming UDP packet, extracting SSRC and routing the audio\n * payload (RTP header stripped) to every subscriber for the mapped user. */\n public onPacket(packet: Uint8Array): void {\n if (packet.length < 12) return;\n\n const view = new DataView(\n packet.buffer,\n packet.byteOffset,\n packet.byteLength,\n );\n const ssrc = view.getUint32(8, false);\n\n const userId = this.#ssrcMap.get(ssrc);\n if (!userId) return;\n const controllers = this.#streams.get(userId);\n if (!controllers || controllers.size === 0) return;\n\n // Compute the RTP payload offset: 12-byte fixed header + CSRC list, plus\n // the extension header when the X bit is set.\n const csrcCount = packet[0] & 0x0f;\n let offset = 12 + csrcCount * 4;\n const hasExtension = (packet[0] & 0x10) !== 0;\n if (hasExtension) {\n if (packet.length < offset + 4) return;\n const extWords = view.getUint16(offset + 2, false);\n offset += 4 + extWords * 4;\n }\n if (packet.length < offset) return;\n const payload = packet.slice(offset);\n\n for (const controller of controllers) {\n try {\n controller.enqueue(payload);\n } catch {\n // Ignore errors on closed streams.\n }\n }\n }\n}\n\n// ─── Discord.js-familiar helpers (@discordjs/voice) ───────────────────────────\n\n/** Discord.js-familiar alias for {@link VoiceConnectionState}. Member values are Lunibee's. */\nexport { VoiceConnectionState as VoiceConnectionStatus };\n\n/** Active connections by guild, as tracked by {@link joinVoiceChannel}. */\nconst voiceConnections = new Map<string, VoiceConnection>();\n\n/**\n * Joins (or moves to) a voice channel, reusing the guild's tracked connection.\n * Mirrors `@discordjs/voice`'s `joinVoiceChannel`; transports are still attached\n * with {@link VoiceConnection.attachTransports}.\n * @param options Guild, channel and self mute/deaf flags.\n * @returns The guild's voice connection.\n * @throws {TypeError} If the guild or channel ID is empty.\n */\nexport function joinVoiceChannel(\n options: VoiceConnectionOptions & { channelId: string },\n): VoiceConnection {\n let connection = voiceConnections.get(options.guildId);\n if (!connection) {\n connection = new VoiceConnection(options);\n const created = connection;\n // VoiceConnection emits stateChange as (next, previous).\n created.on(\"stateChange\", (next) => {\n if (\n next === VoiceConnectionState.Destroyed &&\n voiceConnections.get(created.guildId) === created\n )\n voiceConnections.delete(created.guildId);\n });\n voiceConnections.set(options.guildId, connection);\n }\n connection.setChannel(options.channelId);\n connection.setSuppression(options);\n connection.connect();\n return connection;\n}\n\n/** Returns the connection tracked by {@link joinVoiceChannel} for a guild, if any. */\nexport function getVoiceConnection(\n guildId: string,\n): VoiceConnection | undefined {\n return voiceConnections.get(guildId);\n}\n\n/** Creates an {@link AudioPlayer}. Mirrors `@discordjs/voice`'s `createAudioPlayer`. */\nexport function createAudioPlayer(): AudioPlayer {\n return new AudioPlayer();\n}\n\n/** Creates an {@link AudioStream}. Mirrors `@discordjs/voice`'s `createAudioResource`. */\nexport function createAudioResource(\n stream: ReadableStream<Uint8Array>,\n metadata?: { title?: string; duration?: number },\n): AudioStream {\n return new AudioStream(stream, metadata);\n}\n\n/**\n * Resolves once a connection or player reaches `status`, or rejects after `timeoutMs`.\n * Mirrors `@discordjs/voice`'s `entersState`.\n * @returns The same target, for chaining.\n * @throws {VoiceError} If the state is not reached in time.\n */\nexport function entersState<T extends VoiceConnection | AudioPlayer>(\n target: T,\n status: T extends VoiceConnection ? VoiceConnectionState : AudioPlayerState,\n timeoutMs: number,\n): Promise<T> {\n if (target.state === status) return Promise.resolve(target);\n const emitter = target as unknown as {\n on(\n event: \"stateChange\",\n fn: (from: unknown, to: unknown) => void,\n ): unknown;\n off(\n event: \"stateChange\",\n fn: (from: unknown, to: unknown) => void,\n ): unknown;\n };\n // VoiceConnection emits (next, previous); AudioPlayer emits (from, to).\n const isConnection = target instanceof VoiceConnection;\n return new Promise<T>((resolve, reject) => {\n const onChange = (first: unknown, second: unknown): void => {\n if ((isConnection ? first : second) !== status) return;\n clearTimeout(timer);\n emitter.off(\"stateChange\", onChange);\n resolve(target);\n };\n const timer = setTimeout(() => {\n emitter.off(\"stateChange\", onChange);\n reject(\n new VoiceError(\n `Did not enter state \"${String(status)}\" within ${timeoutMs}ms.`,\n ),\n );\n }, timeoutMs);\n emitter.on(\"stateChange\", onChange);\n });\n}\n"
5
+ "/** Voice session lifecycle states. */\r\nexport enum VoiceConnectionState {\r\n Disconnected = \"disconnected\",\r\n Connecting = \"connecting\",\r\n Connected = \"connected\",\r\n Destroyed = \"destroyed\",\r\n}\r\n\r\n/** Voice protocol transport errors. */\r\nexport class VoiceError extends Error {\r\n /** Creates a voice error. @param message Error message. @param options Optional native error options. */\r\n public constructor(message: string, options?: ErrorOptions) {\r\n super(message, options);\r\n this.name = \"VoiceError\";\r\n }\r\n}\r\n\r\n/** Options used to establish a voice session. */\r\nexport interface VoiceConnectionOptions {\r\n /** Guild containing the voice channel. */\r\n guildId: string;\r\n /** Voice channel to join. */\r\n channelId?: string;\r\n /** Whether the self user should be muted. */\r\n selfMute?: boolean;\r\n /** Whether the self user should be deafened. */\r\n selfDeaf?: boolean;\r\n}\r\n\r\n/** UDP transport supplied by the runtime-specific voice implementation. */\r\nexport interface VoiceUdpTransport {\r\n /** Sends an RTP/RTCP packet. @param packet Packet bytes. @param host Destination host. @param port Destination port. @returns Optional completion promise. */\r\n send(packet: Uint8Array, host: string, port: number): Promise<void> | void;\r\n /** Closes the UDP socket. @returns Nothing. */\r\n close(): void;\r\n /** Optional listener for incoming UDP packets. */\r\n onMessage?(listener: (packet: Uint8Array) => void): void;\r\n}\r\n\r\n/** A JSON frame for the voice gateway, e.g. `{ op: 5, d: { speaking, delay, ssrc } }`. */\r\nexport interface VoiceGatewayPayload {\r\n op: number;\r\n d: Record<string, string | number | boolean | null>;\r\n}\r\n\r\n/** Voice Gateway transport abstraction. */\r\nexport interface VoiceGatewayTransport {\r\n /** Opens the voice gateway. @param endpoint Voice gateway endpoint. @param sessionId Voice session identifier. @returns Promise fulfilled when connected. */\r\n connect(endpoint: string, sessionId: string): Promise<void>;\r\n /** Sends a voice gateway payload. @param payload Gateway payload. @returns Nothing. */\r\n send(payload: VoiceGatewayPayload): void;\r\n /** Closes the voice gateway. @param code Optional close code. @param reason Optional close reason. @returns Nothing. */\r\n close(code?: number, reason?: string): void;\r\n}\r\n\r\n/** Speaking flags used by the voice gateway. */\r\nexport const SpeakingFlags = {\r\n Microphone: 1,\r\n Soundshare: 2,\r\n Priority: 4,\r\n} as const;\r\n\r\n/** Voice session event payloads. */\r\nexport interface VoiceEvents {\r\n /** State transition event. */\r\n stateChange: [VoiceConnectionState, VoiceConnectionState];\r\n /** Speaking flag event. */\r\n speaking: [number, number];\r\n /** Voice transport error event. */\r\n error: [VoiceError];\r\n}\r\n\r\ntype ListenerSets<E extends Record<keyof E, unknown[]>> = {\r\n [K in keyof E]?: Set<(...args: E[K]) => unknown>;\r\n};\r\n\r\n/** Returns the listener set for `event`, creating it on first use. */\r\nfunction listenerSet<E extends Record<keyof E, unknown[]>, K extends keyof E>(\r\n sets: ListenerSets<E>,\r\n event: K,\r\n): Set<(...args: E[K]) => unknown> {\r\n return (sets[event] ??= new Set());\r\n}\r\n\r\n/** Tracks a Discord voice session and exposes transport-independent lifecycle controls. */\r\nexport class VoiceConnection {\r\n /** Current voice connection state. */\r\n public state: VoiceConnectionState = VoiceConnectionState.Disconnected;\r\n /** Guild associated with this voice session. */\r\n public readonly guildId: string;\r\n /** Voice channel associated with this session. */\r\n public channelId?: string;\r\n /** Whether the client is self-muted. */\r\n public selfMute: boolean;\r\n /** Whether the client is self-deafened. */\r\n public selfDeaf: boolean;\r\n /** Active voice gateway transport, when attached. */\r\n public gateway?: VoiceGatewayTransport;\r\n /** Active UDP transport, when attached. */\r\n public udp?: VoiceUdpTransport;\r\n /** Registered lifecycle listeners. */\r\n #listeners: ListenerSets<VoiceEvents> = {};\r\n /** Receives incoming audio. */\r\n public readonly receiver: VoiceReceiver;\r\n\r\n /** Creates a voice connection state manager. @param options Guild ID or connection options. @throws {TypeError} If the guild ID is empty. */\r\n public constructor(options: string | VoiceConnectionOptions) {\r\n const resolved =\r\n typeof options === \"string\" ? { guildId: options } : options;\r\n if (!resolved.guildId?.trim()) {\r\n throw new TypeError(\r\n \"A guild ID is required for a voice connection.\",\r\n );\r\n }\r\n this.guildId = resolved.guildId;\r\n this.channelId = resolved.channelId;\r\n this.selfMute = resolved.selfMute ?? false;\r\n this.selfDeaf = resolved.selfDeaf ?? false;\r\n this.receiver = new VoiceReceiver(this);\r\n }\r\n\r\n /** Registers a voice lifecycle listener. @param event Event name. @param listener Listener callback. @returns This connection. @throws {TypeError} If listener is not callable. */\r\n public on<K extends keyof VoiceEvents>(\r\n event: K,\r\n listener: (...args: VoiceEvents[K]) => unknown,\r\n ): this {\r\n if (typeof listener !== \"function\")\r\n throw new TypeError(\"Voice listener must be a function.\");\r\n listenerSet(this.#listeners, event).add(listener);\r\n return this;\r\n }\r\n\r\n /** Removes a voice lifecycle listener. @param event Event name. @param listener Listener callback. @returns This connection. */\r\n public off<K extends keyof VoiceEvents>(\r\n event: K,\r\n listener: (...args: VoiceEvents[K]) => unknown,\r\n ): this {\r\n this.#listeners[event]?.delete(listener);\r\n return this;\r\n }\r\n\r\n /** Attaches protocol transports, replacing and cleaning up the old pair when present. @param gateway Voice gateway transport. @param udp UDP transport. @returns This connection. @throws {VoiceError} If the connection is destroyed. @throws {TypeError} If a transport is missing. */\r\n public attachTransports(\r\n gateway: VoiceGatewayTransport,\r\n udp: VoiceUdpTransport,\r\n ): this {\r\n this.assertUsable();\r\n if (!gateway || !udp)\r\n throw new TypeError(\r\n \"Both voice gateway and UDP transports are required.\",\r\n );\r\n if (this.gateway || this.udp) this.#closeTransports();\r\n this.gateway = gateway;\r\n this.udp = udp;\r\n // Capture the transport this listener belongs to so that packets from a\r\n // later-replaced transport (whose listener cannot be unregistered — the\r\n // VoiceUdpTransport interface exposes no removal API) are ignored.\r\n const attachedUdp = udp;\r\n if (attachedUdp.onMessage) {\r\n attachedUdp.onMessage((packet) => {\r\n if (this.udp === attachedUdp) this.receiver.onPacket(packet);\r\n });\r\n }\r\n return this;\r\n }\r\n\r\n /** Begins the connection lifecycle. @returns Nothing. @throws {VoiceError} If the connection is destroyed. */\r\n public connect(): void {\r\n this.assertUsable();\r\n if (this.state === VoiceConnectionState.Connected) return;\r\n this.#transition(VoiceConnectionState.Connecting);\r\n if (this.state !== VoiceConnectionState.Connecting) return;\r\n this.#transition(VoiceConnectionState.Connected);\r\n }\r\n\r\n /** Disconnects the current voice session. @returns Nothing. */\r\n public disconnect(): void {\r\n if (this.state === VoiceConnectionState.Destroyed) return;\r\n this.#closeTransports();\r\n this.receiver.close();\r\n this.#transition(VoiceConnectionState.Disconnected);\r\n }\r\n\r\n /** Permanently destroys this voice connection. @returns Nothing. */\r\n public destroy(): void {\r\n if (this.state === VoiceConnectionState.Destroyed) return;\r\n this.#closeTransports();\r\n this.receiver.close();\r\n this.channelId = undefined;\r\n this.#transition(VoiceConnectionState.Destroyed);\r\n this.#listeners = {};\r\n }\r\n\r\n /** Updates the voice channel and connection state. @param channelId Voice channel identifier. @returns Nothing. @throws {VoiceError} If the connection is destroyed. @throws {TypeError} If channelId is empty. */\r\n public setChannel(channelId: string): void {\r\n this.assertUsable();\r\n if (!channelId?.trim())\r\n throw new TypeError(\"A voice channel ID is required.\");\r\n this.channelId = channelId;\r\n }\r\n\r\n /** Updates self mute/deaf state. @param options Suppression flags. @returns Nothing. @throws {VoiceError} If the connection is destroyed. */\r\n public setSuppression(options: {\r\n selfMute?: boolean;\r\n selfDeaf?: boolean;\r\n }): void {\r\n this.assertUsable();\r\n if (options.selfMute !== undefined) this.selfMute = options.selfMute;\r\n if (options.selfDeaf !== undefined) this.selfDeaf = options.selfDeaf;\r\n }\r\n\r\n /** Publishes a speaking state through the attached voice gateway. @param flags Speaking flags. @returns Nothing. @throws {VoiceError} If the connection is destroyed or has no gateway transport. */\r\n public setSpeaking(flags: number = SpeakingFlags.Microphone): void {\r\n this.assertUsable();\r\n if (!this.gateway) {\r\n throw new VoiceError(\r\n \"A voice gateway transport is required to change speaking state.\",\r\n );\r\n }\r\n this.gateway.send({ op: 5, d: { speaking: flags, delay: 0, ssrc: 0 } });\r\n this.#emit(\"speaking\", flags, 0);\r\n }\r\n\r\n /** Ensures the connection is still usable. @returns Nothing. @throws {VoiceError} If destroyed. */\r\n #assertUsable(): void {\r\n if (this.state === VoiceConnectionState.Destroyed) {\r\n throw new VoiceError(\"Voice connection has been destroyed.\");\r\n }\r\n }\r\n\r\n /** Closes both active transports while isolating cleanup failures. @returns Nothing. */\r\n #closeTransports(): void {\r\n try {\r\n this.gateway?.close(1000, \"Voice connection replaced or closed\");\r\n } catch (error) {\r\n this.#emit(\r\n \"error\",\r\n error instanceof VoiceError\r\n ? error\r\n : new VoiceError(\"Voice gateway cleanup failed.\", {\r\n cause: error,\r\n }),\r\n );\r\n }\r\n try {\r\n this.udp?.close();\r\n } catch (error) {\r\n this.#emit(\r\n \"error\",\r\n error instanceof VoiceError\r\n ? error\r\n : new VoiceError(\"Voice UDP cleanup failed.\", {\r\n cause: error,\r\n }),\r\n );\r\n }\r\n this.gateway = undefined;\r\n this.udp = undefined;\r\n }\r\n\r\n /** Transitions to a new lifecycle state and emits the transition event. @param next Next state. @returns Nothing. */\r\n #transition(next: VoiceConnectionState): void {\r\n const previous = this.state;\r\n this.state = next;\r\n if (previous === next) return;\r\n for (const listener of this.#listeners.stateChange ?? []) {\r\n try {\r\n void listener(next, previous);\r\n } catch (error) {\r\n this.#emit(\r\n \"error\",\r\n error instanceof VoiceError\r\n ? error\r\n : new VoiceError(\"Voice event listener failed.\", {\r\n cause: error,\r\n }),\r\n );\r\n }\r\n if (this.state !== next && next !== VoiceConnectionState.Destroyed)\r\n return;\r\n }\r\n }\r\n\r\n /** Emits a voice event while isolating consumer failures. @param event Event name. @param args Event arguments. @returns Nothing. */\r\n #emit<K extends keyof VoiceEvents>(\r\n event: K,\r\n ...args: VoiceEvents[K]\r\n ): void {\r\n for (const listener of this.#listeners[event] ?? []) {\r\n try {\r\n void listener(...args);\r\n } catch {\r\n /* Consumer failures are isolated. */\r\n }\r\n }\r\n }\r\n\r\n /** Ensures the connection is still usable. @returns Nothing. @throws {VoiceError} If destroyed. */\r\n private assertUsable(): void {\r\n this.#assertUsable();\r\n }\r\n}\r\n\r\n// ─── Audio Pipeline ───────────────────────────────────────────────────────────\r\n\r\n/** Audio player lifecycle states. */\r\nexport type AudioPlayerState = \"idle\" | \"playing\" | \"paused\" | \"stopped\";\r\n\r\n/** An audio stream — wraps a `ReadableStream<Uint8Array>` with optional metadata. */\r\nexport class AudioStream {\r\n /** Underlying binary stream. */\r\n public readonly stream: ReadableStream<Uint8Array>;\r\n /** Optional human-readable title for the stream. */\r\n public readonly title?: string;\r\n /** Optional duration hint in seconds. */\r\n public readonly duration?: number;\r\n\r\n /** Creates an audio stream.\r\n * @param stream Binary audio data stream.\r\n * @param metadata Optional metadata: title and duration in seconds.\r\n */\r\n public constructor(\r\n stream: ReadableStream<Uint8Array>,\r\n metadata?: { title?: string; duration?: number },\r\n ) {\r\n this.stream = stream;\r\n this.title = metadata?.title;\r\n this.duration = metadata?.duration;\r\n }\r\n}\r\n\r\ntype AudioPlayerListener<K extends keyof AudioPlayerEvents> = (\r\n ...args: AudioPlayerEvents[K]\r\n) => unknown;\r\n\r\ninterface AudioPlayerEvents {\r\n stateChange: [from: AudioPlayerState, to: AudioPlayerState];\r\n error: [error: Error];\r\n finish: [];\r\n}\r\n\r\n/**\r\n * Lightweight audio player with a simple state machine.\r\n *\r\n * States:\r\n * - `idle` — no stream loaded.\r\n * - `playing` — actively feeding audio data.\r\n * - `paused` — temporarily suspended; can be resumed.\r\n * - `stopped` — stream was stopped and discarded; load a new stream to play again.\r\n *\r\n * Events: `stateChange(from, to)`, `error(err)`, `finish()`.\r\n */\r\nexport class AudioPlayer {\r\n #state: AudioPlayerState = \"idle\";\r\n #stream?: AudioStream;\r\n #reader?: ReadableStreamDefaultReader<Uint8Array>;\r\n /** Resolves the pump's pause await when the player is resumed or stopped. */\r\n #resumeSignal?: () => void;\r\n readonly #playerListeners: ListenerSets<AudioPlayerEvents> = {};\r\n\r\n /** Current player state. */\r\n public get state(): AudioPlayerState {\r\n return this.#state;\r\n }\r\n\r\n /** The audio stream currently loaded, if any. */\r\n public get current(): AudioStream | undefined {\r\n return this.#stream;\r\n }\r\n\r\n /**\r\n * Begins playing an audio stream.\r\n * If a stream is already playing, it is stopped first; the previous\r\n * stream's reader cancellation is awaited before the new stream starts so\r\n * the old and new pump loops cannot run concurrently.\r\n * @param audioStream Stream to play.\r\n */\r\n public async play(audioStream: AudioStream): Promise<void> {\r\n if (this.#state === \"playing\" || this.#state === \"paused\")\r\n await this.stop();\r\n this.#stream = audioStream;\r\n this.#reader = audioStream.stream.getReader();\r\n this.#transition(\"playing\");\r\n this.#pump().catch((err) => this.#emitError(err));\r\n }\r\n\r\n /** Pauses a currently-playing stream. No-op if not playing. */\r\n public pause(): void {\r\n if (this.#state !== \"playing\") return;\r\n this.#transition(\"paused\");\r\n }\r\n\r\n /** Resumes a paused stream. No-op if not paused. */\r\n public resume(): void {\r\n if (this.#state !== \"paused\") return;\r\n this.#transition(\"playing\");\r\n // Wake the existing pump loop rather than starting a second one.\r\n const signal = this.#resumeSignal;\r\n this.#resumeSignal = undefined;\r\n signal?.();\r\n }\r\n\r\n /**\r\n * Stops and discards the current stream. The returned promise resolves once\r\n * the underlying reader's cancellation has settled, so a subsequent `play`\r\n * never overlaps with the previous pump loop.\r\n */\r\n public async stop(): Promise<void> {\r\n if (this.#state === \"idle\" || this.#state === \"stopped\") return;\r\n const reader = this.#reader;\r\n this.#reader = undefined;\r\n this.#stream = undefined;\r\n this.#transition(\"stopped\");\r\n // Release a pump loop that is currently awaiting a resume.\r\n const signal = this.#resumeSignal;\r\n this.#resumeSignal = undefined;\r\n signal?.();\r\n if (reader) {\r\n try {\r\n await reader.cancel();\r\n } catch {\r\n /* Cancellation failures are non-fatal. */\r\n }\r\n }\r\n }\r\n\r\n /** Registers an event listener. @returns `this` for chaining. */\r\n public on<K extends keyof AudioPlayerEvents>(\r\n event: K,\r\n listener: AudioPlayerListener<K>,\r\n ): this {\r\n listenerSet(this.#playerListeners, event).add(listener);\r\n return this;\r\n }\r\n\r\n /** Removes an event listener. @returns `this` for chaining. */\r\n public off<K extends keyof AudioPlayerEvents>(\r\n event: K,\r\n listener: AudioPlayerListener<K>,\r\n ): this {\r\n this.#playerListeners[event]?.delete(listener);\r\n return this;\r\n }\r\n\r\n /**\r\n * Handles a decoded audio chunk. The base implementation is a no-op;\r\n * subclasses override this to pipe audio to a UDP socket or other sink.\r\n * @param chunk A chunk of audio bytes read from the current stream.\r\n */\r\n protected onChunk(chunk: Uint8Array): void {\r\n void chunk;\r\n }\r\n\r\n /** Reads chunks from the reader while the player is in a playing state. */\r\n async #pump(): Promise<void> {\r\n const reader = this.#reader;\r\n if (!reader) return;\r\n while (true) {\r\n // Suspend without polling: park until resume()/stop() signals us.\r\n if (this.#state === \"paused\") {\r\n await new Promise<void>((resolve) => {\r\n this.#resumeSignal = resolve;\r\n });\r\n }\r\n // Abort if stopped/idle (e.g. resolved by stop()).\r\n if (this.#state !== \"playing\") return;\r\n const { done, value } = await reader.read();\r\n if (done) {\r\n if (this.#state === \"playing\" || this.#state === \"paused\") {\r\n this.#transition(\"idle\");\r\n this.#stream = undefined;\r\n this.#reader = undefined;\r\n this.#emit(\"finish\");\r\n }\r\n return;\r\n }\r\n // A stop() during the read wins — drop the chunk and exit.\r\n if (this.#state !== \"playing\" && this.#state !== \"paused\") return;\r\n // Deliver the chunk to the sink hook; loop back to honour pause state.\r\n this.onChunk(value);\r\n }\r\n }\r\n\r\n #transition(next: AudioPlayerState): void {\r\n if (this.#state === next) return;\r\n const from = this.#state;\r\n this.#state = next;\r\n this.#emit(\"stateChange\", from, next);\r\n }\r\n\r\n #emitError(err: unknown): void {\r\n this.#emit(\r\n \"error\",\r\n err instanceof Error ? err : new VoiceError(String(err)),\r\n );\r\n }\r\n\r\n #emit<K extends keyof AudioPlayerEvents>(\r\n event: K,\r\n ...args: AudioPlayerEvents[K]\r\n ): void {\r\n for (const listener of this.#playerListeners[event] ?? []) {\r\n try {\r\n void listener(...args);\r\n } catch {\r\n /* Listener failures are isolated. */\r\n }\r\n }\r\n }\r\n}\r\n\r\n// ─── Audio Receive Pipeline ───────────────────────────────────────────────────\r\n\r\n/** Tracks incoming audio and maps SSRCs to readable streams per user. */\r\nexport class VoiceReceiver {\r\n /** The connection this receiver is attached to. */\r\n public readonly connection: VoiceConnection;\r\n readonly #ssrcMap = new Map<number, string>();\r\n /** Active stream controllers per user; a Set so multiple subscribers to the\r\n * same user all receive audio instead of the last one overwriting earlier ones. */\r\n readonly #streams = new Map<\r\n string,\r\n Set<ReadableStreamDefaultController<Uint8Array>>\r\n >();\r\n\r\n /** Creates a VoiceReceiver. @param connection Voice connection. */\r\n public constructor(connection: VoiceConnection) {\r\n this.connection = connection;\r\n }\r\n\r\n /** Maps an SSRC to a User ID (usually from gateway speaking events). */\r\n public mapSsrc(ssrc: number, userId: string): void {\r\n this.#ssrcMap.set(ssrc, userId);\r\n }\r\n\r\n /** Subscribes to incoming audio from a specific user. @param userId User identifier. @returns A binary readable stream of the audio. */\r\n public subscribe(userId: string): AudioStream {\r\n // A destroyed connection never closes its receiver again, so a late\r\n // subscriber would wait forever.\r\n if (this.connection.state === VoiceConnectionState.Destroyed)\r\n throw new VoiceError(\"Voice connection has been destroyed.\");\r\n let controllerRef: ReadableStreamDefaultController<Uint8Array>;\r\n const stream = new ReadableStream<Uint8Array>({\r\n start: (controller) => {\r\n controllerRef = controller;\r\n let set = this.#streams.get(userId);\r\n if (!set) this.#streams.set(userId, (set = new Set()));\r\n set.add(controller);\r\n },\r\n cancel: () => {\r\n const set = this.#streams.get(userId);\r\n if (set) {\r\n set.delete(controllerRef);\r\n if (set.size === 0) this.#streams.delete(userId);\r\n }\r\n },\r\n });\r\n return new AudioStream(stream, { title: `User ${userId} Audio` });\r\n }\r\n\r\n /** Ends every subscriber stream and forgets SSRC mappings. Called when the\r\n * connection disconnects or is destroyed, so readers do not wait forever. */\r\n public close(): void {\r\n for (const controllers of this.#streams.values())\r\n for (const controller of controllers) {\r\n try {\r\n controller.close();\r\n } catch {\r\n // Already closed or errored by the consumer.\r\n }\r\n }\r\n this.#streams.clear();\r\n this.#ssrcMap.clear();\r\n }\r\n\r\n /** Processes an incoming UDP packet, extracting SSRC and routing the audio\r\n * payload (RTP header stripped) to every subscriber for the mapped user. */\r\n public onPacket(packet: Uint8Array): void {\r\n if (packet.length < 12) return;\r\n\r\n const view = new DataView(\r\n packet.buffer,\r\n packet.byteOffset,\r\n packet.byteLength,\r\n );\r\n const ssrc = view.getUint32(8, false);\r\n\r\n const userId = this.#ssrcMap.get(ssrc);\r\n if (!userId) return;\r\n const controllers = this.#streams.get(userId);\r\n if (!controllers || controllers.size === 0) return;\r\n\r\n // Compute the RTP payload offset: 12-byte fixed header + CSRC list, plus\r\n // the extension header when the X bit is set.\r\n const first = view.getUint8(0);\r\n const csrcCount = first & 0x0f;\r\n let offset = 12 + csrcCount * 4;\r\n const hasExtension = (first & 0x10) !== 0;\r\n if (hasExtension) {\r\n if (packet.length < offset + 4) return;\r\n const extWords = view.getUint16(offset + 2, false);\r\n offset += 4 + extWords * 4;\r\n }\r\n if (packet.length < offset) return;\r\n const payload = packet.slice(offset);\r\n\r\n for (const controller of controllers) {\r\n try {\r\n controller.enqueue(payload);\r\n } catch {\r\n // Ignore errors on closed streams.\r\n }\r\n }\r\n }\r\n}\r\n\r\n// ─── Discord.js-familiar helpers (@discordjs/voice) ───────────────────────────\r\n\r\n/** Discord.js-familiar alias for {@link VoiceConnectionState}. Member values are Lunibee's. */\r\nexport { VoiceConnectionState as VoiceConnectionStatus };\r\n\r\n/** Active connections by guild, as tracked by {@link joinVoiceChannel}. */\r\nconst voiceConnections = new Map<string, VoiceConnection>();\r\n\r\n/**\r\n * Joins (or moves to) a voice channel, reusing the guild's tracked connection.\r\n * Mirrors `@discordjs/voice`'s `joinVoiceChannel`; transports are still attached\r\n * with {@link VoiceConnection.attachTransports}.\r\n * @param options Guild, channel and self mute/deaf flags.\r\n * @returns The guild's voice connection.\r\n * @throws {TypeError} If the guild or channel ID is empty.\r\n */\r\nexport function joinVoiceChannel(\r\n options: VoiceConnectionOptions & { channelId: string },\r\n): VoiceConnection {\r\n let connection = voiceConnections.get(options.guildId);\r\n if (!connection) {\r\n connection = new VoiceConnection(options);\r\n const created = connection;\r\n // VoiceConnection emits stateChange as (next, previous).\r\n created.on(\"stateChange\", (next) => {\r\n if (\r\n next === VoiceConnectionState.Destroyed &&\r\n voiceConnections.get(created.guildId) === created\r\n )\r\n voiceConnections.delete(created.guildId);\r\n });\r\n voiceConnections.set(options.guildId, connection);\r\n }\r\n connection.setChannel(options.channelId);\r\n connection.setSuppression(options);\r\n connection.connect();\r\n return connection;\r\n}\r\n\r\n/** Returns the connection tracked by {@link joinVoiceChannel} for a guild, if any. */\r\nexport function getVoiceConnection(\r\n guildId: string,\r\n): VoiceConnection | undefined {\r\n return voiceConnections.get(guildId);\r\n}\r\n\r\n/** Creates an {@link AudioPlayer}. Mirrors `@discordjs/voice`'s `createAudioPlayer`. */\r\nexport function createAudioPlayer(): AudioPlayer {\r\n return new AudioPlayer();\r\n}\r\n\r\n/** Creates an {@link AudioStream}. Mirrors `@discordjs/voice`'s `createAudioResource`. */\r\nexport function createAudioResource(\r\n stream: ReadableStream<Uint8Array>,\r\n metadata?: { title?: string; duration?: number },\r\n): AudioStream {\r\n return new AudioStream(stream, metadata);\r\n}\r\n\r\n/**\r\n * Resolves once a connection or player reaches `status`, or rejects after `timeoutMs`.\r\n * Mirrors `@discordjs/voice`'s `entersState`.\r\n * @returns The same target, for chaining.\r\n * @throws {VoiceError} If the state is not reached in time.\r\n */\r\nexport function entersState<T extends VoiceConnection | AudioPlayer>(\r\n target: T,\r\n status: T extends VoiceConnection ? VoiceConnectionState : AudioPlayerState,\r\n timeoutMs: number,\r\n): Promise<T> {\r\n if (target.state === status) return Promise.resolve(target);\r\n const emitter = target as unknown as {\r\n on(\r\n event: \"stateChange\",\r\n fn: (from: unknown, to: unknown) => void,\r\n ): unknown;\r\n off(\r\n event: \"stateChange\",\r\n fn: (from: unknown, to: unknown) => void,\r\n ): unknown;\r\n };\r\n // VoiceConnection emits (next, previous); AudioPlayer emits (from, to).\r\n const isConnection = target instanceof VoiceConnection;\r\n return new Promise<T>((resolve, reject) => {\r\n const onChange = (first: unknown, second: unknown): void => {\r\n if ((isConnection ? first : second) !== status) return;\r\n clearTimeout(timer);\r\n emitter.off(\"stateChange\", onChange);\r\n resolve(target);\r\n };\r\n const timer = setTimeout(() => {\r\n emitter.off(\"stateChange\", onChange);\r\n reject(\r\n new VoiceError(\r\n `Did not enter state \"${String(status)}\" within ${timeoutMs}ms.`,\r\n ),\r\n );\r\n }, timeoutMs);\r\n emitter.on(\"stateChange\", onChange);\r\n });\r\n}\r\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "9E96BE25F2F4C69B64756E2164756E21",
7
+ "mappings": ";;AACO,IAAK;AAAA,CAAL,CAAK,yBAAL;AAAA,EACH,uCAAe;AAAA,EACf,qCAAa;AAAA,EACb,oCAAY;AAAA,EACZ,oCAAY;AAAA,GAJJ;AAAA;AAQL,MAAM,mBAAmB,MAAM;AAAA,EAE3B,WAAW,CAAC,SAAiB,SAAwB;AAAA,IACxD,MAAM,SAAS,OAAO;AAAA,IACtB,KAAK,OAAO;AAAA;AAEpB;AAyCO,IAAM,gBAAgB;AAAA,EACzB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,UAAU;AACd;AAiBA,SAAS,WAAoE,CACzE,MACA,OAC+B;AAAA,EAC/B,OAAQ,KAAK,WAAW,IAAI;AAAA;AAAA;AAIzB,MAAM,gBAAgB;AAAA,EAElB,QAA8B;AAAA,EAErB;AAAA,EAET;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEP,aAAwC,CAAC;AAAA,EAEzB;AAAA,EAGT,WAAW,CAAC,SAA0C;AAAA,IACzD,MAAM,WACF,OAAO,YAAY,WAAW,EAAE,SAAS,QAAQ,IAAI;AAAA,IACzD,IAAI,CAAC,SAAS,SAAS,KAAK,GAAG;AAAA,MAC3B,MAAM,IAAI,UACN,gDACJ;AAAA,IACJ;AAAA,IACA,KAAK,UAAU,SAAS;AAAA,IACxB,KAAK,YAAY,SAAS;AAAA,IAC1B,KAAK,WAAW,SAAS,YAAY;AAAA,IACrC,KAAK,WAAW,SAAS,YAAY;AAAA,IACrC,KAAK,WAAW,IAAI,cAAc,IAAI;AAAA;AAAA,EAInC,EAA+B,CAClC,OACA,UACI;AAAA,IACJ,IAAI,OAAO,aAAa;AAAA,MACpB,MAAM,IAAI,UAAU,oCAAoC;AAAA,IAC5D,YAAY,KAAK,YAAY,KAAK,EAAE,IAAI,QAAQ;AAAA,IAChD,OAAO;AAAA;AAAA,EAIJ,GAAgC,CACnC,OACA,UACI;AAAA,IACJ,KAAK,WAAW,QAAQ,OAAO,QAAQ;AAAA,IACvC,OAAO;AAAA;AAAA,EAIJ,gBAAgB,CACnB,SACA,KACI;AAAA,IACJ,KAAK,aAAa;AAAA,IAClB,IAAI,CAAC,WAAW,CAAC;AAAA,MACb,MAAM,IAAI,UACN,qDACJ;AAAA,IACJ,IAAI,KAAK,WAAW,KAAK;AAAA,MAAK,KAAK,iBAAiB;AAAA,IACpD,KAAK,UAAU;AAAA,IACf,KAAK,MAAM;AAAA,IAIX,MAAM,cAAc;AAAA,IACpB,IAAI,YAAY,WAAW;AAAA,MACvB,YAAY,UAAU,CAAC,WAAW;AAAA,QAC9B,IAAI,KAAK,QAAQ;AAAA,UAAa,KAAK,SAAS,SAAS,MAAM;AAAA,OAC9D;AAAA,IACL;AAAA,IACA,OAAO;AAAA;AAAA,EAIJ,OAAO,GAAS;AAAA,IACnB,KAAK,aAAa;AAAA,IAClB,IAAI,KAAK,UAAU;AAAA,MAAgC;AAAA,IACnD,KAAK,YAAY,6BAA+B;AAAA,IAChD,IAAI,KAAK,UAAU;AAAA,MAAiC;AAAA,IACpD,KAAK,YAAY,2BAA8B;AAAA;AAAA,EAI5C,UAAU,GAAS;AAAA,IACtB,IAAI,KAAK,UAAU;AAAA,MAAgC;AAAA,IACnD,KAAK,iBAAiB;AAAA,IACtB,KAAK,SAAS,MAAM;AAAA,IACpB,KAAK,YAAY,iCAAiC;AAAA;AAAA,EAI/C,OAAO,GAAS;AAAA,IACnB,IAAI,KAAK,UAAU;AAAA,MAAgC;AAAA,IACnD,KAAK,iBAAiB;AAAA,IACtB,KAAK,SAAS,MAAM;AAAA,IACpB,KAAK,YAAY;AAAA,IACjB,KAAK,YAAY,2BAA8B;AAAA,IAC/C,KAAK,aAAa,CAAC;AAAA;AAAA,EAIhB,UAAU,CAAC,WAAyB;AAAA,IACvC,KAAK,aAAa;AAAA,IAClB,IAAI,CAAC,WAAW,KAAK;AAAA,MACjB,MAAM,IAAI,UAAU,iCAAiC;AAAA,IACzD,KAAK,YAAY;AAAA;AAAA,EAId,cAAc,CAAC,SAGb;AAAA,IACL,KAAK,aAAa;AAAA,IAClB,IAAI,QAAQ,aAAa;AAAA,MAAW,KAAK,WAAW,QAAQ;AAAA,IAC5D,IAAI,QAAQ,aAAa;AAAA,MAAW,KAAK,WAAW,QAAQ;AAAA;AAAA,EAIzD,WAAW,CAAC,QAAgB,cAAc,YAAkB;AAAA,IAC/D,KAAK,aAAa;AAAA,IAClB,IAAI,CAAC,KAAK,SAAS;AAAA,MACf,MAAM,IAAI,WACN,iEACJ;AAAA,IACJ;AAAA,IACA,KAAK,QAAQ,KAAK,EAAE,IAAI,GAAG,GAAG,EAAE,UAAU,OAAO,OAAO,GAAG,MAAM,EAAE,EAAE,CAAC;AAAA,IACtE,KAAK,MAAM,YAAY,OAAO,CAAC;AAAA;AAAA,EAInC,aAAa,GAAS;AAAA,IAClB,IAAI,KAAK,UAAU,6BAAgC;AAAA,MAC/C,MAAM,IAAI,WAAW,sCAAsC;AAAA,IAC/D;AAAA;AAAA,EAIJ,gBAAgB,GAAS;AAAA,IACrB,IAAI;AAAA,MACA,KAAK,SAAS,MAAM,MAAM,qCAAqC;AAAA,MACjE,OAAO,OAAO;AAAA,MACZ,KAAK,MACD,SACA,iBAAiB,aACX,QACA,IAAI,WAAW,iCAAiC;AAAA,QAC5C,OAAO;AAAA,MACX,CAAC,CACX;AAAA;AAAA,IAEJ,IAAI;AAAA,MACA,KAAK,KAAK,MAAM;AAAA,MAClB,OAAO,OAAO;AAAA,MACZ,KAAK,MACD,SACA,iBAAiB,aACX,QACA,IAAI,WAAW,6BAA6B;AAAA,QACxC,OAAO;AAAA,MACX,CAAC,CACX;AAAA;AAAA,IAEJ,KAAK,UAAU;AAAA,IACf,KAAK,MAAM;AAAA;AAAA,EAIf,WAAW,CAAC,MAAkC;AAAA,IAC1C,MAAM,WAAW,KAAK;AAAA,IACtB,KAAK,QAAQ;AAAA,IACb,IAAI,aAAa;AAAA,MAAM;AAAA,IACvB,WAAW,YAAY,KAAK,WAAW,eAAe,CAAC,GAAG;AAAA,MACtD,IAAI;AAAA,QACK,SAAS,MAAM,QAAQ;AAAA,QAC9B,OAAO,OAAO;AAAA,QACZ,KAAK,MACD,SACA,iBAAiB,aACX,QACA,IAAI,WAAW,gCAAgC;AAAA,UAC3C,OAAO;AAAA,QACX,CAAC,CACX;AAAA;AAAA,MAEJ,IAAI,KAAK,UAAU,QAAQ,SAAS;AAAA,QAChC;AAAA,IACR;AAAA;AAAA,EAIJ,KAAkC,CAC9B,UACG,MACC;AAAA,IACJ,WAAW,YAAY,KAAK,WAAW,UAAU,CAAC,GAAG;AAAA,MACjD,IAAI;AAAA,QACK,SAAS,GAAG,IAAI;AAAA,QACvB,MAAM;AAAA,IAGZ;AAAA;AAAA,EAII,YAAY,GAAS;AAAA,IACzB,KAAK,cAAc;AAAA;AAE3B;AAAA;AAQO,MAAM,YAAY;AAAA,EAEL;AAAA,EAEA;AAAA,EAEA;AAAA,EAMT,WAAW,CACd,QACA,UACF;AAAA,IACE,KAAK,SAAS;AAAA,IACd,KAAK,QAAQ,UAAU;AAAA,IACvB,KAAK,WAAW,UAAU;AAAA;AAElC;AAAA;AAuBO,MAAM,YAAY;AAAA,EACrB,SAA2B;AAAA,EAC3B;AAAA,EACA;AAAA,EAEA;AAAA,EACS,mBAAoD,CAAC;AAAA,MAGnD,KAAK,GAAqB;AAAA,IACjC,OAAO,KAAK;AAAA;AAAA,MAIL,OAAO,GAA4B;AAAA,IAC1C,OAAO,KAAK;AAAA;AAAA,OAUH,KAAI,CAAC,aAAyC;AAAA,IACvD,IAAI,KAAK,WAAW,aAAa,KAAK,WAAW;AAAA,MAC7C,MAAM,KAAK,KAAK;AAAA,IACpB,KAAK,UAAU;AAAA,IACf,KAAK,UAAU,YAAY,OAAO,UAAU;AAAA,IAC5C,KAAK,YAAY,SAAS;AAAA,IAC1B,KAAK,MAAM,EAAE,MAAM,CAAC,QAAQ,KAAK,WAAW,GAAG,CAAC;AAAA;AAAA,EAI7C,KAAK,GAAS;AAAA,IACjB,IAAI,KAAK,WAAW;AAAA,MAAW;AAAA,IAC/B,KAAK,YAAY,QAAQ;AAAA;AAAA,EAItB,MAAM,GAAS;AAAA,IAClB,IAAI,KAAK,WAAW;AAAA,MAAU;AAAA,IAC9B,KAAK,YAAY,SAAS;AAAA,IAE1B,MAAM,SAAS,KAAK;AAAA,IACpB,KAAK,gBAAgB;AAAA,IACrB,SAAS;AAAA;AAAA,OAQA,KAAI,GAAkB;AAAA,IAC/B,IAAI,KAAK,WAAW,UAAU,KAAK,WAAW;AAAA,MAAW;AAAA,IACzD,MAAM,SAAS,KAAK;AAAA,IACpB,KAAK,UAAU;AAAA,IACf,KAAK,UAAU;AAAA,IACf,KAAK,YAAY,SAAS;AAAA,IAE1B,MAAM,SAAS,KAAK;AAAA,IACpB,KAAK,gBAAgB;AAAA,IACrB,SAAS;AAAA,IACT,IAAI,QAAQ;AAAA,MACR,IAAI;AAAA,QACA,MAAM,OAAO,OAAO;AAAA,QACtB,MAAM;AAAA,IAGZ;AAAA;AAAA,EAIG,EAAqC,CACxC,OACA,UACI;AAAA,IACJ,YAAY,KAAK,kBAAkB,KAAK,EAAE,IAAI,QAAQ;AAAA,IACtD,OAAO;AAAA;AAAA,EAIJ,GAAsC,CACzC,OACA,UACI;AAAA,IACJ,KAAK,iBAAiB,QAAQ,OAAO,QAAQ;AAAA,IAC7C,OAAO;AAAA;AAAA,EAQD,OAAO,CAAC,OAAyB;AAAA,OAKrC,KAAK,GAAkB;AAAA,IACzB,MAAM,SAAS,KAAK;AAAA,IACpB,IAAI,CAAC;AAAA,MAAQ;AAAA,IACb,OAAO,MAAM;AAAA,MAET,IAAI,KAAK,WAAW,UAAU;AAAA,QAC1B,MAAM,IAAI,QAAc,CAAC,YAAY;AAAA,UACjC,KAAK,gBAAgB;AAAA,SACxB;AAAA,MACL;AAAA,MAEA,IAAI,KAAK,WAAW;AAAA,QAAW;AAAA,MAC/B,QAAQ,MAAM,UAAU,MAAM,OAAO,KAAK;AAAA,MAC1C,IAAI,MAAM;AAAA,QACN,IAAI,KAAK,WAAW,aAAa,KAAK,WAAW,UAAU;AAAA,UACvD,KAAK,YAAY,MAAM;AAAA,UACvB,KAAK,UAAU;AAAA,UACf,KAAK,UAAU;AAAA,UACf,KAAK,MAAM,QAAQ;AAAA,QACvB;AAAA,QACA;AAAA,MACJ;AAAA,MAEA,IAAI,KAAK,WAAW,aAAa,KAAK,WAAW;AAAA,QAAU;AAAA,MAE3D,KAAK,QAAQ,KAAK;AAAA,IACtB;AAAA;AAAA,EAGJ,WAAW,CAAC,MAA8B;AAAA,IACtC,IAAI,KAAK,WAAW;AAAA,MAAM;AAAA,IAC1B,MAAM,OAAO,KAAK;AAAA,IAClB,KAAK,SAAS;AAAA,IACd,KAAK,MAAM,eAAe,MAAM,IAAI;AAAA;AAAA,EAGxC,UAAU,CAAC,KAAoB;AAAA,IAC3B,KAAK,MACD,SACA,eAAe,QAAQ,MAAM,IAAI,WAAW,OAAO,GAAG,CAAC,CAC3D;AAAA;AAAA,EAGJ,KAAwC,CACpC,UACG,MACC;AAAA,IACJ,WAAW,YAAY,KAAK,iBAAiB,UAAU,CAAC,GAAG;AAAA,MACvD,IAAI;AAAA,QACK,SAAS,GAAG,IAAI;AAAA,QACvB,MAAM;AAAA,IAGZ;AAAA;AAER;AAAA;AAKO,MAAM,cAAc;AAAA,EAEP;AAAA,EACP,WAAW,IAAI;AAAA,EAGf,WAAW,IAAI;AAAA,EAMjB,WAAW,CAAC,YAA6B;AAAA,IAC5C,KAAK,aAAa;AAAA;AAAA,EAIf,OAAO,CAAC,MAAc,QAAsB;AAAA,IAC/C,KAAK,SAAS,IAAI,MAAM,MAAM;AAAA;AAAA,EAI3B,SAAS,CAAC,QAA6B;AAAA,IAG1C,IAAI,KAAK,WAAW,UAAU;AAAA,MAC1B,MAAM,IAAI,WAAW,sCAAsC;AAAA,IAC/D,IAAI;AAAA,IACJ,MAAM,SAAS,IAAI,eAA2B;AAAA,MAC1C,OAAO,CAAC,eAAe;AAAA,QACnB,gBAAgB;AAAA,QAChB,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM;AAAA,QAClC,IAAI,CAAC;AAAA,UAAK,KAAK,SAAS,IAAI,QAAS,MAAM,IAAI,GAAM;AAAA,QACrD,IAAI,IAAI,UAAU;AAAA;AAAA,MAEtB,QAAQ,MAAM;AAAA,QACV,MAAM,MAAM,KAAK,SAAS,IAAI,MAAM;AAAA,QACpC,IAAI,KAAK;AAAA,UACL,IAAI,OAAO,aAAa;AAAA,UACxB,IAAI,IAAI,SAAS;AAAA,YAAG,KAAK,SAAS,OAAO,MAAM;AAAA,QACnD;AAAA;AAAA,IAER,CAAC;AAAA,IACD,OAAO,IAAI,YAAY,QAAQ,EAAE,OAAO,QAAQ,eAAe,CAAC;AAAA;AAAA,EAK7D,KAAK,GAAS;AAAA,IACjB,WAAW,eAAe,KAAK,SAAS,OAAO;AAAA,MAC3C,WAAW,cAAc,aAAa;AAAA,QAClC,IAAI;AAAA,UACA,WAAW,MAAM;AAAA,UACnB,MAAM;AAAA,MAGZ;AAAA,IACJ,KAAK,SAAS,MAAM;AAAA,IACpB,KAAK,SAAS,MAAM;AAAA;AAAA,EAKjB,QAAQ,CAAC,QAA0B;AAAA,IACtC,IAAI,OAAO,SAAS;AAAA,MAAI;AAAA,IAExB,MAAM,OAAO,IAAI,SACb,OAAO,QACP,OAAO,YACP,OAAO,UACX;AAAA,IACA,MAAM,OAAO,KAAK,UAAU,GAAG,KAAK;AAAA,IAEpC,MAAM,SAAS,KAAK,SAAS,IAAI,IAAI;AAAA,IACrC,IAAI,CAAC;AAAA,MAAQ;AAAA,IACb,MAAM,cAAc,KAAK,SAAS,IAAI,MAAM;AAAA,IAC5C,IAAI,CAAC,eAAe,YAAY,SAAS;AAAA,MAAG;AAAA,IAI5C,MAAM,QAAQ,KAAK,SAAS,CAAC;AAAA,IAC7B,MAAM,YAAY,QAAQ;AAAA,IAC1B,IAAI,SAAS,KAAK,YAAY;AAAA,IAC9B,MAAM,gBAAgB,QAAQ,QAAU;AAAA,IACxC,IAAI,cAAc;AAAA,MACd,IAAI,OAAO,SAAS,SAAS;AAAA,QAAG;AAAA,MAChC,MAAM,WAAW,KAAK,UAAU,SAAS,GAAG,KAAK;AAAA,MACjD,UAAU,IAAI,WAAW;AAAA,IAC7B;AAAA,IACA,IAAI,OAAO,SAAS;AAAA,MAAQ;AAAA,IAC5B,MAAM,UAAU,OAAO,MAAM,MAAM;AAAA,IAEnC,WAAW,cAAc,aAAa;AAAA,MAClC,IAAI;AAAA,QACA,WAAW,QAAQ,OAAO;AAAA,QAC5B,MAAM;AAAA,IAGZ;AAAA;AAER;AAQA,IAAM,mBAAmB,IAAI;AAUtB,SAAS,gBAAgB,CAC5B,SACe;AAAA,EACf,IAAI,aAAa,iBAAiB,IAAI,QAAQ,OAAO;AAAA,EACrD,IAAI,CAAC,YAAY;AAAA,IACb,aAAa,IAAI,gBAAgB,OAAO;AAAA,IACxC,MAAM,UAAU;AAAA,IAEhB,QAAQ,GAAG,eAAe,CAAC,SAAS;AAAA,MAChC,IACI,SAAS,+BACT,iBAAiB,IAAI,QAAQ,OAAO,MAAM;AAAA,QAE1C,iBAAiB,OAAO,QAAQ,OAAO;AAAA,KAC9C;AAAA,IACD,iBAAiB,IAAI,QAAQ,SAAS,UAAU;AAAA,EACpD;AAAA,EACA,WAAW,WAAW,QAAQ,SAAS;AAAA,EACvC,WAAW,eAAe,OAAO;AAAA,EACjC,WAAW,QAAQ;AAAA,EACnB,OAAO;AAAA;AAIJ,SAAS,kBAAkB,CAC9B,SAC2B;AAAA,EAC3B,OAAO,iBAAiB,IAAI,OAAO;AAAA;AAIhC,SAAS,iBAAiB,GAAgB;AAAA,EAC7C,OAAO,IAAI;AAAA;AAIR,SAAS,mBAAmB,CAC/B,QACA,UACW;AAAA,EACX,OAAO,IAAI,YAAY,QAAQ,QAAQ;AAAA;AASpC,SAAS,WAAoD,CAChE,QACA,QACA,WACU;AAAA,EACV,IAAI,OAAO,UAAU;AAAA,IAAQ,OAAO,QAAQ,QAAQ,MAAM;AAAA,EAC1D,MAAM,UAAU;AAAA,EAWhB,MAAM,eAAe,kBAAkB;AAAA,EACvC,OAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AAAA,IACvC,MAAM,WAAW,CAAC,OAAgB,WAA0B;AAAA,MACxD,KAAK,eAAe,QAAQ,YAAY;AAAA,QAAQ;AAAA,MAChD,aAAa,KAAK;AAAA,MAClB,QAAQ,IAAI,eAAe,QAAQ;AAAA,MACnC,QAAQ,MAAM;AAAA;AAAA,IAElB,MAAM,QAAQ,WAAW,MAAM;AAAA,MAC3B,QAAQ,IAAI,eAAe,QAAQ;AAAA,MACnC,OACI,IAAI,WACA,wBAAwB,OAAO,MAAM,aAAa,cACtD,CACJ;AAAA,OACD,SAAS;AAAA,IACZ,QAAQ,GAAG,eAAe,QAAQ;AAAA,GACrC;AAAA;",
8
+ "debugId": "854FAC176777C99264756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -42,8 +42,12 @@ export declare class PlainTextDecoder implements GatewayDecoder {
42
42
  */
43
43
  export declare class ZlibStreamDecoder implements GatewayDecoder {
44
44
  #private;
45
- /** @param create Inflater factory; injected in tests. */
46
- constructor(create?: () => Inflate);
45
+ /**
46
+ * @param create Inflater factory; injected in tests.
47
+ * @param maxFrameBytes Largest inflated frame accepted (default 64 MiB);
48
+ * past it the stream fails instead of growing until memory runs out.
49
+ */
50
+ constructor(create?: () => Inflate, maxFrameBytes?: number);
47
51
  /**
48
52
  * A text frame on a compressed connection is already a complete frame, so
49
53
  * it is returned synchronously and never touches the inflater. Discord
@@ -1,6 +1,6 @@
1
1
  import { type SocketFactory } from "./transport.js";
2
2
  import { type GatewayPayload, type GatewayProperties, type GatewayPresence, type GatewayIntentResolvable } from "@lunibee/types";
3
- export { classifyFrame, classifyPayload, identifyPayload, resumePayload, heartbeatPayload, type GatewayAction, type ProtocolResult, type ProtocolViolation, type IdentifyOptions, type IdentifyProperties, } from "./protocol.js";
3
+ export { classifyFrame, classifyPayload, identifyPayload, resumePayload, heartbeatPayload, type GatewayAction, type ProtocolResult, type ProtocolViolation, type IdentifyOptions, type IdentifyProperties, type IdentifyData, } from "./protocol.js";
4
4
  export { WebSocketTransport, type TransportHandlers, type TransportOptions, type SocketFactory, } from "./transport.js";
5
5
  export { createDecoder, PlainTextDecoder, ZlibStreamDecoder, type GatewayDecoder, } from "./decoder.js";
6
6
  export { GatewayReconnect, classifyCloseCode, FATAL_CLOSE_CODES, IDENTIFY_CLOSE_CODES, type CloseAction, type ReconnectOptions, type ScheduleResult, type ScheduleRefusal, } from "./reconnect.js";
@@ -12,18 +12,30 @@ export { GatewayState, Status, GatewayError } from "./state.js";
12
12
  import { GatewayState } from "./state.js";
13
13
  /** Gateway connection configuration. */
14
14
  export interface GatewayOptions {
15
- /** Authentication token. */ token: string;
16
- /** Gateway intent bitfield or resolvable. */ intents: GatewayIntentResolvable;
17
- /** Shard identifier. */ shardId?: number;
18
- /** Total shard count. */ shardCount?: number;
19
- /** Whether automatic reconnect is enabled. */ reconnect?: boolean;
20
- /** Maximum reconnect attempts. */ maxReconnectAttempts?: number;
21
- /** Initial reconnect delay. */ reconnectBaseDelay?: number;
22
- /** Maximum reconnect delay. */ reconnectMaxDelay?: number;
23
- /** Heartbeat acknowledgement timeout. */ heartbeatAckTimeout?: number;
24
- /** Zombie connection timeout. */ zombieTimeout?: number;
25
- /** Identification properties. */ properties?: GatewayProperties;
26
- /** Presence data. */ presence?: GatewayPresence;
15
+ /** Authentication token. */
16
+ token: string;
17
+ /** Gateway intent bitfield or resolvable. */
18
+ intents: GatewayIntentResolvable;
19
+ /** Shard identifier. */
20
+ shardId?: number;
21
+ /** Total shard count. */
22
+ shardCount?: number;
23
+ /** Whether automatic reconnect is enabled. */
24
+ reconnect?: boolean;
25
+ /** Maximum reconnect attempts. */
26
+ maxReconnectAttempts?: number;
27
+ /** Initial reconnect delay. */
28
+ reconnectBaseDelay?: number;
29
+ /** Maximum reconnect delay. */
30
+ reconnectMaxDelay?: number;
31
+ /** Heartbeat acknowledgement timeout. */
32
+ heartbeatAckTimeout?: number;
33
+ /** Zombie connection timeout. */
34
+ zombieTimeout?: number;
35
+ /** Identification properties. */
36
+ properties?: GatewayProperties;
37
+ /** Presence data. */
38
+ presence?: GatewayPresence;
27
39
  /**
28
40
  * Whether to enable zlib-stream transport compression.
29
41
  * Decoded with a persistent `node:zlib` inflate stream, matching
package/dist/ws/index.js CHANGED
@@ -73,7 +73,26 @@ class GatewaySession {
73
73
  return resume ? { type: "resume", ...resume } : { type: "identify" };
74
74
  }
75
75
  connectURL(fallback) {
76
- return this.resumeInfo()?.resumeURL ?? fallback;
76
+ const resume = this.resumeInfo()?.resumeURL;
77
+ return resume !== undefined && isTrustedGatewayURL(resume, fallback) ? resume : fallback;
78
+ }
79
+ }
80
+ function isTrustedGatewayURL(url, fallback) {
81
+ let parsed;
82
+ try {
83
+ parsed = new URL(url);
84
+ } catch {
85
+ return false;
86
+ }
87
+ if (parsed.protocol !== "wss:" || parsed.username || parsed.password)
88
+ return false;
89
+ const host = parsed.hostname.toLowerCase();
90
+ if (host === "discord.gg" || host.endsWith(".discord.gg"))
91
+ return true;
92
+ try {
93
+ return host === new URL(fallback).hostname.toLowerCase();
94
+ } catch {
95
+ return false;
77
96
  }
78
97
  }
79
98
 
@@ -349,12 +368,15 @@ class PlainTextDecoder {
349
368
 
350
369
  class ZlibStreamDecoder {
351
370
  #create;
371
+ #maxFrameBytes;
352
372
  #inflate;
353
373
  #chunks = [];
374
+ #size = 0;
354
375
  #failure;
355
376
  #pendingReject;
356
- constructor(create = () => createInflate()) {
377
+ constructor(create = () => createInflate(), maxFrameBytes = 64 * 1024 * 1024) {
357
378
  this.#create = create;
379
+ this.#maxFrameBytes = maxFrameBytes;
358
380
  }
359
381
  push(data) {
360
382
  if (typeof data === "string")
@@ -373,6 +395,7 @@ class ZlibStreamDecoder {
373
395
  await this.#awaiting((settle) => inflate.flush(zlibConstants.Z_SYNC_FLUSH, () => settle()));
374
396
  const parts = this.#chunks;
375
397
  this.#chunks = [];
398
+ this.#size = 0;
376
399
  if (parts.length === 0)
377
400
  return [];
378
401
  const total = parts.reduce((size, part) => size + part.length, 0);
@@ -388,6 +411,7 @@ class ZlibStreamDecoder {
388
411
  const inflate = this.#inflate;
389
412
  this.#inflate = undefined;
390
413
  this.#chunks = [];
414
+ this.#size = 0;
391
415
  this.#failure = undefined;
392
416
  this.#pendingReject = undefined;
393
417
  if (!inflate)
@@ -415,7 +439,17 @@ class ZlibStreamDecoder {
415
439
  }
416
440
  #open() {
417
441
  const inflate = this.#create();
418
- inflate.on("data", (chunk) => this.#chunks.push(chunk));
442
+ inflate.on("data", (chunk) => {
443
+ if (this.#failure)
444
+ return;
445
+ this.#size += chunk.length;
446
+ if (this.#size > this.#maxFrameBytes) {
447
+ this.#chunks = [];
448
+ inflate.emit("error", new Error(`Gateway frame inflated past ${this.#maxFrameBytes} bytes.`));
449
+ return;
450
+ }
451
+ this.#chunks.push(chunk);
452
+ });
419
453
  inflate.on("error", (error) => {
420
454
  this.#failure = error;
421
455
  const reject = this.#pendingReject;
@@ -658,9 +692,9 @@ function classifyPayload(payload) {
658
692
  }
659
693
  function identifyPayload(options) {
660
694
  const props = options.properties ?? {};
661
- const os = props.os ?? "Android";
662
- const browser = props.browser ?? "Discord Android";
663
- const device = props.device ?? "Discord Android";
695
+ const os = props.os ?? process.platform;
696
+ const browser = props.browser ?? "Lunibee";
697
+ const device = props.device ?? "Lunibee";
664
698
  return {
665
699
  op: GatewayOpcodes.Identify,
666
700
  d: {
@@ -756,28 +790,26 @@ var IntentBits = {
756
790
  directMessagePolls: 1 << 25
757
791
  };
758
792
  function resolveGatewayIntents(intents) {
759
- if (typeof intents === "number")
760
- return intents;
761
- if (intents && typeof intents === "object" && !Array.isArray(intents))
793
+ if (Array.isArray(intents))
794
+ return intents.reduce((acc, intent) => acc | resolveIntent(intent), 0);
795
+ if (typeof intents === "object")
762
796
  return intents.bitfield;
763
- if (Array.isArray(intents)) {
764
- return intents.reduce((acc, intent) => {
765
- return acc | resolveGatewayIntents(intent);
766
- }, 0);
767
- }
768
- if (typeof intents === "string") {
769
- if (intents in GatewayIntentBits)
770
- return GatewayIntentBits[intents];
771
- if (intents in IntentBits)
772
- return IntentBits[intents];
773
- const lower = intents.charAt(0).toLowerCase() + intents.slice(1);
774
- if (lower in IntentBits)
775
- return IntentBits[lower];
776
- const num = Number(intents);
777
- if (!Number.isNaN(num))
778
- return num;
779
- }
780
- return 0;
797
+ return resolveIntent(intents);
798
+ }
799
+ function resolveIntent(intent) {
800
+ if (typeof intent === "number")
801
+ return intent;
802
+ const bits = {
803
+ ...IntentBits,
804
+ ...GatewayIntentBits
805
+ };
806
+ const lower = intent.charAt(0).toLowerCase() + intent.slice(1);
807
+ if (Object.hasOwn(bits, intent))
808
+ return bits[intent];
809
+ if (Object.hasOwn(bits, lower))
810
+ return bits[lower];
811
+ const num = Number(intent);
812
+ return Number.isNaN(num) ? 0 : num;
781
813
  }
782
814
  // packages/ws/src/state.ts
783
815
  var GatewayState;
@@ -854,9 +886,9 @@ class Gateway {
854
886
  heartbeatAckTimeout: 1e4,
855
887
  zombieTimeout: 30000,
856
888
  properties: {
857
- os: "Android",
858
- browser: "Discord Android",
859
- device: "Discord Android"
889
+ os: process.platform,
890
+ browser: "Lunibee",
891
+ device: "Lunibee"
860
892
  },
861
893
  presence: {
862
894
  status: "online",
@@ -1193,4 +1225,4 @@ export {
1193
1225
  resumePayload
1194
1226
  };
1195
1227
 
1196
- //# debugId=0B3294008B8C43CB64756E2164756E21
1228
+ //# debugId=57DB54C5251644A264756E2164756E21