diff --git a/core/src/Client.ts b/core/src/Client.ts index ab4e71f6..1d2aa9c3 100644 --- a/core/src/Client.ts +++ b/core/src/Client.ts @@ -194,7 +194,6 @@ export class Client extends EventEmitter { connect = true, details?: Partial, "type">> ) { - await this.fetchConfiguration(); this.session = { token, type, id: details?.id || ulid(), name: details?.name || "" }; this.api = new API({ baseURL: this.options.apiURL, @@ -202,6 +201,7 @@ export class Client extends EventEmitter { revolt: type == "user" ? { token } : token, }, }); + await this.fetchConfiguration(); if (connect) await this.ws.connect(); } /** diff --git a/voice/src/VoiceClient.ts b/voice/src/VoiceClient.ts index ea4af3f9..981ac7bd 100644 --- a/voice/src/VoiceClient.ts +++ b/voice/src/VoiceClient.ts @@ -1,10 +1,11 @@ import EventEmitter from "eventemitter3"; import type * as MSCBrowser from "mediasoup-client"; import type * as MSCNode from "msc-node"; -import { Client, MiniMapEmitter, VoiceChannel } from "revkit"; +import { Client, MiniMapEmitter } from "revkit"; import Signaling from "./Signaling"; import type { MSCPlatform, MediaSoup } from "./msc"; import { + RevkitClientOptions, VoiceStatus, WSEvents, type ProduceType, @@ -61,6 +62,9 @@ export class VoiceClient< return this.status == VoiceStatus.CONNECTED && !!this.channelID; } + public token: string; + public type: "user" | "bot"; + public client = new Client(); public channelID: string | null = null; public get channel() { return this.client.channels.get(this.channelID); @@ -68,20 +72,28 @@ export class VoiceClient< /** * The base voice client. It's recommended to use the platform-specific clients instead. - * @param client The RevKit client to use. * @param msc The MediaSoup client to use. (for tree-shaking) * @param createDevice A function called to create a new MediaSoup Device for the client. * @param consumeTrack A callback that is run to play a new MediaStreamTrack. The function returned will be called when the stream is closed. (leave out to disable consuming media) */ constructor( public readonly platform: Platform, - public client: Client, + client: Client | RevkitClientOptions, private msc: Platform extends "node" ? typeof MSCNode : typeof MSCBrowser, private createDevice: () => MSC["Device"], private consumeTrack?: VoiceClientConsumer ) { super(); this.supported = this.msc.detectDevice() !== undefined; + if (client instanceof Client) { + this.client = client; + this.token = this.client.session.token; + this.type = this.client.session.type; + } else { + this.client = new Client(client.baseURL ? { apiURL: client.baseURL } : undefined); + this.token = client.token; + this.type = client.type; + } this.signaling.on( "data", @@ -378,7 +390,15 @@ export class VoiceClient< this.emit("status", status); } - public async connect(channel: VoiceChannel) { + public async connect(channelID?: string) { + if (!this.client.session) { + await this.client.login(this.token, this.type, false); + await this.client.users.fetch("@me"); + } + + const channel = + channelID == undefined ? this.channel : await this.client.channels.fetch(channelID); + if (this.status > VoiceStatus.READY) return; if (!this.supported) throw new Error("RTC is unavailable."); if (!channel.isVoice()) throw new Error("Not a voice channel."); diff --git a/voice/src/browser.ts b/voice/src/browser.ts index a041f395..a20101b6 100644 --- a/voice/src/browser.ts +++ b/voice/src/browser.ts @@ -1,7 +1,7 @@ import * as MSC from "mediasoup-client"; -import type { Client } from "revkit"; import { VoiceClient as BaseVoiceClient, type VoiceClientConsumer } from "./VoiceClient"; -import type { ProduceType } from "./types"; +import type { ProduceType, RevkitClientOptions } from "./types"; +import { Client } from "revkit"; export const DEFAULT_CONSUMER: VoiceClientConsumer<"browser"> = (type, track) => { if (type == "audio") { @@ -20,10 +20,9 @@ export const DEFAULT_CONSUMER: VoiceClientConsumer<"browser"> = (type, track) => */ export default class VoiceClient extends BaseVoiceClient<"browser"> { /** - * @param client The RevKit client to use. * @param trackConsumer A function that is called when there is a new `MediaStreamTrack` to play. The function returned will be called when the track ends. */ - constructor(client: Client, trackConsumer?: VoiceClientConsumer<"browser">) { + constructor(client: Client | RevkitClientOptions, trackConsumer?: VoiceClientConsumer<"browser">) { super("browser", client, MSC, () => new MSC.Device(), trackConsumer); } diff --git a/voice/src/node.ts b/voice/src/node.ts index a1597dcc..278922a4 100644 --- a/voice/src/node.ts +++ b/voice/src/node.ts @@ -4,10 +4,10 @@ import * as MSC from "msc-node"; import os from "os"; import path from "path"; import { FFmpeg, VolumeTransformer } from "prism-media"; -import type { Client } from "revkit"; +import { Client } from "revkit"; import { Readable } from "stream"; import { VoiceClient as BaseVoiceClient } from "./VoiceClient"; -import type { ProduceType, VoiceParticipant } from "./types"; +import type { ProduceType, RevkitClientOptions, VoiceParticipant } from "./types"; const AUDIO_ENCODING = "s16le", RTP_PAYLOAD_TYPE = 100, @@ -38,10 +38,9 @@ export default class VoiceClient extends BaseVoiceClient<"node"> { public port: number = 5002; /** - * @param client The RevKit client to use. * @param options Additional options for the player. Some of them also apply to incoming tracks. (you shouldn't need to mess with these) */ - constructor(client: Client, options: Partial = {}) { + constructor(client: Client | RevkitClientOptions, options: Partial = {}) { const opts: VoiceClientOptions = { args: [], audioChannels: 2, diff --git a/voice/src/types.ts b/voice/src/types.ts index 953d13ea..326f7f00 100644 --- a/voice/src/types.ts +++ b/voice/src/types.ts @@ -114,3 +114,9 @@ export enum VoiceStatus { RTC_CONNECTING, CONNECTED, } + +export interface RevkitClientOptions { + token: string; + type: "user" | "bot"; + baseURL?: string; +}