From 53397978e0cf880f8fd6191b4cfb7b1962426806 Mon Sep 17 00:00:00 2001
From: Pawel Panek
Date: Wed, 7 Jul 2021 14:04:33 +0200
Subject: [PATCH] Add IAudioChannel.ModifyAsync API
---
.../Channels/AudioChannelProperties.cs | 18 ++++++++
.../Entities/Channels/IAudioChannel.cs | 12 +++++
.../Entities/Channels/RestGroupChannel.cs | 1 +
.../Entities/Channels/RestVoiceChannel.cs | 1 +
.../DiscordSocketApiClient.cs | 6 ++-
.../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, 97 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 2b0ab8b42..89665eefe 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
@@ -196,6 +196,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(); }
//IChannel
Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
index 3f3aa96c6..9d62938a8 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
@@ -76,6 +76,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(); }
//IGuildChannel
///
diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
index 65fd23d3f..8379e1caf 100644
--- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
+++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
@@ -284,7 +284,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,
@@ -294,6 +293,11 @@ namespace Discord.API
};
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)
{
options = RequestOptions.CreateOrClone(options);
diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
index ab8c76aeb..fa274cf8b 100644
--- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
+++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
@@ -311,6 +311,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(); }
//IChannel
///
diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
index bf4a63c9f..27820372f 100644
--- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
+++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
@@ -76,6 +76,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 9af4ad57e..0153ad833 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;
@@ -39,6 +40,7 @@ namespace Discord.WebSocket
private ImmutableArray _emotes;
private ImmutableArray _features;
private AudioClient _audioClient;
+ private VoiceStateUpdateParams _voiceStateUpdateParams;
#pragma warning restore IDISP002, IDISP006
///
@@ -1054,11 +1056,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
}
@@ -1093,7 +1103,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
{
@@ -1140,7 +1150,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 arent cleaned up
diff --git a/test/Discord.Net.Tests.Unit/MockedEntities/MockedGroupChannel.cs b/test/Discord.Net.Tests.Unit/MockedEntities/MockedGroupChannel.cs
index 6daf6a9c8..007dd0409 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 eb617125d..55066a82b 100644
--- a/test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs
+++ b/test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs
@@ -58,6 +58,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();