Lots more websocket and logging bugfixes :D
This commit is contained in:
@@ -1,16 +1,19 @@
|
|||||||
using Discord.API.Rest;
|
using Discord.API.Gateway;
|
||||||
|
using Discord.API.Rest;
|
||||||
using Discord.Net;
|
using Discord.Net;
|
||||||
using Discord.Net.Converters;
|
using Discord.Net.Converters;
|
||||||
using Discord.Net.Queue;
|
using Discord.Net.Queue;
|
||||||
using Discord.Net.Rest;
|
using Discord.Net.Rest;
|
||||||
using Discord.Net.WebSockets;
|
using Discord.Net.WebSockets;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -21,7 +24,9 @@ namespace Discord.API
|
|||||||
{
|
{
|
||||||
public class DiscordApiClient : IDisposable
|
public class DiscordApiClient : IDisposable
|
||||||
{
|
{
|
||||||
internal event Func<SentRequestEventArgs, Task> SentRequest;
|
public event Func<string, string, double, Task> SentRequest;
|
||||||
|
public event Func<int, Task> SentGatewayMessage;
|
||||||
|
public event Func<GatewayOpCodes, string, JToken, Task> ReceivedGatewayEvent;
|
||||||
|
|
||||||
private readonly RequestQueue _requestQueue;
|
private readonly RequestQueue _requestQueue;
|
||||||
private readonly JsonSerializer _serializer;
|
private readonly JsonSerializer _serializer;
|
||||||
@@ -29,6 +34,7 @@ namespace Discord.API
|
|||||||
private readonly IWebSocketClient _gatewayClient;
|
private readonly IWebSocketClient _gatewayClient;
|
||||||
private readonly SemaphoreSlim _connectionLock;
|
private readonly SemaphoreSlim _connectionLock;
|
||||||
private CancellationTokenSource _loginCancelToken, _connectCancelToken;
|
private CancellationTokenSource _loginCancelToken, _connectCancelToken;
|
||||||
|
private string _authToken;
|
||||||
private bool _isDisposed;
|
private bool _isDisposed;
|
||||||
|
|
||||||
public LoginState LoginState { get; private set; }
|
public LoginState LoginState { get; private set; }
|
||||||
@@ -48,6 +54,26 @@ namespace Discord.API
|
|||||||
{
|
{
|
||||||
_gatewayClient = webSocketProvider();
|
_gatewayClient = webSocketProvider();
|
||||||
_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent);
|
_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent);
|
||||||
|
_gatewayClient.BinaryMessage += async (data, index, count) =>
|
||||||
|
{
|
||||||
|
using (var compressed = new MemoryStream(data, index + 2, count - 2))
|
||||||
|
using (var decompressed = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress))
|
||||||
|
zlib.CopyTo(decompressed);
|
||||||
|
decompressed.Position = 0;
|
||||||
|
using (var reader = new StreamReader(decompressed))
|
||||||
|
{
|
||||||
|
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(reader.ReadToEnd());
|
||||||
|
await ReceivedGatewayEvent.Raise((GatewayOpCodes)msg.Operation, msg.Type, msg.Payload as JToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_gatewayClient.TextMessage += async text =>
|
||||||
|
{
|
||||||
|
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(text);
|
||||||
|
await ReceivedGatewayEvent.Raise((GatewayOpCodes)msg.Operation, msg.Type, msg.Payload as JToken).ConfigureAwait(false);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
|
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
|
||||||
@@ -95,6 +121,7 @@ namespace Discord.API
|
|||||||
_loginCancelToken = new CancellationTokenSource();
|
_loginCancelToken = new CancellationTokenSource();
|
||||||
|
|
||||||
AuthTokenType = TokenType.User;
|
AuthTokenType = TokenType.User;
|
||||||
|
_authToken = null;
|
||||||
_restClient.SetHeader("authorization", null);
|
_restClient.SetHeader("authorization", null);
|
||||||
await _requestQueue.SetCancelToken(_loginCancelToken.Token).ConfigureAwait(false);
|
await _requestQueue.SetCancelToken(_loginCancelToken.Token).ConfigureAwait(false);
|
||||||
_restClient.SetCancelToken(_loginCancelToken.Token);
|
_restClient.SetCancelToken(_loginCancelToken.Token);
|
||||||
@@ -106,6 +133,7 @@ namespace Discord.API
|
|||||||
}
|
}
|
||||||
|
|
||||||
AuthTokenType = tokenType;
|
AuthTokenType = tokenType;
|
||||||
|
_authToken = token;
|
||||||
switch (tokenType)
|
switch (tokenType)
|
||||||
{
|
{
|
||||||
case TokenType.Bot:
|
case TokenType.Bot:
|
||||||
@@ -181,7 +209,10 @@ namespace Discord.API
|
|||||||
_gatewayClient.SetCancelToken(_connectCancelToken.Token);
|
_gatewayClient.SetCancelToken(_connectCancelToken.Token);
|
||||||
|
|
||||||
var gatewayResponse = await GetGateway().ConfigureAwait(false);
|
var gatewayResponse = await GetGateway().ConfigureAwait(false);
|
||||||
await _gatewayClient.Connect(gatewayResponse.Url).ConfigureAwait(false);
|
var url = $"{gatewayResponse.Url}?v={DiscordConfig.GatewayAPIVersion}&encoding={DiscordConfig.GatewayEncoding}";
|
||||||
|
await _gatewayClient.Connect(url).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await SendIdentify().ConfigureAwait(false);
|
||||||
|
|
||||||
ConnectionState = ConnectionState.Connected;
|
ConnectionState = ConnectionState.Connected;
|
||||||
}
|
}
|
||||||
@@ -226,13 +257,13 @@ namespace Discord.API
|
|||||||
=> SendInternal(method, endpoint, multipartArgs, true, bucket);
|
=> SendInternal(method, endpoint, multipartArgs, true, bucket);
|
||||||
public async Task<TResponse> Send<TResponse>(string method, string endpoint, GlobalBucket bucket = GlobalBucket.General)
|
public async Task<TResponse> Send<TResponse>(string method, string endpoint, GlobalBucket bucket = GlobalBucket.General)
|
||||||
where TResponse : class
|
where TResponse : class
|
||||||
=> Deserialize<TResponse>(await SendInternal(method, endpoint, null, false, bucket).ConfigureAwait(false));
|
=> DeserializeJson<TResponse>(await SendInternal(method, endpoint, null, false, bucket).ConfigureAwait(false));
|
||||||
public async Task<TResponse> Send<TResponse>(string method, string endpoint, object payload, GlobalBucket bucket = GlobalBucket.General)
|
public async Task<TResponse> Send<TResponse>(string method, string endpoint, object payload, GlobalBucket bucket = GlobalBucket.General)
|
||||||
where TResponse : class
|
where TResponse : class
|
||||||
=> Deserialize<TResponse>(await SendInternal(method, endpoint, payload, false, bucket).ConfigureAwait(false));
|
=> DeserializeJson<TResponse>(await SendInternal(method, endpoint, payload, false, bucket).ConfigureAwait(false));
|
||||||
public async Task<TResponse> Send<TResponse>(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GlobalBucket bucket = GlobalBucket.General)
|
public async Task<TResponse> Send<TResponse>(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GlobalBucket bucket = GlobalBucket.General)
|
||||||
where TResponse : class
|
where TResponse : class
|
||||||
=> Deserialize<TResponse>(await SendInternal(method, endpoint, multipartArgs, false, bucket).ConfigureAwait(false));
|
=> DeserializeJson<TResponse>(await SendInternal(method, endpoint, multipartArgs, false, bucket).ConfigureAwait(false));
|
||||||
|
|
||||||
public Task Send(string method, string endpoint, GuildBucket bucket, ulong guildId)
|
public Task Send(string method, string endpoint, GuildBucket bucket, ulong guildId)
|
||||||
=> SendInternal(method, endpoint, null, true, bucket, guildId);
|
=> SendInternal(method, endpoint, null, true, bucket, guildId);
|
||||||
@@ -242,13 +273,13 @@ namespace Discord.API
|
|||||||
=> SendInternal(method, endpoint, multipartArgs, true, bucket, guildId);
|
=> SendInternal(method, endpoint, multipartArgs, true, bucket, guildId);
|
||||||
public async Task<TResponse> Send<TResponse>(string method, string endpoint, GuildBucket bucket, ulong guildId)
|
public async Task<TResponse> Send<TResponse>(string method, string endpoint, GuildBucket bucket, ulong guildId)
|
||||||
where TResponse : class
|
where TResponse : class
|
||||||
=> Deserialize<TResponse>(await SendInternal(method, endpoint, null, false, bucket, guildId).ConfigureAwait(false));
|
=> DeserializeJson<TResponse>(await SendInternal(method, endpoint, null, false, bucket, guildId).ConfigureAwait(false));
|
||||||
public async Task<TResponse> Send<TResponse>(string method, string endpoint, object payload, GuildBucket bucket, ulong guildId)
|
public async Task<TResponse> Send<TResponse>(string method, string endpoint, object payload, GuildBucket bucket, ulong guildId)
|
||||||
where TResponse : class
|
where TResponse : class
|
||||||
=> Deserialize<TResponse>(await SendInternal(method, endpoint, payload, false, bucket, guildId).ConfigureAwait(false));
|
=> DeserializeJson<TResponse>(await SendInternal(method, endpoint, payload, false, bucket, guildId).ConfigureAwait(false));
|
||||||
public async Task<TResponse> Send<TResponse>(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GuildBucket bucket, ulong guildId)
|
public async Task<TResponse> Send<TResponse>(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GuildBucket bucket, ulong guildId)
|
||||||
where TResponse : class
|
where TResponse : class
|
||||||
=> Deserialize<TResponse>(await SendInternal(method, endpoint, multipartArgs, false, bucket, guildId).ConfigureAwait(false));
|
=> DeserializeJson<TResponse>(await SendInternal(method, endpoint, multipartArgs, false, bucket, guildId).ConfigureAwait(false));
|
||||||
|
|
||||||
private Task<Stream> SendInternal(string method, string endpoint, object payload, bool headerOnly, GlobalBucket bucket)
|
private Task<Stream> SendInternal(string method, string endpoint, object payload, bool headerOnly, GlobalBucket bucket)
|
||||||
=> SendInternal(method, endpoint, payload, headerOnly, BucketGroup.Global, (int)bucket, 0);
|
=> SendInternal(method, endpoint, payload, headerOnly, BucketGroup.Global, (int)bucket, 0);
|
||||||
@@ -264,13 +295,12 @@ namespace Discord.API
|
|||||||
var stopwatch = Stopwatch.StartNew();
|
var stopwatch = Stopwatch.StartNew();
|
||||||
string json = null;
|
string json = null;
|
||||||
if (payload != null)
|
if (payload != null)
|
||||||
json = Serialize(payload);
|
json = SerializeJson(payload);
|
||||||
var responseStream = await _requestQueue.Send(new RestRequest(_restClient, method, endpoint, json, headerOnly), group, bucketId, guildId).ConfigureAwait(false);
|
var responseStream = await _requestQueue.Send(new RestRequest(_restClient, method, endpoint, json, headerOnly), group, bucketId, guildId).ConfigureAwait(false);
|
||||||
int bytes = headerOnly ? 0 : (int)responseStream.Length;
|
|
||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
|
|
||||||
double milliseconds = ToMilliseconds(stopwatch);
|
double milliseconds = ToMilliseconds(stopwatch);
|
||||||
await SentRequest.Raise(new SentRequestEventArgs(method, endpoint, bytes, milliseconds)).ConfigureAwait(false);
|
await SentRequest.Raise(method, endpoint, milliseconds).ConfigureAwait(false);
|
||||||
|
|
||||||
return responseStream;
|
return responseStream;
|
||||||
}
|
}
|
||||||
@@ -282,11 +312,28 @@ namespace Discord.API
|
|||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
|
|
||||||
double milliseconds = ToMilliseconds(stopwatch);
|
double milliseconds = ToMilliseconds(stopwatch);
|
||||||
await SentRequest.Raise(new SentRequestEventArgs(method, endpoint, bytes, milliseconds)).ConfigureAwait(false);
|
await SentRequest.Raise(method, endpoint, milliseconds).ConfigureAwait(false);
|
||||||
|
|
||||||
return responseStream;
|
return responseStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task SendGateway(GatewayOpCodes opCode, object payload, GlobalBucket bucket = GlobalBucket.Gateway)
|
||||||
|
=> SendGateway((int)opCode, payload, BucketGroup.Global, (int)bucket, 0);
|
||||||
|
public Task SendGateway(VoiceOpCodes opCode, object payload, GlobalBucket bucket = GlobalBucket.Gateway)
|
||||||
|
=> SendGateway((int)opCode, payload, BucketGroup.Global, (int)bucket, 0);
|
||||||
|
public Task SendGateway(GatewayOpCodes opCode, object payload, GuildBucket bucket, ulong guildId)
|
||||||
|
=> SendGateway((int)opCode, payload, BucketGroup.Guild, (int)bucket, guildId);
|
||||||
|
public Task SendGateway(VoiceOpCodes opCode, object payload, GuildBucket bucket, ulong guildId)
|
||||||
|
=> SendGateway((int)opCode, payload, BucketGroup.Guild, (int)bucket, guildId);
|
||||||
|
private async Task SendGateway(int opCode, object payload, BucketGroup group, int bucketId, ulong guildId)
|
||||||
|
{
|
||||||
|
//TODO: Add ETF
|
||||||
|
byte[] bytes = null;
|
||||||
|
payload = new WebSocketMessage { Operation = opCode, Payload = payload };
|
||||||
|
if (payload != null)
|
||||||
|
bytes = Encoding.UTF8.GetBytes(SerializeJson(payload));
|
||||||
|
await _requestQueue.Send(new WebSocketRequest(_gatewayClient, bytes, true), group, bucketId, guildId).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
//Auth
|
//Auth
|
||||||
public async Task ValidateToken()
|
public async Task ValidateToken()
|
||||||
@@ -299,6 +346,21 @@ namespace Discord.API
|
|||||||
{
|
{
|
||||||
return await Send<GetGatewayResponse>("GET", "gateway").ConfigureAwait(false);
|
return await Send<GetGatewayResponse>("GET", "gateway").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
public async Task SendIdentify(int largeThreshold = 100, bool useCompression = true)
|
||||||
|
{
|
||||||
|
var props = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["$device"] = "Discord.Net"
|
||||||
|
};
|
||||||
|
var msg = new IdentifyParams()
|
||||||
|
{
|
||||||
|
Token = _authToken,
|
||||||
|
Properties = props,
|
||||||
|
LargeThreshold = largeThreshold,
|
||||||
|
UseCompression = useCompression
|
||||||
|
};
|
||||||
|
await SendGateway(GatewayOpCodes.Identify, msg).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
//Channels
|
//Channels
|
||||||
public async Task<Channel> GetChannel(ulong channelId)
|
public async Task<Channel> GetChannel(ulong channelId)
|
||||||
@@ -986,7 +1048,7 @@ namespace Discord.API
|
|||||||
|
|
||||||
//Helpers
|
//Helpers
|
||||||
private static double ToMilliseconds(Stopwatch stopwatch) => Math.Round((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2);
|
private static double ToMilliseconds(Stopwatch stopwatch) => Math.Round((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2);
|
||||||
private string Serialize(object value)
|
private string SerializeJson(object value)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder(256);
|
var sb = new StringBuilder(256);
|
||||||
using (TextWriter text = new StringWriter(sb, CultureInfo.InvariantCulture))
|
using (TextWriter text = new StringWriter(sb, CultureInfo.InvariantCulture))
|
||||||
@@ -994,7 +1056,7 @@ namespace Discord.API
|
|||||||
_serializer.Serialize(writer, value);
|
_serializer.Serialize(writer, value);
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
private T Deserialize<T>(Stream jsonStream)
|
private T DeserializeJson<T>(Stream jsonStream)
|
||||||
{
|
{
|
||||||
using (TextReader text = new StreamReader(jsonStream))
|
using (TextReader text = new StreamReader(jsonStream))
|
||||||
using (JsonReader reader = new JsonTextReader(text))
|
using (JsonReader reader = new JsonTextReader(text))
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Discord.API
|
|
||||||
{
|
|
||||||
public class DiscordAPISocketClient
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Discord.API.Gateway
|
namespace Discord.API.Gateway
|
||||||
{
|
{
|
||||||
public enum OpCodes : byte
|
public enum GatewayOpCodes : byte
|
||||||
{
|
{
|
||||||
/// <summary> C←S - Used to send most events. </summary>
|
/// <summary> C←S - Used to send most events. </summary>
|
||||||
Dispatch = 0,
|
Dispatch = 0,
|
||||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Discord.API.Gateway
|
namespace Discord.API.Gateway
|
||||||
{
|
{
|
||||||
public class IdentifyCommand
|
public class IdentifyParams
|
||||||
{
|
{
|
||||||
[JsonProperty("token")]
|
[JsonProperty("token")]
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Discord.API.Gateway
|
namespace Discord.API.Gateway
|
||||||
{
|
{
|
||||||
public class RequestMembersCommand
|
public class RequestMembersParams
|
||||||
{
|
{
|
||||||
[JsonProperty("guild_id")]
|
[JsonProperty("guild_id")]
|
||||||
public ulong[] GuildId { get; set; }
|
public ulong[] GuildId { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Discord.API.Gateway
|
namespace Discord.API.Gateway
|
||||||
{
|
{
|
||||||
public class ResumeCommand
|
public class ResumeParams
|
||||||
{
|
{
|
||||||
[JsonProperty("session_id")]
|
[JsonProperty("session_id")]
|
||||||
public string SessionId { get; set; }
|
public string SessionId { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Discord.API.Gateway
|
namespace Discord.API.Gateway
|
||||||
{
|
{
|
||||||
public class UpdateStatusCommand
|
public class UpdateStatusParams
|
||||||
{
|
{
|
||||||
[JsonProperty("idle_since")]
|
[JsonProperty("idle_since")]
|
||||||
public long? IdleSince { get; set; }
|
public long? IdleSince { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Discord.API.Gateway
|
namespace Discord.API.Gateway
|
||||||
{
|
{
|
||||||
public class UpdateVoiceCommand
|
public class UpdateVoiceParams
|
||||||
{
|
{
|
||||||
[JsonProperty("guild_id")]
|
[JsonProperty("guild_id")]
|
||||||
public ulong? GuildId { get; set; }
|
public ulong? GuildId { get; set; }
|
||||||
|
|||||||
18
src/Discord.Net/API/Voice/VoiceOpCodes.cs
Normal file
18
src/Discord.Net/API/Voice/VoiceOpCodes.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Discord.API.Gateway
|
||||||
|
{
|
||||||
|
public enum VoiceOpCodes : byte
|
||||||
|
{
|
||||||
|
/// <summary> C→S - Used to associate a connection with a token. </summary>
|
||||||
|
Identify = 0,
|
||||||
|
/// <summary> C→S - Used to specify configuration. </summary>
|
||||||
|
SelectProtocol = 1,
|
||||||
|
/// <summary> C←S - Used to notify that the voice connection was successful and informs the client of available protocols. </summary>
|
||||||
|
Ready = 2,
|
||||||
|
/// <summary> C↔S - Used to keep the connection alive and measure latency. </summary>
|
||||||
|
Heartbeat = 3,
|
||||||
|
/// <summary> C←S - Used to provide an encryption key to the client. </summary>
|
||||||
|
SessionDescription = 4,
|
||||||
|
/// <summary> C↔S - Used to inform that a certain user is speaking. </summary>
|
||||||
|
Speaking = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace Discord.API
|
namespace Discord.API
|
||||||
{
|
{
|
||||||
public class WebSocketMessage
|
public class WebSocketMessage
|
||||||
{
|
{
|
||||||
[JsonProperty("op")]
|
[JsonProperty("op")]
|
||||||
public int? Operation { get; set; }
|
public int Operation { get; set; }
|
||||||
[JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
[JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public uint? Sequence { get; set; }
|
public uint? Sequence { get; set; }
|
||||||
[JsonProperty("d")]
|
[JsonProperty("d")]
|
||||||
public JToken Payload { get; set; }
|
public object Payload { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ namespace Discord
|
|||||||
public static string Version { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly?.GetName().Version.ToString(3) ?? "Unknown";
|
public static string Version { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly?.GetName().Version.ToString(3) ?? "Unknown";
|
||||||
public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})";
|
public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})";
|
||||||
|
|
||||||
public const int GatewayAPIVersion = 3;
|
public const int GatewayAPIVersion = 3; //TODO: Upgrade to 4
|
||||||
|
public const string GatewayEncoding = "json";
|
||||||
|
|
||||||
public const string ClientAPIUrl = "https://discordapp.com/api/";
|
public const string ClientAPIUrl = "https://discordapp.com/api/";
|
||||||
public const string CDNUrl = "https://cdn.discordapp.com/";
|
public const string CDNUrl = "https://cdn.discordapp.com/";
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
internal static class EventExtensions
|
internal static class EventExtensions
|
||||||
{
|
{
|
||||||
|
//TODO: Optimize these for if there is only 1 subscriber (can we do this?)
|
||||||
public static async Task Raise(this Func<Task> eventHandler)
|
public static async Task Raise(this Func<Task> eventHandler)
|
||||||
{
|
{
|
||||||
var subscriptions = eventHandler?.GetInvocationList();
|
var subscriptions = eventHandler?.GetInvocationList();
|
||||||
@@ -32,7 +33,7 @@ namespace Discord
|
|||||||
await (subscriptions[i] as Func<T1, T2, Task>).Invoke(arg1, arg2).ConfigureAwait(false);
|
await (subscriptions[i] as Func<T1, T2, Task>).Invoke(arg1, arg2).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static async Task Raise<T1, T2, T3>(this Func<T1, T2, Task> eventHandler, T1 arg1, T2 arg2, T3 arg3)
|
public static async Task Raise<T1, T2, T3>(this Func<T1, T2, T3, Task> eventHandler, T1 arg1, T2 arg2, T3 arg3)
|
||||||
{
|
{
|
||||||
var subscriptions = eventHandler?.GetInvocationList();
|
var subscriptions = eventHandler?.GetInvocationList();
|
||||||
if (subscriptions != null)
|
if (subscriptions != null)
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Discord.Net.Rest
|
|
||||||
{
|
|
||||||
public class SentRequestEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public string Method { get; }
|
|
||||||
public string Endpoint { get; }
|
|
||||||
public int ResponseLength { get; }
|
|
||||||
public double Milliseconds { get; }
|
|
||||||
|
|
||||||
public SentRequestEventArgs(string method, string endpoint, int responseLength, double milliseconds)
|
|
||||||
{
|
|
||||||
Method = method;
|
|
||||||
Endpoint = endpoint;
|
|
||||||
ResponseLength = responseLength;
|
|
||||||
Milliseconds = milliseconds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ namespace Discord.Logging
|
|||||||
{
|
{
|
||||||
public LogSeverity Level { get; }
|
public LogSeverity Level { get; }
|
||||||
|
|
||||||
public event Func<LogMessageEventArgs, Task> Message;
|
public event Func<LogMessage, Task> Message;
|
||||||
|
|
||||||
internal LogManager(LogSeverity minSeverity)
|
internal LogManager(LogSeverity minSeverity)
|
||||||
{
|
{
|
||||||
@@ -17,32 +17,32 @@ namespace Discord.Logging
|
|||||||
public async Task Log(LogSeverity severity, string source, string message, Exception ex = null)
|
public async Task Log(LogSeverity severity, string source, string message, Exception ex = null)
|
||||||
{
|
{
|
||||||
if (severity <= Level)
|
if (severity <= Level)
|
||||||
await Message.Raise(new LogMessageEventArgs(severity, source, message, ex)).ConfigureAwait(false);
|
await Message.Raise(new LogMessage(severity, source, message, ex)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
public async Task Log(LogSeverity severity, string source, FormattableString message, Exception ex = null)
|
public async Task Log(LogSeverity severity, string source, FormattableString message, Exception ex = null)
|
||||||
{
|
{
|
||||||
if (severity <= Level)
|
if (severity <= Level)
|
||||||
await Message.Raise(new LogMessageEventArgs(severity, source, message.ToString(), ex)).ConfigureAwait(false);
|
await Message.Raise(new LogMessage(severity, source, message.ToString(), ex)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
public async Task Log(LogSeverity severity, string source, Exception ex)
|
public async Task Log(LogSeverity severity, string source, Exception ex)
|
||||||
{
|
{
|
||||||
if (severity <= Level)
|
if (severity <= Level)
|
||||||
await Message.Raise(new LogMessageEventArgs(severity, source, null, ex)).ConfigureAwait(false);
|
await Message.Raise(new LogMessage(severity, source, null, ex)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
async Task ILogger.Log(LogSeverity severity, string message, Exception ex)
|
async Task ILogger.Log(LogSeverity severity, string message, Exception ex)
|
||||||
{
|
{
|
||||||
if (severity <= Level)
|
if (severity <= Level)
|
||||||
await Message.Raise(new LogMessageEventArgs(severity, "Discord", message, ex)).ConfigureAwait(false);
|
await Message.Raise(new LogMessage(severity, "Discord", message, ex)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
async Task ILogger.Log(LogSeverity severity, FormattableString message, Exception ex)
|
async Task ILogger.Log(LogSeverity severity, FormattableString message, Exception ex)
|
||||||
{
|
{
|
||||||
if (severity <= Level)
|
if (severity <= Level)
|
||||||
await Message.Raise(new LogMessageEventArgs(severity, "Discord", message.ToString(), ex)).ConfigureAwait(false);
|
await Message.Raise(new LogMessage(severity, "Discord", message.ToString(), ex)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
async Task ILogger.Log(LogSeverity severity, Exception ex)
|
async Task ILogger.Log(LogSeverity severity, Exception ex)
|
||||||
{
|
{
|
||||||
if (severity <= Level)
|
if (severity <= Level)
|
||||||
await Message.Raise(new LogMessageEventArgs(severity, "Discord", null, ex)).ConfigureAwait(false);
|
await Message.Raise(new LogMessage(severity, "Discord", null, ex)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Error(string source, string message, Exception ex = null)
|
public Task Error(string source, string message, Exception ex = null)
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ using System.Text;
|
|||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
public class LogMessageEventArgs : EventArgs
|
public struct LogMessage
|
||||||
{
|
{
|
||||||
public LogSeverity Severity { get; }
|
public LogSeverity Severity { get; }
|
||||||
public string Source { get; }
|
public string Source { get; }
|
||||||
public string Message { get; }
|
public string Message { get; }
|
||||||
public Exception Exception { get; }
|
public Exception Exception { get; }
|
||||||
|
|
||||||
public LogMessageEventArgs(LogSeverity severity, string source, string message, Exception exception = null)
|
public LogMessage(LogSeverity severity, string source, string message, Exception exception = null)
|
||||||
{
|
{
|
||||||
Severity = severity;
|
Severity = severity;
|
||||||
Source = source;
|
Source = source;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Discord.Logging
|
namespace Discord.Logging
|
||||||
{
|
{
|
||||||
@@ -15,44 +16,44 @@ namespace Discord.Logging
|
|||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log(LogSeverity severity, string message, Exception exception = null)
|
public Task Log(LogSeverity severity, string message, Exception exception = null)
|
||||||
=> _manager.Log(severity, Name, message, exception);
|
=> _manager.Log(severity, Name, message, exception);
|
||||||
public void Log(LogSeverity severity, FormattableString message, Exception exception = null)
|
public Task Log(LogSeverity severity, FormattableString message, Exception exception = null)
|
||||||
=> _manager.Log(severity, Name, message, exception);
|
=> _manager.Log(severity, Name, message, exception);
|
||||||
|
|
||||||
public void Error(string message, Exception exception = null)
|
public Task Error(string message, Exception exception = null)
|
||||||
=> _manager.Error(Name, message, exception);
|
=> _manager.Error(Name, message, exception);
|
||||||
public void Error(FormattableString message, Exception exception = null)
|
public Task Error(FormattableString message, Exception exception = null)
|
||||||
=> _manager.Error(Name, message, exception);
|
=> _manager.Error(Name, message, exception);
|
||||||
public void Error(Exception exception)
|
public Task Error(Exception exception)
|
||||||
=> _manager.Error(Name, exception);
|
=> _manager.Error(Name, exception);
|
||||||
|
|
||||||
public void Warning(string message, Exception exception = null)
|
public Task Warning(string message, Exception exception = null)
|
||||||
=> _manager.Warning(Name, message, exception);
|
=> _manager.Warning(Name, message, exception);
|
||||||
public void Warning(FormattableString message, Exception exception = null)
|
public Task Warning(FormattableString message, Exception exception = null)
|
||||||
=> _manager.Warning(Name, message, exception);
|
=> _manager.Warning(Name, message, exception);
|
||||||
public void Warning(Exception exception)
|
public Task Warning(Exception exception)
|
||||||
=> _manager.Warning(Name, exception);
|
=> _manager.Warning(Name, exception);
|
||||||
|
|
||||||
public void Info(string message, Exception exception = null)
|
public Task Info(string message, Exception exception = null)
|
||||||
=> _manager.Info(Name, message, exception);
|
=> _manager.Info(Name, message, exception);
|
||||||
public void Info(FormattableString message, Exception exception = null)
|
public Task Info(FormattableString message, Exception exception = null)
|
||||||
=> _manager.Info(Name, message, exception);
|
=> _manager.Info(Name, message, exception);
|
||||||
public void Info(Exception exception)
|
public Task Info(Exception exception)
|
||||||
=> _manager.Info(Name, exception);
|
=> _manager.Info(Name, exception);
|
||||||
|
|
||||||
public void Verbose(string message, Exception exception = null)
|
public Task Verbose(string message, Exception exception = null)
|
||||||
=> _manager.Verbose(Name, message, exception);
|
=> _manager.Verbose(Name, message, exception);
|
||||||
public void Verbose(FormattableString message, Exception exception = null)
|
public Task Verbose(FormattableString message, Exception exception = null)
|
||||||
=> _manager.Verbose(Name, message, exception);
|
=> _manager.Verbose(Name, message, exception);
|
||||||
public void Verbose(Exception exception)
|
public Task Verbose(Exception exception)
|
||||||
=> _manager.Verbose(Name, exception);
|
=> _manager.Verbose(Name, exception);
|
||||||
|
|
||||||
public void Debug(string message, Exception exception = null)
|
public Task Debug(string message, Exception exception = null)
|
||||||
=> _manager.Debug(Name, message, exception);
|
=> _manager.Debug(Name, message, exception);
|
||||||
public void Debug(FormattableString message, Exception exception = null)
|
public Task Debug(FormattableString message, Exception exception = null)
|
||||||
=> _manager.Debug(Name, message, exception);
|
=> _manager.Debug(Name, message, exception);
|
||||||
public void Debug(Exception exception)
|
public Task Debug(Exception exception)
|
||||||
=> _manager.Debug(Name, exception);
|
=> _manager.Debug(Name, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Discord.Net.Queue
|
|||||||
private readonly RequestQueueBucket[] _globalBuckets;
|
private readonly RequestQueueBucket[] _globalBuckets;
|
||||||
private readonly Dictionary<ulong, RequestQueueBucket>[] _guildBuckets;
|
private readonly Dictionary<ulong, RequestQueueBucket>[] _guildBuckets;
|
||||||
private CancellationTokenSource _clearToken;
|
private CancellationTokenSource _clearToken;
|
||||||
private CancellationToken? _parentToken;
|
private CancellationToken _parentToken;
|
||||||
private CancellationToken _cancelToken;
|
private CancellationToken _cancelToken;
|
||||||
|
|
||||||
public RequestQueue()
|
public RequestQueue()
|
||||||
@@ -20,10 +20,12 @@ namespace Discord.Net.Queue
|
|||||||
_lock = new SemaphoreSlim(1, 1);
|
_lock = new SemaphoreSlim(1, 1);
|
||||||
_globalBuckets = new RequestQueueBucket[Enum.GetValues(typeof(GlobalBucket)).Length];
|
_globalBuckets = new RequestQueueBucket[Enum.GetValues(typeof(GlobalBucket)).Length];
|
||||||
_guildBuckets = new Dictionary<ulong, RequestQueueBucket>[Enum.GetValues(typeof(GuildBucket)).Length];
|
_guildBuckets = new Dictionary<ulong, RequestQueueBucket>[Enum.GetValues(typeof(GuildBucket)).Length];
|
||||||
|
|
||||||
_clearToken = new CancellationTokenSource();
|
_clearToken = new CancellationTokenSource();
|
||||||
_cancelToken = _clearToken.Token;
|
_cancelToken = CancellationToken.None;
|
||||||
|
_parentToken = CancellationToken.None;
|
||||||
}
|
}
|
||||||
internal async Task SetCancelToken(CancellationToken cancelToken)
|
public async Task SetCancelToken(CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
await Lock().ConfigureAwait(false);
|
await Lock().ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
@@ -34,7 +36,17 @@ namespace Discord.Net.Queue
|
|||||||
finally { Unlock(); }
|
finally { Unlock(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Stream> Send(IQueuedRequest request, BucketGroup group, int bucketId, ulong guildId)
|
internal Task<Stream> Send(RestRequest request, BucketGroup group, int bucketId, ulong guildId)
|
||||||
|
{
|
||||||
|
request.CancelToken = _cancelToken;
|
||||||
|
return Send(request as IQueuedRequest, group, bucketId, guildId);
|
||||||
|
}
|
||||||
|
internal Task<Stream> Send(WebSocketRequest request, BucketGroup group, int bucketId, ulong guildId)
|
||||||
|
{
|
||||||
|
request.CancelToken = _cancelToken;
|
||||||
|
return Send(request as IQueuedRequest, group, bucketId, guildId);
|
||||||
|
}
|
||||||
|
private async Task<Stream> Send(IQueuedRequest request, BucketGroup group, int bucketId, ulong guildId)
|
||||||
{
|
{
|
||||||
RequestQueueBucket bucket;
|
RequestQueueBucket bucket;
|
||||||
|
|
||||||
@@ -121,13 +133,13 @@ namespace Discord.Net.Queue
|
|||||||
return bucket;
|
return bucket;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void DestroyGlobalBucket(GlobalBucket type)
|
public void DestroyGlobalBucket(GlobalBucket type)
|
||||||
{
|
{
|
||||||
//Assume this object is locked
|
//Assume this object is locked
|
||||||
|
|
||||||
_globalBuckets[(int)type] = null;
|
_globalBuckets[(int)type] = null;
|
||||||
}
|
}
|
||||||
internal void DestroyGuildBucket(GuildBucket type, ulong guildId)
|
public void DestroyGuildBucket(GuildBucket type, ulong guildId)
|
||||||
{
|
{
|
||||||
//Assume this object is locked
|
//Assume this object is locked
|
||||||
|
|
||||||
@@ -153,7 +165,7 @@ namespace Discord.Net.Queue
|
|||||||
_clearToken?.Cancel();
|
_clearToken?.Cancel();
|
||||||
_clearToken = new CancellationTokenSource();
|
_clearToken = new CancellationTokenSource();
|
||||||
if (_parentToken != null)
|
if (_parentToken != null)
|
||||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_clearToken.Token, _parentToken.Value).Token;
|
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_clearToken.Token, _parentToken).Token;
|
||||||
else
|
else
|
||||||
_cancelToken = _clearToken.Token;
|
_cancelToken = _clearToken.Token;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Discord.Net.Queue
|
|||||||
public bool HeaderOnly { get; }
|
public bool HeaderOnly { get; }
|
||||||
public IReadOnlyDictionary<string, object> MultipartParams { get; }
|
public IReadOnlyDictionary<string, object> MultipartParams { get; }
|
||||||
public TaskCompletionSource<Stream> Promise { get; }
|
public TaskCompletionSource<Stream> Promise { get; }
|
||||||
public CancellationToken CancelToken { get; internal set; }
|
public CancellationToken CancelToken { get; set; }
|
||||||
|
|
||||||
public bool IsMultipart => MultipartParams != null;
|
public bool IsMultipart => MultipartParams != null;
|
||||||
|
|
||||||
|
|||||||
@@ -9,25 +9,26 @@ namespace Discord.Net.Queue
|
|||||||
{
|
{
|
||||||
public IWebSocketClient Client { get; }
|
public IWebSocketClient Client { get; }
|
||||||
public byte[] Data { get; }
|
public byte[] Data { get; }
|
||||||
public int Offset { get; }
|
public int DataIndex { get; }
|
||||||
public int Bytes { get; }
|
public int DataCount { get; }
|
||||||
public bool IsText { get; }
|
public bool IsText { get; }
|
||||||
public CancellationToken CancelToken { get; }
|
|
||||||
public TaskCompletionSource<Stream> Promise { get; }
|
public TaskCompletionSource<Stream> Promise { get; }
|
||||||
|
public CancellationToken CancelToken { get; set; }
|
||||||
|
|
||||||
public WebSocketRequest(byte[] data, bool isText, CancellationToken cancelToken) : this(data, 0, data.Length, isText, cancelToken) { }
|
public WebSocketRequest(IWebSocketClient client, byte[] data, bool isText) : this(client, data, 0, data.Length, isText) { }
|
||||||
public WebSocketRequest(byte[] data, int offset, int length, bool isText, CancellationToken cancelToken)
|
public WebSocketRequest(IWebSocketClient client, byte[] data, int index, int count, bool isText)
|
||||||
{
|
{
|
||||||
|
Client = client;
|
||||||
Data = data;
|
Data = data;
|
||||||
Offset = offset;
|
DataIndex = index;
|
||||||
Bytes = length;
|
DataCount = count;
|
||||||
IsText = isText;
|
IsText = isText;
|
||||||
Promise = new TaskCompletionSource<Stream>();
|
Promise = new TaskCompletionSource<Stream>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Stream> Send()
|
public async Task<Stream> Send()
|
||||||
{
|
{
|
||||||
await Client.Send(Data, Offset, Bytes, IsText).ConfigureAwait(false);
|
await Client.Send(Data, DataIndex, DataCount, IsText).ConfigureAwait(false);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Discord.Net.WebSockets
|
|
||||||
{
|
|
||||||
public class BinaryMessageEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public byte[] Data { get; }
|
|
||||||
|
|
||||||
public BinaryMessageEventArgs(byte[] data) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,8 +14,8 @@ namespace Discord.Net.WebSockets
|
|||||||
public const int SendChunkSize = 4 * 1024; //4KB
|
public const int SendChunkSize = 4 * 1024; //4KB
|
||||||
private const int HR_TIMEOUT = -2147012894;
|
private const int HR_TIMEOUT = -2147012894;
|
||||||
|
|
||||||
public event Func<BinaryMessageEventArgs, Task> BinaryMessage;
|
public event Func<byte[], int, int, Task> BinaryMessage;
|
||||||
public event Func<TextMessageEventArgs, Task> TextMessage;
|
public event Func<string, Task> TextMessage;
|
||||||
|
|
||||||
private readonly ClientWebSocket _client;
|
private readonly ClientWebSocket _client;
|
||||||
private Task _task;
|
private Task _task;
|
||||||
@@ -79,12 +79,12 @@ namespace Discord.Net.WebSockets
|
|||||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
|
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Send(byte[] data, int offset, int count, bool isText)
|
public async Task Send(byte[] data, int index, int count, bool isText)
|
||||||
{
|
{
|
||||||
//TODO: If connection is temporarily down, retry?
|
//TODO: If connection is temporarily down, retry?
|
||||||
int frameCount = (int)Math.Ceiling((double)count / SendChunkSize);
|
int frameCount = (int)Math.Ceiling((double)count / SendChunkSize);
|
||||||
|
|
||||||
for (int i = 0; i < frameCount; i++, offset += SendChunkSize)
|
for (int i = 0; i < frameCount; i++, index += SendChunkSize)
|
||||||
{
|
{
|
||||||
bool isLast = i == (frameCount - 1);
|
bool isLast = i == (frameCount - 1);
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ namespace Discord.Net.WebSockets
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _client.SendAsync(new ArraySegment<byte>(data, offset, count), isText ? WebSocketMessageType.Text : WebSocketMessageType.Binary, isLast, _cancelToken).ConfigureAwait(false);
|
await _client.SendAsync(new ArraySegment<byte>(data, index, count), isText ? WebSocketMessageType.Text : WebSocketMessageType.Binary, isLast, _cancelToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT)
|
catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT)
|
||||||
{
|
{
|
||||||
@@ -139,11 +139,11 @@ namespace Discord.Net.WebSockets
|
|||||||
|
|
||||||
var array = stream.ToArray();
|
var array = stream.ToArray();
|
||||||
if (result.MessageType == WebSocketMessageType.Binary)
|
if (result.MessageType == WebSocketMessageType.Binary)
|
||||||
await BinaryMessage.Raise(new BinaryMessageEventArgs(array)).ConfigureAwait(false);
|
await BinaryMessage.Raise(array, 0, array.Length).ConfigureAwait(false);
|
||||||
else if (result.MessageType == WebSocketMessageType.Text)
|
else if (result.MessageType == WebSocketMessageType.Text)
|
||||||
{
|
{
|
||||||
string text = Encoding.UTF8.GetString(array, 0, array.Length);
|
string text = Encoding.UTF8.GetString(array, 0, array.Length);
|
||||||
await TextMessage.Raise(new TextMessageEventArgs(text)).ConfigureAwait(false);
|
await TextMessage.Raise(text).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Discord.Net.WebSockets
|
namespace Discord.Net.WebSockets
|
||||||
{
|
{
|
||||||
//TODO: Add ETF
|
|
||||||
public interface IWebSocketClient
|
public interface IWebSocketClient
|
||||||
{
|
{
|
||||||
event Func<BinaryMessageEventArgs, Task> BinaryMessage;
|
event Func<byte[], int, int, Task> BinaryMessage;
|
||||||
event Func<TextMessageEventArgs, Task> TextMessage;
|
event Func<string, Task> TextMessage;
|
||||||
|
|
||||||
void SetHeader(string key, string value);
|
void SetHeader(string key, string value);
|
||||||
void SetCancelToken(CancellationToken cancelToken);
|
void SetCancelToken(CancellationToken cancelToken);
|
||||||
@@ -16,6 +15,6 @@ namespace Discord.Net.WebSockets
|
|||||||
Task Connect(string host);
|
Task Connect(string host);
|
||||||
Task Disconnect();
|
Task Disconnect();
|
||||||
|
|
||||||
Task Send(byte[] data, int offset, int length, bool isText);
|
Task Send(byte[] data, int index, int count, bool isText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Discord.Net.WebSockets
|
|
||||||
{
|
|
||||||
public class TextMessageEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public string Message { get; }
|
|
||||||
|
|
||||||
public TextMessageEventArgs(string msg) { Message = msg; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@ namespace Discord.Rest
|
|||||||
//TODO: Log Logins/Logouts
|
//TODO: Log Logins/Logouts
|
||||||
public sealed class DiscordClient : IDiscordClient, IDisposable
|
public sealed class DiscordClient : IDiscordClient, IDisposable
|
||||||
{
|
{
|
||||||
public event Func<LogMessageEventArgs, Task> Log;
|
public event Func<LogMessage, Task> Log;
|
||||||
public event Func<Task> LoggedIn, LoggedOut;
|
public event Func<Task> LoggedIn, LoggedOut;
|
||||||
|
|
||||||
private readonly Logger _discordLogger, _restLogger;
|
private readonly Logger _discordLogger, _restLogger;
|
||||||
@@ -39,7 +39,7 @@ namespace Discord.Rest
|
|||||||
config = new DiscordConfig();
|
config = new DiscordConfig();
|
||||||
|
|
||||||
_log = new LogManager(config.LogLevel);
|
_log = new LogManager(config.LogLevel);
|
||||||
_log.Message += async e => await Log.Raise(e).ConfigureAwait(false);
|
_log.Message += async msg => await Log.Raise(msg).ConfigureAwait(false);
|
||||||
_discordLogger = _log.CreateLogger("Discord");
|
_discordLogger = _log.CreateLogger("Discord");
|
||||||
_restLogger = _log.CreateLogger("Rest");
|
_restLogger = _log.CreateLogger("Rest");
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ namespace Discord.Rest
|
|||||||
_requestQueue = new RequestQueue();
|
_requestQueue = new RequestQueue();
|
||||||
|
|
||||||
ApiClient = new API.DiscordApiClient(config.RestClientProvider, requestQueue: _requestQueue);
|
ApiClient = new API.DiscordApiClient(config.RestClientProvider, requestQueue: _requestQueue);
|
||||||
ApiClient.SentRequest += async e => await _log.Verbose("Rest", $"{e.Method} {e.Endpoint}: {e.Milliseconds} ms").ConfigureAwait(false);
|
ApiClient.SentRequest += async (method, endpoint, millis) => await _log.Verbose("Rest", $"{method} {endpoint}: {millis} ms").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Login(string email, string password)
|
public async Task Login(string email, string password)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,10 @@ namespace Discord.WebSocket
|
|||||||
public string Username { get; private set; }
|
public string Username { get; private set; }
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public DMChannel DMChannel { get; internal set; }
|
public DMChannel DMChannel { get; internal set; }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Game? CurrentGame { get; internal set; }
|
||||||
|
/// <inheritdoc />
|
||||||
|
public UserStatus Status { get; internal set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, _avatarId);
|
public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, _avatarId);
|
||||||
@@ -65,11 +69,6 @@ namespace Discord.WebSocket
|
|||||||
public override string ToString() => $"{Username}#{Discriminator}";
|
public override string ToString() => $"{Username}#{Discriminator}";
|
||||||
private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})";
|
private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id})";
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
Game? IUser.CurrentGame => null;
|
|
||||||
/// <inheritdoc />
|
|
||||||
UserStatus IUser.Status => UserStatus.Unknown;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
async Task<IDMChannel> IUser.CreateDMChannel()
|
async Task<IDMChannel> IUser.CreateDMChannel()
|
||||||
=> await CreateDMChannel().ConfigureAwait(false);
|
=> await CreateDMChannel().ConfigureAwait(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user