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 Discord.Audio;
|
||||||
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
@@ -27,5 +28,16 @@ namespace Discord
|
|||||||
/// A task representing the asynchronous operation for disconnecting from the audio channel.
|
/// A task representing the asynchronous operation for disconnecting from the audio channel.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
Task DisconnectAsync();
|
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>
|
/// <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<IAudioClient> IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
|
||||||
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
|
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
|
||||||
|
Task IAudioChannel.ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options) { throw new NotSupportedException(); }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IChannel
|
#region IChannel
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ namespace Discord.Rest
|
|||||||
/// <exception cref="NotSupportedException">Connecting to a REST-based channel is not supported.</exception>
|
/// <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<IAudioClient> IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
|
||||||
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
|
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
|
||||||
|
Task IAudioChannel.ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options) { throw new NotSupportedException(); }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IGuildChannel
|
#region IGuildChannel
|
||||||
|
|||||||
@@ -311,7 +311,6 @@ namespace Discord.API
|
|||||||
}
|
}
|
||||||
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
|
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
|
||||||
{
|
{
|
||||||
options = RequestOptions.CreateOrClone(options);
|
|
||||||
var payload = new VoiceStateUpdateParams
|
var payload = new VoiceStateUpdateParams
|
||||||
{
|
{
|
||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
@@ -319,6 +318,12 @@ namespace Discord.API
|
|||||||
SelfDeaf = selfDeaf,
|
SelfDeaf = selfDeaf,
|
||||||
SelfMute = selfMute
|
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);
|
await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
public async Task SendGuildSyncAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
|
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>
|
/// <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<IAudioClient> IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); }
|
||||||
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
|
Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); }
|
||||||
|
Task IAudioChannel.ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options) { throw new NotSupportedException(); }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region IChannel
|
#region IChannel
|
||||||
|
|||||||
@@ -82,6 +82,12 @@ namespace Discord.WebSocket
|
|||||||
public async Task DisconnectAsync()
|
public async Task DisconnectAsync()
|
||||||
=> await Guild.DisconnectAudioAsync();
|
=> await Guild.DisconnectAudioAsync();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options = null)
|
||||||
|
{
|
||||||
|
await Guild.ModifyAudioAsync(Id, func, options).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override SocketGuildUser GetUser(ulong id)
|
public override SocketGuildUser GetUser(ulong id)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Discord.API.Gateway;
|
||||||
using Discord.Audio;
|
using Discord.Audio;
|
||||||
using Discord.Rest;
|
using Discord.Rest;
|
||||||
using System;
|
using System;
|
||||||
@@ -45,6 +46,7 @@ namespace Discord.WebSocket
|
|||||||
private ImmutableArray<GuildEmote> _emotes;
|
private ImmutableArray<GuildEmote> _emotes;
|
||||||
|
|
||||||
private AudioClient _audioClient;
|
private AudioClient _audioClient;
|
||||||
|
private VoiceStateUpdateParams _voiceStateUpdateParams;
|
||||||
#pragma warning restore IDISP002, IDISP006
|
#pragma warning restore IDISP002, IDISP006
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -1593,11 +1595,19 @@ namespace Discord.WebSocket
|
|||||||
promise = new TaskCompletionSource<AudioClient>();
|
promise = new TaskCompletionSource<AudioClient>();
|
||||||
_audioConnectPromise = promise;
|
_audioConnectPromise = promise;
|
||||||
|
|
||||||
|
_voiceStateUpdateParams = new VoiceStateUpdateParams
|
||||||
|
{
|
||||||
|
GuildId = Id,
|
||||||
|
ChannelId = channelId,
|
||||||
|
SelfDeaf = selfDeaf,
|
||||||
|
SelfMute = selfMute
|
||||||
|
};
|
||||||
|
|
||||||
if (external)
|
if (external)
|
||||||
{
|
{
|
||||||
#pragma warning disable IDISP001
|
#pragma warning disable IDISP001
|
||||||
var _ = promise.TrySetResultAsync(null);
|
var _ = promise.TrySetResultAsync(null);
|
||||||
await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false);
|
await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams).ConfigureAwait(false);
|
||||||
return null;
|
return null;
|
||||||
#pragma warning restore IDISP001
|
#pragma warning restore IDISP001
|
||||||
}
|
}
|
||||||
@@ -1632,7 +1642,7 @@ namespace Discord.WebSocket
|
|||||||
#pragma warning restore IDISP003
|
#pragma warning restore IDISP003
|
||||||
}
|
}
|
||||||
|
|
||||||
await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false);
|
await Discord.ApiClient.SendVoiceStateUpdateAsync(_voiceStateUpdateParams).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -1679,7 +1689,38 @@ namespace Discord.WebSocket
|
|||||||
await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, null, false, false).ConfigureAwait(false);
|
await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, null, false, false).ConfigureAwait(false);
|
||||||
_audioClient?.Dispose();
|
_audioClient?.Dispose();
|
||||||
_audioClient = null;
|
_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)
|
internal async Task FinishConnectAudio(string url, string token)
|
||||||
{
|
{
|
||||||
//TODO: Mem Leak: Disconnected/Connected handlers aren't cleaned up
|
//TODO: Mem Leak: Disconnected/Connected handlers aren't cleaned up
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ namespace Discord
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task ModifyAsync(Action<AudioChannelProperties> func, RequestOptions options)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public IDisposable EnterTypingState(RequestOptions options = null)
|
public IDisposable EnterTypingState(RequestOptions options = null)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ namespace Discord
|
|||||||
throw new NotImplementedException();
|
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)
|
public Task<ICategoryChannel> GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|||||||
Reference in New Issue
Block a user