shoukaku-bun 4.2.0-b

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.
@@ -0,0 +1,21 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any */
2
+ import type { NodeOption } from '../../Shoukaku';
3
+ import { Connector } from '../Connector';
4
+
5
+ export class DiscordJS extends Connector {
6
+ // sendPacket is where your library send packets to Discord Gateway
7
+ public sendPacket(shardId: number, payload: any, important: boolean): void {
8
+ return this.client.ws.shards.get(shardId)?.send(payload, important);
9
+ }
10
+ // getId is a getter where the lib stores the client user (the one logged in as a bot) id
11
+ public getId(): string {
12
+ return this.client.user.id;
13
+ }
14
+ // Listen attaches the event listener to the library you are using
15
+ public listen(nodes: NodeOption[]): void {
16
+ // Only attach to ready event once, refer to your library for its ready event
17
+ this.client.once('clientReady', () => this.ready(nodes));
18
+ // Attach to the raw websocket event, this event must be 1:1 on spec with dapi (most libs implement this)
19
+ this.client.on('raw', (packet: any) => this.raw(packet));
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any */
2
+ import type { NodeOption } from '../../Shoukaku';
3
+ import { Connector } from '../Connector';
4
+
5
+ export class Eris extends Connector {
6
+ // sendPacket is where your library send packets to Discord Gateway
7
+ public sendPacket(shardId: number, payload: any, important: boolean): void {
8
+ return this.client.shards.get(shardId)?.sendWS(payload.op, payload.d, important);
9
+ }
10
+ // getId is a getter where the lib stores the client user (the one logged in as a bot) id
11
+ public getId(): string {
12
+ return this.client.user.id;
13
+ }
14
+ // Listen attaches the event listener to the library you are using
15
+ public listen(nodes: NodeOption[]): void {
16
+ // Only attach to ready event once, refer to your library for its ready event
17
+ this.client.once('ready', () => this.ready(nodes));
18
+ // Attach to the raw websocket event, this event must be 1:1 on spec with dapi (most libs implement this)
19
+ this.client.on('rawWS', (packet: any) => this.raw(packet));
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any */
2
+ import type { NodeOption } from '../../Shoukaku';
3
+ import { Connector } from '../Connector';
4
+
5
+ export class OceanicJS extends Connector {
6
+ // sendPacket is where your library send packets to Discord Gateway
7
+ public sendPacket(shardId: number, payload: any, important: boolean): void {
8
+ return this.client.shards.get(shardId)?.send(payload.op, payload.d, important);
9
+ }
10
+ // getId is a getter where the lib stores the client user (the one logged in as a bot) id
11
+ public getId(): string {
12
+ return this.client.user.id;
13
+ }
14
+ // Listen attaches the event listener to the library you are using
15
+ public listen(nodes: NodeOption[]): void {
16
+ // Only attach to ready event once, refer to your library for its ready event
17
+ this.client.once('ready', () => this.ready(nodes));
18
+ // Attach to the raw websocket event, this event must be 1:1 on spec with dapi (most libs implement this)
19
+ this.client.on('packet', (packet: any) => this.raw(packet));
20
+ }
21
+ }
@@ -0,0 +1,26 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any */
2
+ import type { NodeOption } from '../../Shoukaku';
3
+ import { Connector } from '../Connector';
4
+
5
+ export class Seyfert extends Connector {
6
+ // sendPacket is where your library send packets to Discord Gateway
7
+ public sendPacket(shardId: number, payload: unknown, important: boolean): void {
8
+ return this.client.gateway.send(shardId, payload);
9
+ }
10
+ // getId is a getter where the lib stores the client user (the one logged in as a bot) id
11
+ public getId(): string {
12
+ return this.client.botId;
13
+ }
14
+ // Listen attaches the event listener to the library you are using
15
+ public listen(nodes: NodeOption[]): void {
16
+ this.client.events.values.RAW = {
17
+ data: { name: 'raw' },
18
+ run: (packet: any) => {
19
+ // Only attach to ready event once, refer to your library for its ready event
20
+ if (packet.t === 'READY') return this.ready(nodes);
21
+ // Attach to the raw websocket event, this event must be 1:1 on spec with dapi (most libs implement this)
22
+ return this.raw(packet);
23
+ }
24
+ };
25
+ }
26
+ }
@@ -0,0 +1,4 @@
1
+ export * from './DiscordJS';
2
+ export * from './Eris';
3
+ export * from './OceanicJS';
4
+ export * from './Seyfert';
@@ -0,0 +1,248 @@
1
+ import { EventEmitter, once } from 'node:events';
2
+ import { State, VoiceState } from '../Constants';
3
+ import type { Shoukaku, VoiceChannelOptions } from '../Shoukaku';
4
+
5
+ /**
6
+ * Represents the partial payload from a stateUpdate event
7
+ */
8
+ export interface StateUpdatePartial {
9
+ channel_id?: string;
10
+ session_id?: string;
11
+ self_deaf: boolean;
12
+ self_mute: boolean;
13
+ }
14
+
15
+ /**
16
+ * Represents the payload from a serverUpdate event
17
+ */
18
+ export interface ServerUpdate {
19
+ token: string;
20
+ guild_id: string;
21
+ endpoint: string;
22
+ }
23
+
24
+ /**
25
+ * Represents a connection to a Discord voice channel
26
+ */
27
+ export class Connection extends EventEmitter {
28
+ /**
29
+ * The manager where this connection is on
30
+ */
31
+ public manager: Shoukaku;
32
+ /**
33
+ * GuildId of the connection that is being managed by this instance
34
+ */
35
+ public guildId: string;
36
+ /**
37
+ * VoiceChannelId of the connection that is being managed by this instance
38
+ */
39
+ public channelId: string | null;
40
+ /**
41
+ * ShardId where this connection sends data on
42
+ */
43
+ public shardId: number;
44
+ /**
45
+ * Mute status in connected voice channel
46
+ */
47
+ public muted: boolean;
48
+ /**
49
+ * Deafen status in connected voice channel
50
+ */
51
+ public deafened: boolean;
52
+ /**
53
+ * Id of the voice channel where this instance was connected before the current channelId
54
+ */
55
+ public lastChannelId: string | null;
56
+ /**
57
+ * Id of the currently active voice channel connection
58
+ */
59
+ public sessionId: string | null;
60
+ /**
61
+ * Region of connected voice channel
62
+ */
63
+ public region: string | null;
64
+ /**
65
+ * Last region of the connected voice channel
66
+ */
67
+ public lastRegion: string | null;
68
+ /**
69
+ * Cached serverUpdate event from Lavalink
70
+ */
71
+ public serverUpdate: ServerUpdate | null;
72
+ /**
73
+ * Connection state
74
+ */
75
+ public state: State;
76
+ /**
77
+ * @param manager The manager of this connection
78
+ * @param options The options to pass in connection creation
79
+ * @param options.guildId GuildId in which voice channel to connect to is located
80
+ * @param options.shardId ShardId in which the guild exists
81
+ * @param options.channelId ChannelId of voice channel to connect to
82
+ * @param options.deaf Optional boolean value to specify whether to deafen the current bot user
83
+ * @param options.mute Optional boolean value to specify whether to mute the current bot user
84
+ */
85
+ constructor(manager: Shoukaku, options: VoiceChannelOptions) {
86
+ super();
87
+ this.manager = manager;
88
+ this.guildId = options.guildId;
89
+ this.channelId = options.channelId;
90
+ this.shardId = options.shardId;
91
+ this.muted = options.mute ?? false;
92
+ this.deafened = options.deaf ?? false;
93
+ this.lastChannelId = null;
94
+ this.sessionId = null;
95
+ this.region = null;
96
+ this.lastRegion = null;
97
+ this.serverUpdate = null;
98
+ this.state = State.DISCONNECTED;
99
+ }
100
+
101
+ /**
102
+ * Set the deafen status for the current bot user
103
+ * @param deaf Boolean value to indicate whether to deafen or undeafen
104
+ * @defaultValue false
105
+ */
106
+ public setDeaf(deaf = false): void {
107
+ this.deafened = deaf;
108
+ this.sendVoiceUpdate();
109
+ }
110
+
111
+ /**
112
+ * Set the mute status for the current bot user
113
+ * @param mute Boolean value to indicate whether to mute or unmute
114
+ * @defaultValue false
115
+ */
116
+ public setMute(mute = false): void {
117
+ this.muted = mute;
118
+ this.sendVoiceUpdate();
119
+ }
120
+
121
+ /**
122
+ * Disconnect the current bot user from the connected voice channel
123
+ * @internal
124
+ */
125
+ public disconnect(): void {
126
+ if (this.state === State.DISCONNECTED) return;
127
+ this.channelId = null;
128
+ this.deafened = false;
129
+ this.muted = false;
130
+ this.removeAllListeners();
131
+ this.sendVoiceUpdate();
132
+ this.state = State.DISCONNECTED;
133
+ this.debug(`[Voice] -> [Node] & [Discord] : Connection Destroyed | Guild: ${this.guildId}`);
134
+ }
135
+
136
+ /**
137
+ * Connect the current bot user to a voice channel
138
+ * @internal
139
+ */
140
+ public async connect(): Promise<void> {
141
+ if (this.state === State.CONNECTING || this.state === State.CONNECTED) return;
142
+
143
+ this.state = State.CONNECTING;
144
+ this.sendVoiceUpdate();
145
+ this.debug(`[Voice] -> [Discord] : Requesting Connection | Guild: ${this.guildId}`);
146
+
147
+ const controller = new AbortController();
148
+ const timeout = setTimeout(() => controller.abort(), this.manager.options.voiceConnectionTimeout * 1000);
149
+
150
+ try {
151
+ const [ status ] = await once(this, 'connectionUpdate', { signal: controller.signal }) as [ VoiceState ];
152
+ if (status !== VoiceState.SESSION_READY) {
153
+ switch (status) {
154
+ case VoiceState.SESSION_ID_MISSING: throw new Error('The voice connection is not established due to missing session id');
155
+ case VoiceState.SESSION_ENDPOINT_MISSING: throw new Error('The voice connection is not established due to missing connection endpoint');
156
+ }
157
+ }
158
+ this.state = State.CONNECTED;
159
+ } catch (e: unknown) {
160
+ const error = e as Error;
161
+ this.debug(`[Voice] </- [Discord] : Request Connection Failed | Guild: ${this.guildId}`);
162
+ if (error.name === 'AbortError')
163
+ throw new Error(`The voice connection is not established in ${this.manager.options.voiceConnectionTimeout} seconds`);
164
+ throw error;
165
+ } finally {
166
+ clearTimeout(timeout);
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Updates SessionId, ChannelId, Deafen and Mute data of this instance
172
+ * @param options
173
+ * @param options.session_id Id of the current session
174
+ * @param options.channel_id Id of the connected voice channel
175
+ * @param options.self_deaf Boolean that indicates if the current bot user is deafened or not
176
+ * @param options.self_mute Boolean that indicates if the current bot user is muted or not
177
+ * @internal
178
+ */
179
+ public setStateUpdate({ session_id, channel_id, self_deaf, self_mute }: StateUpdatePartial): void {
180
+ this.lastChannelId = this.channelId?.repeat(1) ?? null;
181
+ this.channelId = channel_id ?? null;
182
+
183
+ if (this.channelId && this.lastChannelId !== this.channelId) {
184
+ this.debug(`[Voice] <- [Discord] : Channel Moved | Old Channel: ${this.channelId} Guild: ${this.guildId}`);
185
+ }
186
+
187
+ if (!this.channelId) {
188
+ this.state = State.DISCONNECTED;
189
+ this.debug(`[Voice] <- [Discord] : Channel Disconnected | Guild: ${this.guildId}`);
190
+ }
191
+
192
+ this.deafened = self_deaf;
193
+ this.muted = self_mute;
194
+ this.sessionId = session_id ?? null;
195
+ this.debug(`[Voice] <- [Discord] : State Update Received | Channel: ${this.channelId} Session ID: ${session_id} Guild: ${this.guildId}`);
196
+ }
197
+
198
+ /**
199
+ * Sets the server update data for this connection
200
+ * @internal
201
+ */
202
+ public setServerUpdate(data: ServerUpdate): void {
203
+ if (!data.endpoint) {
204
+ this.emit('connectionUpdate', VoiceState.SESSION_ENDPOINT_MISSING);
205
+ return;
206
+ }
207
+ if (!this.sessionId) {
208
+ this.emit('connectionUpdate', VoiceState.SESSION_ID_MISSING);
209
+ return;
210
+ }
211
+
212
+ this.lastRegion = this.region?.repeat(1) ?? null;
213
+ this.region = data.endpoint.split('.').shift()?.replace(/[0-9]/g, '') ?? null;
214
+
215
+ if (this.region && this.lastRegion !== this.region) {
216
+ this.debug(`[Voice] <- [Discord] : Voice Region Moved | Old Region: ${this.lastRegion} New Region: ${this.region} Guild: ${this.guildId}`);
217
+ }
218
+
219
+ this.serverUpdate = data;
220
+ this.emit('connectionUpdate', VoiceState.SESSION_READY);
221
+ this.debug(`[Voice] <- [Discord] : Server Update Received | Server: ${this.region} Guild: ${this.guildId}`);
222
+ }
223
+
224
+ /**
225
+ * Send voice data to discord
226
+ * @internal
227
+ */
228
+ private sendVoiceUpdate() {
229
+ this.send({ guild_id: this.guildId, channel_id: this.channelId, self_deaf: this.deafened, self_mute: this.muted });
230
+ }
231
+
232
+ /**
233
+ * Send data to Discord
234
+ * @param data The data to send
235
+ * @internal
236
+ */
237
+ private send(data: unknown): void {
238
+ this.manager.connector.sendPacket(this.shardId, { op: 4, d: data }, false);
239
+ }
240
+
241
+ /**
242
+ * Emits a debug log
243
+ * @internal
244
+ */
245
+ private debug(message: string): void {
246
+ this.manager.emit('debug', this.constructor.name, message);
247
+ }
248
+ }