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:
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user