Merged Discord.Net.Audio into the main project, starting adding IVoiceChannel.ConnectAsync()
This commit is contained in:
@@ -1,17 +0,0 @@
|
|||||||
using Discord.Net.WebSockets;
|
|
||||||
|
|
||||||
namespace Discord.Audio
|
|
||||||
{
|
|
||||||
public class AudioConfig
|
|
||||||
{
|
|
||||||
/// <summary> Gets or sets the time (in milliseconds) to wait for the websocket to connect and initialize. </summary>
|
|
||||||
public int ConnectionTimeout { get; set; } = 30000;
|
|
||||||
/// <summary> Gets or sets the time (in milliseconds) to wait after an unexpected disconnect before reconnecting. </summary>
|
|
||||||
public int ReconnectDelay { get; set; } = 1000;
|
|
||||||
/// <summary> Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying. </summary>
|
|
||||||
public int FailedReconnectDelay { get; set; } = 15000;
|
|
||||||
|
|
||||||
/// <summary> Gets or sets the provider used to generate new websocket connections. </summary>
|
|
||||||
public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="14.0.25123" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25123</VisualStudioVersion>
|
|
||||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<ProjectGuid>ddfcc44f-934e-478a-978c-69cdda2a1c5b</ProjectGuid>
|
|
||||||
<RootNamespace>Discord.Audio</RootNamespace>
|
|
||||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
|
||||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup>
|
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
|
||||||
</Project>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Discord.Audio
|
|
||||||
{
|
|
||||||
internal class Logger
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Discord
|
|
||||||
{
|
|
||||||
public class AsyncEvent<T>
|
|
||||||
{
|
|
||||||
private readonly object _subLock = new object();
|
|
||||||
internal ImmutableArray<T> _subscriptions;
|
|
||||||
|
|
||||||
public IReadOnlyList<T> Subscriptions => _subscriptions;
|
|
||||||
|
|
||||||
public AsyncEvent()
|
|
||||||
{
|
|
||||||
_subscriptions = ImmutableArray.Create<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(T subscriber)
|
|
||||||
{
|
|
||||||
lock (_subLock)
|
|
||||||
_subscriptions = _subscriptions.Add(subscriber);
|
|
||||||
}
|
|
||||||
public void Remove(T subscriber)
|
|
||||||
{
|
|
||||||
lock (_subLock)
|
|
||||||
_subscriptions = _subscriptions.Remove(subscriber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class EventExtensions
|
|
||||||
{
|
|
||||||
public static async Task InvokeAsync(this AsyncEvent<Func<Task>> eventHandler)
|
|
||||||
{
|
|
||||||
var subscribers = eventHandler.Subscriptions;
|
|
||||||
if (subscribers.Count > 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < subscribers.Count; i++)
|
|
||||||
await subscribers[i].Invoke().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static async Task InvokeAsync<T>(this AsyncEvent<Func<T, Task>> eventHandler, T arg)
|
|
||||||
{
|
|
||||||
var subscribers = eventHandler.Subscriptions;
|
|
||||||
for (int i = 0; i < subscribers.Count; i++)
|
|
||||||
await subscribers[i].Invoke(arg).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
public static async Task InvokeAsync<T1, T2>(this AsyncEvent<Func<T1, T2, Task>> eventHandler, T1 arg1, T2 arg2)
|
|
||||||
{
|
|
||||||
var subscribers = eventHandler.Subscriptions;
|
|
||||||
for (int i = 0; i < subscribers.Count; i++)
|
|
||||||
await subscribers[i].Invoke(arg1, arg2).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
public static async Task InvokeAsync<T1, T2, T3>(this AsyncEvent<Func<T1, T2, T3, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3)
|
|
||||||
{
|
|
||||||
var subscribers = eventHandler.Subscriptions;
|
|
||||||
for (int i = 0; i < subscribers.Count; i++)
|
|
||||||
await subscribers[i].Invoke(arg1, arg2, arg3).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
public static async Task InvokeAsync<T1, T2, T3, T4>(this AsyncEvent<Func<T1, T2, T3, T4, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
|
||||||
{
|
|
||||||
var subscribers = eventHandler.Subscriptions;
|
|
||||||
for (int i = 0; i < subscribers.Count; i++)
|
|
||||||
await subscribers[i].Invoke(arg1, arg2, arg3, arg4).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
public static async Task InvokeAsync<T1, T2, T3, T4, T5>(this AsyncEvent<Func<T1, T2, T3, T4, T5, Task>> eventHandler, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
|
|
||||||
{
|
|
||||||
var subscribers = eventHandler.Subscriptions;
|
|
||||||
for (int i = 0; i < subscribers.Count; i++)
|
|
||||||
await subscribers[i].Invoke(arg1, arg2, arg3, arg4, arg5).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "1.0.0-dev",
|
|
||||||
"description": "A Discord.Net extension adding audio support.",
|
|
||||||
"authors": [ "RogueException" ],
|
|
||||||
|
|
||||||
"packOptions": {
|
|
||||||
"tags": [ "discord", "discordapp" ],
|
|
||||||
"licenseUrl": "http://opensource.org/licenses/MIT",
|
|
||||||
"projectUrl": "https://github.com/RogueException/Discord.Net",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/RogueException/Discord.Net"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"buildOptions": {
|
|
||||||
"allowUnsafe": true,
|
|
||||||
"warningsAsErrors": false
|
|
||||||
},
|
|
||||||
|
|
||||||
"dependencies": {
|
|
||||||
"Discord.Net": "1.0.0-dev",
|
|
||||||
"System.Runtime.InteropServices": "4.1.0"
|
|
||||||
},
|
|
||||||
|
|
||||||
"frameworks": {
|
|
||||||
"netstandard1.3": {
|
|
||||||
"imports": [
|
|
||||||
"dotnet5.4",
|
|
||||||
"dnxcore50",
|
|
||||||
"portable-net45+win8"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -398,6 +398,17 @@ namespace Discord.API
|
|||||||
{
|
{
|
||||||
await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false);
|
await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
|
||||||
|
{
|
||||||
|
var payload = new VoiceStateUpdateParams
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
SelfDeaf = selfDeaf,
|
||||||
|
SelfMute = selfMute
|
||||||
|
};
|
||||||
|
await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
//Channels
|
//Channels
|
||||||
public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null)
|
public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Discord.Audio
|
namespace Discord.Audio
|
||||||
{
|
{
|
||||||
public class AudioAPIClient
|
public class DiscordVoiceAPIClient
|
||||||
{
|
{
|
||||||
public const int MaxBitrate = 128;
|
public const int MaxBitrate = 128;
|
||||||
private const string Mode = "xsalsa20_poly1305";
|
private const string Mode = "xsalsa20_poly1305";
|
||||||
@@ -40,7 +40,7 @@ namespace Discord.Audio
|
|||||||
public string SessionId { get; }
|
public string SessionId { get; }
|
||||||
public ConnectionState ConnectionState { get; private set; }
|
public ConnectionState ConnectionState { get; private set; }
|
||||||
|
|
||||||
internal AudioAPIClient(ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, JsonSerializer serializer = null)
|
internal DiscordVoiceAPIClient(ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, JsonSerializer serializer = null)
|
||||||
{
|
{
|
||||||
GuildId = guildId;
|
GuildId = guildId;
|
||||||
_userId = userId;
|
_userId = userId;
|
||||||
12
src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs
Normal file
12
src/Discord.Net/API/Gateway/GuildEmojiUpdateEvent.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Discord.API.Gateway
|
||||||
|
{
|
||||||
|
public class GuildEmojiUpdateEvent
|
||||||
|
{
|
||||||
|
[JsonProperty("guild_id")]
|
||||||
|
public ulong GuildId;
|
||||||
|
[JsonProperty("emojis")]
|
||||||
|
public Emoji[] Emojis;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Discord.API.Gateway
|
namespace Discord.API.Gateway
|
||||||
{
|
{
|
||||||
public class RequestMembersParams
|
public class RequestMembersParams
|
||||||
{
|
{
|
||||||
[JsonProperty("guild_id")]
|
|
||||||
public IEnumerable<ulong> GuildIds { get; set; }
|
|
||||||
[JsonProperty("query")]
|
[JsonProperty("query")]
|
||||||
public string Query { get; set; }
|
public string Query { get; set; }
|
||||||
[JsonProperty("limit")]
|
[JsonProperty("limit")]
|
||||||
public int Limit { get; set; }
|
public int Limit { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("guild_id")]
|
||||||
|
public IEnumerable<ulong> GuildIds { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
public IEnumerable<IGuild> Guilds { set { GuildIds = value.Select(x => x.Id); } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Discord.API.Gateway
|
|
||||||
{
|
|
||||||
public class UpdateVoiceParams
|
|
||||||
{
|
|
||||||
[JsonProperty("guild_id")]
|
|
||||||
public ulong? GuildId { get; set; }
|
|
||||||
[JsonProperty("channel_id")]
|
|
||||||
public ulong? ChannelId { get; set; }
|
|
||||||
[JsonProperty("self_mute")]
|
|
||||||
public bool IsSelfMuted { get; set; }
|
|
||||||
[JsonProperty("self_deaf")]
|
|
||||||
public bool IsSelfDeafened { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
21
src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs
Normal file
21
src/Discord.Net/API/Gateway/VoiceStateUpdateParams.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Discord.API.Gateway
|
||||||
|
{
|
||||||
|
public class VoiceStateUpdateParams
|
||||||
|
{
|
||||||
|
[JsonProperty("self_mute")]
|
||||||
|
public bool SelfMute { get; set; }
|
||||||
|
[JsonProperty("self_deaf")]
|
||||||
|
public bool SelfDeaf { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("guild_id")]
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
public IGuild Guild { set { GuildId = value.Id; } }
|
||||||
|
[JsonProperty("channel_id")]
|
||||||
|
public ulong? ChannelId { get; set; }
|
||||||
|
[JsonIgnore]
|
||||||
|
public IChannel Channel { set { ChannelId = value?.Id; } }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
using Discord.API.Voice;
|
using Discord.API.Voice;
|
||||||
using Discord.Logging;
|
using Discord.Logging;
|
||||||
using Discord.Net.Converters;
|
using Discord.Net.Converters;
|
||||||
|
using Discord.Net.WebSockets;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Discord.Audio
|
namespace Discord.Audio
|
||||||
{
|
{
|
||||||
public class AudioClient
|
internal class AudioClient : IAudioClient
|
||||||
{
|
{
|
||||||
public event Func<Task> Connected
|
public event Func<Task> Connected
|
||||||
{
|
{
|
||||||
@@ -30,12 +30,11 @@ namespace Discord.Audio
|
|||||||
}
|
}
|
||||||
private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>();
|
private readonly AsyncEvent<Func<int, int, Task>> _latencyUpdatedEvent = new AsyncEvent<Func<int, int, Task>>();
|
||||||
|
|
||||||
private readonly ILogger _webSocketLogger;
|
private readonly ILogger _webSocketLogger, _udpLogger;
|
||||||
#if BENCHMARK
|
#if BENCHMARK
|
||||||
private readonly ILogger _benchmarkLogger;
|
private readonly ILogger _benchmarkLogger;
|
||||||
#endif
|
#endif
|
||||||
private readonly JsonSerializer _serializer;
|
private readonly JsonSerializer _serializer;
|
||||||
private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay;
|
|
||||||
internal readonly SemaphoreSlim _connectionLock;
|
internal readonly SemaphoreSlim _connectionLock;
|
||||||
|
|
||||||
private TaskCompletionSource<bool> _connectTask;
|
private TaskCompletionSource<bool> _connectTask;
|
||||||
@@ -45,20 +44,18 @@ namespace Discord.Audio
|
|||||||
private bool _isReconnecting;
|
private bool _isReconnecting;
|
||||||
private string _url;
|
private string _url;
|
||||||
|
|
||||||
public AudioAPIClient ApiClient { get; private set; }
|
private DiscordSocketClient Discord { get; }
|
||||||
/// <summary> Gets the current connection state of this client. </summary>
|
public DiscordVoiceAPIClient ApiClient { get; private set; }
|
||||||
public ConnectionState ConnectionState { get; private set; }
|
public ConnectionState ConnectionState { get; private set; }
|
||||||
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
|
|
||||||
public int Latency { get; private set; }
|
public int Latency { get; private set; }
|
||||||
|
|
||||||
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
/// <summary> Creates a new REST/WebSocket discord client. </summary>
|
||||||
internal AudioClient(ulong guildId, ulong userId, string sessionId, string token, AudioConfig config, ILogManager logManager)
|
internal AudioClient(DiscordSocketClient discord, ulong guildId, ulong userId, string sessionId, string token, WebSocketProvider webSocketProvider, ILogManager logManager)
|
||||||
{
|
{
|
||||||
_connectionTimeout = config.ConnectionTimeout;
|
Discord = discord;
|
||||||
_reconnectDelay = config.ReconnectDelay;
|
|
||||||
_failedReconnectDelay = config.FailedReconnectDelay;
|
|
||||||
|
|
||||||
_webSocketLogger = logManager.CreateLogger("AudioWS");
|
_webSocketLogger = logManager.CreateLogger("Audio");
|
||||||
|
_udpLogger = logManager.CreateLogger("AudioUDP");
|
||||||
#if BENCHMARK
|
#if BENCHMARK
|
||||||
_benchmarkLogger = logManager.CreateLogger("Benchmark");
|
_benchmarkLogger = logManager.CreateLogger("Benchmark");
|
||||||
#endif
|
#endif
|
||||||
@@ -72,8 +69,7 @@ namespace Discord.Audio
|
|||||||
e.ErrorContext.Handled = true;
|
e.ErrorContext.Handled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var webSocketProvider = config.WebSocketProvider; //TODO: Clean this check
|
ApiClient = new DiscordVoiceAPIClient(guildId, userId, sessionId, token, webSocketProvider);
|
||||||
ApiClient = new AudioAPIClient(guildId, userId, sessionId, token, config.WebSocketProvider);
|
|
||||||
|
|
||||||
ApiClient.SentGatewayMessage += async opCode => await _webSocketLogger.DebugAsync($"Sent {(VoiceOpCode)opCode}").ConfigureAwait(false);
|
ApiClient.SentGatewayMessage += async opCode => await _webSocketLogger.DebugAsync($"Sent {(VoiceOpCode)opCode}").ConfigureAwait(false);
|
||||||
ApiClient.ReceivedEvent += ProcessMessageAsync;
|
ApiClient.ReceivedEvent += ProcessMessageAsync;
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
namespace Discord.Audio
|
using System;
|
||||||
|
|
||||||
|
namespace Discord.Audio
|
||||||
{
|
{
|
||||||
|
[Flags]
|
||||||
public enum AudioMode : byte
|
public enum AudioMode : byte
|
||||||
{
|
{
|
||||||
|
Disabled = 0,
|
||||||
Outgoing = 1,
|
Outgoing = 1,
|
||||||
Incoming = 2,
|
Incoming = 2,
|
||||||
Both = Outgoing | Incoming
|
Both = Outgoing | Incoming
|
||||||
20
src/Discord.Net/Audio/IAudioClient.cs
Normal file
20
src/Discord.Net/Audio/IAudioClient.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Discord.Audio
|
||||||
|
{
|
||||||
|
public interface IAudioClient
|
||||||
|
{
|
||||||
|
event Func<Task> Connected;
|
||||||
|
event Func<Task> Disconnected;
|
||||||
|
event Func<int, int, Task> LatencyUpdated;
|
||||||
|
|
||||||
|
DiscordVoiceAPIClient ApiClient { get; }
|
||||||
|
/// <summary> Gets the current connection state of this client. </summary>
|
||||||
|
ConnectionState ConnectionState { get; }
|
||||||
|
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
|
||||||
|
int Latency { get; }
|
||||||
|
|
||||||
|
Task DisconnectAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ namespace Discord.Audio.Opus
|
|||||||
{
|
{
|
||||||
if (channels != 1 && channels != 2)
|
if (channels != 1 && channels != 2)
|
||||||
throw new ArgumentOutOfRangeException(nameof(channels));
|
throw new ArgumentOutOfRangeException(nameof(channels));
|
||||||
if (bitrate != null && (bitrate < 1 || bitrate > AudioAPIClient.MaxBitrate))
|
if (bitrate != null && (bitrate < 1 || bitrate > DiscordVoiceAPIClient.MaxBitrate))
|
||||||
throw new ArgumentOutOfRangeException(nameof(bitrate));
|
throw new ArgumentOutOfRangeException(nameof(bitrate));
|
||||||
|
|
||||||
OpusError error;
|
OpusError error;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Discord.Net.Audio
|
namespace Discord.Net.Audio.Sodium
|
||||||
{
|
{
|
||||||
public unsafe static class LibSodium
|
public unsafe static class SecretBox
|
||||||
{
|
{
|
||||||
[DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("libsodium", EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern int SecretBoxEasy(byte* output, byte[] input, long inputLength, byte[] nonce, byte[] secret);
|
private static extern int SecretBoxEasy(byte* output, byte[] input, long inputLength, byte[] nonce, byte[] secret);
|
||||||
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
|
//TODO: Add event docstrings
|
||||||
public partial class DiscordSocketClient
|
public partial class DiscordSocketClient
|
||||||
{
|
{
|
||||||
//General
|
//General
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Discord.API.Gateway;
|
using Discord.API.Gateway;
|
||||||
|
using Discord.Audio;
|
||||||
using Discord.Extensions;
|
using Discord.Extensions;
|
||||||
using Discord.Logging;
|
using Discord.Logging;
|
||||||
using Discord.Net.Converters;
|
using Discord.Net.Converters;
|
||||||
|
using Discord.Net.WebSockets;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
@@ -14,9 +16,6 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
//TODO: Add event docstrings
|
|
||||||
//TODO: Add reconnect logic (+ensure the heartbeat task to shut down)
|
|
||||||
//TODO: Add resume logic
|
|
||||||
public partial class DiscordSocketClient : DiscordClient, IDiscordClient
|
public partial class DiscordSocketClient : DiscordClient, IDiscordClient
|
||||||
{
|
{
|
||||||
private readonly ConcurrentQueue<ulong> _largeGuilds;
|
private readonly ConcurrentQueue<ulong> _largeGuilds;
|
||||||
@@ -25,9 +24,6 @@ namespace Discord
|
|||||||
private readonly ILogger _benchmarkLogger;
|
private readonly ILogger _benchmarkLogger;
|
||||||
#endif
|
#endif
|
||||||
private readonly JsonSerializer _serializer;
|
private readonly JsonSerializer _serializer;
|
||||||
private readonly int _connectionTimeout, _reconnectDelay, _failedReconnectDelay;
|
|
||||||
private readonly int _largeThreshold;
|
|
||||||
private readonly int _totalShards;
|
|
||||||
|
|
||||||
private string _sessionId;
|
private string _sessionId;
|
||||||
private int _lastSeq;
|
private int _lastSeq;
|
||||||
@@ -46,9 +42,17 @@ namespace Discord
|
|||||||
public ConnectionState ConnectionState { get; private set; }
|
public ConnectionState ConnectionState { get; private set; }
|
||||||
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
|
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
|
||||||
public int Latency { get; private set; }
|
public int Latency { get; private set; }
|
||||||
|
|
||||||
|
//From DiscordConfig
|
||||||
|
internal int TotalShards { get; private set; }
|
||||||
|
internal int ConnectionTimeout { get; private set; }
|
||||||
|
internal int ReconnectDelay { get; private set; }
|
||||||
|
internal int FailedReconnectDelay { get; private set; }
|
||||||
internal int MessageCacheSize { get; private set; }
|
internal int MessageCacheSize { get; private set; }
|
||||||
|
internal int LargeThreshold { get; private set; }
|
||||||
|
internal AudioMode AudioMode { get; private set; }
|
||||||
internal DataStore DataStore { get; private set; }
|
internal DataStore DataStore { get; private set; }
|
||||||
|
internal WebSocketProvider WebSocketProvider { get; private set; }
|
||||||
|
|
||||||
internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser;
|
internal CachedSelfUser CurrentUser => _currentUser as CachedSelfUser;
|
||||||
internal IReadOnlyCollection<CachedGuild> Guilds => DataStore.Guilds;
|
internal IReadOnlyCollection<CachedGuild> Guilds => DataStore.Guilds;
|
||||||
@@ -62,15 +66,15 @@ namespace Discord
|
|||||||
: base(config)
|
: base(config)
|
||||||
{
|
{
|
||||||
ShardId = config.ShardId;
|
ShardId = config.ShardId;
|
||||||
_totalShards = config.TotalShards;
|
TotalShards = config.TotalShards;
|
||||||
|
ConnectionTimeout = config.ConnectionTimeout;
|
||||||
_connectionTimeout = config.ConnectionTimeout;
|
ReconnectDelay = config.ReconnectDelay;
|
||||||
_reconnectDelay = config.ReconnectDelay;
|
FailedReconnectDelay = config.FailedReconnectDelay;
|
||||||
_failedReconnectDelay = config.FailedReconnectDelay;
|
|
||||||
|
|
||||||
MessageCacheSize = config.MessageCacheSize;
|
MessageCacheSize = config.MessageCacheSize;
|
||||||
_largeThreshold = config.LargeThreshold;
|
LargeThreshold = config.LargeThreshold;
|
||||||
|
AudioMode = config.AudioMode;
|
||||||
|
WebSocketProvider = config.WebSocketProvider;
|
||||||
|
|
||||||
_gatewayLogger = _log.CreateLogger("Gateway");
|
_gatewayLogger = _log.CreateLogger("Gateway");
|
||||||
#if BENCHMARK
|
#if BENCHMARK
|
||||||
_benchmarkLogger = _log.CreateLogger("Benchmark");
|
_benchmarkLogger = _log.CreateLogger("Benchmark");
|
||||||
@@ -471,7 +475,7 @@ namespace Discord
|
|||||||
case GatewayOpCode.Dispatch:
|
case GatewayOpCode.Dispatch:
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
//Global
|
//Connection
|
||||||
case "READY":
|
case "READY":
|
||||||
{
|
{
|
||||||
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
|
||||||
@@ -507,6 +511,11 @@ namespace Discord
|
|||||||
await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false);
|
await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "RESUMED":
|
||||||
|
await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false);
|
||||||
|
|
||||||
|
await _gatewayLogger.InfoAsync("Resume").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
|
||||||
//Guilds
|
//Guilds
|
||||||
case "GUILD_CREATE":
|
case "GUILD_CREATE":
|
||||||
@@ -569,6 +578,28 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "GUILD_EMOJI_UPDATE": //TODO: Add
|
||||||
|
{
|
||||||
|
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_EMOJI_UPDATE)").ConfigureAwait(false);
|
||||||
|
|
||||||
|
var data = (payload as JToken).ToObject<API.Gateway.GuildEmojiUpdateEvent>(_serializer);
|
||||||
|
var guild = DataStore.GetGuild(data.GuildId);
|
||||||
|
if (guild != null)
|
||||||
|
{
|
||||||
|
var before = guild.Clone();
|
||||||
|
guild.Update(data, UpdateSource.WebSocket);
|
||||||
|
await _guildUpdatedEvent.InvokeAsync(before, guild).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _gatewayLogger.WarningAsync("GUILD_EMOJI_UPDATE referenced an unknown guild.").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case "GUILD_INTEGRATIONS_UPDATE":
|
||||||
|
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
case "GUILD_DELETE":
|
case "GUILD_DELETE":
|
||||||
{
|
{
|
||||||
var data = (payload as JToken).ToObject<ExtendedGuild>(_serializer);
|
var data = (payload as JToken).ToObject<ExtendedGuild>(_serializer);
|
||||||
@@ -1099,26 +1130,17 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "VOICE_SERVER_UPDATE":
|
||||||
|
await _gatewayLogger.DebugAsync("Ignored Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
|
||||||
//Ignored
|
//Ignored (User only)
|
||||||
case "USER_SETTINGS_UPDATE":
|
case "USER_SETTINGS_UPDATE":
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
case "MESSAGE_ACK": //TODO: Add (User only)
|
case "MESSAGE_ACK":
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false);
|
await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
case "GUILD_EMOJIS_UPDATE": //TODO: Add
|
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_EMOJIS_UPDATE)").ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
case "GUILD_INTEGRATIONS_UPDATE": //TODO: Add
|
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
case "VOICE_SERVER_UPDATE": //TODO: Add
|
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
case "RESUMED": //TODO: Add
|
|
||||||
await _gatewayLogger.DebugAsync("Ignored Dispatch (RESUMED)").ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
|
|
||||||
//Others
|
//Others
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Discord.Net.WebSockets;
|
using Discord.Audio;
|
||||||
|
using Discord.Net.WebSockets;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
@@ -28,7 +29,10 @@ namespace Discord
|
|||||||
/// Decreasing this may reduce CPU usage while increasing login time and network usage.
|
/// Decreasing this may reduce CPU usage while increasing login time and network usage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int LargeThreshold { get; set; } = 250;
|
public int LargeThreshold { get; set; } = 250;
|
||||||
|
|
||||||
|
/// <summary> Gets or sets the type of audio this DiscordClient supports. </summary>
|
||||||
|
public AudioMode AudioMode { get; set; } = AudioMode.Disabled;
|
||||||
|
|
||||||
/// <summary> Gets or sets the provider used to generate new websocket connections. </summary>
|
/// <summary> Gets or sets the provider used to generate new websocket connections. </summary>
|
||||||
public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient();
|
public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Discord.API.Rest;
|
using Discord.API.Rest;
|
||||||
|
using Discord.Audio;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -13,5 +14,7 @@ namespace Discord
|
|||||||
|
|
||||||
/// <summary> Modifies this voice channel. </summary>
|
/// <summary> Modifies this voice channel. </summary>
|
||||||
Task ModifyAsync(Action<ModifyVoiceChannelParams> func);
|
Task ModifyAsync(Action<ModifyVoiceChannelParams> func);
|
||||||
|
/// <summary> Connects to this voice channel. </summary>
|
||||||
|
Task<IAudioClient> ConnectAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Model = Discord.API.Channel;
|
using Model = Discord.API.Channel;
|
||||||
|
using Discord.Audio;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
@@ -49,6 +50,8 @@ namespace Discord
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual Task<IAudioClient> ConnectAsync() { throw new NotSupportedException(); }
|
||||||
|
|
||||||
private string DebuggerDisplay => $"{Name} ({Id}, Voice)";
|
private string DebuggerDisplay => $"{Name} ({Id}, Voice)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Discord.API.Rest;
|
using Discord.API.Rest;
|
||||||
|
using Discord.Audio;
|
||||||
using Discord.Extensions;
|
using Discord.Extensions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -311,6 +312,7 @@ namespace Discord
|
|||||||
IReadOnlyCollection<Emoji> IGuild.Emojis => Emojis;
|
IReadOnlyCollection<Emoji> IGuild.Emojis => Emojis;
|
||||||
IReadOnlyCollection<string> IGuild.Features => Features;
|
IReadOnlyCollection<string> IGuild.Features => Features;
|
||||||
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }
|
Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); }
|
||||||
|
IAudioClient IGuild.AudioClient => null;
|
||||||
|
|
||||||
IRole IGuild.GetRole(ulong id) => GetRole(id);
|
IRole IGuild.GetRole(ulong id) => GetRole(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Discord.API.Rest;
|
using Discord.API.Rest;
|
||||||
|
using Discord.Audio;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
@@ -37,6 +38,8 @@ namespace Discord
|
|||||||
/// <summary> Gets the id of the region hosting this guild's voice channels. </summary>
|
/// <summary> Gets the id of the region hosting this guild's voice channels. </summary>
|
||||||
string VoiceRegionId { get; }
|
string VoiceRegionId { get; }
|
||||||
|
|
||||||
|
/// <summary> Gets the IAudioClient currently associated with this guild. </summary>
|
||||||
|
IAudioClient AudioClient { get; }
|
||||||
/// <summary> Gets the built-in role containing all users in this guild. </summary>
|
/// <summary> Gets the built-in role containing all users in this guild. </summary>
|
||||||
IRole EveryoneRole { get; }
|
IRole EveryoneRole { get; }
|
||||||
/// <summary> Gets a collection of all custom emojis for this guild. </summary>
|
/// <summary> Gets a collection of all custom emojis for this guild. </summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Discord.Extensions;
|
using Discord.Audio;
|
||||||
|
using Discord.Extensions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -6,6 +7,7 @@ using System.Collections.Immutable;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ChannelModel = Discord.API.Channel;
|
using ChannelModel = Discord.API.Channel;
|
||||||
|
using EmojiUpdateModel = Discord.API.Gateway.GuildEmojiUpdateEvent;
|
||||||
using ExtendedModel = Discord.API.Gateway.ExtendedGuild;
|
using ExtendedModel = Discord.API.Gateway.ExtendedGuild;
|
||||||
using MemberModel = Discord.API.GuildMember;
|
using MemberModel = Discord.API.GuildMember;
|
||||||
using Model = Discord.API.Guild;
|
using Model = Discord.API.Guild;
|
||||||
@@ -25,6 +27,7 @@ namespace Discord
|
|||||||
public bool Available { get; private set; }
|
public bool Available { get; private set; }
|
||||||
public int MemberCount { get; private set; }
|
public int MemberCount { get; private set; }
|
||||||
public int DownloadedMemberCount { get; private set; }
|
public int DownloadedMemberCount { get; private set; }
|
||||||
|
public IAudioClient AudioClient { get; private set; }
|
||||||
|
|
||||||
public bool HasAllMembers => _downloaderPromise.Task.IsCompleted;
|
public bool HasAllMembers => _downloaderPromise.Task.IsCompleted;
|
||||||
public Task DownloaderPromise => _downloaderPromise.Task;
|
public Task DownloaderPromise => _downloaderPromise.Task;
|
||||||
@@ -102,6 +105,16 @@ namespace Discord
|
|||||||
_voiceStates = voiceStates;
|
_voiceStates = voiceStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Update(EmojiUpdateModel model, UpdateSource source)
|
||||||
|
{
|
||||||
|
if (source == UpdateSource.Rest && IsAttached) return;
|
||||||
|
|
||||||
|
var emojis = ImmutableArray.CreateBuilder<Emoji>(model.Emojis.Length);
|
||||||
|
for (int i = 0; i < model.Emojis.Length; i++)
|
||||||
|
emojis.Add(new Emoji(model.Emojis[i]));
|
||||||
|
Emojis = emojis.ToImmutableArray();
|
||||||
|
}
|
||||||
|
|
||||||
public override Task<IGuildChannel> GetChannelAsync(ulong id) => Task.FromResult<IGuildChannel>(GetChannel(id));
|
public override Task<IGuildChannel> GetChannelAsync(ulong id) => Task.FromResult<IGuildChannel>(GetChannel(id));
|
||||||
public override Task<IReadOnlyCollection<IGuildChannel>> GetChannelsAsync() => Task.FromResult<IReadOnlyCollection<IGuildChannel>>(Channels);
|
public override Task<IReadOnlyCollection<IGuildChannel>> GetChannelsAsync() => Task.FromResult<IReadOnlyCollection<IGuildChannel>>(Channels);
|
||||||
public void AddChannel(ChannelModel model, DataStore dataStore, ConcurrentHashSet<ulong> channels = null)
|
public void AddChannel(ChannelModel model, DataStore dataStore, ConcurrentHashSet<ulong> channels = null)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using Discord.Audio;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -33,6 +35,19 @@ namespace Discord
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<IAudioClient> ConnectAsync()
|
||||||
|
{
|
||||||
|
var audioMode = Discord.AudioMode;
|
||||||
|
if (audioMode == AudioMode.Disabled)
|
||||||
|
throw new InvalidOperationException($"Audio is not enabled on this client, {nameof(DiscordSocketConfig.AudioMode)} in {nameof(DiscordSocketConfig)} must be set.");
|
||||||
|
|
||||||
|
await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, Id,
|
||||||
|
(audioMode & AudioMode.Incoming) == 0,
|
||||||
|
(audioMode & AudioMode.Outgoing) == 0).ConfigureAwait(false);
|
||||||
|
return null;
|
||||||
|
//TODO: Block and return
|
||||||
|
}
|
||||||
|
|
||||||
public CachedVoiceChannel Clone() => MemberwiseClone() as CachedVoiceChannel;
|
public CachedVoiceChannel Clone() => MemberwiseClone() as CachedVoiceChannel;
|
||||||
|
|
||||||
ICachedChannel ICachedChannel.Clone() => Clone();
|
ICachedChannel ICachedChannel.Clone() => Clone();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"System.Net.Http": "4.1.0",
|
"System.Net.Http": "4.1.0",
|
||||||
"System.Net.WebSockets.Client": "4.0.0",
|
"System.Net.WebSockets.Client": "4.0.0",
|
||||||
"System.Reflection.Extensions": "4.0.1",
|
"System.Reflection.Extensions": "4.0.1",
|
||||||
|
"System.Runtime.InteropServices": "4.1.0",
|
||||||
"System.Runtime.Serialization.Primitives": "4.1.1",
|
"System.Runtime.Serialization.Primitives": "4.1.1",
|
||||||
"System.Text.RegularExpressions": "4.1.0"
|
"System.Text.RegularExpressions": "4.1.0"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user