2018-07-19 12:42:48 +00:00
using System ;
2019-03-01 16:58:48 +00:00
using System.Collections.Concurrent ;
2018-07-19 12:42:48 +00:00
using System.Collections.Generic ;
2019-01-10 19:46:47 +00:00
using System.Diagnostics ;
2019-02-24 12:31:14 +00:00
using System.IO ;
2018-07-19 12:42:48 +00:00
using System.Reflection ;
using System.Threading ;
2019-08-24 15:46:10 +00:00
using CompatBot.Utils ;
2018-07-19 12:42:48 +00:00
using DSharpPlus.Entities ;
2019-03-01 16:58:48 +00:00
using Microsoft.Extensions.Configuration ;
2019-03-01 17:34:39 +00:00
using Microsoft.Extensions.Configuration.UserSecrets ;
2019-07-24 11:25:43 +00:00
using Microsoft.Extensions.Logging ;
2018-10-05 17:56:44 +00:00
using NLog ;
2019-07-24 11:25:43 +00:00
using NLog.Extensions.Logging ;
2019-03-01 13:15:54 +00:00
using NLog.Filters ;
2018-10-05 17:56:44 +00:00
using NLog.Targets ;
using NLog.Targets.Wrappers ;
2019-07-24 11:25:43 +00:00
using ILogger = NLog . ILogger ;
using LogLevel = NLog . LogLevel ;
2018-07-19 12:42:48 +00:00
namespace CompatBot
{
internal static class Config
{
2019-03-01 16:58:48 +00:00
private static readonly IConfigurationRoot config ;
2018-10-05 17:56:44 +00:00
internal static readonly ILogger Log ;
2019-07-24 11:25:43 +00:00
internal static readonly ILoggerFactory LoggerFactory ;
2019-03-01 16:58:48 +00:00
internal static readonly ConcurrentDictionary < string , string > inMemorySettings = new ConcurrentDictionary < string , string > ( ) ;
2018-07-19 12:42:48 +00:00
public static readonly CancellationTokenSource Cts = new CancellationTokenSource ( ) ;
2018-08-18 15:37:58 +00:00
public static readonly TimeSpan ModerationTimeThreshold = TimeSpan . FromHours ( 12 ) ;
2018-07-19 12:42:48 +00:00
public static readonly TimeSpan DefaultTimeout = TimeSpan . FromSeconds ( 30 ) ;
2019-03-15 10:26:06 +00:00
public static readonly TimeSpan LogParsingTimeout = TimeSpan . FromSeconds ( 30 ) ;
2019-03-01 16:58:48 +00:00
public static readonly TimeSpan BuildTimeDifferenceForOutdatedBuilds = TimeSpan . FromDays ( 3 ) ;
2019-05-10 18:31:56 +00:00
public static readonly TimeSpan ShutupTimeLimit = TimeSpan . FromMinutes ( 5 ) ;
2019-01-10 19:46:47 +00:00
public static readonly Stopwatch Uptime = Stopwatch . StartNew ( ) ;
2019-03-01 16:58:48 +00:00
// these settings could be configured either through `$ dotnet user-secrets`, or through environment variables (e.g. launchSettings.json, etc)
public static string CommandPrefix = > config . GetValue ( nameof ( CommandPrefix ) , "!" ) ;
public static string AutoRemoveCommandPrefix = > config . GetValue ( nameof ( AutoRemoveCommandPrefix ) , "." ) ;
public static ulong BotGuildId = > config . GetValue ( nameof ( BotGuildId ) , 272035812277878785 ul ) ; // discord server where the bot is supposed to be
public static ulong BotGeneralChannelId = > config . GetValue ( nameof ( BotGeneralChannelId ) , 272035812277878785 ul ) ; // #rpcs3; main or general channel where noobs come first thing
public static ulong BotChannelId = > config . GetValue ( nameof ( BotChannelId ) , 291679908067803136 ul ) ; // #compatbot; this is used for !compat/!top results and new builds announcements
public static ulong BotSpamId = > config . GetValue ( nameof ( BotSpamId ) , 319224795785068545 ul ) ; // #bot-spam; this is a dedicated channel for bot abuse
public static ulong BotLogId = > config . GetValue ( nameof ( BotLogId ) , 436972161572536329 ul ) ; // #bot-log; a private channel for admin mod queue
public static ulong BotRulesChannelId = > config . GetValue ( nameof ( BotRulesChannelId ) , 311894275015049216 ul ) ; // #rules-info; used to give links to rules
public static ulong BotAdminId = > config . GetValue ( nameof ( BotAdminId ) , 267367850706993152 ul ) ; // discord user id for a bot admin
public static ulong ThumbnailSpamId = > config . GetValue ( nameof ( ThumbnailSpamId ) , 475678410098606100 ul ) ; // whatever private chat where bot can upload game covers for future embedding
public static int ProductCodeLookupHistoryThrottle = > config . GetValue ( nameof ( ProductCodeLookupHistoryThrottle ) , 7 ) ;
public static int TopLimit = > config . GetValue ( nameof ( TopLimit ) , 15 ) ;
public static int AttachmentSizeLimit = > config . GetValue ( nameof ( AttachmentSizeLimit ) , 8 * 1024 * 1024 ) ;
public static int LogSizeLimit = > config . GetValue ( nameof ( LogSizeLimit ) , 64 * 1024 * 1024 ) ;
public static int MinimumBufferSize = > config . GetValue ( nameof ( MinimumBufferSize ) , 512 ) ;
public static int BuildNumberDifferenceForOutdatedBuilds = > config . GetValue ( nameof ( BuildNumberDifferenceForOutdatedBuilds ) , 10 ) ;
2019-07-26 15:40:27 +00:00
public static int MinimumPiracyTriggerLength = > config . GetValue ( nameof ( MinimumPiracyTriggerLength ) , 4 ) ;
2019-11-19 15:15:33 +00:00
public static int MaxSyscallResultLines = > config . GetValue ( nameof ( MaxSyscallResultLines ) , 13 ) ;
2019-07-26 15:40:27 +00:00
2019-03-01 16:58:48 +00:00
public static string Token = > config . GetValue ( nameof ( Token ) , "" ) ;
2019-09-23 15:01:38 +00:00
public static string LogPath = > config . GetValue ( nameof ( LogPath ) , "./logs/" ) ; // paths are relative to the working directory
2019-03-01 16:58:48 +00:00
public static string IrdCachePath = > config . GetValue ( nameof ( IrdCachePath ) , "./ird/" ) ;
2019-09-23 15:01:38 +00:00
internal static string CurrentLogPath = > Path . GetFullPath ( Path . Combine ( LogPath , "bot.log" ) ) ;
2019-03-01 17:34:39 +00:00
public static string GoogleApiConfigPath
{
get
{
2019-10-31 11:00:27 +00:00
if ( SandboxDetector . Detect ( ) = = SandboxType . Docker )
2019-08-24 15:46:10 +00:00
return "/bot-config/credentials.json" ;
2019-03-01 17:34:39 +00:00
if ( Assembly . GetEntryAssembly ( ) . GetCustomAttribute < UserSecretsIdAttribute > ( ) is UserSecretsIdAttribute attribute )
{
var path = Path . GetDirectoryName ( PathHelper . GetSecretsPathFromSecretsId ( attribute . UserSecretsId ) ) ;
path = Path . Combine ( path , "credentials.json" ) ;
if ( File . Exists ( path ) )
return path ;
}
2019-08-24 15:46:10 +00:00
2019-03-01 17:34:39 +00:00
return "Properties/credentials.json" ;
}
}
2018-07-19 12:42:48 +00:00
public static class Colors
{
public static readonly DiscordColor Help = DiscordColor . Azure ;
public static readonly DiscordColor DownloadLinks = new DiscordColor ( 0x3b88c3 ) ;
public static readonly DiscordColor Maintenance = new DiscordColor ( 0xffff00 ) ;
2018-08-18 15:37:58 +00:00
public static readonly DiscordColor CompatStatusNothing = new DiscordColor ( 0x455556 ) ; // colors mimic compat list statuses
2018-07-19 12:42:48 +00:00
public static readonly DiscordColor CompatStatusLoadable = new DiscordColor ( 0xe74c3c ) ;
public static readonly DiscordColor CompatStatusIntro = new DiscordColor ( 0xe08a1e ) ;
public static readonly DiscordColor CompatStatusIngame = new DiscordColor ( 0xf9b32f ) ;
public static readonly DiscordColor CompatStatusPlayable = new DiscordColor ( 0x1ebc61 ) ;
public static readonly DiscordColor CompatStatusUnknown = new DiscordColor ( 0x3198ff ) ;
public static readonly DiscordColor LogResultFailed = DiscordColor . Gray ;
2018-08-18 15:37:58 +00:00
public static readonly DiscordColor LogAlert = new DiscordColor ( 0xf04747 ) ; // colors mimic discord statuses
public static readonly DiscordColor LogNotice = new DiscordColor ( 0xfaa61a ) ;
public static readonly DiscordColor LogInfo = new DiscordColor ( 0x43b581 ) ;
public static readonly DiscordColor LogUnknown = new DiscordColor ( 0x747f8d ) ;
2019-01-08 00:13:55 +00:00
public static readonly DiscordColor PrOpen = new DiscordColor ( 0x2cbe4e ) ;
public static readonly DiscordColor PrMerged = new DiscordColor ( 0x6f42c1 ) ;
public static readonly DiscordColor PrClosed = new DiscordColor ( 0xcb2431 ) ;
2019-08-22 14:47:28 +00:00
public static readonly DiscordColor UpdateStatusGood = DiscordColor . Green ;
public static readonly DiscordColor UpdateStatusBad = DiscordColor . Yellow ;
2018-07-19 12:42:48 +00:00
}
public static class Reactions
{
public static readonly DiscordEmoji Success = DiscordEmoji . FromUnicode ( "👌" ) ;
public static readonly DiscordEmoji Failure = DiscordEmoji . FromUnicode ( "⛔" ) ;
public static readonly DiscordEmoji Denied = DiscordEmoji . FromUnicode ( "👮" ) ;
public static readonly DiscordEmoji Starbucks = DiscordEmoji . FromUnicode ( "☕" ) ;
2018-08-18 15:37:58 +00:00
public static readonly DiscordEmoji Moderated = DiscordEmoji . FromUnicode ( "🔨" ) ;
2018-11-25 20:34:23 +00:00
public static readonly DiscordEmoji No = DiscordEmoji . FromUnicode ( "😐" ) ;
2019-01-08 14:25:39 +00:00
public static readonly DiscordEmoji PleaseWait = DiscordEmoji . FromUnicode ( "👀" ) ;
2019-01-29 19:32:51 +00:00
public static readonly DiscordEmoji PiracyCheck = DiscordEmoji . FromUnicode ( "🔨" ) ;
2019-05-10 18:31:56 +00:00
public static readonly DiscordEmoji Shutup = DiscordEmoji . FromUnicode ( "🔇" ) ;
2019-11-18 19:47:45 +00:00
public static readonly DiscordEmoji BadUpdate = DiscordEmoji . FromUnicode ( "⚠\ufe0f" ) ;
2018-07-19 12:42:48 +00:00
}
public static class Moderation
{
public static readonly int StarbucksThreshold = 5 ;
public static readonly IReadOnlyList < ulong > Channels = new List < ulong >
{
272875751773306881 ,
319224795785068545 ,
} . AsReadOnly ( ) ;
public static readonly IReadOnlyCollection < string > RoleWhiteList = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase )
{
"Administrator" ,
"Community Manager" ,
"Web Developer" ,
"Moderator" ,
"Lead Graphics Developer" ,
"Lead Core Developer" ,
"Developers" ,
"Affiliated" ,
"Contributors" ,
} ;
2019-01-24 17:42:53 +00:00
public static readonly IReadOnlyCollection < string > RoleSmartList = new HashSet < string > ( RoleWhiteList , StringComparer . InvariantCultureIgnoreCase )
{
"Testers" ,
"Helpers"
} ;
2018-07-19 12:42:48 +00:00
}
static Config ( )
{
try
{
2019-03-01 16:58:48 +00:00
config = new ConfigurationBuilder ( )
2019-03-06 15:49:57 +00:00
. AddUserSecrets ( Assembly . GetExecutingAssembly ( ) ) // lower priority
2019-08-24 15:46:10 +00:00
. AddEnvironmentVariables ( )
2019-03-01 16:58:48 +00:00
. AddInMemoryCollection ( inMemorySettings ) // higher priority
. Build ( ) ;
2018-10-05 17:56:44 +00:00
Log = GetLog ( ) ;
2019-07-24 11:25:43 +00:00
LoggerFactory = new NLogLoggerFactory ( ) ;
2019-10-26 13:27:30 +00:00
Log . Info ( "Log path: " + CurrentLogPath ) ;
2018-07-19 12:42:48 +00:00
}
catch ( Exception e )
{
Console . ForegroundColor = ConsoleColor . Red ;
Console . WriteLine ( "Error initializing settings: " + e . Message ) ;
Console . ResetColor ( ) ;
}
}
2018-10-05 17:56:44 +00:00
private static ILogger GetLog ( )
{
var config = new NLog . Config . LoggingConfiguration ( ) ;
var fileTarget = new FileTarget ( "logfile" ) {
2019-09-23 15:01:38 +00:00
FileName = CurrentLogPath ,
2018-10-05 17:56:44 +00:00
ArchiveEvery = FileArchivePeriod . Day ,
2018-11-01 13:11:30 +00:00
ArchiveNumbering = ArchiveNumberingMode . DateAndSequence ,
2018-10-05 17:56:44 +00:00
KeepFileOpen = true ,
ConcurrentWrites = false ,
AutoFlush = false ,
OpenFileFlushTimeout = 1 ,
2019-03-01 13:15:54 +00:00
Layout = "${longdate} ${sequenceid:padding=6} ${level:uppercase=true:padding=-5} ${message} ${onexception:" +
"${newline}${exception:format=ToString}" +
":when=not contains('${exception:format=ShortType}','TaskCanceledException')}" ,
2018-10-05 17:56:44 +00:00
} ;
var asyncFileTarget = new AsyncTargetWrapper ( fileTarget )
{
TimeToSleepBetweenBatches = 0 ,
OverflowAction = AsyncTargetWrapperOverflowAction . Block ,
BatchSize = 500 ,
} ;
2019-03-01 13:15:54 +00:00
var logTarget = new ColoredConsoleTarget ( "logconsole" ) {
Layout = "${longdate} ${level:uppercase=true:padding=-5} ${message} ${onexception:" +
"${newline}${exception:format=Message}" +
":when=not contains('${exception:format=ShortType}','TaskCanceledException')}" ,
} ;
2018-10-05 17:56:44 +00:00
#if DEBUG
config . AddRule ( LogLevel . Trace , LogLevel . Fatal , logTarget , "default" ) ; // only echo messages from default logger to the console
#else
2018-10-05 18:23:45 +00:00
config . AddRule ( LogLevel . Info , LogLevel . Fatal , logTarget , "default" ) ;
2018-10-05 17:56:44 +00:00
#endif
config . AddRule ( LogLevel . Debug , LogLevel . Fatal , asyncFileTarget ) ;
2019-03-01 13:15:54 +00:00
var filter = new ConditionBasedFilter { Condition = "contains('${message}','TaskCanceledException')" , Action = FilterResult . Ignore , } ;
foreach ( var rule in config . LoggingRules )
rule . Filters . Add ( filter ) ;
2018-10-05 17:56:44 +00:00
LogManager . Configuration = config ;
return LogManager . GetLogger ( "default" ) ;
}
2018-07-19 12:42:48 +00:00
}
}