Browse Source

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>
tags/3.0.0
ppaneksamsung GitHub 3 years ago
parent
commit
a4b0c4f1e3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 98 additions and 3 deletions
  1. +18
    -0
      src/Discord.Net.Core/Entities/Channels/AudioChannelProperties.cs
  2. +12
    -0
      src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs
  3. +1
    -0
      src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
  4. +1
    -0
      src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
  5. +6
    -1
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
  6. +1
    -0
      src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
  7. +6
    -0
      src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
  8. +43
    -2
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  9. +5
    -0
      test/Discord.Net.Tests.Unit/MockedEntities/MockedGroupChannel.cs
  10. +5
    -0
      test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs

+ 18
- 0
src/Discord.Net.Core/Entities/Channels/AudioChannelProperties.cs 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; }
}
}

+ 12
- 0
src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs 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);
}
}

+ 1
- 0
src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs 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


+ 1
- 0
src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs 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


+ 6
- 1
src/Discord.Net.WebSocket/DiscordSocketApiClient.cs 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)


+ 1
- 0
src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs 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


+ 6
- 0
src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs 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)
{


+ 43
- 2
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs 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


+ 5
- 0
test/Discord.Net.Tests.Unit/MockedEntities/MockedGroupChannel.cs 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();


+ 5
- 0
test/Discord.Net.Tests.Unit/MockedEntities/MockedVoiceChannel.cs 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();


Loading…
Cancel
Save