From a4b0c4f1e3d96efb1568f08a6f696c9cf147a90c Mon Sep 17 00:00:00 2001
From: ppaneksamsung
Date: Sun, 19 Dec 2021 08:42:08 +0100
Subject: [PATCH] Allow clients to send 'Gateway Voice State Update' command
(#1888)
* Expose SendVoiceStateUpdateAsync API to clients
Fixes #1882
* Revert "Expose SendVoiceStateUpdateAsync API to clients"
This reverts commit 1a11cae7
* Add IAudioChannel.ModifyAsync API
* fix NRE with request options
Co-authored-by: Quin Lynch <49576606+quinchs@users.noreply.github.com>
Co-authored-by: quin lynch
---
.../Channels/AudioChannelProperties.cs | 18 ++++++++
.../Entities/Channels/IAudioChannel.cs | 12 +++++
.../Entities/Channels/RestGroupChannel.cs | 1 +
.../Entities/Channels/RestVoiceChannel.cs | 1 +
.../DiscordSocketApiClient.cs | 7 ++-
.../Entities/Channels/SocketGroupChannel.cs | 1 +
.../Entities/Channels/SocketVoiceChannel.cs | 6 +++
.../Entities/Guilds/SocketGuild.cs | 45 ++++++++++++++++++-
.../MockedEntities/MockedGroupChannel.cs | 5 +++
.../MockedEntities/MockedVoiceChannel.cs | 5 +++
10 files changed, 98 insertions(+), 3 deletions(-)
create mode 100644 src/Discord.Net.Core/Entities/Channels/AudioChannelProperties.cs
diff --git a/src/Discord.Net.Core/Entities/Channels/AudioChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/AudioChannelProperties.cs
new file mode 100644
index 000000000..01d436f2a
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Channels/AudioChannelProperties.cs
@@ -0,0 +1,18 @@
+namespace Discord
+{
+ ///
+ /// Provides properties that are used to modify an with the specified changes.
+ ///
+ public class AudioChannelProperties
+ {
+ ///
+ /// Sets whether the user should be muted.
+ ///
+ public Optional SelfMute { get; set; }
+
+ ///
+ /// Sets whether the user should be deafened.
+ ///
+ public Optional SelfDeaf { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs b/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs
index 179f4b03e..dfab58f07 100644
--- a/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs
@@ -1,4 +1,5 @@
using Discord.Audio;
+using System;
using System.Threading.Tasks;
namespace Discord
@@ -27,5 +28,16 @@ namespace Discord
/// A task representing the asynchronous operation for disconnecting from the audio channel.
///
Task DisconnectAsync();
+
+ ///
+ /// Modifies this audio channel.
+ ///
+ /// The properties to modify the channel with.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous modification operation.
+ ///
+ ///
+ Task ModifyAsync(Action func, RequestOptions options = null);
}
}
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
index 267b4dc52..1240f6d67 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
@@ -211,6 +211,7 @@ namespace Discord.Rest
/// Connecting to a group channel is not supported.
Task IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
+ Task IAudioChannel.ModifyAsync(Action func, RequestOptions options) { throw new NotSupportedException(); }
#endregion
#region IChannel
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
index 239c00467..98ff6ac6e 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
@@ -95,6 +95,7 @@ namespace Discord.Rest
/// Connecting to a REST-based channel is not supported.
Task IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
+ Task IAudioChannel.ModifyAsync(Action func, RequestOptions options) { throw new NotSupportedException(); }
#endregion
#region IGuildChannel
diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
index 3c0f3d4a8..150da9d89 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
@@ -311,7 +311,6 @@ namespace Discord.API
}
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
{
- options = RequestOptions.CreateOrClone(options);
var payload = new VoiceStateUpdateParams
{
GuildId = guildId,
@@ -319,6 +318,12 @@ namespace Discord.API
SelfDeaf = selfDeaf,
SelfMute = selfMute
};
+ options = RequestOptions.CreateOrClone(options);
+ await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false);
+ }
+ public async Task SendVoiceStateUpdateAsync(VoiceStateUpdateParams payload, RequestOptions options = null)
+ {
+ options = RequestOptions.CreateOrClone(options);
await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false);
}
public async Task SendGuildSyncAsync(IEnumerable guildIds, RequestOptions options = null)
diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
index f1debb864..0451f97ab 100644
--- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
+++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
@@ -344,6 +344,7 @@ namespace Discord.WebSocket
/// Connecting to a group channel is not supported.
Task IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
+ Task IAudioChannel.ModifyAsync(Action func, RequestOptions options) { throw new NotSupportedException(); }
#endregion
#region IChannel
diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
index 08b976bfe..498862697 100644
--- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
+++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
@@ -82,6 +82,12 @@ namespace Discord.WebSocket
public async Task DisconnectAsync()
=> await Guild.DisconnectAudioAsync();
+ ///
+ public async Task ModifyAsync(Action func, RequestOptions options = null)
+ {
+ await Guild.ModifyAudioAsync(Id, func, options).ConfigureAwait(false);
+ }
+
///
public override SocketGuildUser GetUser(ulong id)
{
diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
index 697d5fe82..084a42c55 100644
--- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
+++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
@@ -1,3 +1,4 @@
+using Discord.API.Gateway;
using Discord.Audio;
using Discord.Rest;
using System;
@@ -45,6 +46,7 @@ namespace Discord.WebSocket
private ImmutableArray _emotes;
private AudioClient _audioClient;
+ private VoiceStateUpdateParams _voiceStateUpdateParams;
#pragma warning restore IDISP002, IDISP006
///
@@ -1593,11 +1595,19 @@ namespace Discord.WebSocket
promise = new TaskCompletionSource();
_audioConnectPromise = promise;
+ _voiceStateUpdateParams = new VoiceStateUpdateParams
+ {
+ GuildId = Id,
+ ChannelId = channelId,
+ SelfDeaf = selfDeaf,
+ SelfMute = selfMute
+ };
+
if (external)
{
#pragma warning disable IDISP001
var _ = promise.TrySetResultAsync(null);
- await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false);
+ await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams).ConfigureAwait(false);
return null;
#pragma warning restore IDISP001
}
@@ -1632,7 +1642,7 @@ namespace Discord.WebSocket
#pragma warning restore IDISP003
}
- await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false);
+ await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams).ConfigureAwait(false);
}
catch
{
@@ -1679,7 +1689,38 @@ namespace Discord.WebSocket
await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, null, false, false).ConfigureAwait(false);
_audioClient?.Dispose();
_audioClient = null;
+ _voiceStateUpdateParams = null;
}
+
+ internal async Task ModifyAudioAsync(ulong channelId, Action func, RequestOptions options)
+ {
+ await _audioLock.WaitAsync().ConfigureAwait(false);
+ try
+ {
+ await ModifyAudioInternalAsync(channelId, func, options).ConfigureAwait(false);
+ }
+ finally
+ {
+ _audioLock.Release();
+ }
+ }
+
+ private async Task ModifyAudioInternalAsync(ulong channelId, Action func, RequestOptions options)
+ {
+ if (_voiceStateUpdateParams == null || _voiceStateUpdateParams.ChannelId != channelId)
+ throw new InvalidOperationException("Cannot modify properties of not connected audio channel");
+
+ var props = new AudioChannelProperties();
+ func(props);
+
+ if (props.SelfDeaf.IsSpecified)
+ _voiceStateUpdateParams.SelfDeaf = props.SelfDeaf.Value;
+ if (props.SelfMute.IsSpecified)
+ _voiceStateUpdateParams.SelfMute = props.SelfMute.Value;
+
+ await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams, options).ConfigureAwait(false);
+ }
+
internal async Task FinishConnectAudio(string url, string token)
{
//TODO: Mem Leak: Disconnected/Connected handlers aren't cleaned up
diff --git a/test/Discord.Net.Tests.Unit/MockedEntities/MockedGroupChannel.cs b/test/Discord.Net.Tests.Unit/MockedEntities/MockedGroupChannel.cs
index 6b134d92f..3330b7bce 100644
--- a/test/Discord.Net.Tests.Unit/MockedEntities/MockedGroupChannel.cs
+++ b/test/Discord.Net.Tests.Unit/MockedEntities/MockedGroupChannel.cs
@@ -41,6 +41,11 @@ namespace Discord
throw new NotImplementedException();
}
+ public Task ModifyAsync(Action func, RequestOptions options)
+ {
+ throw new NotImplementedException();
+ }
+
public IDisposable EnterTypingState(RequestOptions options = null)
{
throw new NotImplementedException();
diff --git a/test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs b/test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs
index 4514dfc97..e053636a9 100644
--- a/test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs
+++ b/test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs
@@ -64,6 +64,11 @@ namespace Discord
throw new NotImplementedException();
}
+ public Task ModifyAsync(Action func, RequestOptions options)
+ {
+ throw new NotImplementedException();
+ }
+
public Task GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
throw new NotImplementedException();