diff --git a/packages/discord.js/src/structures/VoiceState.js b/packages/discord.js/src/structures/VoiceState.js
index be77df020..af0d1d723 100644
--- a/packages/discord.js/src/structures/VoiceState.js
+++ b/packages/discord.js/src/structures/VoiceState.js
@@ -205,29 +205,64 @@ class VoiceState extends Base {
return this.guild.members.edit(this.id, { channel }, reason);
}
+ /**
+ * Data to edit the logged in user's own voice state with, when in a stage channel
+ * @typedef {Object} VoiceStateEditData
+ * @property {boolean} [requestToSpeak] Whether or not the client is requesting to become a speaker.
+ * Only available to the logged in user's own voice state.
+ * @property {boolean} [suppressed] Whether or not the user should be suppressed.
+ */
+
+ /**
+ * Edits this voice state. Currently only available when in a stage channel
+ * @param {VoiceStateEditData} data The data to edit the voice state with
+ * @returns {Promise}
+ */
+ async edit(data) {
+ if (this.channel?.type !== ChannelType.GuildStageVoice) throw new Error('VOICE_NOT_STAGE_CHANNEL');
+
+ const target = this.client.user.id === this.id ? '@me' : this.id;
+
+ if (target !== '@me' && typeof data.requestToSpeak !== 'undefined') {
+ throw new Error('VOICE_STATE_NOT_OWN');
+ }
+
+ if (!['boolean', 'undefined'].includes(typeof data.requestToSpeak)) {
+ throw new TypeError('VOICE_STATE_INVALID_TYPE', 'requestToSpeak');
+ }
+
+ if (!['boolean', 'undefined'].includes(typeof data.suppressed)) {
+ throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed');
+ }
+
+ await this.client.rest.patch(Routes.guildVoiceState(this.guild.id, target), {
+ body: {
+ channel_id: this.channelId,
+ request_to_speak_timestamp: data.requestToSpeak
+ ? new Date().toISOString()
+ : data.requestToSpeak === false
+ ? null
+ : undefined,
+ suppress: data.suppressed,
+ },
+ });
+ return this;
+ }
+
/**
* Toggles the request to speak in the channel.
* Only applicable for stage channels and for the client's own voice state.
- * @param {boolean} [request=true] Whether or not the client is requesting to become a speaker.
+ * @param {boolean} [requestToSpeak=true] Whether or not the client is requesting to become a speaker.
* @example
* // Making the client request to speak in a stage channel (raise its hand)
* guild.me.voice.setRequestToSpeak(true);
* @example
* // Making the client cancel a request to speak
* guild.me.voice.setRequestToSpeak(false);
- * @returns {Promise}
+ * @returns {Promise}
*/
- async setRequestToSpeak(request = true) {
- if (this.channel?.type !== ChannelType.GuildStageVoice) throw new Error('VOICE_NOT_STAGE_CHANNEL');
-
- if (this.client.user.id !== this.id) throw new Error('VOICE_STATE_NOT_OWN');
-
- await this.client.rest.patch(Routes.guildVoiceState(this.guild.id), {
- body: {
- channel_id: this.channelId,
- request_to_speak_timestamp: request ? new Date().toISOString() : null,
- },
- });
+ setRequestToSpeak(requestToSpeak = true) {
+ return this.edit({ requestToSpeak });
}
/**
@@ -245,21 +280,10 @@ class VoiceState extends Base {
* @example
* // Moving another user to the audience, or cancelling their invite to speak
* voiceState.setSuppressed(true);
- * @returns {Promise}
+ * @returns {Promise}
*/
- async setSuppressed(suppressed = true) {
- if (typeof suppressed !== 'boolean') throw new TypeError('VOICE_STATE_INVALID_TYPE', 'suppressed');
-
- if (this.channel?.type !== ChannelType.GuildStageVoice) throw new Error('VOICE_NOT_STAGE_CHANNEL');
-
- const target = this.client.user.id === this.id ? '@me' : this.id;
-
- await this.client.rest.patch(Routes.guildVoiceState(this.guild.id, target), {
- body: {
- channel_id: this.channelId,
- suppress: suppressed,
- },
- });
+ setSuppressed(suppressed = true) {
+ return this.edit({ suppressed });
}
toJSON() {
diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts
index 30ab2a843..535521e97 100644
--- a/packages/discord.js/typings/index.d.ts
+++ b/packages/discord.js/typings/index.d.ts
@@ -2517,8 +2517,9 @@ export class VoiceState extends Base {
public setMute(mute?: boolean, reason?: string): Promise;
public disconnect(reason?: string): Promise;
public setChannel(channel: GuildVoiceChannelResolvable | null, reason?: string): Promise;
- public setRequestToSpeak(request?: boolean): Promise;
- public setSuppressed(suppressed?: boolean): Promise;
+ public setRequestToSpeak(request?: boolean): Promise;
+ public setSuppressed(suppressed?: boolean): Promise;
+ public edit(data: VoiceStateEditData): Promise;
}
export class Webhook extends WebhookMixin() {
@@ -5129,6 +5130,11 @@ export type VoiceBasedChannelTypes = VoiceBasedChannel['type'];
export type VoiceChannelResolvable = Snowflake | VoiceChannel;
+export interface VoiceStateEditData {
+ requestToSpeak?: boolean;
+ suppressed?: boolean;
+}
+
export type WebhookClientData = WebhookClientDataIdWithToken | WebhookClientDataURL;
export interface WebhookClientDataIdWithToken {