lunibee 0.3.1 → 0.3.3

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 (81) hide show
  1. package/dist/builders/attachment.d.ts +1 -0
  2. package/dist/builders/clone.d.ts +10 -0
  3. package/dist/builders/commands.d.ts +1 -1
  4. package/dist/builders/components.d.ts +24 -5
  5. package/dist/builders/index.js +64 -17
  6. package/dist/builders/index.js.map +10 -9
  7. package/dist/collection/index.d.ts +1 -0
  8. package/dist/collection/index.js +297 -19
  9. package/dist/collection/index.js.map +5 -4
  10. package/dist/collection/redis.d.ts +98 -0
  11. package/dist/core/collector.d.ts +8 -0
  12. package/dist/core/events.d.ts +1 -0
  13. package/dist/core/index.d.ts +3 -0
  14. package/dist/core/index.js +6242 -4733
  15. package/dist/core/index.js.map +47 -40
  16. package/dist/core/permissions.d.ts +3 -402
  17. package/dist/core/redis-cache.d.ts +85 -0
  18. package/dist/formatters/index.d.ts +5 -1
  19. package/dist/formatters/index.js +7 -3
  20. package/dist/formatters/index.js.map +3 -3
  21. package/dist/handlers/index.js +11 -2
  22. package/dist/handlers/index.js.map +3 -3
  23. package/dist/index.js +8688 -6136
  24. package/dist/index.js.map +67 -59
  25. package/dist/managers/application.d.ts +10 -6
  26. package/dist/managers/base.d.ts +11 -1
  27. package/dist/managers/guild.d.ts +3 -1
  28. package/dist/managers/index.d.ts +6 -1
  29. package/dist/managers/index.js +383 -109
  30. package/dist/managers/index.js.map +20 -23
  31. package/dist/managers/member.d.ts +3 -1
  32. package/dist/managers/message.d.ts +13 -4
  33. package/dist/managers/thread.d.ts +3 -3
  34. package/dist/rest/decoder.d.ts +2 -0
  35. package/dist/rest/errors.d.ts +5 -2
  36. package/dist/rest/headers.d.ts +31 -0
  37. package/dist/rest/identity.d.ts +31 -0
  38. package/dist/rest/index.d.ts +97 -6
  39. package/dist/rest/index.js +1355 -220
  40. package/dist/rest/index.js.map +16 -13
  41. package/dist/rest/limiter.d.ts +75 -10
  42. package/dist/rest/redis.d.ts +103 -22
  43. package/dist/rest/scheduler.d.ts +13 -0
  44. package/dist/rest/store.d.ts +135 -12
  45. package/dist/rest/telemetry.d.ts +151 -0
  46. package/dist/rest/webhook.d.ts +18 -6
  47. package/dist/sharding/bus.d.ts +4 -2
  48. package/dist/sharding/cluster.d.ts +29 -1
  49. package/dist/sharding/health.d.ts +1 -1
  50. package/dist/sharding/host.d.ts +13 -2
  51. package/dist/sharding/identify.d.ts +2 -0
  52. package/dist/sharding/index.d.ts +8 -0
  53. package/dist/sharding/index.js +579 -65
  54. package/dist/sharding/index.js.map +17 -15
  55. package/dist/sharding/leader.d.ts +34 -0
  56. package/dist/sharding/remote.d.ts +36 -2
  57. package/dist/sharding/supervisor.d.ts +1 -1
  58. package/dist/sharding/thread.d.ts +30 -0
  59. package/dist/sharding/transport.d.ts +6 -5
  60. package/dist/sharding/worker.d.ts +11 -1
  61. package/dist/structures/base.d.ts +16 -5
  62. package/dist/structures/index.d.ts +1 -1
  63. package/dist/structures/index.js +57 -91
  64. package/dist/structures/index.js.map +10 -18
  65. package/dist/structures/options.d.ts +3 -2
  66. package/dist/structures/permissions.d.ts +409 -0
  67. package/dist/structures/resources.d.ts +4 -1
  68. package/dist/types/gateway.d.ts +4 -0
  69. package/dist/types/index.d.ts +14 -4
  70. package/dist/types/index.js.map +3 -3
  71. package/dist/utils/args.d.ts +3 -1
  72. package/dist/utils/index.d.ts +6 -1
  73. package/dist/utils/index.js +17 -10
  74. package/dist/utils/index.js.map +4 -4
  75. package/dist/voice/index.d.ts +7 -1
  76. package/dist/voice/index.js +33 -10
  77. package/dist/voice/index.js.map +3 -3
  78. package/dist/ws/index.d.ts +2 -0
  79. package/dist/ws/index.js +32 -4
  80. package/dist/ws/index.js.map +5 -5
  81. package/package.json +21 -17
@@ -54,16 +54,20 @@ class VoiceConnection {
54
54
  this.#listeners[event]?.delete(listener);
55
55
  return this;
56
56
  }
57
+ #listening = new WeakSet;
57
58
  attachTransports(gateway, udp) {
58
59
  this.assertUsable();
59
60
  if (!gateway || !udp)
60
61
  throw new TypeError("Both voice gateway and UDP transports are required.");
62
+ if (this.gateway === gateway && this.udp === udp)
63
+ return this;
61
64
  if (this.gateway || this.udp)
62
65
  this.#closeTransports();
63
66
  this.gateway = gateway;
64
67
  this.udp = udp;
65
68
  const attachedUdp = udp;
66
- if (attachedUdp.onMessage) {
69
+ if (attachedUdp.onMessage && !this.#listening.has(attachedUdp)) {
70
+ this.#listening.add(attachedUdp);
67
71
  attachedUdp.onMessage((packet) => {
68
72
  if (this.udp === attachedUdp)
69
73
  this.receiver.onPacket(packet);
@@ -109,13 +113,13 @@ class VoiceConnection {
109
113
  if (options.selfDeaf !== undefined)
110
114
  this.selfDeaf = options.selfDeaf;
111
115
  }
112
- setSpeaking(flags = SpeakingFlags.Microphone) {
116
+ setSpeaking(flags = SpeakingFlags.Microphone, ssrc = 0) {
113
117
  this.assertUsable();
114
118
  if (!this.gateway) {
115
119
  throw new VoiceError("A voice gateway transport is required to change speaking state.");
116
120
  }
117
- this.gateway.send({ op: 5, d: { speaking: flags, delay: 0, ssrc: 0 } });
118
- this.#emit("speaking", flags, 0);
121
+ this.gateway.send({ op: 5, d: { speaking: flags, delay: 0, ssrc } });
122
+ this.#emit("speaking", flags, ssrc);
119
123
  }
120
124
  #assertUsable() {
121
125
  if (this.state === "destroyed" /* Destroyed */) {
@@ -185,6 +189,8 @@ class AudioPlayer {
185
189
  #stream;
186
190
  #reader;
187
191
  #resumeSignal;
192
+ #generation = 0;
193
+ #ticket = 0;
188
194
  #playerListeners = {};
189
195
  get state() {
190
196
  return this.#state;
@@ -193,12 +199,20 @@ class AudioPlayer {
193
199
  return this.#stream;
194
200
  }
195
201
  async play(audioStream) {
202
+ const ticket = ++this.#ticket;
196
203
  if (this.#state === "playing" || this.#state === "paused")
197
204
  await this.stop();
205
+ if (ticket !== this.#ticket) {
206
+ audioStream.stream.cancel().catch(() => {
207
+ return;
208
+ });
209
+ return;
210
+ }
211
+ const generation = ++this.#generation;
198
212
  this.#stream = audioStream;
199
213
  this.#reader = audioStream.stream.getReader();
200
214
  this.#transition("playing");
201
- this.#pump().catch((err) => this.#emitError(err));
215
+ this.#pump(generation).catch((err) => this.#emitError(err));
202
216
  }
203
217
  pause() {
204
218
  if (this.#state !== "playing")
@@ -216,6 +230,7 @@ class AudioPlayer {
216
230
  async stop() {
217
231
  if (this.#state === "idle" || this.#state === "stopped")
218
232
  return;
233
+ this.#generation++;
219
234
  const reader = this.#reader;
220
235
  this.#reader = undefined;
221
236
  this.#stream = undefined;
@@ -238,7 +253,7 @@ class AudioPlayer {
238
253
  return this;
239
254
  }
240
255
  onChunk(chunk) {}
241
- async#pump() {
256
+ async#pump(generation) {
242
257
  const reader = this.#reader;
243
258
  if (!reader)
244
259
  return;
@@ -248,9 +263,11 @@ class AudioPlayer {
248
263
  this.#resumeSignal = resolve;
249
264
  });
250
265
  }
251
- if (this.#state !== "playing")
266
+ if (generation !== this.#generation || this.#state !== "playing")
252
267
  return;
253
268
  const { done, value } = await reader.read();
269
+ if (generation !== this.#generation)
270
+ return;
254
271
  if (done) {
255
272
  if (this.#state === "playing" || this.#state === "paused") {
256
273
  this.#transition("idle");
@@ -314,7 +331,7 @@ class VoiceReceiver {
314
331
  this.#streams.delete(userId);
315
332
  }
316
333
  }
317
- });
334
+ }, { highWaterMark: 50 });
318
335
  return new AudioStream(stream, { title: `User ${userId} Audio` });
319
336
  }
320
337
  close() {
@@ -331,6 +348,8 @@ class VoiceReceiver {
331
348
  if (packet.length < 12)
332
349
  return;
333
350
  const view = new DataView(packet.buffer, packet.byteOffset, packet.byteLength);
351
+ if ((packet[0] & 192) !== 128)
352
+ return;
334
353
  const ssrc = view.getUint32(8, false);
335
354
  const userId = this.#ssrcMap.get(ssrc);
336
355
  if (!userId)
@@ -353,6 +372,8 @@ class VoiceReceiver {
353
372
  const payload = packet.slice(offset);
354
373
  for (const controller of controllers) {
355
374
  try {
375
+ if ((controller.desiredSize ?? 1) <= 0)
376
+ continue;
356
377
  controller.enqueue(payload);
357
378
  } catch {}
358
379
  }
@@ -360,6 +381,8 @@ class VoiceReceiver {
360
381
  }
361
382
  var voiceConnections = new Map;
362
383
  function joinVoiceChannel(options) {
384
+ if (typeof options.channelId !== "string" || !options.channelId.trim())
385
+ throw new TypeError("A voice channel ID is required.");
363
386
  let connection = voiceConnections.get(options.guildId);
364
387
  if (!connection) {
365
388
  connection = new VoiceConnection(options);
@@ -400,7 +423,7 @@ function entersState(target, status, timeoutMs) {
400
423
  const timer = setTimeout(() => {
401
424
  emitter.off("stateChange", onChange);
402
425
  reject(new VoiceError(`Did not enter state "${String(status)}" within ${timeoutMs}ms.`));
403
- }, timeoutMs);
426
+ }, Math.min(timeoutMs, 2147483647));
404
427
  emitter.on("stateChange", onChange);
405
428
  });
406
429
  }
@@ -420,4 +443,4 @@ export {
420
443
  joinVoiceChannel
421
444
  };
422
445
 
423
- //# debugId=854FAC176777C99264756E2164756E21
446
+ //# debugId=0DF7DB68FBFCF1FC64756E2164756E21
@@ -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/** A JSON frame for the voice gateway, e.g. `{ op: 5, d: { speaking, delay, ssrc } }`. */\nexport interface VoiceGatewayPayload {\n op: number;\n d: Record<string, string | number | boolean | null>;\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: VoiceGatewayPayload): 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 ListenerSets<E extends Record<keyof E, unknown[]>> = {\n [K in keyof E]?: Set<(...args: E[K]) => unknown>;\n};\n\n/** Returns the listener set for `event`, creating it on first use. */\nfunction listenerSet<E extends Record<keyof E, unknown[]>, K extends keyof E>(\n sets: ListenerSets<E>,\n event: K,\n): Set<(...args: E[K]) => unknown> {\n return (sets[event] ??= new Set());\n}\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 #listeners: ListenerSets<VoiceEvents> = {};\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 listenerSet(this.#listeners, event).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[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 = {};\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.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[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. */\n public readonly stream: ReadableStream<Uint8Array>;\n /** Optional human-readable title for the stream. */\n public readonly title?: string;\n /** Optional duration hint in seconds. */\n 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 #playerListeners: ListenerSets<AudioPlayerEvents> = {};\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 listenerSet(this.#playerListeners, event).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 this.#playerListeners[event]?.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.#playerListeners[event] ?? []) {\n try {\n void listener(...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 first = view.getUint8(0);\n const csrcCount = first & 0x0f;\n let offset = 12 + csrcCount * 4;\n const hasExtension = (first & 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. */\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/** A JSON frame for the voice gateway, e.g. `{ op: 5, d: { speaking, delay, ssrc } }`. */\nexport interface VoiceGatewayPayload {\n op: number;\n d: Record<string, string | number | boolean | null>;\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: VoiceGatewayPayload): 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 ListenerSets<E extends Record<keyof E, unknown[]>> = {\n [K in keyof E]?: Set<(...args: E[K]) => unknown>;\n};\n\n/** Returns the listener set for `event`, creating it on first use. */\nfunction listenerSet<E extends Record<keyof E, unknown[]>, K extends keyof E>(\n sets: ListenerSets<E>,\n event: K,\n): Set<(...args: E[K]) => unknown> {\n return (sets[event] ??= new Set());\n}\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 #listeners: ListenerSets<VoiceEvents> = {};\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 listenerSet(this.#listeners, event).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[event]?.delete(listener);\n return this;\n }\n\n readonly #listening = new WeakSet<VoiceUdpTransport>();\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 // The same pair again is a no-op: closing it first would end the\n // transports just attached.\n if (this.gateway === gateway && this.udp === udp) return this;\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 // A transport that comes back must not get a second listener (it\n // cannot be removed), or every datagram would be delivered twice.\n if (attachedUdp.onMessage && !this.#listening.has(attachedUdp)) {\n this.#listening.add(attachedUdp);\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 = {};\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 /**\n * Sends a speaking update (voice op 5). Pass the `ssrc` your voice gateway\n * assigned in READY; Discord uses it to match the audio to this user.\n */\n public setSpeaking(\n flags: number = SpeakingFlags.Microphone,\n ssrc = 0,\n ): 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 } });\n this.#emit(\"speaking\", flags, ssrc);\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.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[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. */\n public readonly stream: ReadableStream<Uint8Array>;\n /** Optional human-readable title for the stream. */\n public readonly title?: string;\n /** Optional duration hint in seconds. */\n 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 /** Identifies the pump loop that owns the player; a stop or a new `play()` retires the old loop. */\n #generation = 0;\n /** The latest `play()` call; an earlier call still waiting for `stop()` gives way to it. */\n #ticket = 0;\n readonly #playerListeners: ListenerSets<AudioPlayerEvents> = {};\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 const ticket = ++this.#ticket;\n if (this.#state === \"playing\" || this.#state === \"paused\")\n await this.stop();\n if (ticket !== this.#ticket) {\n // A newer play() arrived while this one waited for stop(): it wins,\n // and this stream is never read, so release it.\n audioStream.stream.cancel().catch(() => undefined);\n return;\n }\n const generation = ++this.#generation;\n this.#stream = audioStream;\n this.#reader = audioStream.stream.getReader();\n this.#transition(\"playing\");\n this.#pump(generation).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 // The loop reading the old stream must not outlive this call, whatever\n // state a following play() puts the player in.\n this.#generation++;\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 listenerSet(this.#playerListeners, event).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 this.#playerListeners[event]?.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(generation: number): 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()) or replaced.\n if (generation !== this.#generation || this.#state !== \"playing\")\n return;\n const { done, value } = await reader.read();\n // A stop() or a newer play() retired this loop during the read:\n // drop what it read and leave the new stream's state alone.\n if (generation !== this.#generation) return;\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.#playerListeners[event] ?? []) {\n try {\n void listener(...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 {\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 // About a second of 20 ms packets; a reader that falls further\n // behind loses the newest packets instead of growing the heap.\n { highWaterMark: 50 },\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 // RTP version 2 only: anything else is not audio for this stream.\n if ((packet[0]! & 0xc0) !== 0x80) return;\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 first = view.getUint8(0);\n const csrcCount = first & 0x0f;\n let offset = 12 + csrcCount * 4;\n const hasExtension = (first & 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 if ((controller.desiredSize ?? 1) <= 0) continue;\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 * The connection stays tracked after `disconnect()` (a later join reuses it) and\n * leaves the registry only on `destroy()`.\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 // Checked before anything is registered, so a bad call leaves no entry behind.\n if (typeof options.channelId !== \"string\" || !options.channelId.trim())\n throw new TypeError(\"A voice channel ID is required.\");\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 () => {\n emitter.off(\"stateChange\", onChange);\n reject(\n new VoiceError(\n `Did not enter state \"${String(status)}\" within ${timeoutMs}ms.`,\n ),\n );\n },\n Math.min(timeoutMs, 2_147_483_647),\n );\n emitter.on(\"stateChange\", onChange);\n });\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "854FAC176777C99264756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "0DF7DB68FBFCF1FC64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -27,6 +27,8 @@ export interface GatewayOptions {
27
27
  reconnect?: boolean;
28
28
  /** Maximum reconnect attempts. */
29
29
  maxReconnectAttempts?: number;
30
+ /** How long a socket may stay connecting before it is closed and retried, in ms. Default 30000. */
31
+ connectTimeout?: number;
30
32
  /** Initial reconnect delay. */
31
33
  reconnectBaseDelay?: number;
32
34
  /** Maximum reconnect delay. */
package/dist/ws/index.js CHANGED
@@ -535,6 +535,7 @@ class WebSocketTransport {
535
535
  decoded = this.#decoder.push(event.data);
536
536
  } catch (error) {
537
537
  this.#handlers.onError(this.#asError(error));
538
+ this.#failDecoding(generation);
538
539
  return;
539
540
  }
540
541
  if (!(decoded instanceof Promise)) {
@@ -552,6 +553,7 @@ class WebSocketTransport {
552
553
  if (!this.#owns(generation))
553
554
  return;
554
555
  this.#handlers.onError(this.#asError(error));
556
+ this.#failDecoding(generation);
555
557
  });
556
558
  });
557
559
  socket.addEventListener("close", (event) => {
@@ -580,6 +582,10 @@ class WebSocketTransport {
580
582
  return false;
581
583
  }
582
584
  }
585
+ #failDecoding(generation) {
586
+ if (this.#owns(generation))
587
+ this.close(4000, "decoder failure");
588
+ }
583
589
  close(code = 1000, reason = "") {
584
590
  const socket = this.#socket;
585
591
  if (!socket)
@@ -935,6 +941,13 @@ class Gateway {
935
941
  constructor(options) {
936
942
  if (!options.token?.trim())
937
943
  throw new TypeError("A Gateway token is required.");
944
+ for (const key of [
945
+ "reconnectBaseDelay",
946
+ "reconnectMaxDelay",
947
+ "connectTimeout"
948
+ ])
949
+ if (options[key] !== undefined && !(options[key] > 0))
950
+ throw new RangeError(`Gateway ${key} must be greater than 0.`);
938
951
  const resolvedIntents = resolveGatewayIntents(options.intents);
939
952
  if (!Number.isInteger(resolvedIntents) || resolvedIntents < 0)
940
953
  throw new TypeError("Gateway intents must be a non-negative integer.");
@@ -943,6 +956,7 @@ class Gateway {
943
956
  shardCount: 1,
944
957
  reconnect: true,
945
958
  maxReconnectAttempts: Infinity,
959
+ connectTimeout: 30000,
946
960
  reconnectBaseDelay: 1000,
947
961
  reconnectMaxDelay: 30000,
948
962
  heartbeatAckTimeout: 1e4,
@@ -1018,7 +1032,7 @@ class Gateway {
1018
1032
  this.#reconnect.cancel();
1019
1033
  this.#closed = false;
1020
1034
  this.#setState("CONNECT" /* Connect */);
1021
- const connectURL = this.#options.compress ? url.includes("compress=") ? url : `${url}&compress=zlib-stream` : url;
1035
+ const connectURL = this.#options.compress ? url.includes("compress=") ? url : `${url}${url.includes("?") ? "&" : "?"}compress=zlib-stream` : url;
1022
1036
  return this.#reconnect.attempt(() => this.#open(connectURL));
1023
1037
  }
1024
1038
  close() {
@@ -1104,6 +1118,8 @@ class Gateway {
1104
1118
  const count = this.#options.shardCount;
1105
1119
  if (count <= 1)
1106
1120
  return true;
1121
+ if (!/^\d{1,20}$/.test(guildId))
1122
+ return false;
1107
1123
  return Number((BigInt(guildId) >> 22n) % BigInt(count)) === this.#options.shardId;
1108
1124
  }
1109
1125
  requestGuildMembers(data) {
@@ -1119,8 +1135,16 @@ class Gateway {
1119
1135
  return;
1120
1136
  this.#token = this.#session.beginConnection();
1121
1137
  const result = this.#transport.connect(url);
1122
- if (result.ok)
1138
+ if (result.ok) {
1139
+ const token = this.#token;
1140
+ clearTimeout(this.#connectTimer);
1141
+ this.#connectTimer = setTimeout(() => {
1142
+ if (token === this.#token && !this.#closed)
1143
+ this.#transport.close(4000, "connect timeout");
1144
+ }, this.#options.connectTimeout);
1145
+ this.#connectTimer.unref?.();
1123
1146
  return;
1147
+ }
1124
1148
  const failure = this.#normalizeError(result.error);
1125
1149
  this.#emitError(failure);
1126
1150
  if (this.#options.reconnect) {
@@ -1130,7 +1154,9 @@ class Gateway {
1130
1154
  this.#settleConnect(failure);
1131
1155
  }
1132
1156
  }
1157
+ #connectTimer;
1133
1158
  #onOpen() {
1159
+ clearTimeout(this.#connectTimer);
1134
1160
  this.#heartbeat.watch();
1135
1161
  this.#emit("open", undefined);
1136
1162
  this.#settleConnect();
@@ -1198,7 +1224,8 @@ class Gateway {
1198
1224
  this.#emit("RAW", { event: event ?? "UNKNOWN", data });
1199
1225
  if (this.#closed)
1200
1226
  return;
1201
- this.#emit(event ?? "dispatch", data);
1227
+ if (event == null || /^[A-Z][A-Z0-9_]*$/.test(event))
1228
+ this.#emit(event ?? "dispatch", data);
1202
1229
  }
1203
1230
  #handleHello(interval) {
1204
1231
  this.#setState(this.#session.canResume ? "RESUME" /* Resume */ : "HELLO" /* Hello */);
@@ -1261,6 +1288,7 @@ class Gateway {
1261
1288
  this.#reconnect.settle(error);
1262
1289
  }
1263
1290
  #clearTimers() {
1291
+ clearTimeout(this.#connectTimer);
1264
1292
  this.#reconnect.cancel();
1265
1293
  this.#heartbeat.stop();
1266
1294
  }
@@ -1310,4 +1338,4 @@ export {
1310
1338
  resumePayload
1311
1339
  };
1312
1340
 
1313
- //# debugId=E098D30D7AE9366264756E2164756E21
1341
+ //# debugId=49D265E85C1E654C64756E2164756E21