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 <lynchquin@gmail.com>
This commit is contained in:
ppaneksamsung
2021-12-19 08:42:08 +01:00
committed by GitHub
parent 6c7502da68
commit a4b0c4f1e3
10 changed files with 98 additions and 3 deletions

View File

@@ -0,0 +1,18 @@
namespace Discord
{
/// <summary>
/// Provides properties that are used to modify an <see cref="IAudioChannel" /> with the specified changes.
/// </summary>
public class AudioChannelProperties
{
/// <summary>
/// Sets whether the user should be muted.
/// </summary>
public Optional<bool> SelfMute { get; set; }
/// <summary>
/// Sets whether the user should be deafened.
/// </summary>
public Optional<bool> SelfDeaf { get; set; }
}
}

View File

@@ -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.
/// </returns>
Task DisconnectAsync();
/// <summary>
/// Modifies this audio channel.
/// </summary>
/// <param name="func">The properties to modify the channel with.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous modification operation.
/// </returns>
/// <seealso cref="AudioChannelProperties"/>
Task ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options = null);
}
}

View File

@@ -211,6 +211,7 @@ namespace Discord.Rest
/// <exception cref="NotSupportedException">Connecting to a group channel is not supported.</exception>
Task<IAudioClient> IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
Task IAudioChannel.ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options) { throw new NotSupportedException(); }
#endregion
#region IChannel

View File

@@ -95,6 +95,7 @@ namespace Discord.Rest
/// <exception cref="NotSupportedException">Connecting to a REST-based channel is not supported.</exception>
Task<IAudioClient> IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
Task IAudioChannel.ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options) { throw new NotSupportedException(); }
#endregion
#region IGuildChannel

View File

@@ -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<ulong> guildIds, RequestOptions options = null)

View File

@@ -344,6 +344,7 @@ namespace Discord.WebSocket
/// <exception cref="NotSupportedException">Connecting to a group channel is not supported.</exception>
Task<IAudioClient> IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
Task IAudioChannel.ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options) { throw new NotSupportedException(); }
#endregion
#region IChannel

View File

@@ -82,6 +82,12 @@ namespace Discord.WebSocket
public async Task DisconnectAsync()
=> await Guild.DisconnectAudioAsync();
/// <inheritdoc />
public async Task ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options = null)
{
await Guild.ModifyAudioAsync(Id, func, options).ConfigureAwait(false);
}
/// <inheritdoc />
public override SocketGuildUser GetUser(ulong id)
{

View File

@@ -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<GuildEmote> _emotes;
private AudioClient _audioClient;
private VoiceStateUpdateParams _voiceStateUpdateParams;
#pragma warning restore IDISP002, IDISP006
/// <inheritdoc />
@@ -1593,11 +1595,19 @@ namespace Discord.WebSocket
promise = new TaskCompletionSource<AudioClient>();
_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<AudioChannelProperties> 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<AudioChannelProperties> 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

View File

@@ -41,6 +41,11 @@ namespace Discord
throw new NotImplementedException();
}
public Task ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options)
{
throw new NotImplementedException();
}
public IDisposable EnterTypingState(RequestOptions options = null)
{
throw new NotImplementedException();

View File

@@ -64,6 +64,11 @@ namespace Discord
throw new NotImplementedException();
}
public Task ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options)
{
throw new NotImplementedException();
}
public Task<ICategoryChannel> GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
{
throw new NotImplementedException();