@@ -51,14 +51,14 @@ namespace Discord.Commands | |||||
.Description("Returns information about commands.") | .Description("Returns information about commands.") | ||||
.Do(async e => | .Do(async e => | ||||
{ | { | ||||
Channel replyChannel = _config.HelpMode == HelpMode.Public ? e.Channel : await e.User.CreateChannel(); | |||||
Channel replyChannel = _config.HelpMode == HelpMode.Public ? e.Channel : await e.User.CreateChannel().ConfigureAwait(false); | |||||
if (e.Args.Length > 0) //Show command help | if (e.Args.Length > 0) //Show command help | ||||
{ | { | ||||
var map = _map.GetItem(string.Join(" ", e.Args)); | var map = _map.GetItem(string.Join(" ", e.Args)); | ||||
if (map != null) | if (map != null) | ||||
await ShowCommandHelp(map, e.User, e.Channel, replyChannel); | |||||
await ShowCommandHelp(map, e.User, e.Channel, replyChannel).ConfigureAwait(false); | |||||
else | else | ||||
await replyChannel.SendMessage("Unable to display help: Unknown command."); | |||||
await replyChannel.SendMessage("Unable to display help: Unknown command.").ConfigureAwait(false); | |||||
} | } | ||||
else //Show general help | else //Show general help | ||||
await ShowGeneralHelp(e.User, e.Channel, replyChannel); | await ShowGeneralHelp(e.User, e.Channel, replyChannel); | ||||
@@ -509,6 +509,9 @@ | |||||
<Compile Include="..\Discord.Net\Net\TimeoutException.cs"> | <Compile Include="..\Discord.Net\Net\TimeoutException.cs"> | ||||
<Link>Net\TimeoutException.cs</Link> | <Link>Net\TimeoutException.cs</Link> | ||||
</Compile> | </Compile> | ||||
<Compile Include="..\Discord.Net\Net\WebSocketException.cs"> | |||||
<Link>Net\WebSockets\WebSocketException.cs</Link> | |||||
</Compile> | |||||
<Compile Include="..\Discord.Net\Net\WebSockets\GatewaySocket.cs"> | <Compile Include="..\Discord.Net\Net\WebSockets\GatewaySocket.cs"> | ||||
<Link>Net\WebSockets\GatewaySocket.cs</Link> | <Link>Net\WebSockets\GatewaySocket.cs</Link> | ||||
</Compile> | </Compile> | ||||
@@ -19,6 +19,6 @@ | |||||
/// <summary> C←S - Used to notify a client that they must reconnect to another gateway. </summary> | /// <summary> C←S - Used to notify a client that they must reconnect to another gateway. </summary> | ||||
Redirect = 7, | Redirect = 7, | ||||
/// <summary> C→S - Used to request all members that were withheld by large_threshold </summary> | /// <summary> C→S - Used to request all members that were withheld by large_threshold </summary> | ||||
RequestGuildMembers = 8 | |||||
RequestGuildMembers = 99 | |||||
} | } | ||||
} | } |
@@ -159,7 +159,7 @@ namespace Discord.Legacy | |||||
if (messages == null) throw new ArgumentNullException(nameof(messages)); | if (messages == null) throw new ArgumentNullException(nameof(messages)); | ||||
foreach (var message in messages) | foreach (var message in messages) | ||||
await message.Delete(); | |||||
await message.Delete().ConfigureAwait(false); | |||||
} | } | ||||
[Obsolete("Use Channel.DownloadMessages")] | [Obsolete("Use Channel.DownloadMessages")] | ||||
@@ -128,7 +128,7 @@ namespace Discord | |||||
Connected += async (s, e) => | Connected += async (s, e) => | ||||
{ | { | ||||
ClientAPI.CancelToken = CancelToken; | ClientAPI.CancelToken = CancelToken; | ||||
await SendStatus(); | |||||
await SendStatus().ConfigureAwait(false); | |||||
}; | }; | ||||
//Extensibility | //Extensibility | ||||
@@ -250,7 +250,7 @@ namespace Discord | |||||
} | } | ||||
//Cache other stuff | //Cache other stuff | ||||
var regionsResponse = (await ClientAPI.Send(new GetVoiceRegionsRequest())); | |||||
var regionsResponse = (await ClientAPI.Send(new GetVoiceRegionsRequest()).ConfigureAwait(false)); | |||||
_regions = regionsResponse.Select(x => new Region(x.Id, x.Name, x.Hostname, x.Port)) | _regions = regionsResponse.Select(x => new Region(x.Id, x.Name, x.Hostname, x.Port)) | ||||
.ToDictionary(x => x.Id); | .ToDictionary(x => x.Id); | ||||
break; | break; | ||||
@@ -213,7 +213,7 @@ namespace Discord | |||||
#region Invites | #region Invites | ||||
/// <summary> Gets all active (non-expired) invites to this server. </summary> | /// <summary> Gets all active (non-expired) invites to this server. </summary> | ||||
public async Task<IEnumerable<Invite>> GetInvites() | public async Task<IEnumerable<Invite>> GetInvites() | ||||
=> (await Server.GetInvites()).Where(x => x.Channel.Id == Id); | |||||
=> (await Server.GetInvites().ConfigureAwait(false)).Where(x => x.Channel.Id == Id); | |||||
/// <summary> Creates a new invite to this channel. </summary> | /// <summary> Creates a new invite to this channel. </summary> | ||||
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to null to never expire. </param> | /// <param name="maxAge"> Time (in seconds) until the invite expires. Set to null to never expire. </param> | ||||
@@ -0,0 +1,25 @@ | |||||
using System; | |||||
namespace Discord.Net | |||||
{ | |||||
public class WebSocketException : Exception | |||||
{ | |||||
public int Code { get; } | |||||
public string Reason { get; } | |||||
public WebSocketException(int code, string reason) | |||||
: base(GenerateMessage(code, reason)) | |||||
{ | |||||
Code = code; | |||||
Reason = reason; | |||||
} | |||||
private static string GenerateMessage(int? code, string reason) | |||||
{ | |||||
if (!String.IsNullOrEmpty(reason)) | |||||
return $"Received close code {code}: {reason}"; | |||||
else | |||||
return $"Received close code {code}"; | |||||
} | |||||
} | |||||
} |
@@ -13,8 +13,10 @@ namespace Discord.Net.WebSockets | |||||
{ | { | ||||
private int _lastSequence; | private int _lastSequence; | ||||
private string _sessionId; | private string _sessionId; | ||||
private string _token; | |||||
private int _reconnects; | |||||
public string Token { get; internal set; } | |||||
public string Token { get { return _token; } internal set { _token = value; _sessionId = null; } } | |||||
public GatewaySocket(DiscordClient client, JsonSerializer serializer, Logger logger) | public GatewaySocket(DiscordClient client, JsonSerializer serializer, Logger logger) | ||||
: base(client, serializer, logger) | : base(client, serializer, logger) | ||||
@@ -29,20 +31,21 @@ namespace Discord.Net.WebSockets | |||||
public async Task Connect() | public async Task Connect() | ||||
{ | { | ||||
await BeginConnect().ConfigureAwait(false); | await BeginConnect().ConfigureAwait(false); | ||||
SendIdentify(Token); | |||||
if (_sessionId == null) | |||||
SendIdentify(Token); | |||||
else | |||||
SendResume(); | |||||
} | } | ||||
private async Task Redirect() | |||||
{ | |||||
await BeginConnect().ConfigureAwait(false); | |||||
SendResume(); | |||||
} | |||||
private async Task Reconnect() | private async Task Reconnect() | ||||
{ | { | ||||
try | try | ||||
{ | { | ||||
var cancelToken = ParentCancelToken.Value; | var cancelToken = ParentCancelToken.Value; | ||||
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); | |||||
while (!cancelToken.IsCancellationRequested) | |||||
if (_reconnects++ == 0) | |||||
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false); | |||||
else | |||||
await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false); | |||||
while (!cancelToken.IsCancellationRequested) | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
@@ -69,8 +72,15 @@ namespace Discord.Net.WebSockets | |||||
tasks.Add(HeartbeatAsync(CancelToken)); | tasks.Add(HeartbeatAsync(CancelToken)); | ||||
await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false); | await _taskManager.Start(tasks, _cancelTokenSource).ConfigureAwait(false); | ||||
} | } | ||||
protected override Task Cleanup() | |||||
{ | |||||
var ex = _taskManager.Exception; | |||||
if (ex != null && (ex as WebSocketException)?.Code != 1012) | |||||
_sessionId = null; //Reset session unless close code 1012 | |||||
return base.Cleanup(); | |||||
} | |||||
protected override async Task ProcessMessage(string json) | |||||
protected override async Task ProcessMessage(string json) | |||||
{ | { | ||||
await base.ProcessMessage(json).ConfigureAwait(false); | await base.ProcessMessage(json).ConfigureAwait(false); | ||||
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json); | var msg = JsonConvert.DeserializeObject<WebSocketMessage>(json); | ||||
@@ -85,9 +95,10 @@ namespace Discord.Net.WebSockets | |||||
JToken token = msg.Payload as JToken; | JToken token = msg.Payload as JToken; | ||||
if (msg.Type == "READY") | if (msg.Type == "READY") | ||||
{ | { | ||||
var payload = token.ToObject<ReadyEvent>(_serializer); | |||||
_reconnects = 0; | |||||
var payload = token.ToObject<ReadyEvent>(_serializer); | |||||
_sessionId = payload.SessionId; | _sessionId = payload.SessionId; | ||||
_heartbeatInterval = payload.HeartbeatInterval; | |||||
_heartbeatInterval = payload.HeartbeatInterval; | |||||
} | } | ||||
else if (msg.Type == "RESUMED") | else if (msg.Type == "RESUMED") | ||||
{ | { | ||||
@@ -106,7 +117,7 @@ namespace Discord.Net.WebSockets | |||||
{ | { | ||||
Host = payload.Url; | Host = payload.Url; | ||||
Logger.Info("Redirected to " + payload.Url); | Logger.Info("Redirected to " + payload.Url); | ||||
await Redirect().ConfigureAwait(false); | |||||
await Reconnect().ConfigureAwait(false); | |||||
} | } | ||||
} | } | ||||
break; | break; | ||||
@@ -83,13 +83,7 @@ namespace Discord.Net.WebSockets | |||||
{ | { | ||||
Exception ex; | Exception ex; | ||||
if (e is ClosedEventArgs) | if (e is ClosedEventArgs) | ||||
{ | |||||
int code = (e as ClosedEventArgs).Code; | |||||
string reason = (e as ClosedEventArgs).Reason; | |||||
if (String.IsNullOrEmpty(reason)) | |||||
reason = "No reason"; | |||||
ex = new Exception($"Received close code {code}: {reason}"); | |||||
} | |||||
ex = new WebSocketException((e as ClosedEventArgs).Code, (e as ClosedEventArgs).Reason); | |||||
else | else | ||||
ex = new Exception($"Connection lost"); | ex = new Exception($"Connection lost"); | ||||
_taskManager.SignalError(ex, isUnexpected: true); | _taskManager.SignalError(ex, isUnexpected: true); | ||||
@@ -125,7 +125,7 @@ namespace Discord.Net.WebSockets | |||||
_cancelTokenSource = null; | _cancelTokenSource = null; | ||||
_connectedEvent.Reset(); | _connectedEvent.Reset(); | ||||
if (oldState == ConnectionState.Connected) | |||||
if (oldState == ConnectionState.Connecting || oldState == ConnectionState.Connected) | |||||
{ | { | ||||
var ex = _taskManager.Exception; | var ex = _taskManager.Exception; | ||||
if (ex == null) | if (ex == null) | ||||