diff --git a/src/client/voice/VoiceConnection.js b/src/client/voice/VoiceConnection.js index 947e19122..cd2d29a7d 100644 --- a/src/client/voice/VoiceConnection.js +++ b/src/client/voice/VoiceConnection.js @@ -177,20 +177,21 @@ class VoiceConnection extends EventEmitter { /** * Sends a request to the main gateway to join a voice channel. * @param {Object} [options] The options to provide + * @returns {Promise} * @private */ sendVoiceStateUpdate(options = {}) { options = Util.mergeDefault({ guild_id: this.channel.guild.id, channel_id: this.channel.id, - self_mute: false, - self_deaf: false, + self_mute: this.voice ? this.voice.selfMute : false, + self_deaf: this.voice ? this.voice.selfDeaf : false, }, options); const queueLength = this.channel.guild.shard.ratelimit.queue.length; this.emit('debug', `Sending voice state update (queue length is ${queueLength}): ${JSON.stringify(options)}`); - this.channel.guild.shard.send({ + return this.channel.guild.shard.send({ op: OPCodes.VOICE_STATE_UPDATE, d: options, }); diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 1cfe4da9f..1b2bdcdbf 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -51,6 +51,8 @@ const Messages = { VOICE_PRISM_DEMUXERS_NEED_STREAM: 'To play a webm/ogg stream, you need to pass a ReadableStream.', VOICE_STATE_UNCACHED_MEMBER: 'The member of this voice state is uncached.', + VOICE_STATE_NOT_OWN: 'You cannot self-deafen/mute on VoiceStates that do not belong to the ClientUser.', + VOICE_STATE_INVALID_TYPE: name => `${name} must be a boolean.`, UDP_SEND_FAIL: 'Tried to send a UDP packet, but there is no socket available.', UDP_ADDRESS_MALFORMED: 'Malformed UDP address or port.', diff --git a/src/structures/VoiceState.js b/src/structures/VoiceState.js index 0b5f2b058..165aa7b66 100644 --- a/src/structures/VoiceState.js +++ b/src/structures/VoiceState.js @@ -2,6 +2,7 @@ const Base = require('./Base'); const { browser } = require('../util/Constants'); +const { Error, TypeError } = require('../errors'); /** * Represents the voice state for a Guild Member. @@ -138,6 +139,34 @@ class VoiceState extends Base { return this.member ? this.member.edit({ deaf }, reason) : Promise.reject(new Error('VOICE_STATE_UNCACHED_MEMBER')); } + /** + * Self-mutes/unmutes the bot for this voice state. + * @param {boolean} mute Whether or not the bot should be self-muted + * @returns {Promise} true if the voice state was successfully updated, otherwise false + */ + async setSelfMute(mute) { + if (this.id !== this.client.user.id) throw new Error('VOICE_STATE_NOT_OWN'); + if (typeof mute !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'mute'); + if (!this.connection) return false; + this.selfMute = mute; + await this.connection.sendVoiceStateUpdate(); + return true; + } + + /** + * Self-deafens/undeafens the bot for this voice state. + * @param {boolean} deaf Whether or not the bot should be self-deafened + * @returns {Promise} true if the voice state was successfully updated, otherwise false + */ + async setSelfDeaf(deaf) { + if (this.id !== this.client.user.id) return new Error('VOICE_STATE_NOT_OWN'); + if (typeof deaf !== 'boolean') return new TypeError('VOICE_STATE_INVALID_TYPE', 'deaf'); + if (!this.connection) return false; + this.selfDeaf = deaf; + await this.connection.sendVoiceStateUpdate(); + return true; + } + toJSON() { return super.toJSON({ id: true, diff --git a/typings/index.d.ts b/typings/index.d.ts index ed0430dde..215abc3ae 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1202,7 +1202,7 @@ declare module 'discord.js' { private onSessionDescription(mode: string, secret: string): void; private onSpeaking(data: object): void; private reconnect(token: string, endpoint: string): void; - private sendVoiceStateUpdate(options: object): void; + private sendVoiceStateUpdate(options: object): Promise; private setSessionID(sessionID: string): void; private setSpeaking(value: BitFieldResolvable): void; private setTokenAndEndpoint(token: string, endpoint: string): void; @@ -1215,6 +1215,7 @@ declare module 'discord.js' { public receiver: VoiceReceiver; public speaking: Readonly; public status: VoiceStatus; + public readonly voice: VoiceState; public voiceManager: ClientVoiceManager; public disconnect(): void; public play(input: VoiceBroadcast | Readable | string, options?: StreamOptions): StreamDispatcher; @@ -1284,8 +1285,10 @@ declare module 'discord.js' { public sessionID?: string; public readonly speaking: boolean | null; - public setDeaf(mute: boolean, reason?: string): Promise; + public setDeaf(deaf: boolean, reason?: string): Promise; public setMute(mute: boolean, reason?: string): Promise; + public setSelfDeaf(deaf: boolean): Promise; + public setSelfMute(mute: boolean): Promise; } class VolumeInterface extends EventEmitter {