forked from Marilyth/MopsBot-2.0
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReliabilityService.cs
111 lines (95 loc) · 4.28 KB
/
ReliabilityService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
using System;
using System.Threading;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace MopsBot
{
// This service requires that your bot is being run by a daemon that handles
// Exit Code 1 (or any exit code) as a restart.
//
// If you do not have your bot setup to run in a daemon, this service will just
// terminate the process and the bot will not restart.
public class ReliabilityService
{
// --- Begin Configuration Section ---
// How long should we wait on the client to reconnect before resetting?
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(30);
// Should we attempt to reset the client? Set this to false if your client is still locking up.
private static readonly bool _attemptReset = true;
// Change log levels if desired:
private static readonly LogSeverity _debug = LogSeverity.Debug;
private static readonly LogSeverity _info = LogSeverity.Info;
private static readonly LogSeverity _critical = LogSeverity.Critical;
// --- End Configuration Section ---
private readonly DiscordSocketClient _discord;
private readonly Func<LogMessage, Task> _logger;
private CancellationTokenSource _cts;
public ReliabilityService(DiscordSocketClient discord, Func<LogMessage, Task> logger = null)
{
_cts = new CancellationTokenSource();
_discord = discord;
_logger = logger ?? (_ => Task.CompletedTask);
_discord.Connected += ConnectedAsync;
_discord.Disconnected += DisconnectedAsync;
}
public Task ConnectedAsync()
{
// Cancel all previous state checks and reset the CancelToken - client is back online
_ = DebugAsync("Client reconnected, resetting cancel tokens...");
_cts.Cancel();
_cts = new CancellationTokenSource();
_ = DebugAsync("Client reconnected, cancel tokens reset.");
return Task.CompletedTask;
}
public Task DisconnectedAsync(Exception _e)
{
// Check the state after <timeout> to see if we reconnected
_ = InfoAsync("Client disconnected, starting timeout task...");
_ = Task.Delay(_timeout, _cts.Token).ContinueWith(async _ =>
{
await DebugAsync("Timeout expired, continuing to check client state...");
await CheckStateAsync();
await DebugAsync("State came back okay");
});
return Task.CompletedTask;
}
private async Task CheckStateAsync()
{
// Client reconnected, no need to reset
if (_discord.ConnectionState == ConnectionState.Connected) return;
if (_attemptReset)
{
await InfoAsync("Attempting to reset the client");
var timeout = Task.Delay(_timeout);
var connect = _discord.StartAsync();
var task = await Task.WhenAny(timeout, connect);
if (task == timeout)
{
await CriticalAsync("Client reset timed out (task deadlocked?), killing process");
FailFast();
}
else if (connect.IsFaulted)
{
await CriticalAsync("Client reset faulted, killing process", connect.Exception);
FailFast();
}
else if (connect.IsCompletedSuccessfully)
await InfoAsync("Client reset succesfully!");
return;
}
await CriticalAsync("Client did not reconnect in time, killing process");
FailFast();
}
private void FailFast()
=> Environment.Exit(1);
// Logging Helpers
private const string LogSource = "Reliability";
private Task DebugAsync(string message)
=> _logger.Invoke(new LogMessage(_debug, LogSource, message));
private Task InfoAsync(string message)
=> _logger.Invoke(new LogMessage(_info, LogSource, message));
private Task CriticalAsync(string message, Exception error = null)
=> _logger.Invoke(new LogMessage(_critical, LogSource, message, error));
}
}