lunibee 0.3.3 → 0.4.0

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 (125) hide show
  1. package/README.md +1 -1
  2. package/dist/builders/commands.d.ts +1 -1
  3. package/dist/builders/components.d.ts +9 -11
  4. package/dist/builders/embed.d.ts +1 -1
  5. package/dist/builders/index.d.ts +1 -1
  6. package/dist/builders/index.js +87 -1298
  7. package/dist/builders/index.js.map +3 -11
  8. package/dist/chunks/index-0wn81eng.js +2492 -0
  9. package/dist/chunks/index-0wn81eng.js.map +17 -0
  10. package/dist/chunks/index-2en489j6.js +40 -0
  11. package/dist/chunks/index-2en489j6.js.map +10 -0
  12. package/dist/chunks/index-3hyps48a.js +203 -0
  13. package/dist/chunks/index-3hyps48a.js.map +11 -0
  14. package/dist/chunks/index-57c5n3ms.js +2274 -0
  15. package/dist/chunks/index-57c5n3ms.js.map +19 -0
  16. package/dist/chunks/index-5yx92e36.js +1170 -0
  17. package/dist/chunks/index-5yx92e36.js.map +14 -0
  18. package/dist/chunks/index-690bp093.js +95 -0
  19. package/dist/chunks/index-690bp093.js.map +10 -0
  20. package/dist/chunks/index-7fp2hvmg.js +2155 -0
  21. package/dist/chunks/index-7fp2hvmg.js.map +24 -0
  22. package/dist/chunks/index-cnmsajvk.js +1119 -0
  23. package/dist/chunks/index-cnmsajvk.js.map +12 -0
  24. package/dist/chunks/index-fdvgk2sw.js +1310 -0
  25. package/dist/chunks/index-fdvgk2sw.js.map +21 -0
  26. package/dist/chunks/index-gyvps8b9.js +214 -0
  27. package/dist/chunks/index-gyvps8b9.js.map +12 -0
  28. package/dist/chunks/index-jzg4bh99.js +1533 -0
  29. package/dist/chunks/index-jzg4bh99.js.map +21 -0
  30. package/dist/chunks/index-mgw2yvrm.js +3547 -0
  31. package/dist/chunks/index-mgw2yvrm.js.map +25 -0
  32. package/dist/chunks/index-yh76b55h.js +3449 -0
  33. package/dist/chunks/index-yh76b55h.js.map +29 -0
  34. package/dist/collection/index.js +9 -1118
  35. package/dist/collection/index.js.map +3 -6
  36. package/dist/core/api.d.ts +3 -3
  37. package/dist/core/channel-create.d.ts +2 -2
  38. package/dist/core/commands.d.ts +3 -3
  39. package/dist/core/events.d.ts +4 -4
  40. package/dist/core/handles.d.ts +4 -4
  41. package/dist/core/index.d.ts +25 -20
  42. package/dist/core/index.js +68 -10130
  43. package/dist/core/index.js.map +3 -65
  44. package/dist/core/permissions.d.ts +1 -1
  45. package/dist/core/redis-cache.d.ts +1 -1
  46. package/dist/formatters/index.js +41 -110
  47. package/dist/formatters/index.js.map +3 -4
  48. package/dist/handlers/index.js +5 -37
  49. package/dist/handlers/index.js.map +3 -4
  50. package/dist/index.d.ts +21 -21
  51. package/dist/index.js +641 -16325
  52. package/dist/index.js.map +3 -92
  53. package/dist/managers/advanced.d.ts +3 -3
  54. package/dist/managers/application.d.ts +2 -2
  55. package/dist/managers/base.d.ts +1 -1
  56. package/dist/managers/emoji.d.ts +2 -2
  57. package/dist/managers/guild-resources.d.ts +6 -6
  58. package/dist/managers/guild.d.ts +3 -3
  59. package/dist/managers/index.d.ts +4 -8
  60. package/dist/managers/index.js +51 -4060
  61. package/dist/managers/index.js.map +3 -29
  62. package/dist/managers/member.d.ts +2 -2
  63. package/dist/managers/message.d.ts +14 -6
  64. package/dist/managers/role.d.ts +2 -2
  65. package/dist/managers/thread.d.ts +3 -3
  66. package/dist/managers/user.d.ts +7 -4
  67. package/dist/rest/index.d.ts +10 -35
  68. package/dist/rest/index.js +57 -2184
  69. package/dist/rest/index.js.map +3 -18
  70. package/dist/rest/limiter.d.ts +9 -1
  71. package/dist/rest/webhook.d.ts +2 -8
  72. package/dist/sharding/bus.d.ts +2 -2
  73. package/dist/sharding/cluster.d.ts +45 -5
  74. package/dist/sharding/context.d.ts +45 -3
  75. package/dist/sharding/gateway-bot.d.ts +2 -1
  76. package/dist/sharding/health.d.ts +1 -1
  77. package/dist/sharding/identify.d.ts +12 -0
  78. package/dist/sharding/index.d.ts +44 -11
  79. package/dist/sharding/index.js +60 -4620
  80. package/dist/sharding/index.js.map +3 -33
  81. package/dist/sharding/supervisor.d.ts +4 -2
  82. package/dist/structures/audit-log.d.ts +1 -1
  83. package/dist/structures/base.d.ts +6 -6
  84. package/dist/structures/channels.d.ts +1 -1
  85. package/dist/structures/embed.d.ts +1 -1
  86. package/dist/structures/index.d.ts +14 -14
  87. package/dist/structures/index.js +109 -2382
  88. package/dist/structures/index.js.map +3 -16
  89. package/dist/structures/interactions.d.ts +13 -9
  90. package/dist/structures/options.d.ts +1 -1
  91. package/dist/structures/permissions.d.ts +2 -2
  92. package/dist/structures/resources.d.ts +10 -10
  93. package/dist/structures/sticker.d.ts +1 -1
  94. package/dist/types/gateway.d.ts +2 -79
  95. package/dist/types/index.d.ts +0 -6
  96. package/dist/types/index.js +41 -272
  97. package/dist/types/index.js.map +3 -6
  98. package/dist/utils/index.js +23 -209
  99. package/dist/utils/index.js.map +3 -5
  100. package/dist/voice/connection.d.ts +200 -0
  101. package/dist/voice/crypto.d.ts +50 -0
  102. package/dist/voice/dave.d.ts +85 -0
  103. package/dist/voice/effects.d.ts +31 -0
  104. package/dist/voice/emitter.d.ts +14 -0
  105. package/dist/voice/gateway.d.ts +59 -0
  106. package/dist/voice/index.d.ts +25 -228
  107. package/dist/voice/index.js +111 -442
  108. package/dist/voice/index.js.map +3 -4
  109. package/dist/voice/libopus.d.ts +88 -0
  110. package/dist/voice/networking.d.ts +80 -0
  111. package/dist/voice/ogg.d.ts +24 -0
  112. package/dist/voice/opus.d.ts +18 -0
  113. package/dist/voice/player.d.ts +79 -0
  114. package/dist/voice/protocol.d.ts +76 -0
  115. package/dist/voice/receiver.d.ts +59 -0
  116. package/dist/voice/resource.d.ts +102 -0
  117. package/dist/voice/udp.d.ts +40 -0
  118. package/dist/ws/close-codes.d.ts +1 -1
  119. package/dist/ws/index.d.ts +25 -4
  120. package/dist/ws/index.js +47 -1336
  121. package/dist/ws/index.js.map +3 -17
  122. package/dist/ws/protocol.d.ts +1 -1
  123. package/dist/ws/send-budget.d.ts +0 -2
  124. package/dist/ws/state.d.ts +0 -10
  125. package/package.json +19 -16
@@ -0,0 +1,88 @@
1
+ import { dlopen, FFIType } from "bun:ffi";
2
+ /** `OPUS_APPLICATION_*`: what the encoder tunes for. */
3
+ export declare const OpusApplication: {
4
+ /** Speech: best for voice chat. */
5
+ readonly Voip: 2048;
6
+ /** Music and mixed content (the default). */
7
+ readonly Audio: 2049;
8
+ /** Lowest delay; disables the speech modes. */
9
+ readonly RestrictedLowDelay: 2051;
10
+ };
11
+ export type OpusApplication = (typeof OpusApplication)[keyof typeof OpusApplication];
12
+ declare const SYMBOLS: {
13
+ readonly opus_encoder_create: {
14
+ readonly args: readonly [FFIType.int32_t, FFIType.int32_t, FFIType.int32_t, FFIType.ptr];
15
+ readonly returns: FFIType.ptr;
16
+ };
17
+ readonly opus_encode: {
18
+ readonly args: readonly [FFIType.ptr, FFIType.ptr, FFIType.int32_t, FFIType.ptr, FFIType.int32_t];
19
+ readonly returns: FFIType.int32_t;
20
+ };
21
+ readonly opus_encoder_destroy: {
22
+ readonly args: readonly [FFIType.ptr];
23
+ readonly returns: FFIType.void;
24
+ };
25
+ readonly opus_decoder_create: {
26
+ readonly args: readonly [FFIType.int32_t, FFIType.int32_t, FFIType.ptr];
27
+ readonly returns: FFIType.ptr;
28
+ };
29
+ readonly opus_decode: {
30
+ readonly args: readonly [FFIType.ptr, FFIType.ptr, FFIType.int32_t, FFIType.ptr, FFIType.int32_t, FFIType.int32_t];
31
+ readonly returns: FFIType.int32_t;
32
+ };
33
+ readonly opus_decoder_destroy: {
34
+ readonly args: readonly [FFIType.ptr];
35
+ readonly returns: FFIType.void;
36
+ };
37
+ readonly opus_strerror: {
38
+ readonly args: readonly [FFIType.int32_t];
39
+ readonly returns: FFIType.ptr;
40
+ };
41
+ readonly opus_get_version_string: {
42
+ readonly args: readonly [];
43
+ readonly returns: FFIType.ptr;
44
+ };
45
+ };
46
+ type Library = ReturnType<typeof dlopen<typeof SYMBOLS>>["symbols"];
47
+ /**
48
+ * Opens the system's libopus through `bun:ffi` (once). `null` when it is not
49
+ * installed: `apt install libopus0`, `brew install opus`, or point
50
+ * `LUNIBEE_OPUS_LIBRARY` at the library file.
51
+ */
52
+ export declare function loadLibopus(paths?: readonly string[]): {
53
+ symbols: Library;
54
+ path: string;
55
+ } | null;
56
+ /** Whether libopus can be loaded (looked up once). */
57
+ export declare function hasLibopus(): boolean;
58
+ /** @internal Forget the lookup, so tests can change `LUNIBEE_OPUS_LIBRARY`. */
59
+ export declare function resetLibopus(): void;
60
+ /** The libopus version string, e.g. `"libopus 1.5.2"`, or `null` when it is not installed. */
61
+ export declare function libopusVersion(): string | null;
62
+ /**
63
+ * Lunibee's Opus encoder and decoder for 48 kHz stereo, on the system's
64
+ * libopus through `bun:ffi`: native speed with nothing to compile. Frees its
65
+ * native state on `destroy()` (and, as a safety net, when it is collected).
66
+ */
67
+ export declare class Opus {
68
+ #private;
69
+ /** @throws {VoiceError} When libopus is not installed or refuses the settings. */
70
+ constructor(application?: OpusApplication);
71
+ /** Where libopus was loaded from. */
72
+ static get libraryPath(): string;
73
+ /**
74
+ * Encodes one 20 ms frame: 960 stereo samples of s16le PCM (3,840 bytes).
75
+ * @returns The Opus packet (a new array).
76
+ * @throws {VoiceError} After `destroy()`, for a frame of the wrong size, or when libopus fails.
77
+ */
78
+ encode(pcm: Uint8Array): Uint8Array;
79
+ /**
80
+ * Decodes one Opus packet to s16le PCM (3,840 bytes for a 20 ms packet).
81
+ * An empty packet stands for a lost one and is concealed.
82
+ * @throws {VoiceError} After `destroy()`, or for a corrupt packet.
83
+ */
84
+ decode(packet: Uint8Array): Uint8Array;
85
+ /** Frees the native encoder and decoder. Safe to call twice. */
86
+ destroy(): void;
87
+ }
88
+ export {};
@@ -0,0 +1,80 @@
1
+ import { type VoiceEncryptionMode } from "./crypto.js";
2
+ import { DaveSession, loadDave } from "./dave.js";
3
+ import { type VoiceSocketFactory } from "./gateway.js";
4
+ import { type UdpSocketFactory } from "./udp.js";
5
+ /** What Discord sent for one voice session (VOICE_STATE_UPDATE + VOICE_SERVER_UPDATE). */
6
+ export interface VoiceServerInfo {
7
+ guildId: string;
8
+ channelId: string;
9
+ userId: string;
10
+ sessionId: string;
11
+ token: string;
12
+ endpoint: string;
13
+ }
14
+ /** Where a {@link VoiceNetworking} is in its handshake. */
15
+ export type NetworkingState = "opening" | "identifying" | "discovering" | "selecting" | "ready" | "resuming" | "closed";
16
+ /** Options for {@link VoiceNetworking}. */
17
+ export interface VoiceNetworkingOptions {
18
+ /** Offer DAVE (end-to-end encryption) when `@snazzah/davey` is installed. Default true. Discord requires it outside stage channels. */
19
+ dave?: boolean;
20
+ /** Opens the voice WebSocket (tests). */
21
+ socketFactory?: VoiceSocketFactory;
22
+ /** Opens the UDP socket (tests). */
23
+ udpFactory?: UdpSocketFactory;
24
+ /** Overrides the voice gateway URL built from the endpoint (tests): a URL, or a function of the endpoint. */
25
+ url?: string | ((endpoint: string) => string);
26
+ debug?: (message: string) => void;
27
+ /** @internal A DAVE implementation in place of `@snazzah/davey` (tests). */
28
+ daveModule?: Awaited<ReturnType<typeof loadDave>>;
29
+ /** @internal Resume backoff (tests): attempts before giving up, first delay and cap in ms. */
30
+ resume?: {
31
+ attempts?: number;
32
+ delay?: number;
33
+ maxDelay?: number;
34
+ };
35
+ }
36
+ /** What a {@link VoiceNetworking} reports. */
37
+ export interface VoiceNetworkingHandlers {
38
+ state?(state: NetworkingState, previous: NetworkingState): void;
39
+ /** The session closed for good (not resumable). */
40
+ close?(code: number, reason: string): void;
41
+ error?(error: Error): void;
42
+ /** An Opus frame from another user, decrypted. */
43
+ audio?(userId: string, frame: Uint8Array): void;
44
+ /** Op 5 from another user: their SSRC. */
45
+ speaking?(userId: string, ssrc: number): void;
46
+ /** Op 13. */
47
+ clientDisconnect?(userId: string): void;
48
+ }
49
+ /**
50
+ * One voice server session: the voice gateway handshake (v8), IP discovery,
51
+ * transport encryption, DAVE, and sending and receiving RTP packets. A new
52
+ * server (VOICE_SERVER_UPDATE) needs a new instance; a recoverable close
53
+ * (network loss, voice server crash) resumes on its own.
54
+ */
55
+ export declare class VoiceNetworking {
56
+ #private;
57
+ state: NetworkingState;
58
+ /** Our SSRC, from READY. */
59
+ ssrc: number;
60
+ /** The negotiated encryption mode, once ready. */
61
+ mode?: VoiceEncryptionMode;
62
+ constructor(info: VoiceServerInfo, handlers?: VoiceNetworkingHandlers, options?: VoiceNetworkingOptions);
63
+ /** Heartbeat round trip in ms, or -1. */
64
+ get ping(): number;
65
+ /** The DAVE session, when Discord negotiated one. */
66
+ get dave(): DaveSession | undefined;
67
+ /** User IDs in the channel, from ops 11 and 13. */
68
+ get connectedUsers(): ReadonlySet<string>;
69
+ /** Sends op 5. Only sends when the flag changes. */
70
+ setSpeaking(speaking: boolean, flags?: number): void;
71
+ /**
72
+ * Encrypts one 20 ms Opus frame (DAVE, then transport) and sends it.
73
+ * Sequence and timestamp advance by one frame either way. A frame DAVE
74
+ * cannot end-to-end encrypt yet (the MLS group is not ready) is dropped.
75
+ * @returns Whether it was sent.
76
+ */
77
+ sendFrame(frame: Uint8Array): boolean;
78
+ /** Ends the session: gateway, UDP socket, DAVE state. Safe to call twice. */
79
+ destroy(): void;
80
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Reads Opus packets out of an Ogg/Opus stream without decoding them (the
3
+ * passthrough path: no Opus library, no re-encoding). Skips the OpusHead and
4
+ * OpusTags headers. Memory is bounded by one page and one packet.
5
+ * @throws {VoiceError} For an Ogg stream that is not 48 kHz Opus with at most two channels.
6
+ */
7
+ export declare function demuxOggOpus(chunks: AsyncIterable<Uint8Array>): AsyncGenerator<Uint8Array>;
8
+ /**
9
+ * Writes Opus packets as an Ogg/Opus stream (48 kHz, stereo), which players
10
+ * and FFmpeg read. Each packet counts as 20 ms (960 samples), as Discord sends.
11
+ * Pages hold up to 50 packets (one second).
12
+ */
13
+ export declare class OggOpusWriter {
14
+ #private;
15
+ constructor(serial?: number);
16
+ /** The two header pages (OpusHead, OpusTags). Write them first. */
17
+ header(vendor?: string): Uint8Array;
18
+ /** Adds one packet. @returns A page once 50 packets are buffered, else an empty array. */
19
+ write(packet: Uint8Array): Uint8Array;
20
+ /** Writes the buffered packets as a page (empty array when there are none). */
21
+ flush(): Uint8Array;
22
+ /** Writes the last page (end-of-stream flag). Further writes throw. */
23
+ end(): Uint8Array;
24
+ }
@@ -0,0 +1,18 @@
1
+ /** One 48 kHz stereo Opus encoder and decoder. Call `destroy()` when done. */
2
+ export interface OpusCodec {
3
+ /** The library behind it: `"libopus"` (the system's, through `bun:ffi`) or `"opusscript"` (WebAssembly). */
4
+ readonly library: string;
5
+ /** Encodes one 20 ms frame of s16le PCM (3,840 bytes). */
6
+ encode(pcm: Uint8Array): Uint8Array;
7
+ /** Decodes one Opus packet to s16le PCM. */
8
+ decode(packet: Uint8Array): Uint8Array;
9
+ destroy(): void;
10
+ }
11
+ /**
12
+ * Creates an Opus codec: Lunibee's `Opus` on the system's libopus when it is
13
+ * installed (native speed, nothing to compile), otherwise `opusscript`, an
14
+ * optional peer dependency. Playing Ogg/Opus files, or anything through
15
+ * FFmpeg without `inlineVolume` or effects, needs neither.
16
+ * @throws {VoiceError} If neither is available.
17
+ */
18
+ export declare function createOpusCodec(): Promise<OpusCodec>;
@@ -0,0 +1,79 @@
1
+ import { Emitter } from "./emitter.js";
2
+ import type { AudioResource } from "./resource.js";
3
+ /** Where an {@link AudioPlayer} is. */
4
+ export declare const AudioPlayerStatus: {
5
+ /** Nothing to play. */
6
+ readonly Idle: "idle";
7
+ /** A resource is loaded but its next frame is not ready yet. */
8
+ readonly Buffering: "buffering";
9
+ /** Sending a frame every 20 ms. */
10
+ readonly Playing: "playing";
11
+ /** Paused by `pause()`. */
12
+ readonly Paused: "paused";
13
+ /** Paused because no subscribed connection can send (`noSubscriber: "pause"`). */
14
+ readonly AutoPaused: "autopaused";
15
+ };
16
+ export type AudioPlayerStatus = (typeof AudioPlayerStatus)[keyof typeof AudioPlayerStatus];
17
+ /** A destination for a player's frames: a voice connection. */
18
+ export interface AudioSink {
19
+ /** Whether it can send now. */
20
+ readonly ready: boolean;
21
+ sendFrame(frame: Uint8Array): boolean;
22
+ setSpeaking(speaking: boolean): void;
23
+ }
24
+ /** Options for {@link AudioPlayer}. */
25
+ export interface AudioPlayerOptions {
26
+ /** With no ready subscriber: `"pause"` (default) waits, `"play"` keeps consuming, `"stop"` stops. */
27
+ noSubscriber?: "pause" | "play" | "stop";
28
+ /** Frames the resource may be late in a row before playback fails. Default 250 (5 s). */
29
+ maxMissedFrames?: number;
30
+ }
31
+ /** What {@link AudioPlayer} emits. */
32
+ export interface AudioPlayerEvents {
33
+ stateChange: [from: AudioPlayerStatus, to: AudioPlayerStatus];
34
+ /** The resource failed (an FFmpeg error, a corrupt stream). The player goes idle. */
35
+ error: [error: Error, resource: AudioResource<unknown>];
36
+ /** The resource played to its end. */
37
+ finish: [resource: AudioResource<unknown>];
38
+ }
39
+ /**
40
+ * Plays {@link AudioResource}s to subscribed connections: one 20 ms Opus
41
+ * frame per tick, on a self-correcting clock. The clock runs only while
42
+ * there is something to send; an idle or paused player has no timer.
43
+ */
44
+ export declare class AudioPlayer extends Emitter<AudioPlayerEvents> {
45
+ #private;
46
+ constructor(options?: AudioPlayerOptions);
47
+ /** Current status. */
48
+ get state(): AudioPlayerStatus;
49
+ /** The resource playing (or paused), if any. */
50
+ get resource(): AudioResource<unknown> | undefined;
51
+ /** Subscribed connections. */
52
+ get subscribers(): ReadonlySet<AudioSink>;
53
+ /** Whether the clock is running (for tests and diagnostics). */
54
+ get ticking(): boolean;
55
+ /**
56
+ * Plays `resource`, replacing (and destroying) the current one.
57
+ * @throws {VoiceError} If the resource already ended or was played before.
58
+ */
59
+ play(resource: AudioResource<unknown>): void;
60
+ /** Pauses. @param silence Send five silence frames first (default true). @returns Whether it paused. */
61
+ pause(silence?: boolean): boolean;
62
+ /** Resumes after `pause()`. @returns Whether it resumed. */
63
+ unpause(): boolean;
64
+ /** Alias of {@link unpause}. */
65
+ resume(): boolean;
66
+ /**
67
+ * Stops and destroys the resource. @param force Skip the five silence
68
+ * frames. @returns Whether anything was playing.
69
+ */
70
+ stop(force?: boolean): boolean;
71
+ /** @internal A connection subscribes. */
72
+ attach(sink: AudioSink): void;
73
+ /** @internal A connection unsubscribes. */
74
+ detach(sink: AudioSink): void;
75
+ /** @internal A subscriber's readiness changed: leave or enter `autopaused`. */
76
+ refresh(): void;
77
+ }
78
+ /** Creates an {@link AudioPlayer}. */
79
+ export declare function createAudioPlayer(options?: AudioPlayerOptions): AudioPlayer;
@@ -0,0 +1,76 @@
1
+ /** Voice gateway version this package speaks. */
2
+ export declare const VOICE_GATEWAY_VERSION = 8;
3
+ /** Voice gateway opcodes (v8, with the DAVE opcodes 21-31). */
4
+ export declare const VoiceOpcodes: {
5
+ readonly Identify: 0;
6
+ readonly SelectProtocol: 1;
7
+ readonly Ready: 2;
8
+ readonly Heartbeat: 3;
9
+ readonly SessionDescription: 4;
10
+ readonly Speaking: 5;
11
+ readonly HeartbeatAck: 6;
12
+ readonly Resume: 7;
13
+ readonly Hello: 8;
14
+ readonly Resumed: 9;
15
+ readonly ClientsConnect: 11;
16
+ readonly ClientDisconnect: 13;
17
+ readonly DavePrepareTransition: 21;
18
+ readonly DaveExecuteTransition: 22;
19
+ readonly DaveTransitionReady: 23;
20
+ readonly DavePrepareEpoch: 24;
21
+ readonly DaveMlsExternalSender: 25;
22
+ readonly DaveMlsKeyPackage: 26;
23
+ readonly DaveMlsProposals: 27;
24
+ readonly DaveMlsCommitWelcome: 28;
25
+ readonly DaveMlsAnnounceCommitTransition: 29;
26
+ readonly DaveMlsWelcome: 30;
27
+ readonly DaveMlsInvalidCommitWelcome: 31;
28
+ };
29
+ export type VoiceOpcodes = (typeof VoiceOpcodes)[keyof typeof VoiceOpcodes];
30
+ /** Voice gateway close codes. */
31
+ export declare const VoiceCloseCodes: {
32
+ readonly UnknownOpcode: 4001;
33
+ readonly FailedToDecode: 4002;
34
+ readonly NotAuthenticated: 4003;
35
+ readonly AuthenticationFailed: 4004;
36
+ readonly AlreadyAuthenticated: 4005;
37
+ readonly SessionNoLongerValid: 4006;
38
+ readonly SessionTimeout: 4009;
39
+ readonly ServerNotFound: 4011;
40
+ readonly UnknownProtocol: 4012;
41
+ readonly Disconnected: 4014;
42
+ readonly VoiceServerCrashed: 4015;
43
+ readonly UnknownEncryptionMode: 4016;
44
+ readonly DaveProtocolRequired: 4017;
45
+ readonly BadRequest: 4020;
46
+ readonly RateLimited: 4021;
47
+ readonly CallTerminated: 4022;
48
+ };
49
+ export type VoiceCloseCodes = (typeof VoiceCloseCodes)[keyof typeof VoiceCloseCodes];
50
+ /**
51
+ * Whether a voice gateway close can be resumed (op 7). Codes below 4000
52
+ * (network or server-side closes) and 4015 (voice server crashed) can; 4006
53
+ * (session invalid), 4014 (removed from the channel) and every other 4xxx
54
+ * cannot.
55
+ */
56
+ export declare function canResumeVoice(code: number): boolean;
57
+ /** RTP payload type Discord uses for Opus. */
58
+ export declare const RTP_OPUS_PAYLOAD_TYPE = 120;
59
+ /** Opus frames are 20 ms of 48 kHz audio. */
60
+ export declare const FRAME_DURATION_MS = 20;
61
+ /** Samples per channel in one 20 ms frame at 48 kHz. */
62
+ export declare const FRAME_SAMPLES = 960;
63
+ /** Bytes of one 20 ms frame of 48 kHz stereo s16le PCM. */
64
+ export declare const PCM_FRAME_BYTES: number;
65
+ /** The Opus frame Discord treats as silence. Five of them end a transmission. */
66
+ export declare const SILENCE_FRAME: Uint8Array;
67
+ /** Speaking flags for op 5. */
68
+ export declare const SpeakingFlags: {
69
+ readonly Microphone: 1;
70
+ readonly Soundshare: 2;
71
+ readonly Priority: 4;
72
+ };
73
+ /** Voice protocol errors. */
74
+ export declare class VoiceError extends Error {
75
+ constructor(message: string, options?: ErrorOptions);
76
+ }
@@ -0,0 +1,59 @@
1
+ /** When a {@link VoiceReceiver.subscribe} stream ends. */
2
+ export type ReceiveEnd = {
3
+ behavior: "manual";
4
+ }
5
+ /** After `duration` ms with no audio (silence frames count as none). Default, 1,000 ms. */
6
+ | {
7
+ behavior: "silence";
8
+ duration?: number;
9
+ };
10
+ /** Options for {@link VoiceReceiver.subscribe}. */
11
+ export interface ReceiveOptions {
12
+ end?: ReceiveEnd;
13
+ /** Frames kept for a slow reader; newer frames are dropped beyond it. Default 50 (1 s). */
14
+ highWaterMark?: number;
15
+ }
16
+ /** Options for {@link VoiceReceiver.record}. */
17
+ export interface RecordOptions {
18
+ /** When the recording ends. Default `{ behavior: "manual" }`: on `unsubscribe()`, cancel, or when the connection ends. */
19
+ end?: ReceiveEnd;
20
+ /** Bytes kept for a slow reader; audio beyond it is replaced by silence. Default 1 MiB. */
21
+ highWaterMark?: number;
22
+ }
23
+ /**
24
+ * Receives other users' audio (low level): decrypted Opus packets per user.
25
+ * SSRCs are mapped to users from op 5; decoding is up to you (`createOpusCodec`).
26
+ */
27
+ export declare class VoiceReceiver {
28
+ #private;
29
+ /** The SSRC Discord assigned to a user, once they spoke. */
30
+ ssrcOf(userId: string): number | undefined;
31
+ /** Users who sent audio in the last 100 ms. */
32
+ get speaking(): ReadonlySet<string>;
33
+ /** Listens for a user starting or stopping to send audio. */
34
+ on(event: "start" | "end", listener: (userId: string) => void): this;
35
+ off(event: "start" | "end", listener: (userId: string) => void): this;
36
+ /**
37
+ * A stream of `userId`'s Opus packets. It ends after a second without
38
+ * audio unless `end.behavior` is `"manual"`; cancelling it unsubscribes.
39
+ */
40
+ subscribe(userId: string, options?: ReceiveOptions): ReadableStream<Uint8Array>;
41
+ /**
42
+ * Records `userId` as an Ogg/Opus stream (48 kHz stereo) that players and
43
+ * FFmpeg read: write it to a file with `Bun.write(path, new Response(stream))`.
44
+ * The audio is not re-encoded. Time the user is silent is kept as silence,
45
+ * counted from the start of the recording, so recordings of several users
46
+ * started together line up.
47
+ */
48
+ record(userId: string, options?: RecordOptions): ReadableStream<Uint8Array>;
49
+ /** Ends every stream of `userId`. @returns Whether there was one. */
50
+ unsubscribe(userId: string): boolean;
51
+ /** @internal Op 5 told us a user's SSRC. */
52
+ mapUser(userId: string, ssrc: number): void;
53
+ /** @internal Op 13: a user left. */
54
+ forgetUser(userId: string): void;
55
+ /** @internal A decrypted frame arrived from `userId`. */
56
+ push(userId: string, frame: Uint8Array): void;
57
+ /** @internal Ends every stream and forgets every user (the connection left). */
58
+ close(): void;
59
+ }
@@ -0,0 +1,102 @@
1
+ import { type AudioEffect } from "./effects.js";
2
+ import { type OpusCodec } from "./opus.js";
3
+ /**
4
+ * What an input holds:
5
+ * - `"arbitrary"`: any format FFmpeg reads (needs `ffmpeg` on the PATH);
6
+ * - `"ogg/opus"`: an Ogg/Opus file or stream, played without re-encoding;
7
+ * - `"opus"`: one Opus packet (20 ms) per chunk;
8
+ * - `"raw"`: 48 kHz stereo s16le PCM (needs an Opus library).
9
+ */
10
+ export type StreamType = "arbitrary" | "ogg/opus" | "opus" | "raw";
11
+ /** What {@link createAudioResource} can play. A string is a file path, or an `http(s)` URL for FFmpeg. */
12
+ export type AudioInput = string | Uint8Array | ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>;
13
+ /** Options for {@link createAudioResource}. */
14
+ export interface AudioResourceOptions<T = unknown> {
15
+ /** What the input holds. Default: from the file extension (`.ogg`/`.opus`/`.pcm`), otherwise `"arbitrary"`. */
16
+ inputType?: StreamType;
17
+ /** Allow `resource.volume.set()`. Decodes and re-encodes Opus input, so it costs CPU. Default false. */
18
+ inlineVolume?: boolean;
19
+ /** Anything you want to keep with the resource (a title, who queued it). */
20
+ metadata?: T;
21
+ /** FFmpeg executable. Default `"ffmpeg"`. */
22
+ ffmpegPath?: string;
23
+ /** Frames read ahead of playback. Default 10 (200 ms); 1 when `live`. */
24
+ prefetch?: number;
25
+ /**
26
+ * PCM effects (`pitchShift`, `robot`, `echo`, `gain`, or your own), run in
27
+ * order on every frame. Like `inlineVolume`, they decode and re-encode.
28
+ * `resource.effects` can be changed while it plays.
29
+ */
30
+ effects?: AudioEffect[];
31
+ /**
32
+ * A live source (a user's voice from `receiver.subscribe()`): the player
33
+ * waits for it without counting missed frames, and frames that pile up
34
+ * beyond 100 ms are dropped to keep the delay low. Default false.
35
+ */
36
+ live?: boolean;
37
+ /** @internal Creates the Opus codec in place of {@link createOpusCodec} (tests). */
38
+ codecFactory?: () => Promise<OpusCodec>;
39
+ }
40
+ /** Inline volume of a resource created with `inlineVolume: true`. */
41
+ export declare class VolumeControl {
42
+ /** Linear gain: 1 is unchanged, 0.5 half, 2 double (clipped). */
43
+ volume: number;
44
+ /** Sets a linear gain. @throws {RangeError} For a negative or non-finite value. */
45
+ set(volume: number): void;
46
+ /** Sets a logarithmic gain in decibels (0 is unchanged). */
47
+ setDecibels(db: number): void;
48
+ /** Scales one s16le PCM frame in place. */
49
+ apply(pcm: Uint8Array): Uint8Array;
50
+ }
51
+ /**
52
+ * An audio source for an {@link AudioPlayer}: a queue of 20 ms Opus frames
53
+ * filled ahead of playback. One resource plays once; create another to play
54
+ * the same input again.
55
+ */
56
+ export declare class AudioResource<T = unknown> {
57
+ #private;
58
+ /** What was passed as `metadata`. */
59
+ readonly metadata: T;
60
+ /** Inline volume, when created with `inlineVolume: true`. */
61
+ readonly volume?: VolumeControl;
62
+ /** The effects in use, when created with `effects`. Changes apply from the next frame. */
63
+ readonly effects?: AudioEffect[];
64
+ /** Whether it is a live source (`live: true`). */
65
+ readonly live: boolean;
66
+ /** Milliseconds of this resource the player has sent. */
67
+ playbackDuration: number;
68
+ /** Whether the player took its first frame. */
69
+ started: boolean;
70
+ /** @internal Use {@link createAudioResource}. */
71
+ constructor(frames: AsyncGenerator<Uint8Array>, options: {
72
+ metadata?: T;
73
+ volume?: VolumeControl;
74
+ effects?: AudioEffect[];
75
+ live?: boolean;
76
+ prefetch?: number;
77
+ cleanup?: () => void;
78
+ });
79
+ /** Whether every frame has been read (or it failed, or was destroyed). */
80
+ get ended(): boolean;
81
+ /** Why it ended early, if it failed. */
82
+ get error(): Error | undefined;
83
+ /** Whether a frame is ready now. */
84
+ get readable(): boolean;
85
+ /**
86
+ * Takes the next Opus frame. @returns A frame, `undefined` when none is
87
+ * ready yet (buffering), or `null` when the resource has ended.
88
+ */
89
+ read(): Uint8Array | null | undefined;
90
+ /** Stops reading: ends FFmpeg and the input, frees the codec. Safe to call twice. */
91
+ destroy(): void;
92
+ }
93
+ /**
94
+ * Creates an {@link AudioResource} from a file path, a URL (FFmpeg), bytes, a
95
+ * `ReadableStream` or an async iterable.
96
+ *
97
+ * Ogg/Opus input is played as it is (passthrough). PCM input, and anything
98
+ * with `inlineVolume`, is encoded with Opus (`opusscript`, an optional
99
+ * peer). Other formats go through FFmpeg, which is killed when
100
+ * the resource ends or is destroyed.
101
+ */
102
+ export declare function createAudioResource<T = unknown>(input: AudioInput, options?: AudioResourceOptions<T>): AudioResource<T>;
@@ -0,0 +1,40 @@
1
+ /** The parts of a UDP socket {@link VoiceUdp} uses; `Bun.udpSocket` provides them. */
2
+ export interface UdpSocketLike {
3
+ send(data: Uint8Array, port: number, address: string): boolean;
4
+ close(): void;
5
+ }
6
+ /** Opens a UDP socket that calls `onData` for each datagram. Replace it in tests. */
7
+ export type UdpSocketFactory = (onData: (data: Uint8Array) => void) => Promise<UdpSocketLike>;
8
+ /** The default factory: an IPv4 `Bun.udpSocket` on an ephemeral port. */
9
+ export declare const bunUdpSocket: UdpSocketFactory;
10
+ /** Builds an IP discovery request (type 0x1, length 70) for `ssrc`. */
11
+ export declare function discoveryRequest(ssrc: number): Uint8Array;
12
+ /** Reads an IP discovery response (type 0x2): our external address and port. */
13
+ export declare function parseDiscoveryResponse(packet: Uint8Array): {
14
+ ip: string;
15
+ port: number;
16
+ } | undefined;
17
+ /**
18
+ * The voice UDP socket: sends audio to the voice server, receives other
19
+ * users' audio, runs IP discovery and a keepalive every 5 s. `close()` ends
20
+ * the socket and its timer; nothing is left open.
21
+ */
22
+ export declare class VoiceUdp {
23
+ #private;
24
+ private constructor();
25
+ /** Opens a socket towards the voice server at `ip:port`. */
26
+ static open(ip: string, port: number, factory?: UdpSocketFactory): Promise<VoiceUdp>;
27
+ /** Whether `close()` was called. */
28
+ get closed(): boolean;
29
+ /** Listens for datagrams. @returns A function that removes the listener. */
30
+ onPacket(listener: (packet: Uint8Array) => void): () => void;
31
+ /** Sends one datagram to the voice server. @returns Whether it was handed to the socket. */
32
+ send(packet: Uint8Array): boolean;
33
+ /** Asks the voice server for our external address. @throws {VoiceError} On timeout or when closed. */
34
+ discover(ssrc: number, timeoutMs?: number): Promise<{
35
+ ip: string;
36
+ port: number;
37
+ }>;
38
+ /** Closes the socket and stops the keepalive. Safe to call twice. */
39
+ close(): void;
40
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Discord Gateway close codes.
3
3
  *
4
- * Discord.js-familiar names and numeric values, matching the Discord Gateway
4
+ * Familiar names and numeric values, matching the Discord Gateway
5
5
  * protocol. Exposed so consumers can branch on named codes instead of magic
6
6
  * numbers; {@link Gateway} uses them internally to decide resume/identify/stop.
7
7
  */
@@ -1,5 +1,5 @@
1
1
  import { type SocketFactory } from "./transport.js";
2
- import { type GatewayPayload, type GatewayProperties, type GatewayPresence, type GatewayIntentResolvable } from "@lunibee/types";
2
+ import { type GatewayPayload, type GatewayProperties, type GatewayPresence, type GatewayIntentResolvable } from "../types/index.js";
3
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";
@@ -7,10 +7,10 @@ export { GatewayReconnect, classifyCloseCode, FATAL_CLOSE_CODES, IDENTIFY_CLOSE_
7
7
  export { GatewayHeartbeat, type HeartbeatOptions, type HeartbeatTimeout, } from "./heartbeat.js";
8
8
  export { GatewaySession, type ResumeInfo, type HandshakeIntent, type InvalidationReason, } from "./session.js";
9
9
  export { GatewayDispatcher, type GatewayListener } from "./dispatcher.js";
10
- export { GatewaySendLimiter, SendBudget } from "./send-budget.js";
10
+ export { GatewaySendLimiter } from "./send-budget.js";
11
11
  export { GatewayOpcodes } from "./opcodes.js";
12
12
  export { GatewayCloseCodes } from "./close-codes.js";
13
- export { GatewayState, Status, GatewayError } from "./state.js";
13
+ export { GatewayState, GatewayError } from "./state.js";
14
14
  import { GatewayState } from "./state.js";
15
15
  import { type GatewayListener } from "./dispatcher.js";
16
16
  /** Gateway connection configuration. */
@@ -57,6 +57,21 @@ export interface GatewayOptions {
57
57
  * Injected to run the Gateway on an alternative transport, or on a stub.
58
58
  */
59
59
  createSocket?: SocketFactory;
60
+ /**
61
+ * Asked before every IDENTIFY the Gateway sends on its own (a reconnect
62
+ * after a session it cannot resume). The first IDENTIFY after `connect()`
63
+ * is not gated: whoever called `connect()` already paced it.
64
+ * `ShardManager` sets it to its cluster's IDENTIFY coordinator.
65
+ * @internal
66
+ */
67
+ identifyGate?: GatewayIdentifyGate;
68
+ }
69
+ /** Hands out IDENTIFY slots to a Gateway; see `GatewayOptions.identifyGate`. @internal */
70
+ export interface GatewayIdentifyGate {
71
+ /** Resolves when the Gateway may IDENTIFY now. A rejection closes the socket, and the reconnect asks again. */
72
+ acquire(): Promise<void>;
73
+ /** The IDENTIFY was sent (or abandoned). */
74
+ release(): void;
60
75
  }
61
76
  /** What {@link Gateway.health} reports. */
62
77
  export interface GatewayHealth {
@@ -77,7 +92,13 @@ export declare class Gateway {
77
92
  ping: number;
78
93
  /** Creates a Gateway connection manager. @param options Gateway configuration. @throws {TypeError|RangeError} If configuration is invalid. */
79
94
  constructor(options: GatewayOptions);
80
- /** Opens the Gateway connection. @param url Gateway WebSocket URL. @returns Promise fulfilled when the socket opens; it rejects with a GatewayError if the Gateway was permanently closed or cannot connect. */
95
+ /**
96
+ * Opens the Gateway connection.
97
+ * @param url Gateway WebSocket URL. Defaults to the URL of the last `connect()`, else Discord's main Gateway.
98
+ * It is remembered: reconnects that IDENTIFY dial it again, and every reconnect (including a RESUME
99
+ * on `resume_gateway_url`) carries its query parameters (`v`, `encoding`, `compress`).
100
+ * @returns Promise fulfilled when the socket opens; it rejects with a GatewayError if the Gateway was permanently closed or cannot connect.
101
+ */
81
102
  connect(url?: string): Promise<void>;
82
103
  /** Permanently closes the Gateway connection. */
83
104
  close(): void;