2018-07-19 12:42:48 +00:00
using System ;
2020-02-20 13:11:48 +00:00
using System.Diagnostics ;
2018-11-05 11:01:31 +00:00
using System.IO ;
2019-10-31 12:18:04 +00:00
using System.Linq ;
2019-08-24 15:46:10 +00:00
using System.Reflection ;
2020-11-02 13:24:03 +00:00
using System.Text ;
2018-08-05 14:36:16 +00:00
using System.Threading ;
2018-07-19 12:42:48 +00:00
using System.Threading.Tasks ;
2021-04-13 13:00:25 +00:00
using CirrusCiClient ;
2018-07-19 12:42:48 +00:00
using CompatBot.Commands ;
2018-07-19 20:55:54 +00:00
using CompatBot.Commands.Converters ;
2018-07-19 12:42:48 +00:00
using CompatBot.Database ;
2018-08-29 16:52:47 +00:00
using CompatBot.Database.Providers ;
2018-07-19 12:42:48 +00:00
using CompatBot.EventHandlers ;
2019-08-24 15:46:10 +00:00
using CompatBot.Utils ;
2021-03-25 11:39:42 +00:00
using CompatBot.Utils.Extensions ;
2018-07-19 12:42:48 +00:00
using DSharpPlus ;
using DSharpPlus.CommandsNext ;
2018-11-12 08:28:52 +00:00
using DSharpPlus.Entities ;
2023-04-15 14:39:51 +00:00
using DSharpPlus.EventArgs ;
2020-10-26 20:08:55 +00:00
using DSharpPlus.Interactivity.Extensions ;
2022-06-02 16:44:49 +00:00
using DSharpPlus.SlashCommands ;
2020-11-02 13:24:03 +00:00
using Microsoft.EntityFrameworkCore ;
2019-08-24 15:46:10 +00:00
using Microsoft.Extensions.Configuration.UserSecrets ;
2018-08-03 16:39:57 +00:00
using Microsoft.Extensions.DependencyInjection ;
2021-03-02 23:03:32 +00:00
using Fortune = CompatBot . Commands . Fortune ;
2018-07-19 12:42:48 +00:00
2022-06-29 19:59:46 +00:00
namespace CompatBot ;
internal static class Program
2018-07-19 12:42:48 +00:00
{
2022-06-29 19:59:46 +00:00
private static readonly SemaphoreSlim InstanceCheck = new ( 0 , 1 ) ;
private static readonly SemaphoreSlim ShutdownCheck = new ( 0 , 1 ) ;
// pre-load the assembly so it won't fail after framework update while the process is still running
private static readonly Assembly DiagnosticsAssembly = Assembly . Load ( typeof ( Process ) . Assembly . GetName ( ) ) ;
internal const ulong InvalidChannelId = 13 ;
internal static async Task Main ( string [ ] args )
2018-07-19 12:42:48 +00:00
{
2022-06-29 19:59:46 +00:00
Config . TelemetryClient ? . TrackEvent ( "startup" ) ;
2018-08-05 14:36:16 +00:00
2022-06-29 19:59:46 +00:00
Console . WriteLine ( "Confinement: " + SandboxDetector . Detect ( ) ) ;
if ( args . Length > 0 & & args [ 0 ] = = "--dry-run" )
2018-07-19 12:42:48 +00:00
{
2022-06-29 19:59:46 +00:00
await OpenSslConfigurator . CheckAndFixSystemConfigAsync ( ) . ConfigureAwait ( false ) ;
Console . WriteLine ( "Database path: " + Path . GetDirectoryName ( Path . GetFullPath ( DbImporter . GetDbPath ( "fake.db" , Environment . SpecialFolder . ApplicationData ) ) ) ) ;
if ( Assembly . GetEntryAssembly ( ) ? . GetCustomAttribute < UserSecretsIdAttribute > ( ) ! = null )
Console . WriteLine ( "Bot config path: " + Path . GetDirectoryName ( Path . GetFullPath ( Config . GoogleApiConfigPath ) ) ) ;
return ;
}
2020-04-13 11:50:50 +00:00
2022-06-29 19:59:46 +00:00
if ( Environment . ProcessId = = 0 )
Config . Log . Info ( "Well, this was unexpected" ) ;
var singleInstanceCheckThread = new Thread ( ( ) = >
{
using var instanceLock = new Mutex ( false , @"Global\RPCS3 Compatibility Bot" ) ;
if ( instanceLock . WaitOne ( 1000 ) )
try
{
InstanceCheck . Release ( ) ;
ShutdownCheck . Wait ( ) ;
}
finally
{
instanceLock . ReleaseMutex ( ) ;
}
} ) ;
try
{
singleInstanceCheckThread . Start ( ) ;
if ( ! await InstanceCheck . WaitAsync ( 1000 ) . ConfigureAwait ( false ) )
2019-08-24 15:46:10 +00:00
{
2022-06-29 19:59:46 +00:00
Config . Log . Fatal ( "Another instance is already running." ) ;
2019-08-24 15:46:10 +00:00
return ;
}
2022-06-29 19:59:46 +00:00
if ( string . IsNullOrEmpty ( Config . Token ) | | Config . Token . Length < 16 )
2019-11-25 13:07:01 +00:00
{
2022-06-29 19:59:46 +00:00
Config . Log . Fatal ( "No token was specified." ) ;
return ;
}
2018-07-19 12:42:48 +00:00
2022-06-29 19:59:46 +00:00
if ( SandboxDetector . Detect ( ) = = SandboxType . Docker )
{
Config . Log . Info ( "Checking OpenSSL system configuration..." ) ;
await OpenSslConfigurator . CheckAndFixSystemConfigAsync ( ) . ConfigureAwait ( false ) ;
2021-06-04 10:03:42 +00:00
2022-06-29 19:59:46 +00:00
Config . Log . Info ( "Checking for updates..." ) ;
try
{
2023-03-06 20:00:09 +00:00
var ( updated , stdout ) = await Sudo . Bot . GitPullAsync ( Config . Cts . Token ) . ConfigureAwait ( false ) ;
2022-06-29 19:59:46 +00:00
if ( ! string . IsNullOrEmpty ( stdout ) & & updated )
Config . Log . Debug ( stdout ) ;
if ( updated )
2019-11-18 20:02:12 +00:00
{
2022-06-29 19:59:46 +00:00
Sudo . Bot . Restart ( InvalidChannelId , "Restarted due to new bot updates not present in this Docker image" ) ;
return ;
2019-11-18 20:02:12 +00:00
}
}
2022-06-29 19:59:46 +00:00
catch ( Exception e )
{
Config . Log . Error ( e , "Failed to check for updates" ) ;
}
}
2019-11-18 20:02:12 +00:00
2022-06-29 19:59:46 +00:00
if ( ! await DbImporter . UpgradeAsync ( Config . Cts . Token ) . ConfigureAwait ( false ) )
return ;
2021-03-09 21:31:38 +00:00
2022-06-29 19:59:46 +00:00
await SqlConfiguration . RestoreAsync ( ) . ConfigureAwait ( false ) ;
Config . Log . Debug ( "Restored configuration variables from persistent storage" ) ;
2020-03-09 19:07:26 +00:00
2022-06-29 19:59:46 +00:00
await StatsStorage . RestoreAsync ( ) . ConfigureAwait ( false ) ;
Config . Log . Debug ( "Restored stats from persistent storage" ) ;
2019-03-01 15:52:37 +00:00
2022-06-29 19:59:46 +00:00
var backgroundTasks = Task . WhenAll (
AmdDriverVersionProvider . RefreshAsync ( ) ,
2020-03-09 17:51:44 +00:00
#if ! DEBUG
2020-10-26 20:08:55 +00:00
ThumbScrapper . GameTdbScraper . RunAsync ( Config . Cts . Token ) ,
2021-04-22 20:37:39 +00:00
//TitleUpdateInfoProvider.RefreshGameUpdateInfoAsync(Config.Cts.Token),
2020-03-09 17:51:44 +00:00
#endif
2022-06-29 19:59:46 +00:00
StatsStorage . BackgroundSaveAsync ( ) ,
CompatList . ImportCompatListAsync ( ) ,
Config . GetAzureDevOpsClient ( ) . GetPipelineDurationAsync ( Config . Cts . Token ) ,
2023-03-07 10:01:50 +00:00
Config . GetCurrentGitRevisionAsync ( Config . Cts . Token ) ,
2023-03-06 20:00:09 +00:00
CirrusCi . GetPipelineDurationAsync ( Config . Cts . Token ) ,
Sudo . Bot . UpdateCheckScheduledAsync ( Config . Cts . Token )
2022-06-29 19:59:46 +00:00
) ;
2018-08-05 14:36:16 +00:00
2022-06-29 19:59:46 +00:00
try
{
if ( ! Directory . Exists ( Config . IrdCachePath ) )
Directory . CreateDirectory ( Config . IrdCachePath ) ;
}
catch ( Exception e )
{
Config . Log . Warn ( e , $"Failed to create new folder {Config.IrdCachePath}: {e.Message}" ) ;
}
2018-11-05 11:01:31 +00:00
2022-06-29 19:59:46 +00:00
var config = new DiscordConfiguration
{
Token = Config . Token ,
TokenType = TokenType . Bot ,
MessageCacheSize = Config . MessageCacheSize ,
LoggerFactory = Config . LoggerFactory ,
Intents = DiscordIntents . All ,
} ;
using var client = new DiscordClient ( config ) ;
var commands = client . UseCommandsNext ( new ( )
{
StringPrefixes = new [ ] { Config . CommandPrefix , Config . AutoRemoveCommandPrefix } ,
Services = new ServiceCollection ( ) . BuildServiceProvider ( ) ,
} ) ;
commands . RegisterConverter ( new TextOnlyDiscordChannelConverter ( ) ) ;
2021-06-03 18:11:10 +00:00
#if DEBUG
2022-06-29 19:59:46 +00:00
commands . RegisterCommands < DevOnly > ( ) ;
2021-06-03 18:11:10 +00:00
#endif
2022-06-29 19:59:46 +00:00
commands . RegisterCommands < Misc > ( ) ;
commands . RegisterCommands < CompatList > ( ) ;
commands . RegisterCommands < Sudo > ( ) ;
commands . RegisterCommands < CommandsManagement > ( ) ;
commands . RegisterCommands < ContentFilters > ( ) ;
commands . RegisterCommands < Warnings > ( ) ;
commands . RegisterCommands < Explain > ( ) ;
commands . RegisterCommands < Psn > ( ) ;
commands . RegisterCommands < Invites > ( ) ;
commands . RegisterCommands < Moderation > ( ) ;
commands . RegisterCommands < Ird > ( ) ;
commands . RegisterCommands < BotMath > ( ) ;
commands . RegisterCommands < Pr > ( ) ;
commands . RegisterCommands < Events > ( ) ;
commands . RegisterCommands < E3 > ( ) ;
commands . RegisterCommands < BotStats > ( ) ;
2022-06-30 11:40:50 +00:00
commands . RegisterCommands < Hardware > ( ) ;
2022-06-29 19:59:46 +00:00
commands . RegisterCommands < Syscall > ( ) ;
commands . RegisterCommands < ForcedNicknames > ( ) ;
commands . RegisterCommands < Minesweeper > ( ) ;
commands . RegisterCommands < Fortune > ( ) ;
if ( ! string . IsNullOrEmpty ( Config . AzureComputerVisionKey ) )
commands . RegisterCommands < Vision > ( ) ;
2020-03-08 12:29:17 +00:00
2022-12-08 19:00:42 +00:00
var slashCommands = client . UseSlashCommands ( ) ;
// Only register to rpcs3 guild for now.
slashCommands . RegisterCommands < SlashMisc > ( Config . BotGuildId ) ;
2022-06-29 19:59:46 +00:00
commands . CommandErrored + = UnknownCommandHandler . OnError ;
2019-02-08 00:23:44 +00:00
2022-06-29 19:59:46 +00:00
client . UseInteractivity ( new ( ) ) ;
2019-02-08 16:55:57 +00:00
2022-06-29 19:59:46 +00:00
client . Ready + = async ( c , _ ) = >
{
Config . Log . Info ( "Bot is ready to serve!" ) ;
Config . Log . Info ( "" ) ;
Config . Log . Info ( $"Bot user id : {c.CurrentUser.Id} ({c.CurrentUser.Username})" ) ;
var owners = c . CurrentApplication . Owners . ToList ( ) ;
var msg = new StringBuilder ( $"Bot admin id{(owners.Count == 1 ? "": " s ")}:" ) ;
if ( owners . Count > 1 )
msg . AppendLine ( ) ;
await using var db = new BotDb ( ) ;
foreach ( var owner in owners )
{
msg . AppendLine ( $"\t{owner.Id} ({owner.Username ?? " ? ? ? "}#{owner.Discriminator ?? " ? ? ? ? "})" ) ;
if ( ! await db . Moderator . AnyAsync ( m = > m . DiscordId = = owner . Id , Config . Cts . Token ) . ConfigureAwait ( false ) )
await db . Moderator . AddAsync ( new ( ) { DiscordId = owner . Id , Sudoer = true } , Config . Cts . Token ) . ConfigureAwait ( false ) ;
}
await db . SaveChangesAsync ( Config . Cts . Token ) . ConfigureAwait ( false ) ;
Config . Log . Info ( msg . ToString ( ) . TrimEnd ) ;
Config . Log . Info ( "" ) ;
} ;
client . GuildAvailable + = async ( c , gaArgs ) = >
{
await BotStatusMonitor . RefreshAsync ( c ) . ConfigureAwait ( false ) ;
Watchdog . DisconnectTimestamps . Clear ( ) ;
Watchdog . TimeSinceLastIncomingMessage . Restart ( ) ;
if ( gaArgs . Guild . Id ! = Config . BotGuildId )
2019-11-25 13:07:01 +00:00
{
2018-09-08 15:41:56 +00:00
#if DEBUG
2022-06-29 19:59:46 +00:00
Config . Log . Warn ( $"Unknown discord server {gaArgs.Guild.Id} ({gaArgs.Guild.Name})" ) ;
2018-09-08 15:41:56 +00:00
#else
2019-11-25 13:07:01 +00:00
Config . Log . Warn ( $"Unknown discord server {gaArgs.Guild.Id} ({gaArgs.Guild.Name}), leaving..." ) ;
await gaArgs . Guild . LeaveAsync ( ) . ConfigureAwait ( false ) ;
2018-09-08 15:41:56 +00:00
#endif
2022-06-29 19:59:46 +00:00
return ;
}
2018-09-08 15:41:56 +00:00
2022-06-29 19:59:46 +00:00
Config . Log . Info ( $"Server {gaArgs.Guild.Name} is available now" ) ;
Config . Log . Info ( $"Checking moderation backlogs in {gaArgs.Guild.Name}..." ) ;
try
{
await Task . WhenAll (
Starbucks . CheckBacklogAsync ( c , gaArgs . Guild ) . ContinueWith ( _ = > Config . Log . Info ( $"Starbucks backlog checked in {gaArgs.Guild.Name}." ) , TaskScheduler . Default ) ,
DiscordInviteFilter . CheckBacklogAsync ( c , gaArgs . Guild ) . ContinueWith ( _ = > Config . Log . Info ( $"Discord invites backlog checked in {gaArgs.Guild.Name}." ) , TaskScheduler . Default )
) . ConfigureAwait ( false ) ;
}
catch ( Exception e )
2019-11-25 13:07:01 +00:00
{
2022-06-29 19:59:46 +00:00
Config . Log . Warn ( e , "Error running backlog tasks" ) ;
}
Config . Log . Info ( $"All moderation backlogs checked in {gaArgs.Guild.Name}." ) ;
} ;
client . GuildAvailable + = ( c , _ ) = > UsernameValidationMonitor . MonitorAsync ( c , true ) ;
client . GuildUnavailable + = ( _ , guArgs ) = >
{
Config . Log . Warn ( $"{guArgs.Guild.Name} is unavailable" ) ;
return Task . CompletedTask ;
} ;
2020-04-02 14:58:43 +00:00
#if ! DEBUG
2020-04-03 11:14:42 +00:00
/ *
2020-04-02 14:58:43 +00:00
client . GuildDownloadCompleted + = async gdcArgs = >
{
foreach ( var guild in gdcArgs . Guilds )
await ModProvider . SyncRolesAsync ( guild . Value ) . ConfigureAwait ( false ) ;
} ;
2020-04-03 11:14:42 +00:00
* /
2020-04-02 14:58:43 +00:00
#endif
2022-06-29 19:59:46 +00:00
client . MessageReactionAdded + = Starbucks . Handler ;
client . MessageReactionAdded + = ContentFilterMonitor . OnReaction ;
2018-07-19 12:42:48 +00:00
2022-06-29 19:59:46 +00:00
var mediaScreenshotMonitor = new MediaScreenshotMonitor ( client ) ;
2023-04-15 14:39:51 +00:00
client . MessageCreated + = Watchdog . OnMessageCreated ;
client . MessageCreated + = new OrderedEventHandlerWrapper < MessageCreateEventArgs > (
new [ ]
{
ContentFilterMonitor . OnMessageCreated , // should be first
DiscordInviteFilter . OnMessageCreated ,
} ,
new [ ]
{
GlobalMessageCache . OnMessageCreated ,
mediaScreenshotMonitor . OnMessageCreated ,
ProductCodeLookup . OnMessageCreated ,
LogParsingHandler . OnMessageCreated ,
LogAsTextMonitor . OnMessageCreated ,
PostLogHelpHandler . OnMessageCreated ,
BotReactionsHandler . OnMessageCreated ,
GithubLinksHandler . OnMessageCreated ,
NewBuildsMonitor . OnMessageCreated ,
TableFlipMonitor . OnMessageCreated ,
IsTheGamePlayableHandler . OnMessageCreated ,
EmpathySimulationHandler . OnMessageCreated ,
} ) . OnEvent ;
2018-08-05 14:36:16 +00:00
2023-04-15 14:39:51 +00:00
client . MessageUpdated + = new OrderedEventHandlerWrapper < MessageUpdateEventArgs > ( new [ ]
{
ContentFilterMonitor . OnMessageUpdated ,
DiscordInviteFilter . OnMessageUpdated ,
} ,
new [ ]
{
GlobalMessageCache . OnMessageUpdated ,
EmpathySimulationHandler . OnMessageUpdated ,
} ) . OnEvent ;
2018-08-05 14:36:16 +00:00
2022-06-29 19:59:46 +00:00
client . MessageDeleted + = GlobalMessageCache . OnMessageDeleted ;
if ( Config . DeletedMessagesLogChannelId > 0 )
client . MessageDeleted + = DeletedMessagesMonitor . OnMessageDeleted ;
client . MessageDeleted + = ThumbnailCacheMonitor . OnMessageDeleted ;
client . MessageDeleted + = EmpathySimulationHandler . OnMessageDeleted ;
2018-08-05 14:36:16 +00:00
2022-06-29 19:59:46 +00:00
client . MessagesBulkDeleted + = GlobalMessageCache . OnMessagesBulkDeleted ;
2020-09-29 10:34:26 +00:00
2022-06-29 19:59:46 +00:00
client . UserUpdated + = UsernameSpoofMonitor . OnUserUpdated ;
client . UserUpdated + = UsernameZalgoMonitor . OnUserUpdated ;
2019-03-13 18:14:00 +00:00
2022-06-29 19:59:46 +00:00
client . GuildMemberAdded + = Greeter . OnMemberAdded ;
client . GuildMemberAdded + = UsernameSpoofMonitor . OnMemberAdded ;
client . GuildMemberAdded + = UsernameZalgoMonitor . OnMemberAdded ;
client . GuildMemberAdded + = UsernameValidationMonitor . OnMemberAdded ;
client . GuildMemberAdded + = UsernameRaidMonitor . OnMemberAdded ;
2019-10-30 00:19:45 +00:00
2022-06-29 19:59:46 +00:00
client . GuildMemberUpdated + = UsernameSpoofMonitor . OnMemberUpdated ;
client . GuildMemberUpdated + = UsernameZalgoMonitor . OnMemberUpdated ;
client . GuildMemberUpdated + = UsernameValidationMonitor . OnMemberUpdated ;
client . GuildMemberUpdated + = UsernameRaidMonitor . OnMemberUpdated ;
2021-06-04 16:01:29 +00:00
#if DEBUG
2022-06-29 19:59:46 +00:00
client . ComponentInteractionCreated + = ( _ , args ) = >
{
Config . Log . Debug ( $"ComponentInteraction: type: {args.Interaction.Type}, id: {args.Interaction.Data.CustomId}, user: {args.Interaction.User}" ) ;
return Task . CompletedTask ;
} ;
2021-06-05 12:39:10 +00:00
#endif
2022-06-29 19:59:46 +00:00
client . ComponentInteractionCreated + = GlobalButtonHandler . OnComponentInteraction ;
2021-06-05 12:39:10 +00:00
2022-06-29 19:59:46 +00:00
Watchdog . DisconnectTimestamps . Enqueue ( DateTime . UtcNow ) ;
2019-11-25 13:07:01 +00:00
2022-06-29 19:59:46 +00:00
try
{
await client . ConnectAsync ( ) . ConfigureAwait ( false ) ;
}
catch ( Exception e )
{
Config . Log . Error ( e , "Failed to connect to Discord: " + e . Message ) ;
throw ;
}
ulong? channelId = null ;
string? restartMsg = null ;
await using ( var db = new BotDb ( ) )
{
var chState = db . BotState . FirstOrDefault ( k = > k . Key = = "bot-restart-channel" ) ;
if ( chState ! = null )
2019-11-25 13:07:01 +00:00
{
2022-06-29 19:59:46 +00:00
if ( ulong . TryParse ( chState . Value , out var ch ) )
channelId = ch ;
db . BotState . Remove ( chState ) ;
2019-11-25 13:07:01 +00:00
}
2022-06-29 19:59:46 +00:00
var msgState = db . BotState . FirstOrDefault ( i = > i . Key = = "bot-restart-msg" ) ;
if ( msgState ! = null )
2019-11-25 13:07:01 +00:00
{
2022-06-29 19:59:46 +00:00
restartMsg = msgState . Value ;
db . BotState . Remove ( msgState ) ;
2019-11-25 13:07:01 +00:00
}
2022-06-29 19:59:46 +00:00
await db . SaveChangesAsync ( ) . ConfigureAwait ( false ) ;
}
if ( string . IsNullOrEmpty ( restartMsg ) )
restartMsg = null ;
2019-11-07 16:48:30 +00:00
2022-06-29 19:59:46 +00:00
if ( channelId . HasValue )
{
Config . Log . Info ( $"Found channelId {channelId}" ) ;
DiscordChannel channel ;
if ( channelId = = InvalidChannelId )
2019-11-25 13:07:01 +00:00
{
2022-06-29 19:59:46 +00:00
channel = await client . GetChannelAsync ( Config . ThumbnailSpamId ) . ConfigureAwait ( false ) ;
await channel . SendMessageAsync ( restartMsg ? ? "Bot has suffered some catastrophic failure and was restarted" ) . ConfigureAwait ( false ) ;
2019-11-25 13:07:01 +00:00
}
else
{
2022-06-29 19:59:46 +00:00
channel = await client . GetChannelAsync ( channelId . Value ) . ConfigureAwait ( false ) ;
await channel . SendMessageAsync ( "Bot is up and running" ) . ConfigureAwait ( false ) ;
2018-07-19 12:42:48 +00:00
}
2018-08-05 14:36:16 +00:00
}
2022-06-29 19:59:46 +00:00
else
2018-10-05 17:56:44 +00:00
{
2022-06-29 19:59:46 +00:00
Config . Log . Debug ( $"Args count: {args.Length}" ) ;
var pArgs = args . Select ( a = > a = = Config . Token ? "<Token>" : $"[{a}]" ) ;
Config . Log . Debug ( "Args: " + string . Join ( " " , pArgs ) ) ;
2018-10-05 17:56:44 +00:00
}
2022-06-29 19:59:46 +00:00
Config . Log . Debug ( "Running RPCS3 update check thread" ) ;
backgroundTasks = Task . WhenAll (
backgroundTasks ,
NewBuildsMonitor . MonitorAsync ( client ) ,
Watchdog . Watch ( client ) ,
InviteWhitelistProvider . CleanupAsync ( client ) ,
UsernameValidationMonitor . MonitorAsync ( client ) ,
Psn . Check . MonitorFwUpdates ( client , Config . Cts . Token ) ,
Watchdog . SendMetrics ( client ) ,
Watchdog . CheckGCStats ( ) ,
mediaScreenshotMonitor . ProcessWorkQueue ( )
) ;
while ( ! Config . Cts . IsCancellationRequested )
2018-08-05 14:36:16 +00:00
{
2022-06-29 19:59:46 +00:00
if ( client . Ping > 1000 )
Config . Log . Warn ( $"High ping detected: {client.Ping}" ) ;
await Task . Delay ( TimeSpan . FromMinutes ( 1 ) , Config . Cts . Token ) . ContinueWith ( _ = > { /* in case it was cancelled */ } , TaskScheduler . Default ) . ConfigureAwait ( false ) ;
2018-07-19 12:42:48 +00:00
}
2022-06-29 19:59:46 +00:00
await backgroundTasks . ConfigureAwait ( false ) ;
}
catch ( Exception e )
{
2020-11-14 10:25:20 +00:00
if ( ! Config . InMemorySettings . ContainsKey ( "shutdown" ) )
2022-06-29 19:59:46 +00:00
Config . Log . Fatal ( e , "Experienced catastrophic failure, attempting to restart..." ) ;
}
finally
{
Config . TelemetryClient ? . Flush ( ) ;
ShutdownCheck . Release ( ) ;
if ( singleInstanceCheckThread . IsAlive )
singleInstanceCheckThread . Join ( 100 ) ;
2018-07-19 12:42:48 +00:00
}
2022-06-29 19:59:46 +00:00
if ( ! Config . InMemorySettings . ContainsKey ( "shutdown" ) )
Sudo . Bot . Restart ( InvalidChannelId , null ) ;
2018-07-19 12:42:48 +00:00
}
2022-06-29 19:59:46 +00:00
}