* 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>
tags/3.0.0
@@ -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(); | ||||