diff --git a/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj b/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
index 280dc619e..99cfdcc0b 100644
--- a/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
+++ b/src/Discord.Net.Audio.Net45/Discord.Net.Audio.csproj
@@ -62,8 +62,8 @@
InternalIsSpeakingEventArgs.cs
-
- Net\VoiceWebSocket.cs
+
+ Net\VoiceSocket.cs
Opus\OpusConverter.cs
@@ -74,15 +74,15 @@
Opus\OpusEncoder.cs
-
- SimpleAudioClient.cs
-
Sodium\SecretBox.cs
UserIsTalkingEventArgs.cs
+
+ VirtualClient.cs
+
VoiceBuffer.cs
diff --git a/src/Discord.Net.Audio/AudioClient.cs b/src/Discord.Net.Audio/AudioClient.cs
index 9657b89a8..715a565d2 100644
--- a/src/Discord.Net.Audio/AudioClient.cs
+++ b/src/Discord.Net.Audio/AudioClient.cs
@@ -1,9 +1,12 @@
using Discord.API.Client.GatewaySocket;
+using Discord.API.Client.Rest;
using Discord.Logging;
+using Discord.Net.Rest;
using Discord.Net.WebSockets;
using Newtonsoft.Json;
using Nito.AsyncEx;
using System;
+using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -41,124 +44,190 @@ namespace Discord.Audio
}
}
+ private readonly DiscordConfig _config;
private readonly AsyncLock _connectionLock;
- private readonly JsonSerializer _serializer;
- private CancellationTokenSource _cancelTokenSource;
+ private readonly TaskManager _taskManager;
+ private ConnectionState _gatewayState;
- internal AudioService Service { get; }
internal Logger Logger { get; }
+
+ /// Gets the unique identifier for this client.
public int Id { get; }
+ /// Gets the service managing this client.
+ public AudioService Service { get; }
+ /// Gets the configuration object used to make this client.
+ public AudioServiceConfig Config { get; }
+ /// Gets the internal RestClient for the Client API endpoint.
+ public RestClient ClientAPI { get; }
+ /// Gets the internal WebSocket for the Gateway event stream.
public GatewaySocket GatewaySocket { get; }
- public VoiceWebSocket VoiceSocket { get; }
+ /// Gets the internal WebSocket for the Voice control stream.
+ public VoiceSocket VoiceSocket { get; }
+ /// Gets the JSON serializer used by this client.
+ public JsonSerializer Serializer { get; }
+ ///
public Stream OutputStream { get; }
+ /// Gets a cancellation token that triggers when the client is manually disconnected.
+ public CancellationToken CancelToken { get; private set; }
+ /// Gets the session id for the current connection.
+ public string SessionId { get; private set; }
+
+ /// Gets the current state of this client.
public ConnectionState State => VoiceSocket.State;
+ /// Gets the server this client is bound to.
public Server Server => VoiceSocket.Server;
+ /// Gets the channel
public Channel Channel => VoiceSocket.Channel;
- public AudioClient(AudioService service, int clientId, Server server, GatewaySocket gatewaySocket, Logger logger)
+ public AudioClient(DiscordClient client, Server server, int id)
{
- Service = service;
- _serializer = service.Client.Serializer;
- Id = clientId;
- GatewaySocket = gatewaySocket;
- Logger = logger;
- OutputStream = new OutStream(this);
+ Id = id;
+ _config = client.Config;
+ Service = client.Audio();
+ Config = Service.Config;
+ Serializer = client.Serializer;
+ _gatewayState = (int)ConnectionState.Disconnected;
- _connectionLock = new AsyncLock();
+ //Logging
+ Logger = client.Log.CreateLogger($"AudioClient #{id}");
- GatewaySocket.ReceivedDispatch += OnReceivedDispatch;
+ //Async
+ _taskManager = new TaskManager(Cleanup, false);
+ _connectionLock = new AsyncLock();
+ CancelToken = new CancellationToken(true);
- VoiceSocket = new VoiceWebSocket(service.Client, this, logger);
+ //Networking
+ if (Config.EnableMultiserver)
+ {
+ ClientAPI = new RestClient(_config, DiscordConfig.ClientAPIUrl, client.Log.CreateLogger($"ClientAPI #{id}"));
+ GatewaySocket = new GatewaySocket(_config, client.Serializer, client.Log.CreateLogger($"Gateway #{id}"));
+ GatewaySocket.Connected += (s, e) =>
+ {
+ if (_gatewayState == ConnectionState.Connecting)
+ EndGatewayConnect();
+ };
+ }
+ else
+ GatewaySocket = client.GatewaySocket;
+ GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e);
+ VoiceSocket = new VoiceSocket(_config, Config, client.Serializer, client.Log.CreateLogger($"Voice #{id}"));
VoiceSocket.Server = server;
-
- /*_voiceSocket.Connected += (s, e) => RaiseVoiceConnected();
- _voiceSocket.Disconnected += async (s, e) =>
- {
- _voiceSocket.CurrentServerId;
- if (voiceServerId != null)
- _gatewaySocket.SendLeaveVoice(voiceServerId.Value);
- await _voiceSocket.Disconnect().ConfigureAwait(false);
- RaiseVoiceDisconnected(socket.CurrentServerId.Value, e);
- if (e.WasUnexpected)
- await socket.Reconnect().ConfigureAwait(false);
- };*/
-
- /*_voiceSocket.IsSpeaking += (s, e) =>
- {
- if (_voiceSocket.State == WebSocketState.Connected)
- {
- var user = _users[e.UserId, socket.CurrentServerId];
- bool value = e.IsSpeaking;
- if (user.IsSpeaking != value)
- {
- user.IsSpeaking = value;
- var channel = _channels[_voiceSocket.CurrentChannelId];
- RaiseUserIsSpeaking(user, channel, value);
- if (Config.TrackActivity)
- user.UpdateActivity();
- }
- }
- };*/
-
- /*this.Connected += (s, e) =>
- {
- _voiceSocket.ParentCancelToken = _cancelToken;
- };*/
+ OutputStream = new OutStream(this);
}
- public async Task Join(Channel channel)
- {
- if (channel == null) throw new ArgumentNullException(nameof(channel));
- if (channel.Type != ChannelType.Voice)
- throw new ArgumentException("Channel must be a voice channel.", nameof(channel));
- if (channel.Server != VoiceSocket.Server)
- throw new ArgumentException("This is channel is not part of the current server.", nameof(channel));
- if (channel == VoiceSocket.Channel) return;
- if (VoiceSocket.Server == null)
- throw new InvalidOperationException("This client has been closed.");
-
- using (await _connectionLock.LockAsync().ConfigureAwait(false))
+ /// Connects to the Discord server with the provided token.
+ public async Task Connect()
+ {
+ if (Config.EnableMultiserver)
+ await BeginGatewayConnect().ConfigureAwait(false);
+ else
{
- VoiceSocket.Channel = channel;
-
- await Task.Run(() =>
+ var cancelSource = new CancellationTokenSource();
+ CancelToken = cancelSource.Token;
+ await _taskManager.Start(new Task[0], cancelSource).ConfigureAwait(false);
+ }
+ }
+ private async Task BeginGatewayConnect()
+ {
+ try
+ {
+ using (await _connectionLock.LockAsync().ConfigureAwait(false))
{
- SendVoiceUpdate();
- VoiceSocket.WaitForConnection(_cancelTokenSource.Token);
- });
+ await Disconnect().ConfigureAwait(false);
+ _taskManager.ClearException();
+
+ ClientAPI.Token = Service.Client.ClientAPI.Token;
+
+ Stopwatch stopwatch = null;
+ if (_config.LogLevel >= LogSeverity.Verbose)
+ stopwatch = Stopwatch.StartNew();
+ _gatewayState = ConnectionState.Connecting;
+
+ var cancelSource = new CancellationTokenSource();
+ CancelToken = cancelSource.Token;
+ ClientAPI.CancelToken = CancelToken;
+
+ await GatewaySocket.Connect(ClientAPI, CancelToken).ConfigureAwait(false);
+
+ await _taskManager.Start(new Task[0], cancelSource).ConfigureAwait(false);
+ GatewaySocket.WaitForConnection(CancelToken);
+
+ if (_config.LogLevel >= LogSeverity.Verbose)
+ {
+ stopwatch.Stop();
+ double seconds = Math.Round(stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerSecond, 2);
+ Logger.Verbose($"Connection took {seconds} sec");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ await _taskManager.SignalError(ex).ConfigureAwait(false);
+ throw;
}
}
+ private void EndGatewayConnect()
+ {
+ _gatewayState = ConnectionState.Connected;
+ }
- public async Task Connect(bool connectGateway)
+ /// Disconnects from the Discord server, canceling any pending requests.
+ public async Task Disconnect()
{
- using (await _connectionLock.LockAsync().ConfigureAwait(false))
- {
- _cancelTokenSource = new CancellationTokenSource();
- var cancelToken = _cancelTokenSource.Token;
- VoiceSocket.ParentCancelToken = cancelToken;
+ await _taskManager.Stop(true).ConfigureAwait(false);
+ if (Config.EnableMultiserver)
+ ClientAPI.Token = null;
+ }
+ private async Task Cleanup()
+ {
+ var oldState = _gatewayState;
+ _gatewayState = ConnectionState.Disconnecting;
- if (connectGateway)
+ if (Config.EnableMultiserver)
+ {
+ if (oldState == ConnectionState.Connected)
{
- GatewaySocket.ParentCancelToken = cancelToken;
- await GatewaySocket.Connect().ConfigureAwait(false);
- GatewaySocket.WaitForConnection(cancelToken);
+ try { await ClientAPI.Send(new LogoutRequest()).ConfigureAwait(false); }
+ catch (OperationCanceledException) { }
}
+
+ await GatewaySocket.Disconnect().ConfigureAwait(false);
+ ClientAPI.Token = null;
}
+
+ var server = VoiceSocket.Server;
+ VoiceSocket.Server = null;
+ VoiceSocket.Channel = null;
+ if (Config.EnableMultiserver)
+ await Service.RemoveClient(server, this).ConfigureAwait(false);
+ SendVoiceUpdate(server.Id, null);
+
+ await VoiceSocket.Disconnect().ConfigureAwait(false);
+ if (Config.EnableMultiserver)
+ await GatewaySocket.Disconnect().ConfigureAwait(false);
+
+ _gatewayState = (int)ConnectionState.Disconnected;
}
- public async Task Disconnect()
+ public async Task Join(Channel channel)
{
+ if (channel == null) throw new ArgumentNullException(nameof(channel));
+ if (channel.Type != ChannelType.Voice)
+ throw new ArgumentException("Channel must be a voice channel.", nameof(channel));
+ if (channel == VoiceSocket.Channel) return;
+ var server = channel.Server;
+ if (server != VoiceSocket.Server)
+ throw new ArgumentException("This is channel is not part of the current server.", nameof(channel));
+ if (VoiceSocket.Server == null)
+ throw new InvalidOperationException("This client has been closed.");
+
+ SendVoiceUpdate(channel.Server.Id, channel.Id);
using (await _connectionLock.LockAsync().ConfigureAwait(false))
- {
- await Service.RemoveClient(VoiceSocket.Server, this).ConfigureAwait(false);
- VoiceSocket.Channel = null;
- SendVoiceUpdate();
- await VoiceSocket.Disconnect();
- }
+ await Task.Run(() => VoiceSocket.WaitForConnection(CancelToken));
}
- private async void OnReceivedDispatch(object sender, WebSocketEventEventArgs e)
+ private async void OnReceivedEvent(WebSocketEventEventArgs e)
{
try
{
@@ -166,11 +235,11 @@ namespace Discord.Audio
{
case "VOICE_STATE_UPDATE":
{
- var data = e.Payload.ToObject(_serializer);
+ var data = e.Payload.ToObject(Serializer);
if (data.GuildId == VoiceSocket.Server?.Id && data.UserId == Service.Client.CurrentUser?.Id)
{
if (data.ChannelId == null)
- await Disconnect();
+ await Disconnect().ConfigureAwait(false);
else
{
var channel = Service.Client.GetChannel(data.ChannelId.Value);
@@ -179,7 +248,7 @@ namespace Discord.Audio
else
{
Logger.Warning("VOICE_STATE_UPDATE referenced an unknown channel, disconnecting.");
- await Disconnect();
+ await Disconnect().ConfigureAwait(false);
}
}
}
@@ -187,13 +256,16 @@ namespace Discord.Audio
break;
case "VOICE_SERVER_UPDATE":
{
- var data = e.Payload.ToObject(_serializer);
+ var data = e.Payload.ToObject(Serializer);
if (data.GuildId == VoiceSocket.Server?.Id)
{
var client = Service.Client;
- VoiceSocket.Token = data.Token;
- VoiceSocket.Host = "wss://" + e.Payload.Value("endpoint").Split(':')[0];
- await VoiceSocket.Connect().ConfigureAwait(false);
+ var id = client.CurrentUser?.Id;
+ if (id != null)
+ {
+ var host = "wss://" + e.Payload.Value("endpoint").Split(':')[0];
+ await VoiceSocket.Connect(host, data.Token, id.Value, GatewaySocket.SessionId, CancelToken).ConfigureAwait(false);
+ }
}
}
break;
@@ -233,15 +305,11 @@ namespace Discord.Audio
VoiceSocket.WaitForQueue();
}
- private void SendVoiceUpdate()
+ public void SendVoiceUpdate(ulong? serverId, ulong? channelId)
{
- var serverId = VoiceSocket.Server?.Id;
- if (serverId != null)
- {
- GatewaySocket.SendUpdateVoice(serverId, VoiceSocket.Channel?.Id,
- (Service.Config.Mode | AudioMode.Outgoing) == 0,
- (Service.Config.Mode | AudioMode.Incoming) == 0);
- }
+ GatewaySocket.SendUpdateVoice(serverId, channelId,
+ (Service.Config.Mode | AudioMode.Outgoing) == 0,
+ (Service.Config.Mode | AudioMode.Incoming) == 0);
}
}
}
diff --git a/src/Discord.Net.Audio/AudioClient.cs.old b/src/Discord.Net.Audio/AudioClient.cs.old
new file mode 100644
index 000000000..17ee171b0
--- /dev/null
+++ b/src/Discord.Net.Audio/AudioClient.cs.old
@@ -0,0 +1,242 @@
+using Discord.API.Client.GatewaySocket;
+using Discord.Logging;
+using Discord.Net.Rest;
+using Discord.Net.WebSockets;
+using Newtonsoft.Json;
+using Nito.AsyncEx;
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Discord.Audio
+{
+ internal class AudioClient : IAudioClient
+ {
+ private class OutStream : Stream
+ {
+ public override bool CanRead => false;
+ public override bool CanSeek => false;
+ public override bool CanWrite => true;
+
+ private readonly AudioClient _client;
+
+ internal OutStream(AudioClient client)
+ {
+ _client = client;
+ }
+
+ public override long Length { get { throw new InvalidOperationException(); } }
+ public override long Position
+ {
+ get { throw new InvalidOperationException(); }
+ set { throw new InvalidOperationException(); }
+ }
+ public override void Flush() { throw new InvalidOperationException(); }
+ public override long Seek(long offset, SeekOrigin origin) { throw new InvalidOperationException(); }
+ public override void SetLength(long value) { throw new InvalidOperationException(); }
+ public override int Read(byte[] buffer, int offset, int count) { throw new InvalidOperationException(); }
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ _client.Send(buffer, offset, count);
+ }
+ }
+
+ private readonly JsonSerializer _serializer;
+ private readonly bool _ownsGateway;
+ private TaskManager _taskManager;
+ private CancellationToken _cancelToken;
+
+ internal AudioService Service { get; }
+ internal Logger Logger { get; }
+ public int Id { get; }
+ public GatewaySocket GatewaySocket { get; }
+ public VoiceSocket VoiceSocket { get; }
+ public Stream OutputStream { get; }
+
+ public ConnectionState State => VoiceSocket.State;
+ public Server Server => VoiceSocket.Server;
+ public Channel Channel => VoiceSocket.Channel;
+
+ public AudioClient(AudioService service, int clientId, Server server, GatewaySocket gatewaySocket, bool ownsGateway, Logger logger)
+ {
+ Service = service;
+ _serializer = service.Client.Serializer;
+ Id = clientId;
+ GatewaySocket = gatewaySocket;
+ _ownsGateway = ownsGateway;
+ Logger = logger;
+ OutputStream = new OutStream(this);
+ _taskManager = new TaskManager(Cleanup, true);
+
+ GatewaySocket.ReceivedDispatch += OnReceivedDispatch;
+
+ VoiceSocket = new VoiceSocket(service.Client.Config, service.Config, service.Client.Serializer, logger);
+ VoiceSocket.Server = server;
+
+ /*_voiceSocket.Connected += (s, e) => RaiseVoiceConnected();
+ _voiceSocket.Disconnected += async (s, e) =>
+ {
+ _voiceSocket.CurrentServerId;
+ if (voiceServerId != null)
+ _gatewaySocket.SendLeaveVoice(voiceServerId.Value);
+ await _voiceSocket.Disconnect().ConfigureAwait(false);
+ RaiseVoiceDisconnected(socket.CurrentServerId.Value, e);
+ if (e.WasUnexpected)
+ await socket.Reconnect().ConfigureAwait(false);
+ };*/
+
+ /*_voiceSocket.IsSpeaking += (s, e) =>
+ {
+ if (_voiceSocket.State == WebSocketState.Connected)
+ {
+ var user = _users[e.UserId, socket.CurrentServerId];
+ bool value = e.IsSpeaking;
+ if (user.IsSpeaking != value)
+ {
+ user.IsSpeaking = value;
+ var channel = _channels[_voiceSocket.CurrentChannelId];
+ RaiseUserIsSpeaking(user, channel, value);
+ if (Config.TrackActivity)
+ user.UpdateActivity();
+ }
+ }
+ };*/
+
+ /*this.Connected += (s, e) =>
+ {
+ _voiceSocket.ParentCancelToken = _cancelToken;
+ };*/
+ }
+
+ public async Task Join(Channel channel)
+ {
+ if (channel == null) throw new ArgumentNullException(nameof(channel));
+ if (channel.Type != ChannelType.Voice)
+ throw new ArgumentException("Channel must be a voice channel.", nameof(channel));
+ if (channel.Server != VoiceSocket.Server)
+ throw new ArgumentException("This is channel is not part of the current server.", nameof(channel));
+ if (channel == VoiceSocket.Channel) return;
+ if (VoiceSocket.Server == null)
+ throw new InvalidOperationException("This client has been closed.");
+
+ SendVoiceUpdate(channel.Server.Id, channel.Id);
+ await Task.Run(() => VoiceSocket.WaitForConnection(_cancelToken));
+ }
+
+ public async Task Connect(RestClient rest = null)
+ {
+ var cancelSource = new CancellationTokenSource();
+ _cancelToken = cancelSource.Token;
+
+ Task[] tasks;
+ if (rest != null)
+ tasks = new Task[] { GatewaySocket.Connect(rest, _cancelToken) };
+ else
+ tasks = new Task[0];
+
+ await _taskManager.Start(tasks, cancelSource);
+ }
+
+ public Task Disconnect() => _taskManager.Stop(true);
+
+ private async Task Cleanup()
+ {
+ var server = VoiceSocket.Server;
+ VoiceSocket.Server = null;
+ VoiceSocket.Channel = null;
+
+ await Service.RemoveClient(server, this).ConfigureAwait(false);
+ SendVoiceUpdate(server.Id, null);
+
+ await VoiceSocket.Disconnect().ConfigureAwait(false);
+ if (_ownsGateway)
+ await GatewaySocket.Disconnect().ConfigureAwait(false);
+ }
+
+ private async void OnReceivedDispatch(object sender, WebSocketEventEventArgs e)
+ {
+ try
+ {
+ switch (e.Type)
+ {
+ case "VOICE_STATE_UPDATE":
+ {
+ var data = e.Payload.ToObject(_serializer);
+ if (data.GuildId == VoiceSocket.Server?.Id && data.UserId == Service.Client.CurrentUser?.Id)
+ {
+ if (data.ChannelId == null)
+ await Disconnect().ConfigureAwait(false);
+ else
+ {
+ var channel = Service.Client.GetChannel(data.ChannelId.Value);
+ if (channel != null)
+ VoiceSocket.Channel = channel;
+ else
+ {
+ Logger.Warning("VOICE_STATE_UPDATE referenced an unknown channel, disconnecting.");
+ await Disconnect().ConfigureAwait(false);
+ }
+ }
+ }
+ }
+ break;
+ case "VOICE_SERVER_UPDATE":
+ {
+ var data = e.Payload.ToObject(_serializer);
+ if (data.GuildId == VoiceSocket.Server?.Id)
+ {
+ var client = Service.Client;
+ var id = client.CurrentUser?.Id;
+ if (id != null)
+ {
+ var host = "wss://" + e.Payload.Value("endpoint").Split(':')[0];
+ await VoiceSocket.Connect(host, data.Token, id.Value, GatewaySocket.SessionId, _cancelToken).ConfigureAwait(false);
+ }
+ }
+ }
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error($"Error handling {e.Type} event", ex);
+ }
+ }
+
+ /// Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer.
+ /// PCM frame to send. This must be a single or collection of uncompressed 48Kz monochannel 20ms PCM frames.
+ /// Number of bytes in this frame.
+ public void Send(byte[] data, int offset, int count)
+ {
+ if (data == null) throw new ArgumentException(nameof(data));
+ if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
+ if (offset < 0) throw new ArgumentOutOfRangeException(nameof(count));
+ if (VoiceSocket.Server == null) return; //Has been closed
+ if (count == 0) return;
+
+ VoiceSocket.SendPCMFrames(data, offset, count);
+ }
+
+ /// Clears the PCM buffer.
+ public void Clear()
+ {
+ if (VoiceSocket.Server == null) return; //Has been closed
+ VoiceSocket.ClearPCMFrames();
+ }
+
+ /// Returns a task that completes once the voice output buffer is empty.
+ public void Wait()
+ {
+ if (VoiceSocket.Server == null) return; //Has been closed
+ VoiceSocket.WaitForQueue();
+ }
+
+ public void SendVoiceUpdate(ulong? serverId, ulong? channelId)
+ {
+ GatewaySocket.SendUpdateVoice(serverId, channelId,
+ (Service.Config.Mode | AudioMode.Outgoing) == 0,
+ (Service.Config.Mode | AudioMode.Incoming) == 0);
+ }
+ }
+}
diff --git a/src/Discord.Net.Audio/AudioService.cs b/src/Discord.Net.Audio/AudioService.cs
index ec134f884..b64bf53f8 100644
--- a/src/Discord.Net.Audio/AudioService.cs
+++ b/src/Discord.Net.Audio/AudioService.cs
@@ -1,4 +1,4 @@
-using Discord.Net.WebSockets;
+using Nito.AsyncEx;
using System;
using System.Collections.Concurrent;
using System.Linq;
@@ -8,8 +8,10 @@ namespace Discord.Audio
{
public class AudioService : IService
{
- private AudioClient _defaultClient;
- private ConcurrentDictionary _voiceClients;
+ private readonly AsyncLock _asyncLock;
+ private AudioClient _defaultClient; //Only used for single server
+ private VirtualClient _currentClient; //Only used for single server
+ private ConcurrentDictionary _voiceClients;
private ConcurrentDictionary _talkingUsers;
private int _nextClientId;
@@ -30,18 +32,20 @@ namespace Discord.Audio
public AudioService(AudioServiceConfig config)
{
Config = config;
- }
+ _asyncLock = new AsyncLock();
+
+ }
void IService.Install(DiscordClient client)
{
Client = client;
Config.Lock();
if (Config.EnableMultiserver)
- _voiceClients = new ConcurrentDictionary();
+ _voiceClients = new ConcurrentDictionary();
else
{
var logger = Client.Log.CreateLogger("Voice");
- _defaultClient = new SimpleAudioClient(this, 0, logger);
+ _defaultClient = new AudioClient(Client, null, 0);
}
_talkingUsers = new ConcurrentDictionary();
@@ -75,68 +79,30 @@ namespace Discord.Audio
{
if (server == null) throw new ArgumentNullException(nameof(server));
- if (!Config.EnableMultiserver)
+ if (Config.EnableMultiserver)
{
- if (server == _defaultClient.Server)
- return (_defaultClient as SimpleAudioClient).CurrentClient;
+ AudioClient client;
+ if (_voiceClients.TryGetValue(server.Id, out client))
+ return client;
else
return null;
}
else
{
- IAudioClient client;
- if (_voiceClients.TryGetValue(server.Id, out client))
- return client;
+ if (server == _currentClient.Server)
+ return _currentClient;
else
return null;
}
}
- private async Task CreateClient(Server server)
- {
- var client = _voiceClients.GetOrAdd(server.Id, _ => null); //Placeholder, so we can't have two clients connecting at once
-
- if (client == null)
- {
- int id = unchecked(++_nextClientId);
-
- var gatewayLogger = Client.Log.CreateLogger($"Gateway #{id}");
- var voiceLogger = Client.Log.CreateLogger($"Voice #{id}");
- var gatewaySocket = new GatewaySocket(Client, gatewayLogger);
- var voiceClient = new AudioClient(this, id, server, Client.GatewaySocket, voiceLogger);
-
- await voiceClient.Connect(true).ConfigureAwait(false);
-
- /*voiceClient.VoiceSocket.FrameReceived += (s, e) =>
- {
- OnFrameReceieved(e);
- };
- voiceClient.VoiceSocket.UserIsSpeaking += (s, e) =>
- {
- var user = server.GetUser(e.UserId);
- OnUserIsSpeakingUpdated(user, e.IsSpeaking);
- };*/
-
- //Update the placeholder only it still exists (RemoveClient wasnt called)
- if (!_voiceClients.TryUpdate(server.Id, voiceClient, null))
- {
- //If it was, cleanup
- await voiceClient.Disconnect().ConfigureAwait(false); ;
- await gatewaySocket.Disconnect().ConfigureAwait(false); ;
- }
- }
- return client;
- }
-
- //TODO: This isn't threadsafe
- internal async Task RemoveClient(Server server, IAudioClient client)
+
+ //Called from AudioClient.Disconnect
+ internal async Task RemoveClient(Server server, AudioClient client)
{
- if (Config.EnableMultiserver && server != null)
+ using (await _asyncLock.LockAsync().ConfigureAwait(false))
{
- if (_voiceClients.TryRemove(server.Id, out client))
- {
- await client.Disconnect();
- await (client as AudioClient).GatewaySocket.Disconnect();
- }
+ if (_voiceClients.TryUpdate(server.Id, null, client))
+ _voiceClients.TryRemove(server.Id, out client);
}
}
@@ -144,16 +110,48 @@ namespace Discord.Audio
{
if (channel == null) throw new ArgumentNullException(nameof(channel));
- if (!Config.EnableMultiserver)
- {
- await (_defaultClient as SimpleAudioClient).Connect(channel, false).ConfigureAwait(false);
- return _defaultClient;
- }
- else
+ var server = channel.Server;
+ using (await _asyncLock.LockAsync().ConfigureAwait(false))
{
- var client = await CreateClient(channel.Server).ConfigureAwait(false);
- await client.Join(channel).ConfigureAwait(false);
- return client;
+ if (Config.EnableMultiserver)
+ {
+ AudioClient client;
+ if (!_voiceClients.TryGetValue(server.Id, out client))
+ {
+ client = new AudioClient(Client, server, unchecked(++_nextClientId));
+ _voiceClients[server.Id] = client;
+
+ await client.Connect().ConfigureAwait(false);
+
+ /*voiceClient.VoiceSocket.FrameReceived += (s, e) =>
+ {
+ OnFrameReceieved(e);
+ };
+ voiceClient.VoiceSocket.UserIsSpeaking += (s, e) =>
+ {
+ var user = server.GetUser(e.UserId);
+ OnUserIsSpeakingUpdated(user, e.IsSpeaking);
+ };*/
+ }
+
+ await client.Join(channel).ConfigureAwait(false);
+ return client;
+ }
+ else
+ {
+ if (_defaultClient.Server != server)
+ {
+ await _defaultClient.Disconnect();
+ _defaultClient.VoiceSocket.Server = server;
+ await _defaultClient.Connect().ConfigureAwait(false);
+ }
+ var client = new VirtualClient(_defaultClient, server);
+ _currentClient = client;
+
+ await client.Join(channel).ConfigureAwait(false);
+ return client;
+ }
+
}
}
@@ -163,15 +161,18 @@ namespace Discord.Audio
if (Config.EnableMultiserver)
{
- IAudioClient client;
+ AudioClient client;
if (_voiceClients.TryRemove(server.Id, out client))
await client.Disconnect().ConfigureAwait(false);
}
else
{
- IAudioClient client = GetClient(server);
- if (client != null)
- await (_defaultClient as SimpleAudioClient).Leave(client as SimpleAudioClient.VirtualClient).ConfigureAwait(false);
+ using (await _asyncLock.LockAsync().ConfigureAwait(false))
+ {
+ var client = GetClient(server) as VirtualClient;
+ if (client != null)
+ await _defaultClient.Disconnect().ConfigureAwait(false);
+ }
}
}
}
diff --git a/src/Discord.Net.Audio/Net/VoiceWebSocket.cs b/src/Discord.Net.Audio/Net/VoiceSocket.cs
similarity index 90%
rename from src/Discord.Net.Audio/Net/VoiceWebSocket.cs
rename to src/Discord.Net.Audio/Net/VoiceSocket.cs
index c7c4ee0e2..bab928b53 100644
--- a/src/Discord.Net.Audio/Net/VoiceWebSocket.cs
+++ b/src/Discord.Net.Audio/Net/VoiceSocket.cs
@@ -19,7 +19,7 @@ using System.Threading.Tasks;
namespace Discord.Net.WebSockets
{
- public partial class VoiceWebSocket : WebSocket
+ public partial class VoiceSocket : WebSocket
{
private const int MaxOpusSize = 4000;
private const string EncryptedMode = "xsalsa20_poly1305";
@@ -27,8 +27,7 @@ namespace Discord.Net.WebSockets
private readonly int _targetAudioBufferLength;
private readonly ConcurrentDictionary _decoders;
- private readonly AudioClient _audioClient;
- private readonly AudioServiceConfig _config;
+ private readonly AudioServiceConfig _audioConfig;
private Task _sendTask, _receiveTask;
private VoiceBuffer _sendBuffer;
private OpusEncoder _encoder;
@@ -41,6 +40,8 @@ namespace Discord.Net.WebSockets
private ushort _sequence;
private string _encryptionMode;
private int _ping;
+ private ulong? _userId;
+ private string _sessionId;
public string Token { get; internal set; }
public Server Server { get; internal set; }
@@ -57,32 +58,37 @@ namespace Discord.Net.WebSockets
internal void OnFrameReceived(ulong userId, ulong channelId, byte[] buffer, int offset, int count)
=> FrameReceived(this, new InternalFrameEventArgs(userId, channelId, buffer, offset, count));
- internal VoiceWebSocket(DiscordClient client, AudioClient audioClient, Logger logger)
- : base(client, logger)
+ internal VoiceSocket(DiscordConfig config, AudioServiceConfig audioConfig, JsonSerializer serializer, Logger logger)
+ : base(config, serializer, logger)
{
- _audioClient = audioClient;
- _config = client.Audio().Config;
+ _audioConfig = audioConfig;
_decoders = new ConcurrentDictionary();
- _targetAudioBufferLength = _config.BufferLength / 20; //20 ms frames
+ _targetAudioBufferLength = _audioConfig.BufferLength / 20; //20 ms frames
_encodingBuffer = new byte[MaxOpusSize];
_ssrcMapping = new ConcurrentDictionary();
- _encoder = new OpusEncoder(48000, _config.Channels, 20, _config.Bitrate, OpusApplication.MusicOrMixed);
- _sendBuffer = new VoiceBuffer((int)Math.Ceiling(_config.BufferLength / (double)_encoder.FrameLength), _encoder.FrameSize);
+ _encoder = new OpusEncoder(48000, _audioConfig.Channels, 20, _audioConfig.Bitrate, OpusApplication.MusicOrMixed);
+ _sendBuffer = new VoiceBuffer((int)Math.Ceiling(_audioConfig.BufferLength / (double)_encoder.FrameLength), _encoder.FrameSize);
}
- public Task Connect()
- => BeginConnect();
+ public Task Connect(string host, string token, ulong userId, string sessionId, CancellationToken parentCancelToken)
+ {
+ Host = host;
+ Token = token;
+ _userId = userId;
+ _sessionId = sessionId;
+ return BeginConnect(parentCancelToken);
+ }
private async Task Reconnect()
{
try
{
- var cancelToken = ParentCancelToken.Value;
- await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
+ var cancelToken = _parentCancelToken;
+ await Task.Delay(_config.ReconnectDelay, cancelToken).ConfigureAwait(false);
while (!cancelToken.IsCancellationRequested)
{
try
{
- await Connect().ConfigureAwait(false);
+ await BeginConnect(_parentCancelToken).ConfigureAwait(false);
break;
}
catch (OperationCanceledException) { throw; }
@@ -90,31 +96,35 @@ namespace Discord.Net.WebSockets
{
Logger.Error("Reconnect failed", ex);
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
- await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
+ await Task.Delay(_config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
}
}
}
catch (OperationCanceledException) { }
}
- public Task Disconnect() => _taskManager.Stop(true);
+ public async Task Disconnect()
+ {
+ await _taskManager.Stop(true).ConfigureAwait(false);
+ _userId = null;
+ }
protected override async Task Run()
{
_udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
List tasks = new List();
- if (_config.Mode.HasFlag(AudioMode.Outgoing))
+ if (_audioConfig.Mode.HasFlag(AudioMode.Outgoing))
_sendTask = Task.Run(() => SendVoiceAsync(CancelToken));
_receiveTask = Task.Run(() => ReceiveVoiceAsync(CancelToken));
- SendIdentify();
+ SendIdentify(_userId.Value, _sessionId);
#if !DOTNET5_4
tasks.Add(WatcherAsync());
#endif
tasks.AddRange(_engine.GetTasks(CancelToken));
tasks.Add(HeartbeatAsync(CancelToken));
- await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
+ await _taskManager.Start(tasks, _cancelSource).ConfigureAwait(false);
}
protected override async Task Cleanup()
{
@@ -148,7 +158,7 @@ namespace Discord.Net.WebSockets
int packetLength, resultOffset, resultLength;
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);
- if ((_config.Mode & AudioMode.Incoming) != 0)
+ if ((_audioConfig.Mode & AudioMode.Incoming) != 0)
{
decodingBuffer = new byte[MaxOpusSize];
nonce = new byte[24];
@@ -184,7 +194,7 @@ namespace Discord.Net.WebSockets
int port = packet[68] | packet[69] << 8;
SendSelectProtocol(ip, port);
- if ((_config.Mode & AudioMode.Incoming) == 0)
+ if ((_audioConfig.Mode & AudioMode.Incoming) == 0)
return; //We dont need this thread anymore
}
else
@@ -395,7 +405,7 @@ namespace Discord.Net.WebSockets
var address = (await Dns.GetHostAddressesAsync(Host.Replace("wss://", "")).ConfigureAwait(false)).FirstOrDefault();
_endpoint = new IPEndPoint(address, payload.Port);
- if (_config.EnableEncryption)
+ if (_audioConfig.EnableEncryption)
{
if (payload.Modes.Contains(EncryptedMode))
{
@@ -467,12 +477,12 @@ namespace Discord.Net.WebSockets
public override void SendHeartbeat()
=> QueueMessage(new HeartbeatCommand());
- public void SendIdentify()
+ public void SendIdentify(ulong id, string sessionId)
=> QueueMessage(new IdentifyCommand
{
GuildId = Server.Id,
- UserId = _client.CurrentUser.Id,
- SessionId = _client.SessionId,
+ UserId = id,
+ SessionId = sessionId,
Token = Token
});
public void SendSelectProtocol(string externalAddress, int externalPort)
diff --git a/src/Discord.Net.Audio/SimpleAudioClient.cs b/src/Discord.Net.Audio/SimpleAudioClient.cs
deleted file mode 100644
index b073e2ed3..000000000
--- a/src/Discord.Net.Audio/SimpleAudioClient.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using Discord.Logging;
-using Nito.AsyncEx;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace Discord.Audio
-{
- internal class SimpleAudioClient : AudioClient
- {
- internal class VirtualClient : IAudioClient
- {
- private readonly SimpleAudioClient _client;
-
- ConnectionState IAudioClient.State => _client.VoiceSocket.State;
- Server IAudioClient.Server => _client.VoiceSocket.Server;
- Channel IAudioClient.Channel => _client.VoiceSocket.Channel;
- Stream IAudioClient.OutputStream => _client.OutputStream;
-
- public VirtualClient(SimpleAudioClient client)
- {
- _client = client;
- }
-
- Task IAudioClient.Disconnect() => _client.Leave(this);
- Task IAudioClient.Join(Channel channel) => _client.Join(channel);
-
- void IAudioClient.Send(byte[] data, int offset, int count) => _client.Send(data, offset, count);
- void IAudioClient.Clear() => _client.Clear();
- void IAudioClient.Wait() => _client.Wait();
- }
-
- private readonly AsyncLock _connectionLock;
-
- internal VirtualClient CurrentClient { get; private set; }
-
- public SimpleAudioClient(AudioService service, int id, Logger logger)
- : base(service, id, null, service.Client.GatewaySocket, logger)
- {
- _connectionLock = new AsyncLock();
- }
-
- //Only disconnects if is current a member of this server
- public async Task Leave(VirtualClient client)
- {
- using (await _connectionLock.LockAsync().ConfigureAwait(false))
- {
- if (CurrentClient == client)
- {
- CurrentClient = null;
- await Disconnect().ConfigureAwait(false);
- }
- }
- }
-
- internal async Task Connect(Channel channel, bool connectGateway)
- {
- using (await _connectionLock.LockAsync().ConfigureAwait(false))
- {
- bool changeServer = channel.Server != VoiceSocket.Server;
- if (changeServer || CurrentClient == null)
- {
- await Disconnect().ConfigureAwait(false);
- CurrentClient = new VirtualClient(this);
- VoiceSocket.Server = channel.Server;
- await Connect(connectGateway).ConfigureAwait(false);
- }
- await Join(channel).ConfigureAwait(false);
- return CurrentClient;
- }
- }
- }
-}
diff --git a/src/Discord.Net.Audio/VirtualClient.cs b/src/Discord.Net.Audio/VirtualClient.cs
new file mode 100644
index 000000000..0d197fb95
--- /dev/null
+++ b/src/Discord.Net.Audio/VirtualClient.cs
@@ -0,0 +1,29 @@
+using System.IO;
+using System.Threading.Tasks;
+
+namespace Discord.Audio
+{
+ internal class VirtualClient : IAudioClient
+ {
+ private readonly AudioClient _client;
+
+ public Server Server { get; }
+
+ public ConnectionState State => _client.VoiceSocket.Server == Server ? _client.VoiceSocket.State : ConnectionState.Disconnected;
+ public Channel Channel => _client.VoiceSocket.Server == Server ? _client.VoiceSocket.Channel : null;
+ public Stream OutputStream => _client.VoiceSocket.Server == Server ? _client.OutputStream : null;
+
+ public VirtualClient(AudioClient client, Server server)
+ {
+ _client = client;
+ Server = server;
+ }
+
+ public Task Disconnect() => _client.Service.Leave(Server);
+ public Task Join(Channel channel) => _client.Join(channel);
+
+ public void Send(byte[] data, int offset, int count) => _client.Send(data, offset, count);
+ public void Clear() => _client.Clear();
+ public void Wait() => _client.Wait();
+ }
+}
diff --git a/src/Discord.Net.Commands/Command.cs b/src/Discord.Net.Commands/Command.cs
index 8aec0b90f..a8addc1b1 100644
--- a/src/Discord.Net.Commands/Command.cs
+++ b/src/Discord.Net.Commands/Command.cs
@@ -5,7 +5,8 @@ using System.Threading.Tasks;
namespace Discord.Commands
{
- public sealed class Command
+ //TODO: Make this more friendly and expose it to be extendable
+ public class Command
{
private string[] _aliases;
internal CommandParameter[] _parameters;
diff --git a/src/Discord.Net.Commands/CommandBuilder.cs b/src/Discord.Net.Commands/CommandBuilder.cs
index 3659f82c5..129dc24ab 100644
--- a/src/Discord.Net.Commands/CommandBuilder.cs
+++ b/src/Discord.Net.Commands/CommandBuilder.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
namespace Discord.Commands
{
+ //TODO: Make this more friendly and expose it to be extendable
public sealed class CommandBuilder
{
private readonly CommandService _service;
@@ -18,17 +19,20 @@ namespace Discord.Commands
public CommandService Service => _service;
- internal CommandBuilder(CommandService service, Command command, string prefix = "", string category = "", IEnumerable initialChecks = null)
+ internal CommandBuilder(CommandService service, string text, string prefix = "", string category = "", IEnumerable initialChecks = null)
{
- _service = service;
- _command = command;
- _command.Category = category;
- _params = new List();
+ _service = service;
+ _prefix = prefix;
+
+ _command = new Command(AppendPrefix(prefix, text));
+ _command.Category = category;
+
if (initialChecks != null)
_checks = new List(initialChecks);
else
_checks = new List();
- _prefix = prefix;
+
+ _params = new List();
_aliases = new List();
_allowRequiredParams = true;
@@ -112,7 +116,7 @@ namespace Discord.Commands
return prefix;
}
}
- public sealed class CommandGroupBuilder
+ public class CommandGroupBuilder
{
private readonly CommandService _service;
private readonly string _prefix;
@@ -154,9 +158,6 @@ namespace Discord.Commands
public CommandBuilder CreateCommand()
=> CreateCommand("");
public CommandBuilder CreateCommand(string cmd)
- {
- var command = new Command(CommandBuilder.AppendPrefix(_prefix, cmd));
- return new CommandBuilder(_service, command, _prefix, _category, _checks);
- }
+ => new CommandBuilder(_service, cmd, _prefix, _category, _checks);
}
}
diff --git a/src/Discord.Net.Commands/CommandParameter.cs b/src/Discord.Net.Commands/CommandParameter.cs
index 413674b78..d7361bef4 100644
--- a/src/Discord.Net.Commands/CommandParameter.cs
+++ b/src/Discord.Net.Commands/CommandParameter.cs
@@ -11,13 +11,13 @@
/// Catches all remaining text as a single optional parameter.
Unparsed
}
- public sealed class CommandParameter
+ public class CommandParameter
{
public string Name { get; }
public int Id { get; internal set; }
public ParameterType Type { get; }
- public CommandParameter(string name, ParameterType type)
+ internal CommandParameter(string name, ParameterType type)
{
Name = name;
Type = type;
diff --git a/src/Discord.Net.Modules/ModuleManager.cs b/src/Discord.Net.Modules/ModuleManager.cs
index bd6f27519..17b2b166b 100644
--- a/src/Discord.Net.Modules/ModuleManager.cs
+++ b/src/Discord.Net.Modules/ModuleManager.cs
@@ -7,7 +7,7 @@ using System.Linq;
namespace Discord.Modules
{
- public sealed class ModuleManager
+ public class ModuleManager
{
public event EventHandler ServerEnabled = delegate { };
public event EventHandler ServerDisabled = delegate { };
diff --git a/src/Discord.Net/API/Client/Common/Channel.cs b/src/Discord.Net/API/Client/Common/Channel.cs
index f54c11e5c..90ed8bf38 100644
--- a/src/Discord.Net/API/Client/Common/Channel.cs
+++ b/src/Discord.Net/API/Client/Common/Channel.cs
@@ -5,7 +5,7 @@ namespace Discord.API.Client
{
public class Channel : ChannelReference
{
- public sealed class PermissionOverwrite
+ public class PermissionOverwrite
{
[JsonProperty("type")]
public string Type { get; set; }
diff --git a/src/Discord.Net/API/Client/Common/ExtendedGuild.cs b/src/Discord.Net/API/Client/Common/ExtendedGuild.cs
index 9da0355ca..bfb1971cc 100644
--- a/src/Discord.Net/API/Client/Common/ExtendedGuild.cs
+++ b/src/Discord.Net/API/Client/Common/ExtendedGuild.cs
@@ -4,7 +4,7 @@ namespace Discord.API.Client
{
public class ExtendedGuild : Guild
{
- public sealed class ExtendedMemberInfo : Member
+ public class ExtendedMemberInfo : Member
{
[JsonProperty("mute")]
public bool? IsServerMuted { get; set; }
diff --git a/src/Discord.Net/API/Client/Common/Guild.cs b/src/Discord.Net/API/Client/Common/Guild.cs
index bfa047c57..fd4519147 100644
--- a/src/Discord.Net/API/Client/Common/Guild.cs
+++ b/src/Discord.Net/API/Client/Common/Guild.cs
@@ -6,7 +6,7 @@ namespace Discord.API.Client
{
public class Guild : GuildReference
{
- public sealed class EmojiData
+ public class EmojiData
{
[JsonProperty("id")]
public string Id { get; set; }
diff --git a/src/Discord.Net/API/Client/Common/InviteReference.cs b/src/Discord.Net/API/Client/Common/InviteReference.cs
index 4c25d9ad3..194165173 100644
--- a/src/Discord.Net/API/Client/Common/InviteReference.cs
+++ b/src/Discord.Net/API/Client/Common/InviteReference.cs
@@ -4,7 +4,7 @@ namespace Discord.API.Client
{
public class InviteReference
{
- public sealed class GuildData : GuildReference
+ public class GuildData : GuildReference
{
[JsonProperty("splash_hash")]
public string Splash { get; set; }
diff --git a/src/Discord.Net/API/Client/Common/MemberPresence.cs b/src/Discord.Net/API/Client/Common/MemberPresence.cs
index 283b9a1d1..589ad46c1 100644
--- a/src/Discord.Net/API/Client/Common/MemberPresence.cs
+++ b/src/Discord.Net/API/Client/Common/MemberPresence.cs
@@ -5,7 +5,7 @@ namespace Discord.API.Client
{
public class MemberPresence : MemberReference
{
- public sealed class GameInfo
+ public class GameInfo
{
[JsonProperty("name")]
public string Name { get; set; }
diff --git a/src/Discord.Net/API/Client/Common/Message.cs b/src/Discord.Net/API/Client/Common/Message.cs
index f89d81d96..7736d0ef1 100644
--- a/src/Discord.Net/API/Client/Common/Message.cs
+++ b/src/Discord.Net/API/Client/Common/Message.cs
@@ -5,7 +5,7 @@ namespace Discord.API.Client
{
public class Message : MessageReference
{
- public sealed class Attachment
+ public class Attachment
{
[JsonProperty("id")]
public string Id { get; set; }
@@ -23,9 +23,9 @@ namespace Discord.API.Client
public int Height { get; set; }
}
- public sealed class Embed
+ public class Embed
{
- public sealed class Reference
+ public class Reference
{
[JsonProperty("url")]
public string Url { get; set; }
@@ -33,7 +33,7 @@ namespace Discord.API.Client
public string Name { get; set; }
}
- public sealed class ThumbnailInfo
+ public class ThumbnailInfo
{
[JsonProperty("url")]
public string Url { get; set; }
@@ -44,7 +44,7 @@ namespace Discord.API.Client
[JsonProperty("height")]
public int Height { get; set; }
}
- public sealed class VideoInfo
+ public class VideoInfo
{
[JsonProperty("url")]
public string Url { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/Heartbeat.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/Heartbeat.cs
index 68a0e98e6..9f3f9cefb 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Commands/Heartbeat.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/Heartbeat.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.GatewaySocket
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class HeartbeatCommand : IWebSocketMessage
+ public class HeartbeatCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.Heartbeat;
object IWebSocketMessage.Payload => EpochTime.GetMilliseconds();
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/Identify.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/Identify.cs
index 76a3a3e6f..2a56143c8 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Commands/Identify.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/Identify.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Discord.API.Client.GatewaySocket
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class IdentifyCommand : IWebSocketMessage
+ public class IdentifyCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.Identify;
object IWebSocketMessage.Payload => this;
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/RequestMembers.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/RequestMembers.cs
index 2a2799c12..ea8ab4a75 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Commands/RequestMembers.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/RequestMembers.cs
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.GatewaySocket
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class RequestMembersCommand : IWebSocketMessage
+ public class RequestMembersCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.RequestGuildMembers;
object IWebSocketMessage.Payload => this;
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/Resume.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/Resume.cs
index f473369cf..15486e577 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Commands/Resume.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/Resume.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.GatewaySocket
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class ResumeCommand : IWebSocketMessage
+ public class ResumeCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.Resume;
object IWebSocketMessage.Payload => this;
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateStatus.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateStatus.cs
index 75bfce892..dff18b08c 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateStatus.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateStatus.cs
@@ -3,13 +3,13 @@
namespace Discord.API.Client.GatewaySocket
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class UpdateStatusCommand : IWebSocketMessage
+ public class UpdateStatusCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.StatusUpdate;
object IWebSocketMessage.Payload => this;
bool IWebSocketMessage.IsPrivate => false;
- public sealed class GameInfo
+ public class GameInfo
{
[JsonProperty("name")]
public string Name { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateVoice.cs b/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateVoice.cs
index 4eced5dcf..3ccf92c65 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateVoice.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Commands/UpdateVoice.cs
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.GatewaySocket
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class UpdateVoiceCommand : IWebSocketMessage
+ public class UpdateVoiceCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.VoiceStateUpdate;
object IWebSocketMessage.Payload => this;
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelCreate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelCreate.cs
index da4e47aa9..ca26fecc7 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelCreate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelCreate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class ChannelCreateEvent : Channel { }
+ public class ChannelCreateEvent : Channel { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelDelete.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelDelete.cs
index 64452dbe2..2b61a7d78 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelDelete.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelDelete.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class ChannelDeleteEvent : Channel { }
+ public class ChannelDeleteEvent : Channel { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelUpdate.cs
index 908c65267..4565ce1bc 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/ChannelUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class ChannelUpdateEvent : Channel { }
+ public class ChannelUpdateEvent : Channel { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanAdd.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanAdd.cs
index 823e3506a..7ba24473a 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanAdd.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanAdd.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildBanAddEvent : MemberReference { }
+ public class GuildBanAddEvent : MemberReference { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanRemove.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanRemove.cs
index a977b8702..a56a98494 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanRemove.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildBanRemove.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildBanRemoveEvent : MemberReference { }
+ public class GuildBanRemoveEvent : MemberReference { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildCreate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildCreate.cs
index 13e3393fc..41c1c71c7 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildCreate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildCreate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildCreateEvent : ExtendedGuild { }
+ public class GuildCreateEvent : ExtendedGuild { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildDelete.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildDelete.cs
index 6850cc2b9..cf824c40e 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildDelete.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildDelete.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildDeleteEvent : ExtendedGuild { }
+ public class GuildDeleteEvent : ExtendedGuild { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildEmojisUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildEmojisUpdate.cs
index 8d2400a7b..06255bdcf 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildEmojisUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildEmojisUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket.Events
{
- //public sealed class GuildEmojisUpdateEvent { }
+ //public class GuildEmojisUpdateEvent { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildIntegrationsUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildIntegrationsUpdate.cs
index d5ac59521..0767b2f8f 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildIntegrationsUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildIntegrationsUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- //public sealed class GuildIntegrationsUpdateEvent { }
+ //public class GuildIntegrationsUpdateEvent { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberAdd.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberAdd.cs
index ab543a41b..4d1d7fed5 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberAdd.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberAdd.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildMemberAddEvent : Member { }
+ public class GuildMemberAddEvent : Member { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberRemove.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberRemove.cs
index cd76cd81a..311186b11 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberRemove.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberRemove.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildMemberRemoveEvent : Member { }
+ public class GuildMemberRemoveEvent : Member { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberUpdate.cs
index 8e67d5e12..9b56a95b0 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMemberUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildMemberUpdateEvent : Member { }
+ public class GuildMemberUpdateEvent : Member { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMembersChunk.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMembersChunk.cs
index 1cb256c79..23be74855 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMembersChunk.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildMembersChunk.cs
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildMembersChunkEvent
+ public class GuildMembersChunkEvent
{
[JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))]
public ulong GuildId { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleCreate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleCreate.cs
index 28f999d5f..3d8e2f459 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleCreate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleCreate.cs
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildRoleCreateEvent
+ public class GuildRoleCreateEvent
{
[JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))]
public ulong GuildId { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleDelete.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleDelete.cs
index 470420157..2ecd2edc5 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleDelete.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleDelete.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildRoleDeleteEvent : RoleReference { }
+ public class GuildRoleDeleteEvent : RoleReference { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleUpdate.cs
index 291bab043..e26b65c4d 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildRoleUpdate.cs
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildRoleUpdateEvent
+ public class GuildRoleUpdateEvent
{
[JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))]
public ulong GuildId { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildUpdate.cs
index ba1665f09..8fc0f1350 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/GuildUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/GuildUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class GuildUpdateEvent : Guild { }
+ public class GuildUpdateEvent : Guild { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageAck.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageAck.cs
index ed4048a62..64c106ef5 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageAck.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageAck.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class MessageAckEvent : MessageReference { }
+ public class MessageAckEvent : MessageReference { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageCreate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageCreate.cs
index 9eb514b61..d6d2ec1cc 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageCreate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageCreate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class MessageCreateEvent : Message { }
+ public class MessageCreateEvent : Message { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageDelete.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageDelete.cs
index 9d160eb58..cfc2df7ff 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageDelete.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageDelete.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class MessageDeleteEvent : MessageReference { }
+ public class MessageDeleteEvent : MessageReference { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageUpdate.cs
index 6fa852749..23521fd93 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/MessageUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/MessageUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class MessageUpdateEvent : Message { }
+ public class MessageUpdateEvent : Message { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/PresenceUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/PresenceUpdate.cs
index 4ecbccf05..c40853336 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/PresenceUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/PresenceUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class PresenceUpdateEvent : MemberPresence { }
+ public class PresenceUpdateEvent : MemberPresence { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/Ready.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/Ready.cs
index b17ffd61d..c672a30ae 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/Ready.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/Ready.cs
@@ -2,9 +2,9 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class ReadyEvent
+ public class ReadyEvent
{
- public sealed class ReadState
+ public class ReadState
{
[JsonProperty("id")]
public string ChannelId { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/Redirect.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/Redirect.cs
index 26b2afd2d..fe9d644d4 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/Redirect.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/Redirect.cs
@@ -2,7 +2,7 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class RedirectEvent
+ public class RedirectEvent
{
[JsonProperty("url")]
public string Url { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/Resumed.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/Resumed.cs
index 7fd608b25..6a50fbe32 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/Resumed.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/Resumed.cs
@@ -2,7 +2,7 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class ResumedEvent
+ public class ResumedEvent
{
[JsonProperty("heartbeat_interval")]
public int HeartbeatInterval { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/TypingStart.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/TypingStart.cs
index ee8abebe6..484cec1bc 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/TypingStart.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/TypingStart.cs
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.GatewaySocket
{
- public sealed class TypingStartEvent
+ public class TypingStartEvent
{
[JsonProperty("user_id"), JsonConverter(typeof(LongStringConverter))]
public ulong UserId { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/UserSettingsUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/UserSettingsUpdate.cs
index 04effff5e..aad938157 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/UserSettingsUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/UserSettingsUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- //public sealed class UserSettingsUpdateEvent { }
+ //public class UserSettingsUpdateEvent { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/UserUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/UserUpdate.cs
index 6b43b34b6..3c366310a 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/UserUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/UserUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class UserUpdateEvent : User { }
+ public class UserUpdateEvent : User { }
}
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceServerUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceServerUpdate.cs
index 0725cc5d8..d305642a1 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceServerUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceServerUpdate.cs
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.GatewaySocket
{
- public sealed class VoiceServerUpdateEvent
+ public class VoiceServerUpdateEvent
{
[JsonProperty("guild_id"), JsonConverter(typeof(LongStringConverter))]
public ulong GuildId { get; set; }
diff --git a/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceStateUpdate.cs b/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceStateUpdate.cs
index 448920b42..f3ba96b17 100644
--- a/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceStateUpdate.cs
+++ b/src/Discord.Net/API/Client/GatewaySocket/Events/VoiceStateUpdate.cs
@@ -1,4 +1,4 @@
namespace Discord.API.Client.GatewaySocket
{
- public sealed class VoiceStateUpdateEvent : MemberVoiceState { }
+ public class VoiceStateUpdateEvent : MemberVoiceState { }
}
diff --git a/src/Discord.Net/API/Client/IWebSocketMessage.cs b/src/Discord.Net/API/Client/IWebSocketMessage.cs
index 715110e51..6f6de535a 100644
--- a/src/Discord.Net/API/Client/IWebSocketMessage.cs
+++ b/src/Discord.Net/API/Client/IWebSocketMessage.cs
@@ -8,7 +8,7 @@ namespace Discord.API.Client
object Payload { get; }
bool IsPrivate { get; }
}
- public sealed class WebSocketMessage
+ public class WebSocketMessage
{
[JsonProperty("op")]
public int? Operation { get; set; }
diff --git a/src/Discord.Net/API/Client/Rest/AcceptInvite.cs b/src/Discord.Net/API/Client/Rest/AcceptInvite.cs
index 639a558ec..865e37c2d 100644
--- a/src/Discord.Net/API/Client/Rest/AcceptInvite.cs
+++ b/src/Discord.Net/API/Client/Rest/AcceptInvite.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class AcceptInviteRequest : IRestRequest
+ public class AcceptInviteRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"invite/{InviteId}";
diff --git a/src/Discord.Net/API/Client/Rest/AckMessage.cs b/src/Discord.Net/API/Client/Rest/AckMessage.cs
index 55b885e52..1678ed34b 100644
--- a/src/Discord.Net/API/Client/Rest/AckMessage.cs
+++ b/src/Discord.Net/API/Client/Rest/AckMessage.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class AckMessageRequest : IRestRequest
+ public class AckMessageRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"channels/{ChannelId}/messages/{MessageId}/ack";
diff --git a/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs b/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs
index 03be2f653..c7a9f57ac 100644
--- a/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs
+++ b/src/Discord.Net/API/Client/Rest/AddChannelPermission.cs
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class AddChannelPermissionsRequest : IRestRequest
+ public class AddChannelPermissionsRequest : IRestRequest
{
string IRestRequest.Method => "PUT";
string IRestRequest.Endpoint => $"channels/{ChannelId}/permissions/{TargetId}";
diff --git a/src/Discord.Net/API/Client/Rest/AddGuildBan.cs b/src/Discord.Net/API/Client/Rest/AddGuildBan.cs
index 295e39259..7699a74e4 100644
--- a/src/Discord.Net/API/Client/Rest/AddGuildBan.cs
+++ b/src/Discord.Net/API/Client/Rest/AddGuildBan.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class AddGuildBanRequest : IRestRequest
+ public class AddGuildBanRequest : IRestRequest
{
string IRestRequest.Method => "PUT";
string IRestRequest.Endpoint => $"guilds/{GuildId}/bans/{UserId}?delete-message-days={PruneDays}";
diff --git a/src/Discord.Net/API/Client/Rest/CreateChannel.cs b/src/Discord.Net/API/Client/Rest/CreateChannel.cs
index 6a083e315..0dc45bc43 100644
--- a/src/Discord.Net/API/Client/Rest/CreateChannel.cs
+++ b/src/Discord.Net/API/Client/Rest/CreateChannel.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class CreateChannelRequest : IRestRequest
+ public class CreateChannelRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"guilds/{GuildId}/channels";
diff --git a/src/Discord.Net/API/Client/Rest/CreateGuild.cs b/src/Discord.Net/API/Client/Rest/CreateGuild.cs
index e7b00934b..baa1f455e 100644
--- a/src/Discord.Net/API/Client/Rest/CreateGuild.cs
+++ b/src/Discord.Net/API/Client/Rest/CreateGuild.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class CreateGuildRequest : IRestRequest
+ public class CreateGuildRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"guilds";
diff --git a/src/Discord.Net/API/Client/Rest/CreateInvite.cs b/src/Discord.Net/API/Client/Rest/CreateInvite.cs
index 2d9ae6a50..a55b9c7e9 100644
--- a/src/Discord.Net/API/Client/Rest/CreateInvite.cs
+++ b/src/Discord.Net/API/Client/Rest/CreateInvite.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class CreateInviteRequest : IRestRequest
+ public class CreateInviteRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"channels/{ChannelId}/invites";
diff --git a/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs b/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs
index 526267590..2d413a8d9 100644
--- a/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs
+++ b/src/Discord.Net/API/Client/Rest/CreatePrivateChannel.cs
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class CreatePrivateChannelRequest : IRestRequest
+ public class CreatePrivateChannelRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"users/@me/channels";
diff --git a/src/Discord.Net/API/Client/Rest/CreateRole.cs b/src/Discord.Net/API/Client/Rest/CreateRole.cs
index 8daa8145c..87715490d 100644
--- a/src/Discord.Net/API/Client/Rest/CreateRole.cs
+++ b/src/Discord.Net/API/Client/Rest/CreateRole.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class CreateRoleRequest : IRestRequest
+ public class CreateRoleRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"guilds/{GuildId}/roles";
diff --git a/src/Discord.Net/API/Client/Rest/DeleteChannel.cs b/src/Discord.Net/API/Client/Rest/DeleteChannel.cs
index 0a44d3b3c..6443c2387 100644
--- a/src/Discord.Net/API/Client/Rest/DeleteChannel.cs
+++ b/src/Discord.Net/API/Client/Rest/DeleteChannel.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class DeleteChannelRequest : IRestRequest
+ public class DeleteChannelRequest : IRestRequest
{
string IRestRequest.Method => "DELETE";
string IRestRequest.Endpoint => $"channels/{ChannelId}";
diff --git a/src/Discord.Net/API/Client/Rest/DeleteInvite.cs b/src/Discord.Net/API/Client/Rest/DeleteInvite.cs
index b469caa00..5de8b348b 100644
--- a/src/Discord.Net/API/Client/Rest/DeleteInvite.cs
+++ b/src/Discord.Net/API/Client/Rest/DeleteInvite.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class DeleteInviteRequest : IRestRequest
+ public class DeleteInviteRequest : IRestRequest
{
string IRestRequest.Method => "DELETE";
string IRestRequest.Endpoint => $"invite/{InviteCode}";
diff --git a/src/Discord.Net/API/Client/Rest/DeleteMessage.cs b/src/Discord.Net/API/Client/Rest/DeleteMessage.cs
index c209baa3a..33921cd1a 100644
--- a/src/Discord.Net/API/Client/Rest/DeleteMessage.cs
+++ b/src/Discord.Net/API/Client/Rest/DeleteMessage.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class DeleteMessageRequest : IRestRequest
+ public class DeleteMessageRequest : IRestRequest
{
string IRestRequest.Method => "DELETE";
string IRestRequest.Endpoint => $"channels/{ChannelId}/messages/{MessageId}";
diff --git a/src/Discord.Net/API/Client/Rest/DeleteRole.cs b/src/Discord.Net/API/Client/Rest/DeleteRole.cs
index 3ad327121..650ece9f2 100644
--- a/src/Discord.Net/API/Client/Rest/DeleteRole.cs
+++ b/src/Discord.Net/API/Client/Rest/DeleteRole.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class DeleteRoleRequest : IRestRequest
+ public class DeleteRoleRequest : IRestRequest
{
string IRestRequest.Method => "DELETE";
string IRestRequest.Endpoint => $"guilds/{GuildId}/roles/{RoleId}";
diff --git a/src/Discord.Net/API/Client/Rest/Gateway.cs b/src/Discord.Net/API/Client/Rest/Gateway.cs
index e728b46a0..ef9486ca1 100644
--- a/src/Discord.Net/API/Client/Rest/Gateway.cs
+++ b/src/Discord.Net/API/Client/Rest/Gateway.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GatewayRequest : IRestRequest
+ public class GatewayRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"gateway";
@@ -11,7 +11,7 @@ namespace Discord.API.Client.Rest
bool IRestRequest.IsPrivate => false;
}
- public sealed class GatewayResponse
+ public class GatewayResponse
{
[JsonProperty("url")]
public string Url { get; set; }
diff --git a/src/Discord.Net/API/Client/Rest/GetBans.cs b/src/Discord.Net/API/Client/Rest/GetBans.cs
index e4638ce32..ee07cb242 100644
--- a/src/Discord.Net/API/Client/Rest/GetBans.cs
+++ b/src/Discord.Net/API/Client/Rest/GetBans.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetBansRequest : IRestRequest
+ public class GetBansRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"guilds/{GuildId}/bans";
diff --git a/src/Discord.Net/API/Client/Rest/GetInvite.cs b/src/Discord.Net/API/Client/Rest/GetInvite.cs
index 708a99e46..27de264f0 100644
--- a/src/Discord.Net/API/Client/Rest/GetInvite.cs
+++ b/src/Discord.Net/API/Client/Rest/GetInvite.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetInviteRequest : IRestRequest
+ public class GetInviteRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"invite/{InviteCode}";
diff --git a/src/Discord.Net/API/Client/Rest/GetInvites.cs b/src/Discord.Net/API/Client/Rest/GetInvites.cs
index dc056ad5a..079c54ef5 100644
--- a/src/Discord.Net/API/Client/Rest/GetInvites.cs
+++ b/src/Discord.Net/API/Client/Rest/GetInvites.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetInvitesRequest : IRestRequest
+ public class GetInvitesRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"guilds/{GuildId}/invites";
diff --git a/src/Discord.Net/API/Client/Rest/GetMessages.cs b/src/Discord.Net/API/Client/Rest/GetMessages.cs
index c5809ded1..b72b05c8b 100644
--- a/src/Discord.Net/API/Client/Rest/GetMessages.cs
+++ b/src/Discord.Net/API/Client/Rest/GetMessages.cs
@@ -4,7 +4,7 @@ using System.Text;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetMessagesRequest : IRestRequest
+ public class GetMessagesRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint
diff --git a/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs b/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs
index 307fd01eb..7dc97ef31 100644
--- a/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs
+++ b/src/Discord.Net/API/Client/Rest/GetVoiceRegions.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetVoiceRegionsRequest : IRestRequest
+ public class GetVoiceRegionsRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"voice/regions";
@@ -11,7 +11,7 @@ namespace Discord.API.Client.Rest
bool IRestRequest.IsPrivate => false;
}
- public sealed class GetVoiceRegionsResponse
+ public class GetVoiceRegionsResponse
{
[JsonProperty("sample_hostname")]
public string Hostname { get; set; }
diff --git a/src/Discord.Net/API/Client/Rest/GetWidget.cs b/src/Discord.Net/API/Client/Rest/GetWidget.cs
index bdb9b3a99..3b1006358 100644
--- a/src/Discord.Net/API/Client/Rest/GetWidget.cs
+++ b/src/Discord.Net/API/Client/Rest/GetWidget.cs
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetWidgetRequest : IRestRequest
+ public class GetWidgetRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"servers/{GuildId}/widget.json";
@@ -19,9 +19,9 @@ namespace Discord.API.Client.Rest
}
}
- public sealed class GetWidgetResponse
+ public class GetWidgetResponse
{
- public sealed class Channel
+ public class Channel
{
[JsonProperty("id"), JsonConverter(typeof(LongStringConverter))]
public ulong Id { get; set; }
@@ -30,7 +30,7 @@ namespace Discord.API.Client.Rest
[JsonProperty("position")]
public int Position { get; set; }
}
- public sealed class User : UserReference
+ public class User : UserReference
{
[JsonProperty("avatar_url")]
public string AvatarUrl { get; set; }
@@ -39,7 +39,7 @@ namespace Discord.API.Client.Rest
[JsonProperty("game")]
public UserGame Game { get; set; }
}
- public sealed class UserGame
+ public class UserGame
{
[JsonProperty("id")]
public int Id { get; set; }
diff --git a/src/Discord.Net/API/Client/Rest/KickMember.cs b/src/Discord.Net/API/Client/Rest/KickMember.cs
index db69fbcd9..96804ff6b 100644
--- a/src/Discord.Net/API/Client/Rest/KickMember.cs
+++ b/src/Discord.Net/API/Client/Rest/KickMember.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class KickMemberRequest : IRestRequest
+ public class KickMemberRequest : IRestRequest
{
string IRestRequest.Method => "DELETE";
string IRestRequest.Endpoint => $"guilds/{GuildId}/members/{UserId}";
diff --git a/src/Discord.Net/API/Client/Rest/LeaveGuild.cs b/src/Discord.Net/API/Client/Rest/LeaveGuild.cs
index 01dec28e0..6a8b3c0cf 100644
--- a/src/Discord.Net/API/Client/Rest/LeaveGuild.cs
+++ b/src/Discord.Net/API/Client/Rest/LeaveGuild.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class LeaveGuildRequest : IRestRequest
+ public class LeaveGuildRequest : IRestRequest
{
string IRestRequest.Method => "DELETE";
string IRestRequest.Endpoint => $"guilds/{GuildId}";
diff --git a/src/Discord.Net/API/Client/Rest/Login.cs b/src/Discord.Net/API/Client/Rest/Login.cs
index f4b0b0c92..ab7efc31b 100644
--- a/src/Discord.Net/API/Client/Rest/Login.cs
+++ b/src/Discord.Net/API/Client/Rest/Login.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class LoginRequest : IRestRequest
+ public class LoginRequest : IRestRequest
{
string IRestRequest.Method => Email != null ? "POST" : "GET";
string IRestRequest.Endpoint => $"auth/login";
@@ -16,7 +16,7 @@ namespace Discord.API.Client.Rest
public string Password { get; set; }
}
- public sealed class LoginResponse
+ public class LoginResponse
{
[JsonProperty("token")]
public string Token { get; set; }
diff --git a/src/Discord.Net/API/Client/Rest/Logout.cs b/src/Discord.Net/API/Client/Rest/Logout.cs
index 5df18dbe1..78f8059e5 100644
--- a/src/Discord.Net/API/Client/Rest/Logout.cs
+++ b/src/Discord.Net/API/Client/Rest/Logout.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class LogoutRequest : IRestRequest
+ public class LogoutRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"auth/logout";
diff --git a/src/Discord.Net/API/Client/Rest/PruneMembers.cs b/src/Discord.Net/API/Client/Rest/PruneMembers.cs
index ea0b86e41..41771f7d6 100644
--- a/src/Discord.Net/API/Client/Rest/PruneMembers.cs
+++ b/src/Discord.Net/API/Client/Rest/PruneMembers.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class PruneMembersRequest : IRestRequest
+ public class PruneMembersRequest : IRestRequest
{
string IRestRequest.Method => IsSimulation ? "GET" : "POST";
string IRestRequest.Endpoint => $"guilds/{GuildId}/prune?days={Days}";
@@ -21,7 +21,7 @@ namespace Discord.API.Client.Rest
}
}
- public sealed class PruneMembersResponse
+ public class PruneMembersResponse
{
[JsonProperty("pruned")]
public int Pruned { get; set; }
diff --git a/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs b/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs
index 3e6e06e90..c704eadbc 100644
--- a/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs
+++ b/src/Discord.Net/API/Client/Rest/RemoveChannelPermission.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class RemoveChannelPermissionsRequest : IRestRequest
+ public class RemoveChannelPermissionsRequest : IRestRequest
{
string IRestRequest.Method => "DELETE";
string IRestRequest.Endpoint => $"channels/{ChannelId}/permissions/{TargetId}";
diff --git a/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs b/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs
index e126f6bc2..c6d48c944 100644
--- a/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs
+++ b/src/Discord.Net/API/Client/Rest/RemoveGuildBan.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class RemoveGuildBanRequest : IRestRequest
+ public class RemoveGuildBanRequest : IRestRequest
{
string IRestRequest.Method => "DELETE";
string IRestRequest.Endpoint => $"guilds/{GuildId}/bans/{UserId}";
diff --git a/src/Discord.Net/API/Client/Rest/ReorderChannels.cs b/src/Discord.Net/API/Client/Rest/ReorderChannels.cs
index 3f768cf07..c481eda43 100644
--- a/src/Discord.Net/API/Client/Rest/ReorderChannels.cs
+++ b/src/Discord.Net/API/Client/Rest/ReorderChannels.cs
@@ -5,7 +5,7 @@ using System.Linq;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class ReorderChannelsRequest : IRestRequest
+ public class ReorderChannelsRequest : IRestRequest
{
string IRestRequest.Method => "PATCH";
string IRestRequest.Endpoint => $"guilds/{GuildId}/channels";
@@ -19,7 +19,7 @@ namespace Discord.API.Client.Rest
}
bool IRestRequest.IsPrivate => false;
- public sealed class Channel
+ public class Channel
{
[JsonProperty("id"), JsonConverter(typeof(LongStringConverter))]
public ulong Id { get; set; }
diff --git a/src/Discord.Net/API/Client/Rest/ReorderRoles.cs b/src/Discord.Net/API/Client/Rest/ReorderRoles.cs
index 5eb9a9d11..23d73541f 100644
--- a/src/Discord.Net/API/Client/Rest/ReorderRoles.cs
+++ b/src/Discord.Net/API/Client/Rest/ReorderRoles.cs
@@ -5,7 +5,7 @@ using System.Linq;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class ReorderRolesRequest : IRestRequest
+ public class ReorderRolesRequest : IRestRequest
{
string IRestRequest.Method => "PATCH";
string IRestRequest.Endpoint => $"guilds/{GuildId}/roles";
@@ -19,7 +19,7 @@ namespace Discord.API.Client.Rest
}
bool IRestRequest.IsPrivate => false;
- public sealed class Role
+ public class Role
{
[JsonProperty("id"), JsonConverter(typeof(LongStringConverter))]
public ulong Id { get; set; }
diff --git a/src/Discord.Net/API/Client/Rest/SendFile.cs b/src/Discord.Net/API/Client/Rest/SendFile.cs
index 7b1a6b084..8d072d0e3 100644
--- a/src/Discord.Net/API/Client/Rest/SendFile.cs
+++ b/src/Discord.Net/API/Client/Rest/SendFile.cs
@@ -4,7 +4,7 @@ using System.IO;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class SendFileRequest : IRestFileRequest
+ public class SendFileRequest : IRestFileRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"channels/{ChannelId}/messages";
diff --git a/src/Discord.Net/API/Client/Rest/SendIsTyping.cs b/src/Discord.Net/API/Client/Rest/SendIsTyping.cs
index abaceb96c..aab017c67 100644
--- a/src/Discord.Net/API/Client/Rest/SendIsTyping.cs
+++ b/src/Discord.Net/API/Client/Rest/SendIsTyping.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class SendIsTypingRequest : IRestRequest
+ public class SendIsTypingRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"channels/{ChannelId}/typing";
diff --git a/src/Discord.Net/API/Client/Rest/SendMessage.cs b/src/Discord.Net/API/Client/Rest/SendMessage.cs
index c58d00d9e..6c6d1ae10 100644
--- a/src/Discord.Net/API/Client/Rest/SendMessage.cs
+++ b/src/Discord.Net/API/Client/Rest/SendMessage.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class SendMessageRequest : IRestRequest
+ public class SendMessageRequest : IRestRequest
{
string IRestRequest.Method => "POST";
string IRestRequest.Endpoint => $"channels/{ChannelId}/messages";
diff --git a/src/Discord.Net/API/Client/Rest/UpdateChannel.cs b/src/Discord.Net/API/Client/Rest/UpdateChannel.cs
index f09c8ba87..cccd4b096 100644
--- a/src/Discord.Net/API/Client/Rest/UpdateChannel.cs
+++ b/src/Discord.Net/API/Client/Rest/UpdateChannel.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class UpdateChannelRequest : IRestRequest
+ public class UpdateChannelRequest : IRestRequest
{
string IRestRequest.Method => "PATCH";
string IRestRequest.Endpoint => $"channels/{ChannelId}";
diff --git a/src/Discord.Net/API/Client/Rest/UpdateGuild.cs b/src/Discord.Net/API/Client/Rest/UpdateGuild.cs
index 163dc437e..4ff530554 100644
--- a/src/Discord.Net/API/Client/Rest/UpdateGuild.cs
+++ b/src/Discord.Net/API/Client/Rest/UpdateGuild.cs
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class UpdateGuildRequest : IRestRequest
+ public class UpdateGuildRequest : IRestRequest
{
string IRestRequest.Method => "PATCH";
string IRestRequest.Endpoint => $"guilds/{GuildId}";
diff --git a/src/Discord.Net/API/Client/Rest/UpdateMember.cs b/src/Discord.Net/API/Client/Rest/UpdateMember.cs
index 1c90560ac..0bc5274d0 100644
--- a/src/Discord.Net/API/Client/Rest/UpdateMember.cs
+++ b/src/Discord.Net/API/Client/Rest/UpdateMember.cs
@@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class UpdateMemberRequest : IRestRequest
+ public class UpdateMemberRequest : IRestRequest
{
string IRestRequest.Method => "PATCH";
string IRestRequest.Endpoint => $"guilds/{GuildId}/members/{UserId}";
diff --git a/src/Discord.Net/API/Client/Rest/UpdateMessage.cs b/src/Discord.Net/API/Client/Rest/UpdateMessage.cs
index ede0a0797..5b4480a4b 100644
--- a/src/Discord.Net/API/Client/Rest/UpdateMessage.cs
+++ b/src/Discord.Net/API/Client/Rest/UpdateMessage.cs
@@ -4,7 +4,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class UpdateMessageRequest : IRestRequest
+ public class UpdateMessageRequest : IRestRequest
{
string IRestRequest.Method => "PATCH";
string IRestRequest.Endpoint => $"channels/{ChannelId}/messages/{MessageId}";
diff --git a/src/Discord.Net/API/Client/Rest/UpdateProfile.cs b/src/Discord.Net/API/Client/Rest/UpdateProfile.cs
index d89e60983..08f28d868 100644
--- a/src/Discord.Net/API/Client/Rest/UpdateProfile.cs
+++ b/src/Discord.Net/API/Client/Rest/UpdateProfile.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class UpdateProfileRequest : IRestRequest
+ public class UpdateProfileRequest : IRestRequest
{
string IRestRequest.Method => "PATCH";
string IRestRequest.Endpoint => $"users/@me";
diff --git a/src/Discord.Net/API/Client/Rest/UpdateRole.cs b/src/Discord.Net/API/Client/Rest/UpdateRole.cs
index 9ebc1e76f..7aac774b7 100644
--- a/src/Discord.Net/API/Client/Rest/UpdateRole.cs
+++ b/src/Discord.Net/API/Client/Rest/UpdateRole.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Client.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class UpdateRoleRequest : IRestRequest
+ public class UpdateRoleRequest : IRestRequest
{
string IRestRequest.Method => "PATCH";
string IRestRequest.Endpoint => $"guilds/{GuildId}/roles/{RoleId}";
diff --git a/src/Discord.Net/API/Client/VoiceSocket/Commands/Heartbeat.cs b/src/Discord.Net/API/Client/VoiceSocket/Commands/Heartbeat.cs
index a9727dd91..349a8a28b 100644
--- a/src/Discord.Net/API/Client/VoiceSocket/Commands/Heartbeat.cs
+++ b/src/Discord.Net/API/Client/VoiceSocket/Commands/Heartbeat.cs
@@ -1,6 +1,6 @@
namespace Discord.API.Client.VoiceSocket
{
- public sealed class HeartbeatCommand : IWebSocketMessage
+ public class HeartbeatCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.Heartbeat;
object IWebSocketMessage.Payload => EpochTime.GetMilliseconds();
diff --git a/src/Discord.Net/API/Client/VoiceSocket/Commands/Identify.cs b/src/Discord.Net/API/Client/VoiceSocket/Commands/Identify.cs
index 1836b234c..fbb38b9d0 100644
--- a/src/Discord.Net/API/Client/VoiceSocket/Commands/Identify.cs
+++ b/src/Discord.Net/API/Client/VoiceSocket/Commands/Identify.cs
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.VoiceSocket
{
- public sealed class IdentifyCommand : IWebSocketMessage
+ public class IdentifyCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.Identify;
object IWebSocketMessage.Payload => this;
diff --git a/src/Discord.Net/API/Client/VoiceSocket/Commands/SelectProtocol.cs b/src/Discord.Net/API/Client/VoiceSocket/Commands/SelectProtocol.cs
index aa8e8127b..d860efe45 100644
--- a/src/Discord.Net/API/Client/VoiceSocket/Commands/SelectProtocol.cs
+++ b/src/Discord.Net/API/Client/VoiceSocket/Commands/SelectProtocol.cs
@@ -2,13 +2,13 @@
namespace Discord.API.Client.VoiceSocket
{
- public sealed class SelectProtocolCommand : IWebSocketMessage
+ public class SelectProtocolCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.SelectProtocol;
object IWebSocketMessage.Payload => this;
bool IWebSocketMessage.IsPrivate => false;
- public sealed class Data
+ public class Data
{
[JsonProperty("address")]
public string Address { get; set; }
diff --git a/src/Discord.Net/API/Client/VoiceSocket/Commands/SetSpeaking.cs b/src/Discord.Net/API/Client/VoiceSocket/Commands/SetSpeaking.cs
index 13ab00524..6022c4d58 100644
--- a/src/Discord.Net/API/Client/VoiceSocket/Commands/SetSpeaking.cs
+++ b/src/Discord.Net/API/Client/VoiceSocket/Commands/SetSpeaking.cs
@@ -2,7 +2,7 @@
namespace Discord.API.Client.VoiceSocket
{
- public sealed class SetSpeakingCommand : IWebSocketMessage
+ public class SetSpeakingCommand : IWebSocketMessage
{
int IWebSocketMessage.OpCode => (int)OpCodes.Speaking;
object IWebSocketMessage.Payload => this;
diff --git a/src/Discord.Net/API/Client/VoiceSocket/Events/Ready.cs b/src/Discord.Net/API/Client/VoiceSocket/Events/Ready.cs
index b0fa34c1d..6fdced897 100644
--- a/src/Discord.Net/API/Client/VoiceSocket/Events/Ready.cs
+++ b/src/Discord.Net/API/Client/VoiceSocket/Events/Ready.cs
@@ -2,7 +2,7 @@
namespace Discord.API.Client.VoiceSocket
{
- public sealed class ReadyEvent
+ public class ReadyEvent
{
[JsonProperty("ssrc")]
public uint SSRC { get; set; }
diff --git a/src/Discord.Net/API/Client/VoiceSocket/Events/SessionDescription.cs b/src/Discord.Net/API/Client/VoiceSocket/Events/SessionDescription.cs
index 13f190cfc..042c5278d 100644
--- a/src/Discord.Net/API/Client/VoiceSocket/Events/SessionDescription.cs
+++ b/src/Discord.Net/API/Client/VoiceSocket/Events/SessionDescription.cs
@@ -2,7 +2,7 @@
namespace Discord.API.Client.VoiceSocket
{
- public sealed class SessionDescriptionEvent
+ public class SessionDescriptionEvent
{
[JsonProperty("secret_key")]
public byte[] SecretKey { get; set; }
diff --git a/src/Discord.Net/API/Client/VoiceSocket/Events/Speaking.cs b/src/Discord.Net/API/Client/VoiceSocket/Events/Speaking.cs
index b3de0f800..59268c4e6 100644
--- a/src/Discord.Net/API/Client/VoiceSocket/Events/Speaking.cs
+++ b/src/Discord.Net/API/Client/VoiceSocket/Events/Speaking.cs
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace Discord.API.Client.VoiceSocket
{
- public sealed class SpeakingEvent
+ public class SpeakingEvent
{
[JsonProperty("user_id"), JsonConverter(typeof(LongStringConverter))]
public ulong UserId { get; set; }
diff --git a/src/Discord.Net/API/Converters.cs b/src/Discord.Net/API/Converters.cs
index 1142e5755..5d80ca99f 100644
--- a/src/Discord.Net/API/Converters.cs
+++ b/src/Discord.Net/API/Converters.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Discord.API.Converters
{
- public sealed class LongStringConverter : JsonConverter
+ public class LongStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
=> objectType == typeof(ulong);
@@ -14,7 +14,7 @@ namespace Discord.API.Converters
=> writer.WriteValue(((ulong)value).ToIdString());
}
- public sealed class NullableLongStringConverter : JsonConverter
+ public class NullableLongStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
=> objectType == typeof(ulong?);
@@ -24,7 +24,7 @@ namespace Discord.API.Converters
=> writer.WriteValue(((ulong?)value).ToIdString());
}
- /*public sealed class LongStringEnumerableConverter : JsonConverter
+ /*public class LongStringEnumerableConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(IEnumerable);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
@@ -55,7 +55,7 @@ namespace Discord.API.Converters
}
}*/
- internal sealed class LongStringArrayConverter : JsonConverter
+ internal class LongStringArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(IEnumerable);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
diff --git a/src/Discord.Net/API/Status/Common/StatusResult.cs b/src/Discord.Net/API/Status/Common/StatusResult.cs
index 314a180c7..74728c578 100644
--- a/src/Discord.Net/API/Status/Common/StatusResult.cs
+++ b/src/Discord.Net/API/Status/Common/StatusResult.cs
@@ -5,7 +5,7 @@ namespace Discord.API.Status
{
public class StatusResult
{
- public sealed class PageData
+ public class PageData
{
[JsonProperty("id")]
public string Id { get; set; }
@@ -17,7 +17,7 @@ namespace Discord.API.Status
public DateTime? UpdatedAt { get; set; }
}
- public sealed class IncidentData
+ public class IncidentData
{
[JsonProperty("id")]
public string Id { get; set; }
@@ -50,7 +50,7 @@ namespace Discord.API.Status
public IncidentUpdateData[] Updates { get; set; }
}
- public sealed class IncidentUpdateData
+ public class IncidentUpdateData
{
[JsonProperty("id")]
public string Id { get; set; }
diff --git a/src/Discord.Net/API/Status/Rest/ActiveMaintenances.cs b/src/Discord.Net/API/Status/Rest/ActiveMaintenances.cs
index 5a8412f96..638c176a5 100644
--- a/src/Discord.Net/API/Status/Rest/ActiveMaintenances.cs
+++ b/src/Discord.Net/API/Status/Rest/ActiveMaintenances.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Status.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetActiveMaintenancesRequest : IRestRequest
+ public class GetActiveMaintenancesRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"scheduled-maintenances/active.json";
diff --git a/src/Discord.Net/API/Status/Rest/AllIncidents.cs b/src/Discord.Net/API/Status/Rest/AllIncidents.cs
index 13f43e022..81a82ce51 100644
--- a/src/Discord.Net/API/Status/Rest/AllIncidents.cs
+++ b/src/Discord.Net/API/Status/Rest/AllIncidents.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Status.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetAllIncidentsRequest : IRestRequest
+ public class GetAllIncidentsRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"incidents.json";
diff --git a/src/Discord.Net/API/Status/Rest/UnresolvedIncidents.cs b/src/Discord.Net/API/Status/Rest/UnresolvedIncidents.cs
index f07de061c..1665dde75 100644
--- a/src/Discord.Net/API/Status/Rest/UnresolvedIncidents.cs
+++ b/src/Discord.Net/API/Status/Rest/UnresolvedIncidents.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Status.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetUnresolvedIncidentsRequest : IRestRequest
+ public class GetUnresolvedIncidentsRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"incidents/unresolved.json";
diff --git a/src/Discord.Net/API/Status/Rest/UpcomingMaintenances.cs b/src/Discord.Net/API/Status/Rest/UpcomingMaintenances.cs
index 769602381..afc812cc9 100644
--- a/src/Discord.Net/API/Status/Rest/UpcomingMaintenances.cs
+++ b/src/Discord.Net/API/Status/Rest/UpcomingMaintenances.cs
@@ -3,7 +3,7 @@
namespace Discord.API.Status.Rest
{
[JsonObject(MemberSerialization.OptIn)]
- public sealed class GetUpcomingMaintenancesRequest : IRestRequest
+ public class GetUpcomingMaintenancesRequest : IRestRequest
{
string IRestRequest.Method => "GET";
string IRestRequest.Endpoint => $"scheduled-maintenances/upcoming.json";
diff --git a/src/Discord.Net/DiscordClient.cs b/src/Discord.Net/DiscordClient.cs
index 5a8cef107..dc0e4b807 100644
--- a/src/Discord.Net/DiscordClient.cs
+++ b/src/Discord.Net/DiscordClient.cs
@@ -31,7 +31,6 @@ namespace Discord
private readonly ConcurrentDictionary _channels;
private readonly ConcurrentDictionary _privateChannels; //Key = RecipientId
private Dictionary _regions;
- private CancellationTokenSource _cancelTokenSource;
internal Logger Logger { get; }
@@ -138,7 +137,7 @@ namespace Discord
//Networking
ClientAPI = new RestClient(Config, DiscordConfig.ClientAPIUrl, Log.CreateLogger("ClientAPI"));
StatusAPI = new RestClient(Config, DiscordConfig.StatusAPIUrl, Log.CreateLogger("StatusAPI"));
- GatewaySocket = new GatewaySocket(this, Log.CreateLogger("Gateway"));
+ GatewaySocket = new GatewaySocket(Config, Serializer, Log.CreateLogger("Gateway"));
GatewaySocket.Connected += (s, e) =>
{
if (State == ConnectionState.Connecting)
@@ -148,7 +147,7 @@ namespace Discord
GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e);
if (Config.UseMessageQueue)
- MessageQueue = new MessageQueue(this, Log.CreateLogger("MessageQueue"));
+ MessageQueue = new MessageQueue(ClientAPI, Log.CreateLogger("MessageQueue"));
//Extensibility
Services = new ServiceManager(this);
@@ -182,9 +181,7 @@ namespace Discord
{
using (await _connectionLock.LockAsync().ConfigureAwait(false))
{
- if (State != ConnectionState.Disconnected)
- await Disconnect().ConfigureAwait(false);
- await _taskManager.Stop().ConfigureAwait(false);
+ await Disconnect().ConfigureAwait(false);
_taskManager.ClearException();
Stopwatch stopwatch = null;
@@ -193,19 +190,20 @@ namespace Discord
State = ConnectionState.Connecting;
_disconnectedEvent.Reset();
- _cancelTokenSource = new CancellationTokenSource();
- CancelToken = _cancelTokenSource.Token;
- GatewaySocket.ParentCancelToken = CancelToken;
+ var cancelSource = new CancellationTokenSource();
+ CancelToken = cancelSource.Token;
+ ClientAPI.CancelToken = CancelToken;
+ StatusAPI.CancelToken = CancelToken;
await Login(email, password, token).ConfigureAwait(false);
- await GatewaySocket.Connect().ConfigureAwait(false);
+ await GatewaySocket.Connect(ClientAPI, CancelToken).ConfigureAwait(false);
List tasks = new List();
tasks.Add(CancelToken.Wait());
if (Config.UseMessageQueue)
tasks.Add(MessageQueue.Run(CancelToken, Config.MessageQueueInterval));
- await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
+ await _taskManager.Start(tasks, cancelSource).ConfigureAwait(false);
GatewaySocket.WaitForConnection(CancelToken);
if (Config.LogLevel >= LogSeverity.Verbose)
@@ -248,8 +246,6 @@ namespace Discord
SaveToken(tokenPath, cacheKey, token);
ClientAPI.Token = token;
- GatewaySocket.Token = token;
- GatewaySocket.SessionId = null;
//Cache other stuff
var regionsResponse = (await ClientAPI.Send(new GetVoiceRegionsRequest()).ConfigureAwait(false));
@@ -261,7 +257,6 @@ namespace Discord
State = ConnectionState.Connected;
_connectedEvent.Set();
- ClientAPI.CancelToken = CancelToken;
SendStatus();
OnConnected();
}
@@ -274,7 +269,10 @@ namespace Discord
State = ConnectionState.Disconnecting;
if (oldState == ConnectionState.Connected)
- await ClientAPI.Send(new LogoutRequest()).ConfigureAwait(false);
+ {
+ try { await ClientAPI.Send(new LogoutRequest()).ConfigureAwait(false); }
+ catch (OperationCanceledException) { }
+ }
if (Config.UseMessageQueue)
MessageQueue.Clear();
@@ -282,8 +280,6 @@ namespace Discord
await GatewaySocket.Disconnect().ConfigureAwait(false);
ClientAPI.Token = null;
- GatewaySocket.Token = null;
- GatewaySocket.SessionId = null;
_servers.Clear();
_channels.Clear();
@@ -481,8 +477,6 @@ namespace Discord
if (Config.LogLevel >= LogSeverity.Verbose)
stopwatch = Stopwatch.StartNew();
var data = e.Payload.ToObject(Serializer);
- GatewaySocket.StartHeartbeat(data.HeartbeatInterval);
- GatewaySocket.SessionId = data.SessionId;
SessionId = data.SessionId;
PrivateUser = new User(this, data.User.Id, null);
PrivateUser.Update(data.User);
@@ -509,12 +503,6 @@ namespace Discord
}
}
break;
- case "RESUMED":
- {
- var data = e.Payload.ToObject(Serializer);
- GatewaySocket.StartHeartbeat(data.HeartbeatInterval);
- }
- break;
//Servers
case "GUILD_CREATE":
@@ -1018,6 +1006,10 @@ namespace Discord
}
break;
+ //Handled in GatewaySocket
+ case "RESUMED":
+ break;
+
//Ignored
case "USER_SETTINGS_UPDATE":
case "GUILD_INTEGRATIONS_UPDATE":
diff --git a/src/Discord.Net/DiscordConfig.cs b/src/Discord.Net/DiscordConfig.cs
index cf2f59f0d..89f746fcd 100644
--- a/src/Discord.Net/DiscordConfig.cs
+++ b/src/Discord.Net/DiscordConfig.cs
@@ -57,7 +57,6 @@ namespace Discord
private DiscordMode _mode = DiscordMode.Bot;
/// User Agent string to use when connecting to Discord.
- [JsonIgnore]
public string UserAgent { get; private set; }
//Rest
diff --git a/src/Discord.Net/Enums/ChannelType.cs b/src/Discord.Net/Enums/ChannelType.cs
index b3d6ce6e8..9ed49a701 100644
--- a/src/Discord.Net/Enums/ChannelType.cs
+++ b/src/Discord.Net/Enums/ChannelType.cs
@@ -2,7 +2,7 @@
namespace Discord
{
- public sealed class ChannelType : StringEnum, IEquatable
+ public class ChannelType : StringEnum, IEquatable
{
/// A text-only channel.
public static ChannelType Text { get; } = new ChannelType("text");
diff --git a/src/Discord.Net/Enums/PermissionTarget.cs b/src/Discord.Net/Enums/PermissionTarget.cs
index 2da27cabc..38a70e013 100644
--- a/src/Discord.Net/Enums/PermissionTarget.cs
+++ b/src/Discord.Net/Enums/PermissionTarget.cs
@@ -1,6 +1,6 @@
namespace Discord
{
- public sealed class PermissionTarget : StringEnum
+ public class PermissionTarget : StringEnum
{
/// A text-only channel.
public static PermissionTarget Role { get; } = new PermissionTarget("role");
diff --git a/src/Discord.Net/Enums/UserStatus.cs b/src/Discord.Net/Enums/UserStatus.cs
index da126a261..80def4234 100644
--- a/src/Discord.Net/Enums/UserStatus.cs
+++ b/src/Discord.Net/Enums/UserStatus.cs
@@ -1,6 +1,6 @@
namespace Discord
{
- public sealed class UserStatus : StringEnum
+ public class UserStatus : StringEnum
{
/// User is currently online and active.
public static UserStatus Online { get; } = new UserStatus("online");
diff --git a/src/Discord.Net/Logging/LogManager.cs b/src/Discord.Net/Logging/LogManager.cs
index f52031df9..576ec7679 100644
--- a/src/Discord.Net/Logging/LogManager.cs
+++ b/src/Discord.Net/Logging/LogManager.cs
@@ -2,7 +2,7 @@
namespace Discord.Logging
{
- public sealed class LogManager
+ public class LogManager
{
private readonly DiscordClient _client;
diff --git a/src/Discord.Net/Logging/Logger.cs b/src/Discord.Net/Logging/Logger.cs
index 6c1cc1a4b..9d7a6111b 100644
--- a/src/Discord.Net/Logging/Logger.cs
+++ b/src/Discord.Net/Logging/Logger.cs
@@ -2,7 +2,7 @@
namespace Discord.Logging
{
- public sealed class Logger
+ public class Logger
{
private readonly LogManager _manager;
diff --git a/src/Discord.Net/MessageQueue.cs b/src/Discord.Net/MessageQueue.cs
index 284e967c7..2759f458e 100644
--- a/src/Discord.Net/MessageQueue.cs
+++ b/src/Discord.Net/MessageQueue.cs
@@ -1,5 +1,6 @@
using Discord.API.Client.Rest;
using Discord.Logging;
+using Discord.Net.Rest;
using System;
using System.Collections.Concurrent;
using System.Net;
@@ -9,7 +10,7 @@ using System.Threading.Tasks;
namespace Discord.Net
{
/// Manages an outgoing message queue for DiscordClient.
- public sealed class MessageQueue
+ public class MessageQueue
{
private interface IQueuedAction
{
@@ -52,7 +53,7 @@ namespace Discord.Net
private const int WarningStart = 30;
private readonly Random _nonceRand;
- private readonly DiscordClient _client;
+ private readonly RestClient _rest;
private readonly Logger _logger;
private readonly ConcurrentQueue _pendingActions;
private readonly ConcurrentDictionary _pendingSends;
@@ -61,9 +62,9 @@ namespace Discord.Net
/// Gets the current number of queued actions.
public int Count { get; private set; }
- internal MessageQueue(DiscordClient client, Logger logger)
+ internal MessageQueue(RestClient rest, Logger logger)
{
- _client = client;
+ _rest = rest;
_logger = logger;
_nonceRand = new Random();
@@ -73,7 +74,7 @@ namespace Discord.Net
internal Message QueueSend(Channel channel, string text, bool isTTS)
{
- Message msg = new Message(0, channel, channel.IsPrivate ? _client.PrivateUser : channel.Server.CurrentUser);
+ Message msg = new Message(0, channel, channel.IsPrivate ? channel.Client.PrivateUser : channel.Server.CurrentUser);
msg.RawText = text;
msg.Text = msg.Resolve(text);
msg.Nonce = GenerateNonce();
@@ -135,7 +136,7 @@ namespace Discord.Net
Nonce = msg.Nonce.ToString(),
IsTTS = msg.IsTTS
};
- var response = await _client.ClientAPI.Send(request).ConfigureAwait(false);
+ var response = await _rest.Send(request).ConfigureAwait(false);
msg.Id = response.Id;
msg.Update(response);
msg.State = MessageState.Normal;
@@ -153,7 +154,7 @@ namespace Discord.Net
{
Content = text
};
- await _client.ClientAPI.Send(request).ConfigureAwait(false);
+ await _rest.Send(request).ConfigureAwait(false);
}
catch (Exception ex) { _logger.Error("Failed to edit message", ex); }
}
@@ -165,7 +166,7 @@ namespace Discord.Net
try
{
var request = new DeleteMessageRequest(msg.Channel.Id, msg.Id);
- await _client.ClientAPI.Send(request).ConfigureAwait(false);
+ await _rest.Send(request).ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { } //Ignore
catch (Exception ex) { _logger.Error("Failed to delete message", ex); }
diff --git a/src/Discord.Net/Models/Channel.cs b/src/Discord.Net/Models/Channel.cs
index b1cfb0e6b..60b10e12f 100644
--- a/src/Discord.Net/Models/Channel.cs
+++ b/src/Discord.Net/Models/Channel.cs
@@ -12,7 +12,7 @@ using APIChannel = Discord.API.Client.Channel;
namespace Discord
{
- public sealed class Channel : IMentionable
+ public class Channel : IMentionable
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
@@ -28,7 +28,7 @@ namespace Discord
}
}
- public sealed class PermissionOverwrite
+ public class PermissionOverwrite
{
public PermissionTarget TargetType { get; }
public ulong TargetId { get; }
diff --git a/src/Discord.Net/Models/Color.cs b/src/Discord.Net/Models/Color.cs
index c62bfecbb..c30f9737c 100644
--- a/src/Discord.Net/Models/Color.cs
+++ b/src/Discord.Net/Models/Color.cs
@@ -2,7 +2,7 @@
namespace Discord
{
- public sealed class Color : IEquatable
+ public class Color : IEquatable
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
diff --git a/src/Discord.Net/Models/Invite.cs b/src/Discord.Net/Models/Invite.cs
index 3bd300098..eac38bdfb 100644
--- a/src/Discord.Net/Models/Invite.cs
+++ b/src/Discord.Net/Models/Invite.cs
@@ -8,11 +8,11 @@ using APIInvite = Discord.API.Client.Invite;
namespace Discord
{
- public sealed class Invite
+ public class Invite
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
- public sealed class ServerInfo
+ public class ServerInfo
{
/// Returns the unique identifier of this server.
public ulong Id { get; }
@@ -25,7 +25,7 @@ namespace Discord
Name = name;
}
}
- public sealed class ChannelInfo
+ public class ChannelInfo
{
/// Returns the unique identifier of this channel.
public ulong Id { get; }
@@ -38,7 +38,7 @@ namespace Discord
Name = name;
}
}
- public sealed class InviterInfo
+ public class InviterInfo
{
/// Returns the unique identifier for this user.
public ulong Id { get; }
diff --git a/src/Discord.Net/Models/Message.cs b/src/Discord.Net/Models/Message.cs
index 2568806eb..b6ef02a6c 100644
--- a/src/Discord.Net/Models/Message.cs
+++ b/src/Discord.Net/Models/Message.cs
@@ -22,7 +22,7 @@ namespace Discord
Failed
}
- public sealed class Message
+ public class Message
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
@@ -111,7 +111,7 @@ namespace Discord
}
}*/
- public sealed class Attachment : File
+ public class Attachment : File
{
/// Unique identifier for this file.
public string Id { get; internal set; }
@@ -123,7 +123,7 @@ namespace Discord
internal Attachment() { }
}
- public sealed class Embed
+ public class Embed
{
/// URL of this embed.
public string Url { get; internal set; }
@@ -143,7 +143,7 @@ namespace Discord
internal Embed() { }
}
- public sealed class EmbedLink
+ public class EmbedLink
{
/// URL of this embed provider.
public string Url { get; internal set; }
diff --git a/src/Discord.Net/Models/Permissions.cs b/src/Discord.Net/Models/Permissions.cs
index 4d4d148ff..e992b748e 100644
--- a/src/Discord.Net/Models/Permissions.cs
+++ b/src/Discord.Net/Models/Permissions.cs
@@ -31,7 +31,7 @@ namespace Discord
UseVoiceActivation = 25
}
- public sealed class ServerPermissions : Permissions
+ public class ServerPermissions : Permissions
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
@@ -60,7 +60,7 @@ namespace Discord
public bool ManageServer { get { return GetBit(PermissionsBits.ManageServer); } set { SetBit(PermissionsBits.ManageServer, value); } }
}
- public sealed class ChannelPermissions : Permissions
+ public class ChannelPermissions : Permissions
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
@@ -167,7 +167,7 @@ namespace Discord
public bool Equals(Permissions permission) => permission?._rawValue == _rawValue;
}
- public sealed class DualChannelPermissions
+ public class DualChannelPermissions
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
diff --git a/src/Discord.Net/Models/Profile.cs b/src/Discord.Net/Models/Profile.cs
index 08e462f1d..077e0731c 100644
--- a/src/Discord.Net/Models/Profile.cs
+++ b/src/Discord.Net/Models/Profile.cs
@@ -6,7 +6,7 @@ using APIUser = Discord.API.Client.User;
namespace Discord
{
- public sealed class Profile
+ public class Profile
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
@@ -72,8 +72,6 @@ namespace Discord
};
var loginResponse = await Client.ClientAPI.Send(loginRequest).ConfigureAwait(false);
Client.ClientAPI.Token = loginResponse.Token;
- Client.GatewaySocket.Token = loginResponse.Token;
- Client.GatewaySocket.SessionId = null;
}
}
diff --git a/src/Discord.Net/Models/Region.cs b/src/Discord.Net/Models/Region.cs
index dcb8de12b..5b0354048 100644
--- a/src/Discord.Net/Models/Region.cs
+++ b/src/Discord.Net/Models/Region.cs
@@ -1,6 +1,6 @@
namespace Discord
{
- public sealed class Region
+ public class Region
{
public string Id { get; }
public string Name { get; }
diff --git a/src/Discord.Net/Models/Role.cs b/src/Discord.Net/Models/Role.cs
index a7aa805d2..8334baa63 100644
--- a/src/Discord.Net/Models/Role.cs
+++ b/src/Discord.Net/Models/Role.cs
@@ -9,7 +9,7 @@ using APIRole = Discord.API.Client.Role;
namespace Discord
{
- public sealed class Role : IMentionable
+ public class Role : IMentionable
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
diff --git a/src/Discord.Net/Models/Server.cs b/src/Discord.Net/Models/Server.cs
index f0ef11bcd..13f80572e 100644
--- a/src/Discord.Net/Models/Server.cs
+++ b/src/Discord.Net/Models/Server.cs
@@ -12,7 +12,7 @@ using System.Threading.Tasks;
namespace Discord
{
/// Represents a Discord server (also known as a guild).
- public sealed class Server
+ public class Server
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
@@ -21,7 +21,7 @@ namespace Discord
internal static string GetSplashUrl(ulong serverId, string splashId)
=> splashId != null ? $"{DiscordConfig.ClientAPIUrl}guilds/{serverId}/splashes/{splashId}.jpg" : null;
- public sealed class Emoji
+ public class Emoji
{
public string Id { get; }
diff --git a/src/Discord.Net/Models/User.cs b/src/Discord.Net/Models/User.cs
index ef9200b10..2ae7223f5 100644
--- a/src/Discord.Net/Models/User.cs
+++ b/src/Discord.Net/Models/User.cs
@@ -9,7 +9,7 @@ using APIMember = Discord.API.Client.Member;
namespace Discord
{
- public sealed class User
+ public class User
{
private readonly static Action _cloner = DynamicIL.CreateCloner();
diff --git a/src/Discord.Net/Net/HttpException.cs b/src/Discord.Net/Net/HttpException.cs
index afb17bcaf..8bfdbf73b 100644
--- a/src/Discord.Net/Net/HttpException.cs
+++ b/src/Discord.Net/Net/HttpException.cs
@@ -7,7 +7,7 @@ namespace Discord.Net
#if NET46
[Serializable]
#endif
- public sealed class HttpException : Exception
+ public class HttpException : Exception
{
public HttpStatusCode StatusCode { get; }
diff --git a/src/Discord.Net/Net/Rest/BuiltInEngine.cs b/src/Discord.Net/Net/Rest/BuiltInEngine.cs
index cb37d6648..7e422b49a 100644
--- a/src/Discord.Net/Net/Rest/BuiltInEngine.cs
+++ b/src/Discord.Net/Net/Rest/BuiltInEngine.cs
@@ -13,7 +13,7 @@ using Nito.AsyncEx;
namespace Discord.Net.Rest
{
- internal sealed class BuiltInEngine : IRestEngine
+ internal class BuiltInEngine : IRestEngine
{
private const int HR_SECURECHANNELFAILED = -2146233079;
diff --git a/src/Discord.Net/Net/Rest/RestClient.cs b/src/Discord.Net/Net/Rest/RestClient.cs
index 727182037..8e5ce180c 100644
--- a/src/Discord.Net/Net/Rest/RestClient.cs
+++ b/src/Discord.Net/Net/Rest/RestClient.cs
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Discord.Net.Rest
{
- public sealed partial class RestClient
+ public partial class RestClient
{
private struct RestResults
{
diff --git a/src/Discord.Net/Net/Rest/SharpRestEngine.cs b/src/Discord.Net/Net/Rest/SharpRestEngine.cs
index 8ec300c11..325808702 100644
--- a/src/Discord.Net/Net/Rest/SharpRestEngine.cs
+++ b/src/Discord.Net/Net/Rest/SharpRestEngine.cs
@@ -11,7 +11,7 @@ using RestSharpClient = RestSharp.RestClient;
namespace Discord.Net.Rest
{
- internal sealed class RestSharpEngine : IRestEngine
+ internal class RestSharpEngine : IRestEngine
{
private const int HR_SECURECHANNELFAILED = -2146233079;
diff --git a/src/Discord.Net/Net/TimeoutException.cs b/src/Discord.Net/Net/TimeoutException.cs
index 542ee542a..051eeb263 100644
--- a/src/Discord.Net/Net/TimeoutException.cs
+++ b/src/Discord.Net/Net/TimeoutException.cs
@@ -5,7 +5,7 @@ namespace Discord.Net
#if NET46
[Serializable]
#endif
- public sealed class TimeoutException : OperationCanceledException
+ public class TimeoutException : OperationCanceledException
{
public TimeoutException()
: base("An operation has timed out.")
diff --git a/src/Discord.Net/Net/WebSocketException.cs b/src/Discord.Net/Net/WebSocketException.cs
index d2c61b644..b845d90c4 100644
--- a/src/Discord.Net/Net/WebSocketException.cs
+++ b/src/Discord.Net/Net/WebSocketException.cs
@@ -2,7 +2,7 @@
namespace Discord.Net
{
- public sealed class WebSocketException : Exception
+ public class WebSocketException : Exception
{
public int Code { get; }
public string Reason { get; }
diff --git a/src/Discord.Net/Net/WebSockets/BuiltInEngine.cs b/src/Discord.Net/Net/WebSockets/BuiltInEngine.cs
index 7c454715d..34224720f 100644
--- a/src/Discord.Net/Net/WebSockets/BuiltInEngine.cs
+++ b/src/Discord.Net/Net/WebSockets/BuiltInEngine.cs
@@ -12,7 +12,7 @@ using WebSocketClient = System.Net.WebSockets.ClientWebSocket;
namespace Discord.Net.WebSockets
{
- internal sealed class BuiltInEngine : IWebSocketEngine
+ internal class BuiltInEngine : IWebSocketEngine
{
private const int ReceiveChunkSize = 12 * 1024; //12KB
private const int SendChunkSize = 4 * 1024; //4KB
@@ -81,7 +81,7 @@ namespace Discord.Net.WebSockets
try
{
- result = await _webSocket.ReceiveAsync(new ArraySegment(buffer), cancelToken);//.ConfigureAwait(false);
+ result = await _webSocket.ReceiveAsync(new ArraySegment(buffer), cancelToken).ConfigureAwait(false);
}
catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT)
{
diff --git a/src/Discord.Net/Net/WebSockets/GatewaySocket.cs b/src/Discord.Net/Net/WebSockets/GatewaySocket.cs
index bd2a8ab6a..1bbe3296d 100644
--- a/src/Discord.Net/Net/WebSockets/GatewaySocket.cs
+++ b/src/Discord.Net/Net/WebSockets/GatewaySocket.cs
@@ -2,6 +2,7 @@
using Discord.API.Client.GatewaySocket;
using Discord.API.Client.Rest;
using Discord.Logging;
+using Discord.Net.Rest;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@@ -11,20 +12,21 @@ using System.Threading.Tasks;
namespace Discord.Net.WebSockets
{
- public sealed class GatewaySocket : WebSocket
+ public class GatewaySocket : WebSocket
{
+ private RestClient _rest;
private uint _lastSequence;
private int _reconnects;
- public string Token { get; internal set; }
- public string SessionId { get; internal set; }
+ //public string Token { get; private set; }
+ public string SessionId { get; private set; }
public event EventHandler ReceivedDispatch = delegate { };
private void OnReceivedDispatch(string type, JToken payload)
=> ReceivedDispatch(this, new WebSocketEventEventArgs(type, payload));
- public GatewaySocket(DiscordClient client, Logger logger)
- : base(client, logger)
+ public GatewaySocket(DiscordConfig config, JsonSerializer serializer, Logger logger)
+ : base(config, serializer, logger)
{
Disconnected += async (s, e) =>
{
@@ -32,17 +34,19 @@ namespace Discord.Net.WebSockets
await Reconnect().ConfigureAwait(false);
};
}
-
- public async Task Connect()
+
+ public async Task Connect(RestClient rest, CancellationToken parentCancelToken)
{
- var gatewayResponse = await _client.ClientAPI.Send(new GatewayRequest()).ConfigureAwait(false);
- Host = gatewayResponse.Url;
- if (Logger.Level >= LogSeverity.Verbose)
- Logger.Verbose($"Login successful, gateway: {gatewayResponse.Url}");
+ _rest = rest;
+ //Token = rest.Token;
+
+ var gatewayResponse = await rest.Send(new GatewayRequest()).ConfigureAwait(false);
+ Logger.Verbose($"Login successful, gateway: {gatewayResponse.Url}");
- await BeginConnect().ConfigureAwait(false);
+ Host = gatewayResponse.Url;
+ await BeginConnect(parentCancelToken).ConfigureAwait(false);
if (SessionId == null)
- SendIdentify(Token);
+ SendIdentify(_rest.Token);
else
SendResume();
}
@@ -50,17 +54,17 @@ namespace Discord.Net.WebSockets
{
try
{
- var cancelToken = ParentCancelToken.Value;
+ var cancelToken = _parentCancelToken;
if (_reconnects++ == 0)
- await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
+ await Task.Delay(_config.ReconnectDelay, cancelToken).ConfigureAwait(false);
else
- await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
+ await Task.Delay(_config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
while (!cancelToken.IsCancellationRequested)
{
try
{
- await Connect().ConfigureAwait(false);
+ await Connect(_rest, _parentCancelToken).ConfigureAwait(false);
break;
}
catch (OperationCanceledException) { throw; }
@@ -68,20 +72,25 @@ namespace Discord.Net.WebSockets
{
Logger.Error("Reconnect failed", ex);
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
- await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
+ await Task.Delay(_config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
}
}
}
catch (OperationCanceledException) { }
}
- public Task Disconnect() => _taskManager.Stop(true);
+ public async Task Disconnect()
+ {
+ await _taskManager.Stop(true).ConfigureAwait(false);
+ //Token = null;
+ SessionId = null;
+ }
protected override async Task Run()
{
List tasks = new List();
tasks.AddRange(_engine.GetTasks(CancelToken));
tasks.Add(HeartbeatAsync(CancelToken));
- await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false);
+ await _taskManager.Start(tasks, _cancelSource).ConfigureAwait(false);
}
protected override Task Cleanup()
{
@@ -103,11 +112,16 @@ namespace Discord.Net.WebSockets
{
case OpCodes.Dispatch:
{
- OnReceivedDispatch(msg.Type, msg.Payload as JToken);
+ if (msg.Type == "READY")
+ SessionId = (msg.Payload as JToken).Value("session_id");
+
+ OnReceivedDispatch(msg.Type, msg.Payload as JToken);
+
if (msg.Type == "READY" || msg.Type == "RESUMED")
{
+ _heartbeatInterval = (msg.Payload as JToken).Value("heartbeat_interval");
_reconnects = 0;
- await EndConnect(); //Complete the connect
+ await EndConnect().ConfigureAwait(false); //Complete the connect
}
}
break;
@@ -142,7 +156,7 @@ namespace Discord.Net.WebSockets
Version = 3,
Token = token,
Properties = props,
- LargeThreshold = _client.Config.UseLargeThreshold ? 100 : (int?)null,
+ LargeThreshold = _config.UseLargeThreshold ? 100 : (int?)null,
UseCompression = true
};
QueueMessage(msg);
@@ -163,11 +177,6 @@ namespace Discord.Net.WebSockets
public void SendRequestMembers(ulong serverId, string query, int limit)
=> QueueMessage(new RequestMembersCommand { GuildId = serverId, Query = query, Limit = limit });
- internal void StartHeartbeat(int interval)
- {
- _heartbeatInterval = interval;
- }
-
//Cancel if either DiscordClient.Disconnect is called, data socket errors or timeout is reached
public override void WaitForConnection(CancellationToken cancelToken)
=> base.WaitForConnection(CancellationTokenSource.CreateLinkedTokenSource(cancelToken, CancelToken).Token);
diff --git a/src/Discord.Net/Net/WebSockets/WS4NetEngine.cs b/src/Discord.Net/Net/WebSockets/WS4NetEngine.cs
index 785e4f813..98eb7db02 100644
--- a/src/Discord.Net/Net/WebSockets/WS4NetEngine.cs
+++ b/src/Discord.Net/Net/WebSockets/WS4NetEngine.cs
@@ -10,7 +10,7 @@ using WebSocketClient = WebSocket4Net.WebSocket;
namespace Discord.Net.WebSockets
{
- internal sealed class WS4NetEngine : IWebSocketEngine
+ internal class WS4NetEngine : IWebSocketEngine
{
private readonly DiscordConfig _config;
private readonly ConcurrentQueue _sendQueue;
diff --git a/src/Discord.Net/Net/WebSockets/WebSocket.cs b/src/Discord.Net/Net/WebSockets/WebSocket.cs
index accf31a07..1057c8d9e 100644
--- a/src/Discord.Net/Net/WebSockets/WebSocket.cs
+++ b/src/Discord.Net/Net/WebSockets/WebSocket.cs
@@ -14,18 +14,18 @@ namespace Discord.Net.WebSockets
{
private readonly AsyncLock _lock;
protected readonly IWebSocketEngine _engine;
- protected readonly DiscordClient _client;
+ protected readonly DiscordConfig _config;
protected readonly ManualResetEventSlim _connectedEvent;
protected readonly TaskManager _taskManager;
protected readonly JsonSerializer _serializer;
- protected CancellationTokenSource _cancelTokenSource;
+ protected CancellationTokenSource _cancelSource;
+ protected CancellationToken _parentCancelToken;
protected int _heartbeatInterval;
private DateTime _lastHeartbeat;
-
+
/// Gets the logger used for this client.
protected internal Logger Logger { get; }
public CancellationToken CancelToken { get; private set; }
- public CancellationToken? ParentCancelToken { get; set; }
public string Host { get; set; }
/// Gets the current connection state of this client.
@@ -38,11 +38,11 @@ namespace Discord.Net.WebSockets
private void OnDisconnected(bool wasUnexpected, Exception error)
=> Disconnected(this, new DisconnectedEventArgs(wasUnexpected, error));
- public WebSocket(DiscordClient client, Logger logger)
+ public WebSocket(DiscordConfig config, JsonSerializer serializer, Logger logger)
{
- _client = client;
+ _config = config;
+ _serializer = serializer;
Logger = logger;
- _serializer = client.Serializer;
_lock = new AsyncLock();
_taskManager = new TaskManager(Cleanup);
@@ -50,9 +50,9 @@ namespace Discord.Net.WebSockets
_connectedEvent = new ManualResetEventSlim(false);
#if !DOTNET5_4
- _engine = new WS4NetEngine(client.Config, _taskManager);
+ _engine = new WS4NetEngine(config, _taskManager);
#else
- _engine = new BuiltInEngine(client.Config);
+ _engine = new BuiltInEngine(config);
#endif
_engine.BinaryMessage += (s, e) =>
{
@@ -69,18 +69,20 @@ namespace Discord.Net.WebSockets
_engine.TextMessage += (s, e) => ProcessMessage(e.Message).Wait();
}
- protected async Task BeginConnect()
+ protected async Task BeginConnect(CancellationToken parentCancelToken)
{
try
{
using (await _lock.LockAsync().ConfigureAwait(false))
{
+ _parentCancelToken = parentCancelToken;
+
await _taskManager.Stop().ConfigureAwait(false);
_taskManager.ClearException();
State = ConnectionState.Connecting;
- _cancelTokenSource = new CancellationTokenSource();
- CancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken.Value).Token;
+ _cancelSource = new CancellationTokenSource();
+ CancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelSource.Token, parentCancelToken).Token;
_lastHeartbeat = DateTime.UtcNow;
await _engine.Connect(Host, CancelToken).ConfigureAwait(false);
@@ -117,7 +119,7 @@ namespace Discord.Net.WebSockets
State = ConnectionState.Disconnecting;
await _engine.Disconnect().ConfigureAwait(false);
- _cancelTokenSource = null;
+ _cancelSource = null;
_connectedEvent.Reset();
if (oldState == ConnectionState.Connecting || oldState == ConnectionState.Connected)
@@ -154,7 +156,7 @@ namespace Discord.Net.WebSockets
{
while (!cancelToken.IsCancellationRequested)
{
- if (this.State == ConnectionState.Connected)
+ if (this.State == ConnectionState.Connected && _heartbeatInterval > 0)
{
SendHeartbeat();
await Task.Delay(_heartbeatInterval, cancelToken).ConfigureAwait(false);
@@ -172,7 +174,7 @@ namespace Discord.Net.WebSockets
{
try
{
- if (!_connectedEvent.Wait(_client.Config.ConnectionTimeout, cancelToken))
+ if (!_connectedEvent.Wait(_config.ConnectionTimeout, cancelToken))
{
if (State != ConnectionState.Connected)
throw new TimeoutException();
diff --git a/src/Discord.Net/ServiceManager.cs b/src/Discord.Net/ServiceManager.cs
index cb1e8e89a..0e6533b90 100644
--- a/src/Discord.Net/ServiceManager.cs
+++ b/src/Discord.Net/ServiceManager.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Discord
{
- public sealed class ServiceManager
+ public class ServiceManager
{
private readonly Dictionary _services;
diff --git a/src/Discord.Net/TaskManager.cs b/src/Discord.Net/TaskManager.cs
index 4934e53d9..7c9a667ce 100644
--- a/src/Discord.Net/TaskManager.cs
+++ b/src/Discord.Net/TaskManager.cs
@@ -9,37 +9,41 @@ using System.Threading.Tasks;
namespace Discord
{
/// Helper class used to manage several tasks and keep them in sync. If any single task errors or stops, all other tasks will also be stopped.
- public sealed class TaskManager
+ public class TaskManager
{
private readonly AsyncLock _lock;
private readonly Func _stopAction;
+ private ExceptionDispatchInfo _stopReason;
private CancellationTokenSource _cancelSource;
private Task _task;
- public bool WasStopExpected => _wasStopExpected;
- private bool _wasStopExpected;
+ public bool StopOnCompletion { get; }
+ public bool WasStopExpected { get; private set; }
public Exception Exception => _stopReason?.SourceException;
- private ExceptionDispatchInfo _stopReason;
- internal TaskManager()
+ internal TaskManager(bool stopOnCompletion)
{
_lock = new AsyncLock();
+ StopOnCompletion = stopOnCompletion;
}
- public TaskManager(Action stopAction)
- : this()
+ public TaskManager(Action stopAction, bool stopOnCompletion = true)
+ : this(stopOnCompletion)
{
_stopAction = TaskHelper.ToAsync(stopAction);
}
- public TaskManager(Func stopAction)
- : this()
+ public TaskManager(Func stopAction, bool stopOnCompletion = true)
+ : this(stopOnCompletion)
{
_stopAction = stopAction;
}
public async Task Start(IEnumerable tasks, CancellationTokenSource cancelSource)
{
+ if (tasks == null) throw new ArgumentNullException(nameof(tasks));
+ if (cancelSource == null) throw new ArgumentNullException(nameof(cancelSource));
+
while (true)
{
var task = _task;
@@ -54,27 +58,36 @@ namespace Discord
continue; //Another thread sneaked in and started this manager before we got a lock, loop and try again
_stopReason = null;
- _wasStopExpected = false;
+ WasStopExpected = false;
Task[] tasksArray = tasks.ToArray();
- Task anyTask = Task.WhenAny(tasksArray);
- Task allTasks = Task.WhenAll(tasksArray);
_task = Task.Run(async () =>
{
- //Wait for the first task to stop or error
- Task firstTask = await anyTask.ConfigureAwait(false);
-
- //Signal the rest of the tasks to stop
- if (firstTask.Exception != null)
- await SignalError(firstTask.Exception).ConfigureAwait(false);
- else
- await SignalStop().ConfigureAwait(false);
-
- //Wait for the other tasks, and signal their errors too just in case
- try { await allTasks.ConfigureAwait(false); }
- catch (AggregateException ex) { await SignalError(ex.InnerExceptions.First()).ConfigureAwait(false); }
- catch (Exception ex) { await SignalError(ex).ConfigureAwait(false); }
+ if (tasksArray.Length > 0)
+ {
+ Task anyTask = tasksArray.Length > 0 ? Task.WhenAny(tasksArray) : null;
+ Task allTasks = tasksArray.Length > 0 ? Task.WhenAll(tasksArray) : null;
+ //Wait for the first task to stop or error
+ Task firstTask = await anyTask.ConfigureAwait(false);
+
+ //Signal the rest of the tasks to stop
+ if (firstTask.Exception != null)
+ await SignalError(firstTask.Exception).ConfigureAwait(false);
+ else if (StopOnCompletion) //Unless we allow for natural completions
+ await SignalStop().ConfigureAwait(false);
+
+ //Wait for the other tasks, and signal their errors too just in case
+ try { await allTasks.ConfigureAwait(false); }
+ catch (AggregateException ex) { await SignalError(ex.InnerExceptions.First()).ConfigureAwait(false); }
+ catch (Exception ex) { await SignalError(ex).ConfigureAwait(false); }
+ }
+
+ if (!StopOnCompletion && !_cancelSource.IsCancellationRequested)
+ {
+ try { await Task.Delay(-1, _cancelSource.Token).ConfigureAwait(false); } //Pause until TaskManager is stopped
+ catch (OperationCanceledException) { }
+ }
//Run the cleanup function within our lock
if (_stopAction != null)
@@ -92,13 +105,9 @@ namespace Discord
using (await _lock.LockAsync().ConfigureAwait(false))
{
if (isExpected)
- _wasStopExpected = true;
-
- if (_task == null) return; //Are we running?
- if (_cancelSource.IsCancellationRequested) return;
+ WasStopExpected = true;
- if (_cancelSource != null)
- _cancelSource.Cancel();
+ Cancel();
}
}
public async Task Stop(bool isExpected = false)
@@ -107,14 +116,11 @@ namespace Discord
using (await _lock.LockAsync().ConfigureAwait(false))
{
if (isExpected)
- _wasStopExpected = true;
+ WasStopExpected = true;
//Cache the task so we still have something to await if Cleanup is run really quickly
- task = _task;
- if (task == null) return; //Are we running?
-
- if (!_cancelSource.IsCancellationRequested && _cancelSource != null)
- _cancelSource.Cancel();
+ task = _task ?? TaskHelper.CompletedTask;
+ Cancel();
}
await task.ConfigureAwait(false);
}
@@ -125,9 +131,7 @@ namespace Discord
{
if (_stopReason != null) return;
- _stopReason = ExceptionDispatchInfo.Capture(ex);
- if (_cancelSource != null)
- _cancelSource.Cancel();
+ Cancel(ex);
}
}
public async Task Error(Exception ex)
@@ -139,15 +143,20 @@ namespace Discord
//Cache the task so we still have something to await if Cleanup is run really quickly
task = _task ?? TaskHelper.CompletedTask;
- if (!_cancelSource.IsCancellationRequested)
- {
- _stopReason = ExceptionDispatchInfo.Capture(ex);
- if (_cancelSource != null)
- _cancelSource.Cancel();
- }
+ Cancel(ex);
}
await task.ConfigureAwait(false);
}
+ private void Cancel(Exception ex = null)
+ {
+ var source = _cancelSource;
+ if (source != null && !source.IsCancellationRequested)
+ {
+ if (ex != null)
+ _stopReason = ExceptionDispatchInfo.Capture(ex);
+ _cancelSource.Cancel();
+ }
+ }
/// Throws an exception if one was captured.
public void ThrowException()
@@ -160,7 +169,7 @@ namespace Discord
using (_lock.Lock())
{
_stopReason = null;
- _wasStopExpected = false;
+ WasStopExpected = false;
}
}
}