@stoatx/client 0.7.0 → 0.8.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.
- package/dist/index.cjs +259 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -1
- package/dist/index.d.ts +81 -1
- package/dist/index.js +264 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.d.cts
CHANGED
|
@@ -4,6 +4,8 @@ import * as stoatApi from 'stoat-api';
|
|
|
4
4
|
export { stoatApi as API };
|
|
5
5
|
import * as util from 'node:util';
|
|
6
6
|
import util__default from 'node:util';
|
|
7
|
+
import { Readable } from 'node:stream';
|
|
8
|
+
import { EventEmitter as EventEmitter$1 } from 'node:events';
|
|
7
9
|
|
|
8
10
|
declare class GatewayManager {
|
|
9
11
|
private client;
|
|
@@ -1142,6 +1144,82 @@ declare class MessageCollector extends Collector<string, Message> {
|
|
|
1142
1144
|
endReason(): string | null;
|
|
1143
1145
|
}
|
|
1144
1146
|
|
|
1147
|
+
interface AudioResourceOptions {
|
|
1148
|
+
volume?: number;
|
|
1149
|
+
inputType?: string;
|
|
1150
|
+
}
|
|
1151
|
+
type AudioSource = string | Readable;
|
|
1152
|
+
declare class AudioResource {
|
|
1153
|
+
readonly stream: Readable;
|
|
1154
|
+
private constructor();
|
|
1155
|
+
static from(source: AudioSource, options?: AudioResourceOptions): AudioResource;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
type AudioPlayerStatus = "idle" | "buffering" | "playing" | "paused" | "stopped";
|
|
1159
|
+
interface AudioPlayerEvents {
|
|
1160
|
+
stateChange: [oldStatus: AudioPlayerStatus, newStatus: AudioPlayerStatus];
|
|
1161
|
+
idle: [];
|
|
1162
|
+
error: [error: Error];
|
|
1163
|
+
}
|
|
1164
|
+
declare class AudioPlayer extends EventEmitter$1<AudioPlayerEvents> {
|
|
1165
|
+
private _status;
|
|
1166
|
+
private _resource;
|
|
1167
|
+
/** Registered VoiceConnections subscribed to this player */
|
|
1168
|
+
private readonly subscribers;
|
|
1169
|
+
get status(): AudioPlayerStatus;
|
|
1170
|
+
get resource(): AudioResource | null;
|
|
1171
|
+
play(resource: AudioResource): void;
|
|
1172
|
+
pause(): void;
|
|
1173
|
+
resume(): void;
|
|
1174
|
+
stop(): void;
|
|
1175
|
+
/** @internal */
|
|
1176
|
+
addSubscriber(conn: VoiceConnection): void;
|
|
1177
|
+
/** @internal */
|
|
1178
|
+
removeSubscriber(conn: VoiceConnection): void;
|
|
1179
|
+
private feed;
|
|
1180
|
+
private transition;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
interface VoiceConnectionEvents {
|
|
1184
|
+
ready: [];
|
|
1185
|
+
disconnect: [];
|
|
1186
|
+
error: [error: Error];
|
|
1187
|
+
}
|
|
1188
|
+
type VoiceConnectionStatus = "connecting" | "ready" | "disconnecting" | "disconnected";
|
|
1189
|
+
declare class VoiceConnection extends EventEmitter$1<VoiceConnectionEvents> {
|
|
1190
|
+
readonly channelId: string;
|
|
1191
|
+
readonly guildId: string | undefined;
|
|
1192
|
+
private readonly room;
|
|
1193
|
+
private _status;
|
|
1194
|
+
private _player;
|
|
1195
|
+
private _audioSource;
|
|
1196
|
+
private _audioTrack;
|
|
1197
|
+
constructor(channelId: string, guildId?: string);
|
|
1198
|
+
get status(): VoiceConnectionStatus;
|
|
1199
|
+
get player(): AudioPlayer | null;
|
|
1200
|
+
/** @internal */
|
|
1201
|
+
connect(url: string, token: string): Promise<void>;
|
|
1202
|
+
subscribe(player: AudioPlayer): void;
|
|
1203
|
+
unsubscribe(): void;
|
|
1204
|
+
_feedStream(stream: Readable): Promise<void>;
|
|
1205
|
+
disconnect(): Promise<void>;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
declare class VoiceManager {
|
|
1209
|
+
private readonly connections;
|
|
1210
|
+
private readonly client;
|
|
1211
|
+
constructor(client: Client);
|
|
1212
|
+
join(channelId: string, guildId?: string): Promise<VoiceConnection>;
|
|
1213
|
+
get(channelId: string): VoiceConnection | undefined;
|
|
1214
|
+
leave(channelId: string): Promise<void>;
|
|
1215
|
+
leaveAll(): Promise<void>;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
declare class VoiceChannel extends TextChannel {
|
|
1219
|
+
join(): Promise<VoiceConnection>;
|
|
1220
|
+
leave(): Promise<void>;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1145
1223
|
type ChannelType = "SavedMessages" | "DirectMessage" | "Group" | "TextChannel";
|
|
1146
1224
|
interface ChannelCreateOptions {
|
|
1147
1225
|
name: string;
|
|
@@ -1249,6 +1327,7 @@ declare abstract class BaseChannel extends Base {
|
|
|
1249
1327
|
isText(): this is TextChannel;
|
|
1250
1328
|
isDM(): this is DMChannel;
|
|
1251
1329
|
isGroup(): this is GroupChannel;
|
|
1330
|
+
isVoice(): this is VoiceChannel;
|
|
1252
1331
|
}
|
|
1253
1332
|
|
|
1254
1333
|
declare class ServerChannelManager {
|
|
@@ -2241,6 +2320,7 @@ declare class Client extends EventEmitter {
|
|
|
2241
2320
|
emojis: EmojiManager;
|
|
2242
2321
|
user: ClientUser | null;
|
|
2243
2322
|
options: ClientOptions;
|
|
2323
|
+
readonly voice: VoiceManager;
|
|
2244
2324
|
constructor(options?: ClientOptions);
|
|
2245
2325
|
/**
|
|
2246
2326
|
* Connects the bot to the Stoat Gateway
|
|
@@ -2259,4 +2339,4 @@ declare class UnknownChannel extends BaseChannel {
|
|
|
2259
2339
|
constructor(client: Client, data: Channel);
|
|
2260
2340
|
}
|
|
2261
2341
|
|
|
2262
|
-
export { Attachment, AttachmentBuilder, type AttachmentMetadata, Base, BaseChannel, BitField, type BitFieldResolvable, type BotInformation, type CDNTag, type ChannelCreateOptions, type ChannelEditOptions, ChannelManager, type ChannelResolvable, type ChannelRolePermissionOptions, type ChannelType, Client, type ClientEvents, type ClientOptions, ClientUser, Collection, Collector, type CollectorOptions, DMChannel, EmbedBuilder, Emoji, type EmojiCreateOptions, type EmojiEditOptions, EmojiManager, type EmojiParent, type EmojiResolvable, type FetchMembersOptions, GatewayManager, GroupChannel, type Interaction, type Masquerade, Member, type MemberBanOptions, type MemberEditOptions, MemberManager, type MemberResolvable, Message, MessageCollector, type MessageCollectorOptions, type MessageFetchOptions, MessageManager, type MessageOptions, MessageReaction, type MessageResolvable, PermissionFlags, type PermissionResolvable, type PermissionString, Permissions, RESTManager, type RawDMChannel, type RawGroupChannel, type RawTextChannel, ReactionCollector, type ReactionCollectorOptions, type ReplyIntent, Role, type RoleCreateOptions, type RoleEditOptions, RoleManager, type RolePermissionOptions, type RoleResolvable, Server, ServerChannelManager, type ServerEditOptions, ServerManager, StoatAPIError, SweeperManager, type SweeperOptions, TextChannel, type TextEmbedData, UnknownChannel, User, type UserEditOptions, UserManager, type UserPresence, type UserProfile, type UserRelationShip, type UserResolvable, type UserStatus };
|
|
2342
|
+
export { Attachment, AttachmentBuilder, type AttachmentMetadata, AudioPlayer, type AudioPlayerEvents, type AudioPlayerStatus, AudioResource, type AudioResourceOptions, type AudioSource, Base, BaseChannel, BitField, type BitFieldResolvable, type BotInformation, type CDNTag, type ChannelCreateOptions, type ChannelEditOptions, ChannelManager, type ChannelResolvable, type ChannelRolePermissionOptions, type ChannelType, Client, type ClientEvents, type ClientOptions, ClientUser, Collection, Collector, type CollectorOptions, DMChannel, EmbedBuilder, Emoji, type EmojiCreateOptions, type EmojiEditOptions, EmojiManager, type EmojiParent, type EmojiResolvable, type FetchMembersOptions, GatewayManager, GroupChannel, type Interaction, type Masquerade, Member, type MemberBanOptions, type MemberEditOptions, MemberManager, type MemberResolvable, Message, MessageCollector, type MessageCollectorOptions, type MessageFetchOptions, MessageManager, type MessageOptions, MessageReaction, type MessageResolvable, PermissionFlags, type PermissionResolvable, type PermissionString, Permissions, RESTManager, type RawDMChannel, type RawGroupChannel, type RawTextChannel, ReactionCollector, type ReactionCollectorOptions, type ReplyIntent, Role, type RoleCreateOptions, type RoleEditOptions, RoleManager, type RolePermissionOptions, type RoleResolvable, Server, ServerChannelManager, type ServerEditOptions, ServerManager, StoatAPIError, SweeperManager, type SweeperOptions, TextChannel, type TextEmbedData, UnknownChannel, User, type UserEditOptions, UserManager, type UserPresence, type UserProfile, type UserRelationShip, type UserResolvable, type UserStatus, VoiceConnection, type VoiceConnectionEvents, type VoiceConnectionStatus, VoiceManager };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import * as stoatApi from 'stoat-api';
|
|
|
4
4
|
export { stoatApi as API };
|
|
5
5
|
import * as util from 'node:util';
|
|
6
6
|
import util__default from 'node:util';
|
|
7
|
+
import { Readable } from 'node:stream';
|
|
8
|
+
import { EventEmitter as EventEmitter$1 } from 'node:events';
|
|
7
9
|
|
|
8
10
|
declare class GatewayManager {
|
|
9
11
|
private client;
|
|
@@ -1142,6 +1144,82 @@ declare class MessageCollector extends Collector<string, Message> {
|
|
|
1142
1144
|
endReason(): string | null;
|
|
1143
1145
|
}
|
|
1144
1146
|
|
|
1147
|
+
interface AudioResourceOptions {
|
|
1148
|
+
volume?: number;
|
|
1149
|
+
inputType?: string;
|
|
1150
|
+
}
|
|
1151
|
+
type AudioSource = string | Readable;
|
|
1152
|
+
declare class AudioResource {
|
|
1153
|
+
readonly stream: Readable;
|
|
1154
|
+
private constructor();
|
|
1155
|
+
static from(source: AudioSource, options?: AudioResourceOptions): AudioResource;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
type AudioPlayerStatus = "idle" | "buffering" | "playing" | "paused" | "stopped";
|
|
1159
|
+
interface AudioPlayerEvents {
|
|
1160
|
+
stateChange: [oldStatus: AudioPlayerStatus, newStatus: AudioPlayerStatus];
|
|
1161
|
+
idle: [];
|
|
1162
|
+
error: [error: Error];
|
|
1163
|
+
}
|
|
1164
|
+
declare class AudioPlayer extends EventEmitter$1<AudioPlayerEvents> {
|
|
1165
|
+
private _status;
|
|
1166
|
+
private _resource;
|
|
1167
|
+
/** Registered VoiceConnections subscribed to this player */
|
|
1168
|
+
private readonly subscribers;
|
|
1169
|
+
get status(): AudioPlayerStatus;
|
|
1170
|
+
get resource(): AudioResource | null;
|
|
1171
|
+
play(resource: AudioResource): void;
|
|
1172
|
+
pause(): void;
|
|
1173
|
+
resume(): void;
|
|
1174
|
+
stop(): void;
|
|
1175
|
+
/** @internal */
|
|
1176
|
+
addSubscriber(conn: VoiceConnection): void;
|
|
1177
|
+
/** @internal */
|
|
1178
|
+
removeSubscriber(conn: VoiceConnection): void;
|
|
1179
|
+
private feed;
|
|
1180
|
+
private transition;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
interface VoiceConnectionEvents {
|
|
1184
|
+
ready: [];
|
|
1185
|
+
disconnect: [];
|
|
1186
|
+
error: [error: Error];
|
|
1187
|
+
}
|
|
1188
|
+
type VoiceConnectionStatus = "connecting" | "ready" | "disconnecting" | "disconnected";
|
|
1189
|
+
declare class VoiceConnection extends EventEmitter$1<VoiceConnectionEvents> {
|
|
1190
|
+
readonly channelId: string;
|
|
1191
|
+
readonly guildId: string | undefined;
|
|
1192
|
+
private readonly room;
|
|
1193
|
+
private _status;
|
|
1194
|
+
private _player;
|
|
1195
|
+
private _audioSource;
|
|
1196
|
+
private _audioTrack;
|
|
1197
|
+
constructor(channelId: string, guildId?: string);
|
|
1198
|
+
get status(): VoiceConnectionStatus;
|
|
1199
|
+
get player(): AudioPlayer | null;
|
|
1200
|
+
/** @internal */
|
|
1201
|
+
connect(url: string, token: string): Promise<void>;
|
|
1202
|
+
subscribe(player: AudioPlayer): void;
|
|
1203
|
+
unsubscribe(): void;
|
|
1204
|
+
_feedStream(stream: Readable): Promise<void>;
|
|
1205
|
+
disconnect(): Promise<void>;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
declare class VoiceManager {
|
|
1209
|
+
private readonly connections;
|
|
1210
|
+
private readonly client;
|
|
1211
|
+
constructor(client: Client);
|
|
1212
|
+
join(channelId: string, guildId?: string): Promise<VoiceConnection>;
|
|
1213
|
+
get(channelId: string): VoiceConnection | undefined;
|
|
1214
|
+
leave(channelId: string): Promise<void>;
|
|
1215
|
+
leaveAll(): Promise<void>;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
declare class VoiceChannel extends TextChannel {
|
|
1219
|
+
join(): Promise<VoiceConnection>;
|
|
1220
|
+
leave(): Promise<void>;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1145
1223
|
type ChannelType = "SavedMessages" | "DirectMessage" | "Group" | "TextChannel";
|
|
1146
1224
|
interface ChannelCreateOptions {
|
|
1147
1225
|
name: string;
|
|
@@ -1249,6 +1327,7 @@ declare abstract class BaseChannel extends Base {
|
|
|
1249
1327
|
isText(): this is TextChannel;
|
|
1250
1328
|
isDM(): this is DMChannel;
|
|
1251
1329
|
isGroup(): this is GroupChannel;
|
|
1330
|
+
isVoice(): this is VoiceChannel;
|
|
1252
1331
|
}
|
|
1253
1332
|
|
|
1254
1333
|
declare class ServerChannelManager {
|
|
@@ -2241,6 +2320,7 @@ declare class Client extends EventEmitter {
|
|
|
2241
2320
|
emojis: EmojiManager;
|
|
2242
2321
|
user: ClientUser | null;
|
|
2243
2322
|
options: ClientOptions;
|
|
2323
|
+
readonly voice: VoiceManager;
|
|
2244
2324
|
constructor(options?: ClientOptions);
|
|
2245
2325
|
/**
|
|
2246
2326
|
* Connects the bot to the Stoat Gateway
|
|
@@ -2259,4 +2339,4 @@ declare class UnknownChannel extends BaseChannel {
|
|
|
2259
2339
|
constructor(client: Client, data: Channel);
|
|
2260
2340
|
}
|
|
2261
2341
|
|
|
2262
|
-
export { Attachment, AttachmentBuilder, type AttachmentMetadata, Base, BaseChannel, BitField, type BitFieldResolvable, type BotInformation, type CDNTag, type ChannelCreateOptions, type ChannelEditOptions, ChannelManager, type ChannelResolvable, type ChannelRolePermissionOptions, type ChannelType, Client, type ClientEvents, type ClientOptions, ClientUser, Collection, Collector, type CollectorOptions, DMChannel, EmbedBuilder, Emoji, type EmojiCreateOptions, type EmojiEditOptions, EmojiManager, type EmojiParent, type EmojiResolvable, type FetchMembersOptions, GatewayManager, GroupChannel, type Interaction, type Masquerade, Member, type MemberBanOptions, type MemberEditOptions, MemberManager, type MemberResolvable, Message, MessageCollector, type MessageCollectorOptions, type MessageFetchOptions, MessageManager, type MessageOptions, MessageReaction, type MessageResolvable, PermissionFlags, type PermissionResolvable, type PermissionString, Permissions, RESTManager, type RawDMChannel, type RawGroupChannel, type RawTextChannel, ReactionCollector, type ReactionCollectorOptions, type ReplyIntent, Role, type RoleCreateOptions, type RoleEditOptions, RoleManager, type RolePermissionOptions, type RoleResolvable, Server, ServerChannelManager, type ServerEditOptions, ServerManager, StoatAPIError, SweeperManager, type SweeperOptions, TextChannel, type TextEmbedData, UnknownChannel, User, type UserEditOptions, UserManager, type UserPresence, type UserProfile, type UserRelationShip, type UserResolvable, type UserStatus };
|
|
2342
|
+
export { Attachment, AttachmentBuilder, type AttachmentMetadata, AudioPlayer, type AudioPlayerEvents, type AudioPlayerStatus, AudioResource, type AudioResourceOptions, type AudioSource, Base, BaseChannel, BitField, type BitFieldResolvable, type BotInformation, type CDNTag, type ChannelCreateOptions, type ChannelEditOptions, ChannelManager, type ChannelResolvable, type ChannelRolePermissionOptions, type ChannelType, Client, type ClientEvents, type ClientOptions, ClientUser, Collection, Collector, type CollectorOptions, DMChannel, EmbedBuilder, Emoji, type EmojiCreateOptions, type EmojiEditOptions, EmojiManager, type EmojiParent, type EmojiResolvable, type FetchMembersOptions, GatewayManager, GroupChannel, type Interaction, type Masquerade, Member, type MemberBanOptions, type MemberEditOptions, MemberManager, type MemberResolvable, Message, MessageCollector, type MessageCollectorOptions, type MessageFetchOptions, MessageManager, type MessageOptions, MessageReaction, type MessageResolvable, PermissionFlags, type PermissionResolvable, type PermissionString, Permissions, RESTManager, type RawDMChannel, type RawGroupChannel, type RawTextChannel, ReactionCollector, type ReactionCollectorOptions, type ReplyIntent, Role, type RoleCreateOptions, type RoleEditOptions, RoleManager, type RolePermissionOptions, type RoleResolvable, Server, ServerChannelManager, type ServerEditOptions, ServerManager, StoatAPIError, SweeperManager, type SweeperOptions, TextChannel, type TextEmbedData, UnknownChannel, User, type UserEditOptions, UserManager, type UserPresence, type UserProfile, type UserRelationShip, type UserResolvable, type UserStatus, VoiceConnection, type VoiceConnectionEvents, type VoiceConnectionStatus, VoiceManager };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/client/Client.ts
|
|
2
|
-
import { EventEmitter as
|
|
2
|
+
import { EventEmitter as EventEmitter4 } from "events";
|
|
3
3
|
|
|
4
4
|
// src/gateway/GatewayManager.ts
|
|
5
5
|
import WebSocket from "ws";
|
|
@@ -1355,13 +1355,14 @@ var RESTManager = class {
|
|
|
1355
1355
|
this.client.emit("debug", `Bucket [${method}:${endpoint}] exhausted. Waiting ${waitTime}ms proactively...`);
|
|
1356
1356
|
await sleep(waitTime);
|
|
1357
1357
|
}
|
|
1358
|
+
const isBodyMethod = ["post", "patch", "put"].includes(method.toLowerCase());
|
|
1358
1359
|
const response = await fetch(url, {
|
|
1359
1360
|
method: method.toUpperCase(),
|
|
1360
1361
|
headers: {
|
|
1361
1362
|
"X-Bot-Token": this.token,
|
|
1362
1363
|
"Content-Type": "application/json"
|
|
1363
1364
|
},
|
|
1364
|
-
...
|
|
1365
|
+
...isBodyMethod ? { body: JSON.stringify(body ?? {}) } : {}
|
|
1365
1366
|
});
|
|
1366
1367
|
const remainingHeader = response.headers.get("x-ratelimit-remaining");
|
|
1367
1368
|
const resetAfterHeader = response.headers.get("x-ratelimit-reset-after");
|
|
@@ -1897,6 +1898,9 @@ var BaseChannel = class extends Base {
|
|
|
1897
1898
|
isGroup() {
|
|
1898
1899
|
return this.type === "Group";
|
|
1899
1900
|
}
|
|
1901
|
+
isVoice() {
|
|
1902
|
+
return this.isText() && this.voice !== null;
|
|
1903
|
+
}
|
|
1900
1904
|
};
|
|
1901
1905
|
|
|
1902
1906
|
// src/structures/TextChannel.ts
|
|
@@ -2207,10 +2211,21 @@ var GroupChannel = class extends BaseChannel {
|
|
|
2207
2211
|
}
|
|
2208
2212
|
};
|
|
2209
2213
|
|
|
2214
|
+
// src/structures/VoiceChannel.ts
|
|
2215
|
+
var VoiceChannel = class extends TextChannel {
|
|
2216
|
+
async join() {
|
|
2217
|
+
return this.client.voice.join(this.id, this.serverId);
|
|
2218
|
+
}
|
|
2219
|
+
async leave() {
|
|
2220
|
+
return this.client.voice.leave(this.id);
|
|
2221
|
+
}
|
|
2222
|
+
};
|
|
2223
|
+
|
|
2210
2224
|
// src/utils/ChannelFactory.ts
|
|
2211
2225
|
function createChannel(client, data) {
|
|
2212
2226
|
switch (data.channel_type) {
|
|
2213
2227
|
case "TextChannel":
|
|
2228
|
+
if (data.voice) return new VoiceChannel(client, data);
|
|
2214
2229
|
return new TextChannel(client, data);
|
|
2215
2230
|
case "DirectMessage":
|
|
2216
2231
|
return new DMChannel(client, data);
|
|
@@ -4356,8 +4371,247 @@ var SweeperManager = class {
|
|
|
4356
4371
|
}
|
|
4357
4372
|
};
|
|
4358
4373
|
|
|
4374
|
+
// src/voice/VoiceConnection.ts
|
|
4375
|
+
import {
|
|
4376
|
+
Room,
|
|
4377
|
+
RoomEvent,
|
|
4378
|
+
TrackSource,
|
|
4379
|
+
LocalAudioTrack,
|
|
4380
|
+
TrackPublishOptions,
|
|
4381
|
+
AudioFrame,
|
|
4382
|
+
AudioSource
|
|
4383
|
+
} from "@livekit/rtc-node";
|
|
4384
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
4385
|
+
var SAMPLE_RATE = 48e3;
|
|
4386
|
+
var CHANNELS = 2;
|
|
4387
|
+
var SAMPLES_PER_FRAME = 960;
|
|
4388
|
+
var BYTES_PER_FRAME = SAMPLES_PER_FRAME * CHANNELS * 2;
|
|
4389
|
+
var VoiceConnection = class extends EventEmitter2 {
|
|
4390
|
+
channelId;
|
|
4391
|
+
guildId;
|
|
4392
|
+
room;
|
|
4393
|
+
_status = "connecting";
|
|
4394
|
+
_player = null;
|
|
4395
|
+
_audioSource = null;
|
|
4396
|
+
_audioTrack = null;
|
|
4397
|
+
constructor(channelId, guildId) {
|
|
4398
|
+
super();
|
|
4399
|
+
this.channelId = channelId;
|
|
4400
|
+
this.guildId = guildId;
|
|
4401
|
+
this.room = new Room();
|
|
4402
|
+
this.room.on(RoomEvent.Disconnected, () => {
|
|
4403
|
+
this._status = "disconnected";
|
|
4404
|
+
this._player?.removeSubscriber(this);
|
|
4405
|
+
this.emit("disconnect");
|
|
4406
|
+
});
|
|
4407
|
+
}
|
|
4408
|
+
get status() {
|
|
4409
|
+
return this._status;
|
|
4410
|
+
}
|
|
4411
|
+
get player() {
|
|
4412
|
+
return this._player;
|
|
4413
|
+
}
|
|
4414
|
+
/** @internal */
|
|
4415
|
+
async connect(url, token) {
|
|
4416
|
+
await this.room.connect(url, token);
|
|
4417
|
+
this._status = "ready";
|
|
4418
|
+
this.emit("ready");
|
|
4419
|
+
}
|
|
4420
|
+
subscribe(player) {
|
|
4421
|
+
if (this._player) this._player.removeSubscriber(this);
|
|
4422
|
+
this._player = player;
|
|
4423
|
+
player.addSubscriber(this);
|
|
4424
|
+
}
|
|
4425
|
+
unsubscribe() {
|
|
4426
|
+
this._player?.removeSubscriber(this);
|
|
4427
|
+
this._player = null;
|
|
4428
|
+
}
|
|
4429
|
+
async _feedStream(stream) {
|
|
4430
|
+
if (!this._audioSource) {
|
|
4431
|
+
this._audioSource = new AudioSource(SAMPLE_RATE, CHANNELS, SAMPLES_PER_FRAME);
|
|
4432
|
+
this._audioTrack = LocalAudioTrack.createAudioTrack("audio", this._audioSource);
|
|
4433
|
+
const options = new TrackPublishOptions();
|
|
4434
|
+
options.source = TrackSource.SOURCE_MICROPHONE;
|
|
4435
|
+
if (!this.room.localParticipant) {
|
|
4436
|
+
throw new Error("Not connected to a room");
|
|
4437
|
+
}
|
|
4438
|
+
await this.room.localParticipant.publishTrack(this._audioTrack, options);
|
|
4439
|
+
}
|
|
4440
|
+
let leftover = Buffer.alloc(0);
|
|
4441
|
+
for await (const chunk of stream) {
|
|
4442
|
+
const buf = Buffer.concat([leftover, chunk]);
|
|
4443
|
+
let offset = 0;
|
|
4444
|
+
while (offset + BYTES_PER_FRAME <= buf.length) {
|
|
4445
|
+
const slice = buf.subarray(offset, offset + BYTES_PER_FRAME);
|
|
4446
|
+
const tmp = slice.buffer.slice(slice.byteOffset, slice.byteOffset + slice.byteLength);
|
|
4447
|
+
const pcm = new Int16Array(tmp);
|
|
4448
|
+
await this._audioSource.captureFrame(new AudioFrame(pcm, SAMPLE_RATE, CHANNELS, SAMPLES_PER_FRAME));
|
|
4449
|
+
offset += BYTES_PER_FRAME;
|
|
4450
|
+
}
|
|
4451
|
+
leftover = buf.subarray(offset);
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4454
|
+
async disconnect() {
|
|
4455
|
+
this._status = "disconnecting";
|
|
4456
|
+
this.unsubscribe();
|
|
4457
|
+
if (this._audioTrack) {
|
|
4458
|
+
await this._audioTrack.close();
|
|
4459
|
+
this._audioTrack = null;
|
|
4460
|
+
this._audioSource = null;
|
|
4461
|
+
}
|
|
4462
|
+
await this.room.disconnect();
|
|
4463
|
+
}
|
|
4464
|
+
};
|
|
4465
|
+
|
|
4466
|
+
// src/voice/VoiceManager.ts
|
|
4467
|
+
var VoiceManager = class {
|
|
4468
|
+
connections = /* @__PURE__ */ new Map();
|
|
4469
|
+
client;
|
|
4470
|
+
constructor(client) {
|
|
4471
|
+
this.client = client;
|
|
4472
|
+
}
|
|
4473
|
+
async join(channelId, guildId) {
|
|
4474
|
+
const existing = this.connections.get(channelId);
|
|
4475
|
+
if (existing?.status === "ready") return existing;
|
|
4476
|
+
const { token, url } = await this.client.rest.post(`/channels/${channelId}/join_call`);
|
|
4477
|
+
const connection = new VoiceConnection(channelId, guildId);
|
|
4478
|
+
this.connections.set(channelId, connection);
|
|
4479
|
+
connection.once("disconnect", () => this.connections.delete(channelId));
|
|
4480
|
+
await connection.connect(url, token);
|
|
4481
|
+
return connection;
|
|
4482
|
+
}
|
|
4483
|
+
get(channelId) {
|
|
4484
|
+
return this.connections.get(channelId);
|
|
4485
|
+
}
|
|
4486
|
+
async leave(channelId) {
|
|
4487
|
+
await this.connections.get(channelId)?.disconnect();
|
|
4488
|
+
}
|
|
4489
|
+
async leaveAll() {
|
|
4490
|
+
await Promise.all([...this.connections.keys()].map((id) => this.leave(id)));
|
|
4491
|
+
}
|
|
4492
|
+
};
|
|
4493
|
+
|
|
4494
|
+
// src/voice/AudioPlayer.ts
|
|
4495
|
+
import { EventEmitter as EventEmitter3 } from "events";
|
|
4496
|
+
var AudioPlayer = class extends EventEmitter3 {
|
|
4497
|
+
_status = "idle";
|
|
4498
|
+
_resource = null;
|
|
4499
|
+
/** Registered VoiceConnections subscribed to this player */
|
|
4500
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
4501
|
+
get status() {
|
|
4502
|
+
return this._status;
|
|
4503
|
+
}
|
|
4504
|
+
get resource() {
|
|
4505
|
+
return this._resource;
|
|
4506
|
+
}
|
|
4507
|
+
play(resource) {
|
|
4508
|
+
this.transition("buffering");
|
|
4509
|
+
this._resource = resource;
|
|
4510
|
+
resource.stream.once("readable", () => {
|
|
4511
|
+
this.transition("playing");
|
|
4512
|
+
this.feed();
|
|
4513
|
+
});
|
|
4514
|
+
resource.stream.once("end", () => {
|
|
4515
|
+
this._resource = null;
|
|
4516
|
+
this.transition("idle");
|
|
4517
|
+
this.emit("idle");
|
|
4518
|
+
});
|
|
4519
|
+
resource.stream.once("error", (err) => {
|
|
4520
|
+
this._resource = null;
|
|
4521
|
+
this.transition("idle");
|
|
4522
|
+
this.emit("error", err);
|
|
4523
|
+
});
|
|
4524
|
+
}
|
|
4525
|
+
pause() {
|
|
4526
|
+
if (this._status !== "playing") return;
|
|
4527
|
+
this._resource?.stream.pause();
|
|
4528
|
+
this.transition("paused");
|
|
4529
|
+
}
|
|
4530
|
+
resume() {
|
|
4531
|
+
if (this._status !== "paused") return;
|
|
4532
|
+
this._resource?.stream.resume();
|
|
4533
|
+
this.transition("playing");
|
|
4534
|
+
}
|
|
4535
|
+
stop() {
|
|
4536
|
+
this._resource?.stream.destroy();
|
|
4537
|
+
this._resource = null;
|
|
4538
|
+
this.transition("stopped");
|
|
4539
|
+
this.transition("idle");
|
|
4540
|
+
this.emit("idle");
|
|
4541
|
+
}
|
|
4542
|
+
/** @internal */
|
|
4543
|
+
addSubscriber(conn) {
|
|
4544
|
+
this.subscribers.add(conn);
|
|
4545
|
+
}
|
|
4546
|
+
/** @internal */
|
|
4547
|
+
removeSubscriber(conn) {
|
|
4548
|
+
this.subscribers.delete(conn);
|
|
4549
|
+
}
|
|
4550
|
+
feed() {
|
|
4551
|
+
if (!this._resource) return;
|
|
4552
|
+
for (const conn of this.subscribers) {
|
|
4553
|
+
conn._feedStream(this._resource.stream);
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
transition(next) {
|
|
4557
|
+
const prev = this._status;
|
|
4558
|
+
this._status = next;
|
|
4559
|
+
this.emit("stateChange", prev, next);
|
|
4560
|
+
}
|
|
4561
|
+
};
|
|
4562
|
+
|
|
4563
|
+
// src/voice/AudioResource.ts
|
|
4564
|
+
import { Readable } from "stream";
|
|
4565
|
+
import { spawn } from "child_process";
|
|
4566
|
+
import { PassThrough } from "stream";
|
|
4567
|
+
import { existsSync } from "fs";
|
|
4568
|
+
var AudioResource = class _AudioResource {
|
|
4569
|
+
stream;
|
|
4570
|
+
constructor(stream) {
|
|
4571
|
+
this.stream = stream;
|
|
4572
|
+
}
|
|
4573
|
+
static from(source, options = {}) {
|
|
4574
|
+
if (typeof source === "string" && !source.startsWith("http") && !existsSync(source)) {
|
|
4575
|
+
throw new Error(`Audio file not found: ${source}`);
|
|
4576
|
+
}
|
|
4577
|
+
const { volume = 1, inputType } = options;
|
|
4578
|
+
const args = [
|
|
4579
|
+
...inputType ? ["-f", inputType] : [],
|
|
4580
|
+
"-i",
|
|
4581
|
+
typeof source === "string" ? source : "pipe:0",
|
|
4582
|
+
"-af",
|
|
4583
|
+
`volume=${volume}`,
|
|
4584
|
+
"-ar",
|
|
4585
|
+
"48000",
|
|
4586
|
+
"-ac",
|
|
4587
|
+
"2",
|
|
4588
|
+
"-f",
|
|
4589
|
+
"s16le",
|
|
4590
|
+
"-acodec",
|
|
4591
|
+
"pcm_s16le",
|
|
4592
|
+
"pipe:1"
|
|
4593
|
+
];
|
|
4594
|
+
const ffmpeg = spawn("ffmpeg", args, { stdio: ["pipe", "pipe", "ignore"] });
|
|
4595
|
+
if (source instanceof Readable) {
|
|
4596
|
+
source.pipe(ffmpeg.stdin);
|
|
4597
|
+
source.once("error", () => ffmpeg.kill());
|
|
4598
|
+
}
|
|
4599
|
+
ffmpeg.stdin?.on("error", () => {
|
|
4600
|
+
});
|
|
4601
|
+
const pass = new PassThrough();
|
|
4602
|
+
ffmpeg.stdout.pipe(pass);
|
|
4603
|
+
ffmpeg.once("error", (err) => pass.destroy(err));
|
|
4604
|
+
ffmpeg.once("close", (code) => {
|
|
4605
|
+
if (code !== 0 && code !== null) {
|
|
4606
|
+
pass.destroy(new Error(`ffmpeg exited with code ${code}`));
|
|
4607
|
+
}
|
|
4608
|
+
});
|
|
4609
|
+
return new _AudioResource(pass);
|
|
4610
|
+
}
|
|
4611
|
+
};
|
|
4612
|
+
|
|
4359
4613
|
// src/client/Client.ts
|
|
4360
|
-
var Client = class extends
|
|
4614
|
+
var Client = class extends EventEmitter4 {
|
|
4361
4615
|
rest;
|
|
4362
4616
|
gateway;
|
|
4363
4617
|
channels;
|
|
@@ -4367,6 +4621,7 @@ var Client = class extends EventEmitter2 {
|
|
|
4367
4621
|
emojis;
|
|
4368
4622
|
user = null;
|
|
4369
4623
|
options;
|
|
4624
|
+
voice;
|
|
4370
4625
|
constructor(options = {}) {
|
|
4371
4626
|
super({ captureRejections: true });
|
|
4372
4627
|
this.options = options;
|
|
@@ -4376,6 +4631,7 @@ var Client = class extends EventEmitter2 {
|
|
|
4376
4631
|
this.servers = new ServerManager(this, options.cacheLimits?.servers);
|
|
4377
4632
|
this.users = new UserManager(this, options.cacheLimits?.users);
|
|
4378
4633
|
this.emojis = new EmojiManager(this, void 0, options.cacheLimits?.emojis);
|
|
4634
|
+
this.voice = new VoiceManager(this);
|
|
4379
4635
|
this.sweepers = new SweeperManager(this, options.sweepers ?? {});
|
|
4380
4636
|
}
|
|
4381
4637
|
/**
|
|
@@ -4470,6 +4726,8 @@ export {
|
|
|
4470
4726
|
API,
|
|
4471
4727
|
Attachment,
|
|
4472
4728
|
AttachmentBuilder,
|
|
4729
|
+
AudioPlayer,
|
|
4730
|
+
AudioResource,
|
|
4473
4731
|
Base,
|
|
4474
4732
|
BaseChannel,
|
|
4475
4733
|
BitField,
|
|
@@ -4504,6 +4762,8 @@ export {
|
|
|
4504
4762
|
TextChannel,
|
|
4505
4763
|
UnknownChannel,
|
|
4506
4764
|
User,
|
|
4507
|
-
UserManager
|
|
4765
|
+
UserManager,
|
|
4766
|
+
VoiceConnection,
|
|
4767
|
+
VoiceManager
|
|
4508
4768
|
};
|
|
4509
4769
|
//# sourceMappingURL=index.js.map
|