-
Notifications
You must be signed in to change notification settings - Fork 0
/
ConnectionMonitor.cs
152 lines (138 loc) · 5.46 KB
/
ConnectionMonitor.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
using System;
using System.Text;
using System.IO;
using System.Linq;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Discord.Net;
using Discord;
using Discord.WebSocket;
using System.Diagnostics;
using FreneticUtilities.FreneticExtensions;
using FreneticUtilities.FreneticDataSyntax;
using FreneticUtilities.FreneticToolkit;
namespace DiscordBotBase
{
/// <summary>Helper to monitor a Discord bot's connectivity. Call <see cref="StartMonitorLoop"/> to start the monitor loop.</summary>
public class ConnectionMonitor(DiscordBot bot)
{
/// <summary>The bot to monitor.</summary>
public DiscordBot Bot = bot;
/// <summary>Whether the bot has ever connected to Discord (since this instance of the class was started).</summary>
public bool ConnectedOnce = false;
/// <summary>Whether the bot believes itself to be currently connected to Discord. (If false, the bot is preparing or running a reconnection cycle).</summary>
public bool ConnectedCurrently = false;
/// <summary>Timespan the monitor should delay between connectivity checks.</summary>
public TimeSpan MonitorLoopTime = new(hours: 0, minutes: 1, seconds: 0);
/// <summary>
/// Whether the monitor has already detected a potential issue (but has not yet enforced a restart,
/// and is allowing a one loop-time delay period to automatically reconnect before enforcing a monitor reconnect).
/// </summary>
public bool MonitorWasFailedAlready = false;
/// <summary>
/// Whether all new bot logic should be stopped (usually indicates that a new bot instance is taking over).
/// Do not use directly outside of monitor, instead use <see cref="ShouldStopAllLogic"/>.
/// </summary>
public bool StopAllLogic = false;
/// <summary>Whether all new bot logic should be stopped (usually indicates that a new bot instance is taking over).</summary>
public bool ShouldStopAllLogic()
{
lock (MonitorLock)
{
return StopAllLogic;
}
}
/// <summary>Restarts the bot.</summary>
public void ForceRestartBot()
{
lock (MonitorLock)
{
StopAllLogic = true;
}
Task.Factory.StartNew(() =>
{
Bot.Shutdown();
});
DiscordBotBaseHelper.LaunchBotThread([], Bot.ClientConfig);
}
/// <summary>Lock object for monitor variables.</summary>
public LockObject MonitorLock = new();
/// <summary>The number of monitor loops thus far that the bot has not received input.</summary>
public long LoopsSilent = 0;
/// <summary>The number of monitor loops thus far that the bot has been active for.</summary>
public long LoopsTotal = 0;
/// <summary>Starts the monitor loop thread.</summary>
public void StartMonitorLoop()
{
Thread thr = new(new ThreadStart(LoopUntilFail)) { Name = "discordbotconnectionmonitor" };
thr.Start();
}
/// <summary>Loops the monitor until the bot has disconnected.</summary>
public void LoopUntilFail()
{
while (true)
{
Task.Delay(MonitorLoopTime).Wait();
if (ShouldStopAllLogic())
{
return;
}
try
{
MonitorLoopOnce();
}
catch (Exception ex)
{
if (ex is ThreadAbortException)
{
throw;
}
Console.WriteLine("Connection monitor loop had exception: " + ex.ToString());
}
}
}
/// <summary>Internal loop method (ran by a separate looping thread handler) for the monitor.</summary>
public void MonitorLoopOnce()
{
bool isConnected;
lock (MonitorLock)
{
LoopsSilent++;
LoopsTotal++;
isConnected = ConnectedCurrently && Bot.Client.ConnectionState == ConnectionState.Connected;
}
if (!isConnected)
{
Console.WriteLine($"Monitor detected disconnected state! Based on ConnectedCurrently={ConnectedCurrently}, ConnectionState={Bot.Client.ConnectionState}, with {LoopsSilent}/{LoopsTotal} silent/total loops.");
}
if (LoopsSilent > 60)
{
Console.WriteLine("Monitor detected over an hour of silence, and is assuming a disconnected state!");
isConnected = false;
}
if (LoopsTotal > 60 * 12)
{
Console.WriteLine("Monitor detected that the bot has been running for over 12 hours, and will restart soon!");
isConnected = false;
}
if (isConnected)
{
MonitorWasFailedAlready = false;
}
else
{
if (MonitorWasFailedAlready)
{
Console.WriteLine("Monitor is enforcing a restart!");
ForceRestartBot();
}
else
{
MonitorWasFailedAlready = true;
}
}
}
}
}