| @@ -56,7 +56,11 @@ namespace Discord.API | |||||
| public int Version = 3; | public int Version = 3; | ||||
| [JsonProperty("properties")] | [JsonProperty("properties")] | ||||
| public Dictionary<string, string> Properties = new Dictionary<string, string>(); | public Dictionary<string, string> Properties = new Dictionary<string, string>(); | ||||
| } | |||||
| [JsonProperty("large_threshold", NullValueHandling = NullValueHandling.Ignore)] | |||||
| public int? LargeThreshold; | |||||
| [JsonProperty("compress", NullValueHandling = NullValueHandling.Ignore)] | |||||
| public bool? Compress; | |||||
| } | |||||
| } | } | ||||
| internal sealed class ResumeCommand : WebSocketMessage<ResumeCommand.Data> | internal sealed class ResumeCommand : WebSocketMessage<ResumeCommand.Data> | ||||
| { | { | ||||
| @@ -26,6 +26,8 @@ namespace Discord.Net.WebSockets | |||||
| LoginCommand msg = new LoginCommand(); | LoginCommand msg = new LoginCommand(); | ||||
| msg.Payload.Token = token; | msg.Payload.Token = token; | ||||
| msg.Payload.Properties["$device"] = "Discord.Net"; | msg.Payload.Properties["$device"] = "Discord.Net"; | ||||
| //msg.Payload.LargeThreshold = 50; | |||||
| msg.Payload.Compress = true; | |||||
| QueueMessage(msg); | QueueMessage(msg); | ||||
| } | } | ||||
| private async Task Redirect(string server) | private async Task Redirect(string server) | ||||
| @@ -67,6 +69,7 @@ namespace Discord.Net.WebSockets | |||||
| protected override async Task ProcessMessage(string json) | protected override async Task ProcessMessage(string json) | ||||
| { | { | ||||
| await base.ProcessMessage(json); | |||||
| var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json); | var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json); | ||||
| if (msg.Sequence.HasValue) | if (msg.Sequence.HasValue) | ||||
| _lastSeq = msg.Sequence.Value; | _lastSeq = msg.Sequence.Value; | ||||
| @@ -5,15 +5,21 @@ using System.Threading.Tasks; | |||||
| namespace Discord.Net.WebSockets | namespace Discord.Net.WebSockets | ||||
| { | { | ||||
| internal class WebSocketMessageEventArgs : EventArgs | |||||
| internal class WebSocketBinaryMessageEventArgs : EventArgs | |||||
| { | |||||
| public readonly byte[] Data; | |||||
| public WebSocketBinaryMessageEventArgs(byte[] data) { Data = data; } | |||||
| } | |||||
| internal class WebSocketTextMessageEventArgs : EventArgs | |||||
| { | { | ||||
| public readonly string Message; | public readonly string Message; | ||||
| public WebSocketMessageEventArgs(string msg) { Message = msg; } | |||||
| public WebSocketTextMessageEventArgs(string msg) { Message = msg; } | |||||
| } | } | ||||
| internal interface IWebSocketEngine | internal interface IWebSocketEngine | ||||
| { | { | ||||
| event EventHandler<WebSocketMessageEventArgs> ProcessMessage; | |||||
| event EventHandler<WebSocketBinaryMessageEventArgs> BinaryMessage; | |||||
| event EventHandler<WebSocketTextMessageEventArgs> TextMessage; | |||||
| Task Connect(string host, CancellationToken cancelToken); | Task Connect(string host, CancellationToken cancelToken); | ||||
| Task Disconnect(); | Task Disconnect(); | ||||
| @@ -1,6 +1,8 @@ | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.IO; | |||||
| using System.IO.Compression; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Runtime.ExceptionServices; | using System.Runtime.ExceptionServices; | ||||
| using System.Threading; | using System.Threading; | ||||
| @@ -51,13 +53,23 @@ namespace Discord.Net.WebSockets | |||||
| _connectedEvent = new ManualResetEventSlim(false); | _connectedEvent = new ManualResetEventSlim(false); | ||||
| _engine = new WebSocketSharpEngine(this, client.Config); | _engine = new WebSocketSharpEngine(this, client.Config); | ||||
| _engine.ProcessMessage += async (s, e) => | |||||
| _engine.BinaryMessage += async (s, e) => | |||||
| { | |||||
| using (var compressed = new MemoryStream(e.Data, 2, e.Data.Length - 2)) | |||||
| using (var decompressed = new MemoryStream()) | |||||
| { | |||||
| using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress)) | |||||
| await zlib.CopyToAsync(decompressed); | |||||
| decompressed.Position = 0; | |||||
| using (var reader = new StreamReader(decompressed)) | |||||
| await ProcessMessage(await reader.ReadToEndAsync()); | |||||
| } | |||||
| }; | |||||
| _engine.TextMessage += async (s, e) => | |||||
| { | { | ||||
| if (_logLevel >= LogMessageSeverity.Debug) | |||||
| RaiseOnLog(LogMessageSeverity.Debug, $"In: {e.Message}"); | |||||
| await ProcessMessage(e.Message); | await ProcessMessage(e.Message); | ||||
| }; | }; | ||||
| } | |||||
| } | |||||
| protected async Task BeginConnect() | protected async Task BeginConnect() | ||||
| { | { | ||||
| @@ -185,7 +197,12 @@ namespace Discord.Net.WebSockets | |||||
| RaiseDisconnected(wasDisconnectUnexpected, _disconnectReason?.SourceException); | RaiseDisconnected(wasDisconnectUnexpected, _disconnectReason?.SourceException); | ||||
| } | } | ||||
| protected abstract Task ProcessMessage(string json); | |||||
| protected virtual Task ProcessMessage(string json) | |||||
| { | |||||
| if (_logLevel >= LogMessageSeverity.Debug) | |||||
| RaiseOnLog(LogMessageSeverity.Debug, $"In: {json}"); | |||||
| return TaskHelper.CompletedTask; | |||||
| } | |||||
| protected abstract object GetKeepAlive(); | protected abstract object GetKeepAlive(); | ||||
| protected void QueueMessage(object message) | protected void QueueMessage(object message) | ||||
| @@ -15,11 +15,17 @@ namespace Discord.Net.WebSockets | |||||
| private readonly WebSocket _parent; | private readonly WebSocket _parent; | ||||
| private WSSharpNWebSocket _webSocket; | private WSSharpNWebSocket _webSocket; | ||||
| public event EventHandler<WebSocketMessageEventArgs> ProcessMessage; | |||||
| private void RaiseProcessMessage(string msg) | |||||
| public event EventHandler<WebSocketBinaryMessageEventArgs> BinaryMessage; | |||||
| public event EventHandler<WebSocketTextMessageEventArgs> TextMessage; | |||||
| private void RaiseBinaryMessage(byte[] data) | |||||
| { | { | ||||
| if (ProcessMessage != null) | |||||
| ProcessMessage(this, new WebSocketMessageEventArgs(msg)); | |||||
| if (BinaryMessage != null) | |||||
| BinaryMessage(this, new WebSocketBinaryMessageEventArgs(data)); | |||||
| } | |||||
| private void RaiseTextMessage(string msg) | |||||
| { | |||||
| if (TextMessage != null) | |||||
| TextMessage(this, new WebSocketTextMessageEventArgs(msg)); | |||||
| } | } | ||||
| internal WebSocketSharpEngine(WebSocket parent, DiscordWSClientConfig config) | internal WebSocketSharpEngine(WebSocket parent, DiscordWSClientConfig config) | ||||
| @@ -34,9 +40,15 @@ namespace Discord.Net.WebSockets | |||||
| _webSocket = new WSSharpNWebSocket(host); | _webSocket = new WSSharpNWebSocket(host); | ||||
| _webSocket.EmitOnPing = false; | _webSocket.EmitOnPing = false; | ||||
| _webSocket.EnableRedirection = true; | _webSocket.EnableRedirection = true; | ||||
| _webSocket.Compression = WebSocketSharp.CompressionMethod.None; | |||||
| _webSocket.Compression = WebSocketSharp.CompressionMethod.Deflate; | |||||
| _webSocket.SetProxy(_config.ProxyUrl, _config.ProxyCredentials?.UserName, _config.ProxyCredentials?.Password); | _webSocket.SetProxy(_config.ProxyUrl, _config.ProxyCredentials?.UserName, _config.ProxyCredentials?.Password); | ||||
| _webSocket.OnMessage += (s, e) => RaiseProcessMessage(e.Data); | |||||
| _webSocket.OnMessage += (s, e) => | |||||
| { | |||||
| if (e.Type == WebSocketSharp.Opcode.Binary) | |||||
| RaiseBinaryMessage(e.RawData); | |||||
| else if (e.Type == WebSocketSharp.Opcode.Text) | |||||
| RaiseTextMessage(e.Data); | |||||
| }; | |||||
| _webSocket.OnError += async (s, e) => | _webSocket.OnError += async (s, e) => | ||||
| { | { | ||||
| _parent.RaiseOnLog(LogMessageSeverity.Error, e.Exception.GetBaseException().Message); | _parent.RaiseOnLog(LogMessageSeverity.Error, e.Exception.GetBaseException().Message); | ||||