From e82560259baf407690ad23494251f4f3a5fee6f9 Mon Sep 17 00:00:00 2001 From: JustArchi Date: Sat, 14 Nov 2020 22:37:00 +0100 Subject: [PATCH] Final code cleanup --- .../CatAPI.cs | 2 +- .../ExamplePlugin.cs | 4 +- .../PeriodicGCPlugin.cs | 2 +- .../GlobalCache.cs | 44 +- .../RequestData.cs | 20 +- .../SteamTokenDumperPlugin.cs | 78 +- ArchiSteamFarm.Tests/Bot.cs | 76 +- ArchiSteamFarm.Tests/Trading.cs | 122 +- ArchiSteamFarm.sln.DotSettings | 4 + ArchiSteamFarm/ASF.cs | 37 +- ArchiSteamFarm/Actions.cs | 12 +- ArchiSteamFarm/ArchiCryptoHelper.cs | 2 +- ArchiSteamFarm/ArchiHandler.cs | 60 +- ArchiSteamFarm/ArchiWebHandler.cs | 74 +- ArchiSteamFarm/Bot.cs | 443 +++--- ArchiSteamFarm/BotConfig.cs | 121 +- ArchiSteamFarm/BotDatabase.cs | 17 +- .../CMsgs/CMsgClientAcknowledgeClanInvite.cs | 4 +- ArchiSteamFarm/CardsFarmer.cs | 141 +- .../Collections/ConcurrentHashSet.cs | 2 +- ArchiSteamFarm/Collections/ConcurrentList.cs | 4 +- .../Collections/FixedSizeConcurrentQueue.cs | 2 +- ArchiSteamFarm/Commands.cs | 1302 +++++++++++------ ArchiSteamFarm/Debugging.cs | 2 +- ArchiSteamFarm/GitHub.cs | 28 +- ArchiSteamFarm/GlobalConfig.cs | 97 +- ArchiSteamFarm/GlobalDatabase.cs | 33 +- ArchiSteamFarm/Helpers/ArchiCacheable.cs | 6 +- .../Helpers/CrossProcessFileBasedSemaphore.cs | 10 +- ArchiSteamFarm/Helpers/SerializableFile.cs | 2 +- ArchiSteamFarm/IPC/ArchiKestrel.cs | 4 +- .../IPC/Controllers/Api/ASFController.cs | 2 +- .../IPC/Controllers/Api/BotController.cs | 14 +- .../IPC/Controllers/Api/NLogController.cs | 4 +- .../IPC/Controllers/Api/TypeController.cs | 6 +- .../IPC/Controllers/Api/WWWController.cs | 2 +- .../ApiAuthenticationMiddleware.cs | 8 +- .../IPC/Integration/EnumSchemaFilter.cs | 4 +- ...actorAuthenticationConfirmationsRequest.cs | 2 +- ArchiSteamFarm/Json/Steam.cs | 10 +- ArchiSteamFarm/MobileAuthenticator.cs | 90 +- ArchiSteamFarm/NLog/ArchiLogger.cs | 6 +- ArchiSteamFarm/NLog/HistoryTarget.cs | 2 +- ArchiSteamFarm/NLog/Logging.cs | 16 +- ArchiSteamFarm/NLog/SteamTarget.cs | 2 +- ArchiSteamFarm/OS.cs | 15 +- ArchiSteamFarm/Plugins/PluginsCore.cs | 10 +- ArchiSteamFarm/Program.cs | 35 +- ArchiSteamFarm/RuntimeCompatibility.cs | 8 +- ArchiSteamFarm/Statistics.cs | 112 +- .../SteamKit2/InMemoryServerListProvider.cs | 2 +- ArchiSteamFarm/SteamPICSChanges.cs | 7 +- ArchiSteamFarm/SteamSaleEvent.cs | 13 +- ArchiSteamFarm/Trading.cs | 190 ++- ArchiSteamFarm/Utilities.cs | 57 +- ArchiSteamFarm/WebBrowser.cs | 232 +-- 56 files changed, 2176 insertions(+), 1428 deletions(-) diff --git a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatAPI.cs b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatAPI.cs index f48799ac6..466fb4496 100644 --- a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatAPI.cs +++ b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/CatAPI.cs @@ -45,7 +45,7 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { } if (string.IsNullOrEmpty(response.Content.Link)) { - throw new ArgumentNullException(nameof(response.Content.Link)); + throw new InvalidOperationException(nameof(response.Content.Link)); } return Uri.EscapeUriString(response.Content!.Link!); diff --git a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs index 13d28fc8d..01d699af4 100644 --- a/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs +++ b/ArchiSteamFarm.CustomPlugins.ExamplePlugin/ExamplePlugin.cs @@ -84,7 +84,7 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { // In comparison with OnBotMessage(), we're using asynchronous CatAPI call here, so we declare our method as async and return the message as usual // Notice how we handle access here as well, it'll work only for FamilySharing+ switch (args[0].ToUpperInvariant()) { - case "CAT" when bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing): + case "CAT" when bot.HasAccess(steamID, BotConfig.EAccess.FamilySharing): // Notice how we can decide whether to use bot's AWH WebBrowser or ASF's one. For Steam-related requests, AWH's one should always be used, for third-party requests like those it doesn't really matter // Still, it makes sense to pass AWH's one, so in case you get some errors or alike, you know from which bot instance they come from. It's similar to using Bot's ArchiLogger compared to ASF's one string? randomCatURL = await CatAPI.GetRandomCatURL(bot.ArchiWebHandler.WebBrowser).ConfigureAwait(false); @@ -147,7 +147,7 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin { // Normally ASF will expect from you async-capable responses, such as Task. This allows you to make your code fully asynchronous which is a core foundation on which ASF is built upon // Since in this method we're not doing any async stuff, instead of defining this method as async (pointless), we just need to wrap our responses in Task.FromResult<>() if (Bot.BotsReadOnly == null) { - throw new ArgumentNullException(nameof(Bot.BotsReadOnly)); + throw new InvalidOperationException(nameof(Bot.BotsReadOnly)); } // As a starter, we can for example ignore messages sent from our own bots, since otherwise they can run into a possible infinite loop of answering themselves diff --git a/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs b/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs index 75536ce7f..907ab52d3 100644 --- a/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs +++ b/ArchiSteamFarm.CustomPlugins.PeriodicGC/PeriodicGCPlugin.cs @@ -32,7 +32,7 @@ namespace ArchiSteamFarm.CustomPlugins.PeriodicGC { internal sealed class PeriodicGCPlugin : IPlugin { private const byte GCPeriod = 60; // In seconds - private static readonly Timer PeriodicGCTimer = new Timer(PerformGC); + private static readonly Timer PeriodicGCTimer = new(PerformGC); public string Name => nameof(PeriodicGCPlugin); diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs index 7351d7e7a..1d3bae4e1 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/GlobalCache.cs @@ -35,25 +35,25 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { private static string SharedFilePath => Path.Combine(ArchiSteamFarm.SharedInfo.ConfigDirectory, nameof(SteamTokenDumper) + ".cache"); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentDictionary AppChangeNumbers = new ConcurrentDictionary(); + private readonly ConcurrentDictionary AppChangeNumbers = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentDictionary AppTokens = new ConcurrentDictionary(); + private readonly ConcurrentDictionary AppTokens = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentDictionary DepotKeys = new ConcurrentDictionary(); + private readonly ConcurrentDictionary DepotKeys = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentDictionary PackageTokens = new ConcurrentDictionary(); + private readonly ConcurrentDictionary PackageTokens = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentHashSet SubmittedAppIDs = new ConcurrentHashSet(); + private readonly ConcurrentHashSet SubmittedAppIDs = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentHashSet SubmittedDepotIDs = new ConcurrentHashSet(); + private readonly ConcurrentHashSet SubmittedDepotIDs = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentHashSet SubmittedPackageIDs = new ConcurrentHashSet(); + private readonly ConcurrentHashSet SubmittedPackageIDs = new(); [JsonProperty(Required = Required.DisallowNull)] internal uint LastChangeNumber { get; private set; } @@ -93,7 +93,11 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } internal async Task OnPICSChanges(uint currentChangeNumber, IReadOnlyCollection> appChanges) { - if ((currentChangeNumber == 0) || (appChanges == null)) { + if (currentChangeNumber == 0) { + throw new ArgumentOutOfRangeException(nameof(currentChangeNumber)); + } + + if (appChanges == null) { throw new ArgumentNullException(nameof(appChanges)); } @@ -119,7 +123,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { internal async Task OnPICSChangesRestart(uint currentChangeNumber) { if (currentChangeNumber == 0) { - throw new ArgumentNullException(nameof(currentChangeNumber)); + throw new ArgumentOutOfRangeException(nameof(currentChangeNumber)); } if (currentChangeNumber <= LastChangeNumber) { @@ -160,8 +164,12 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } internal async Task UpdateAppTokens(IReadOnlyCollection> appTokens, IReadOnlyCollection publicAppIDs) { - if ((appTokens == null) || (publicAppIDs == null)) { - throw new ArgumentNullException(nameof(appTokens) + " || " + nameof(publicAppIDs)); + if (appTokens == null) { + throw new ArgumentNullException(nameof(appTokens)); + } + + if (publicAppIDs == null) { + throw new ArgumentNullException(nameof(publicAppIDs)); } bool save = false; @@ -207,7 +215,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { bool save = false; foreach (SteamApps.DepotKeyCallback depotKeyResult in depotKeyResults) { - if ((depotKeyResult == null) || (depotKeyResult.Result != EResult.OK)) { + if (depotKeyResult?.Result != EResult.OK) { continue; } @@ -260,8 +268,16 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } internal async Task UpdateSubmittedData(IReadOnlyCollection appIDs, IReadOnlyCollection packageIDs, IReadOnlyCollection depotIDs) { - if ((appIDs == null) || (packageIDs == null) || (depotIDs == null)) { - throw new ArgumentNullException(nameof(appIDs) + " || " + nameof(packageIDs) + " || " + nameof(depotIDs)); + if (appIDs == null) { + throw new ArgumentNullException(nameof(appIDs)); + } + + if (packageIDs == null) { + throw new ArgumentNullException(nameof(packageIDs)); + } + + if (depotIDs == null) { + throw new ArgumentNullException(nameof(depotIDs)); } bool save = false; diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/RequestData.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/RequestData.cs index 84c96038f..141fda6d7 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/RequestData.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/RequestData.cs @@ -39,7 +39,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { #pragma warning disable IDE0052 [JsonProperty(PropertyName = "guid", Required = Required.Always)] - private readonly string Guid = ASF.GlobalDatabase?.Guid.ToString("N") ?? throw new ArgumentNullException(nameof(ASF.GlobalDatabase.Guid)); + private readonly string Guid = ASF.GlobalDatabase?.Identifier.ToString("N") ?? throw new InvalidOperationException(nameof(ASF.GlobalDatabase.Identifier)); #pragma warning restore IDE0052 private readonly ulong SteamID; @@ -64,9 +64,21 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { private string SteamIDText => new SteamID(SteamID).Render(); #pragma warning restore IDE0051 - internal RequestData(ulong steamID, IEnumerable> apps, IEnumerable> accessTokens, IEnumerable> depots) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (apps == null) || (accessTokens == null) || (depots == null)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(apps) + " || " + nameof(accessTokens) + " || " + nameof(depots)); + internal RequestData(ulong steamID, IReadOnlyCollection> apps, IReadOnlyCollection> accessTokens, IReadOnlyCollection> depots) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (apps == null) { + throw new ArgumentNullException(nameof(apps)); + } + + if (accessTokens == null) { + throw new ArgumentNullException(nameof(accessTokens)); + } + + if (depots == null) { + throw new ArgumentNullException(nameof(depots)); } SteamID = steamID; diff --git a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs index 4af9bc149..62a94c3d0 100644 --- a/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs +++ b/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SteamTokenDumperPlugin.cs @@ -36,10 +36,10 @@ using SteamKit2; namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { [Export(typeof(IPlugin))] internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotSteamClient, ISteamPICSChanges { - private static readonly ConcurrentDictionary BotSubscriptions = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary BotSynchronizations = new ConcurrentDictionary(); - private static readonly SemaphoreSlim SubmissionSemaphore = new SemaphoreSlim(1, 1); - private static readonly Timer SubmissionTimer = new Timer(async e => await SubmitData().ConfigureAwait(false)); + private static readonly ConcurrentDictionary BotSubscriptions = new(); + private static readonly ConcurrentDictionary BotSynchronizations = new(); + private static readonly SemaphoreSlim SubmissionSemaphore = new(1, 1); + private static readonly Timer SubmissionTimer = new(SubmitData); private static GlobalCache? GlobalCache; @@ -121,8 +121,8 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { return; } - SemaphoreSlim refreshSemaphore = new SemaphoreSlim(1, 1); - Timer refreshTimer = new Timer(async e => await Refresh(bot).ConfigureAwait(false)); + SemaphoreSlim refreshSemaphore = new(1, 1); + Timer refreshTimer = new(async _ => await Refresh(bot).ConfigureAwait(false)); if (!BotSynchronizations.TryAdd(bot, (refreshSemaphore, refreshTimer))) { refreshSemaphore.Dispose(); @@ -132,8 +132,12 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } public void OnBotSteamCallbacksInit(Bot bot, CallbackManager callbackManager) { - if ((bot == null) || (callbackManager == null)) { - throw new ArgumentNullException(nameof(bot) + " || " + nameof(callbackManager)); + if (bot == null) { + throw new ArgumentNullException(nameof(bot)); + } + + if (callbackManager == null) { + throw new ArgumentNullException(nameof(callbackManager)); } if (BotSubscriptions.TryRemove(bot, out IDisposable? subscription)) { @@ -156,8 +160,16 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { public override void OnLoaded() { } public async void OnPICSChanges(uint currentChangeNumber, IReadOnlyDictionary appChanges, IReadOnlyDictionary packageChanges) { - if ((currentChangeNumber == 0) || (appChanges == null) || (packageChanges == null)) { - throw new ArgumentNullException(nameof(currentChangeNumber) + " || " + nameof(appChanges) + " || " + nameof(packageChanges)); + if (currentChangeNumber == 0) { + throw new ArgumentOutOfRangeException(nameof(currentChangeNumber)); + } + + if (appChanges == null) { + throw new ArgumentNullException(nameof(appChanges)); + } + + if (packageChanges == null) { + throw new ArgumentNullException(nameof(packageChanges)); } if (!IsEnabled) { @@ -173,7 +185,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { public async void OnPICSChangesRestart(uint currentChangeNumber) { if (currentChangeNumber == 0) { - throw new ArgumentNullException(nameof(currentChangeNumber)); + throw new ArgumentOutOfRangeException(nameof(currentChangeNumber)); } if (!IsEnabled) { @@ -181,14 +193,18 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } if (GlobalCache == null) { - throw new ArgumentNullException(nameof(GlobalCache)); + throw new InvalidOperationException(nameof(GlobalCache)); } await GlobalCache.OnPICSChangesRestart(currentChangeNumber).ConfigureAwait(false); } private static async void OnLicenseList(Bot bot, SteamApps.LicenseListCallback callback) { - if ((bot == null) || (callback == null)) { + if (bot == null) { + throw new ArgumentNullException(nameof(bot)); + } + + if (callback == null) { throw new ArgumentNullException(nameof(callback)); } @@ -197,7 +213,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } if (GlobalCache == null) { - throw new ArgumentNullException(nameof(GlobalCache)); + throw new InvalidOperationException(nameof(GlobalCache)); } Dictionary packageTokens = callback.LicenseList.GroupBy(license => license.PackageID).ToDictionary(group => group.Key, group => group.OrderByDescending(license => license.TimeCreated).First().AccessToken); @@ -215,8 +231,12 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { return; } - if ((GlobalCache == null) || (ASF.GlobalDatabase == null)) { - throw new ArgumentNullException(nameof(GlobalCache) + " || " + nameof(ASF.GlobalDatabase)); + if (GlobalCache == null) { + throw new InvalidOperationException(nameof(GlobalCache)); + } + + if (ASF.GlobalDatabase == null) { + throw new InvalidOperationException(nameof(GlobalCache)); } if (!BotSynchronizations.TryGetValue(bot, out (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer) synchronization)) { @@ -234,7 +254,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { packageIDs ??= bot.OwnedPackageIDsReadOnly; - HashSet appIDsToRefresh = new HashSet(); + HashSet appIDsToRefresh = new(); foreach (uint packageID in packageIDs) { if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(packageID, out (uint ChangeNumber, HashSet? AppIDs) packageData) || (packageData.AppIDs == null)) { @@ -253,7 +273,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { bot.ArchiLogger.LogGenericInfo($"Retrieving a total of {appIDsToRefresh.Count} app access tokens..."); - HashSet appIDsThisRound = new HashSet(Math.Min(appIDsToRefresh.Count, SharedInfo.AppInfosPerSingleRequest)); + HashSet appIDsThisRound = new(Math.Min(appIDsToRefresh.Count, SharedInfo.AppInfosPerSingleRequest)); using (HashSet.Enumerator enumerator = appIDsToRefresh.GetEnumerator()) { while (true) { @@ -336,9 +356,9 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { appIDsThisRound.Clear(); - Dictionary appChangeNumbers = new Dictionary(); + Dictionary appChangeNumbers = new(); - HashSet> depotTasks = new HashSet>(); + HashSet> depotTasks = new(); foreach (SteamApps.PICSProductInfoCallback.PICSProductInfo app in response.Results.SelectMany(result => result.Apps.Values)) { appChangeNumbers[app.ID] = app.ChangeNumber; @@ -385,9 +405,9 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { } } - private static async Task SubmitData() { + private static async void SubmitData(object? state) { if (Bot.Bots == null) { - throw new ArgumentNullException(nameof(Bot.Bots)); + throw new InvalidOperationException(nameof(Bot.Bots)); } const string request = SharedInfo.ServerURL + "/submit"; @@ -396,8 +416,16 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { return; } - if ((ASF.GlobalConfig == null) || (ASF.WebBrowser == null) || (GlobalCache == null)) { - throw new ArgumentNullException(nameof(ASF.GlobalConfig) + " || " + nameof(ASF.WebBrowser) + " || " + nameof(GlobalCache)); + if (GlobalCache == null) { + throw new InvalidOperationException(nameof(GlobalCache)); + } + + if (ASF.GlobalConfig == null) { + throw new InvalidOperationException(nameof(ASF.GlobalConfig)); + } + + if (ASF.WebBrowser == null) { + throw new InvalidOperationException(nameof(ASF.WebBrowser)); } if (!await SubmissionSemaphore.WaitAsync(0).ConfigureAwait(false)) { @@ -425,7 +453,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper { return; } - RequestData requestData = new RequestData(contributorSteamID, appTokens, packageTokens, depotKeys); + RequestData requestData = new(contributorSteamID, appTokens, packageTokens, depotKeys); ASF.ArchiLogger.LogGenericInfo($"Submitting registered apps/subs/depots: {appTokens.Count}/{packageTokens.Count}/{depotKeys.Count}..."); diff --git a/ArchiSteamFarm.Tests/Bot.cs b/ArchiSteamFarm.Tests/Bot.cs index da45b07f1..3f9d6574e 100644 --- a/ArchiSteamFarm.Tests/Bot.cs +++ b/ArchiSteamFarm.Tests/Bot.cs @@ -32,7 +32,7 @@ namespace ArchiSteamFarm.Tests { public void MoreCardsThanNeeded() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID), CreateCard(1, appID), CreateCard(2, appID), @@ -41,7 +41,7 @@ namespace ArchiSteamFarm.Tests { HashSet itemsToSend = GetItemsForFullBadge(items, 3, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 1 }, { (appID, Steam.Asset.SteamCommunityContextID, 2), 1 }, { (appID, Steam.Asset.SteamCommunityContextID, 3), 1 } @@ -54,7 +54,7 @@ namespace ArchiSteamFarm.Tests { public void MultipleSets() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID), CreateCard(1, appID), CreateCard(2, appID), @@ -63,7 +63,7 @@ namespace ArchiSteamFarm.Tests { HashSet itemsToSend = GetItemsForFullBadge(items, 2, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 2 }, { (appID, Steam.Asset.SteamCommunityContextID, 2), 2 } }; @@ -75,7 +75,7 @@ namespace ArchiSteamFarm.Tests { public void MultipleSetsDifferentAmount() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID, 2), CreateCard(2, appID), CreateCard(2, appID) @@ -83,7 +83,7 @@ namespace ArchiSteamFarm.Tests { HashSet itemsToSend = GetItemsForFullBadge(items, 2, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 2 }, { (appID, Steam.Asset.SteamCommunityContextID, 2), 2 } }; @@ -95,7 +95,7 @@ namespace ArchiSteamFarm.Tests { public void MutliRarityAndType() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID, type: Steam.Asset.EType.TradingCard, rarity: Steam.Asset.ERarity.Common), CreateCard(2, appID, type: Steam.Asset.EType.TradingCard, rarity: Steam.Asset.ERarity.Common), @@ -121,7 +121,7 @@ namespace ArchiSteamFarm.Tests { HashSet itemsToSend = GetItemsForFullBadge(items, 3, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 2 }, { (appID, Steam.Asset.SteamCommunityContextID, 2), 2 }, { (appID, Steam.Asset.SteamCommunityContextID, 3), 3 }, @@ -136,14 +136,14 @@ namespace ArchiSteamFarm.Tests { public void NotAllCardsPresent() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID), CreateCard(2, appID) }; HashSet itemsToSend = GetItemsForFullBadge(items, 3, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint>(0); + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new(0); AssertResultMatchesExpectation(expectedResult, itemsToSend); } @@ -151,14 +151,14 @@ namespace ArchiSteamFarm.Tests { public void OneSet() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID), CreateCard(2, appID) }; HashSet itemsToSend = GetItemsForFullBadge(items, 2, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 1 }, { (appID, Steam.Asset.SteamCommunityContextID, 2), 1 } }; @@ -171,7 +171,7 @@ namespace ArchiSteamFarm.Tests { const uint appID0 = 42; const uint appID1 = 43; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID0), CreateCard(1, appID1) }; @@ -183,7 +183,7 @@ namespace ArchiSteamFarm.Tests { } ); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID0, Steam.Asset.SteamCommunityContextID, 1), 1 }, { (appID1, Steam.Asset.SteamCommunityContextID, 1), 1 } }; @@ -196,7 +196,7 @@ namespace ArchiSteamFarm.Tests { const uint appID0 = 42; const uint appID1 = 43; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID0), CreateCard(1, appID1) }; @@ -208,7 +208,7 @@ namespace ArchiSteamFarm.Tests { } ); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint>(0); + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new(0); AssertResultMatchesExpectation(expectedResult, itemsToSend); } @@ -219,7 +219,7 @@ namespace ArchiSteamFarm.Tests { const uint appID1 = 43; const uint appID2 = 44; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID0), CreateCard(2, appID0), @@ -236,7 +236,7 @@ namespace ArchiSteamFarm.Tests { } ); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID1, Steam.Asset.SteamCommunityContextID, 1), 1 }, { (appID1, Steam.Asset.SteamCommunityContextID, 2), 1 }, { (appID1, Steam.Asset.SteamCommunityContextID, 3), 1 } @@ -249,14 +249,14 @@ namespace ArchiSteamFarm.Tests { public void OtherRarityFullSets() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID, rarity: Steam.Asset.ERarity.Common), CreateCard(1, appID, rarity: Steam.Asset.ERarity.Rare) }; HashSet itemsToSend = GetItemsForFullBadge(items, 1, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 2 } }; @@ -267,14 +267,14 @@ namespace ArchiSteamFarm.Tests { public void OtherRarityNoSets() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID, rarity: Steam.Asset.ERarity.Common), CreateCard(1, appID, rarity: Steam.Asset.ERarity.Rare) }; HashSet itemsToSend = GetItemsForFullBadge(items, 2, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint>(0); + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new(0); AssertResultMatchesExpectation(expectedResult, itemsToSend); } @@ -283,7 +283,7 @@ namespace ArchiSteamFarm.Tests { public void OtherRarityOneSet() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID, rarity: Steam.Asset.ERarity.Common), CreateCard(2, appID, rarity: Steam.Asset.ERarity.Common), CreateCard(1, appID, rarity: Steam.Asset.ERarity.Uncommon), @@ -293,7 +293,7 @@ namespace ArchiSteamFarm.Tests { HashSet itemsToSend = GetItemsForFullBadge(items, 3, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 1 }, { (appID, Steam.Asset.SteamCommunityContextID, 2), 1 }, { (appID, Steam.Asset.SteamCommunityContextID, 3), 1 } @@ -306,14 +306,14 @@ namespace ArchiSteamFarm.Tests { public void OtherTypeFullSets() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID, type: Steam.Asset.EType.TradingCard), CreateCard(1, appID, type: Steam.Asset.EType.FoilTradingCard) }; HashSet itemsToSend = GetItemsForFullBadge(items, 1, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 2 } }; @@ -324,14 +324,14 @@ namespace ArchiSteamFarm.Tests { public void OtherTypeNoSets() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID, type: Steam.Asset.EType.TradingCard), CreateCard(1, appID, type: Steam.Asset.EType.FoilTradingCard) }; HashSet itemsToSend = GetItemsForFullBadge(items, 2, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint>(0); + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new(0); AssertResultMatchesExpectation(expectedResult, itemsToSend); } @@ -340,7 +340,7 @@ namespace ArchiSteamFarm.Tests { public void OtherTypeOneSet() { const uint appID = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID, type: Steam.Asset.EType.TradingCard), CreateCard(2, appID, type: Steam.Asset.EType.TradingCard), CreateCard(1, appID, type: Steam.Asset.EType.FoilTradingCard), @@ -350,7 +350,7 @@ namespace ArchiSteamFarm.Tests { HashSet itemsToSend = GetItemsForFullBadge(items, 3, appID); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID, Steam.Asset.SteamCommunityContextID, 1), 1 }, { (appID, Steam.Asset.SteamCommunityContextID, 2), 1 }, { (appID, Steam.Asset.SteamCommunityContextID, 3), 1 } @@ -363,14 +363,14 @@ namespace ArchiSteamFarm.Tests { public void TooHighAmount() { const uint appID0 = 42; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID0, 2), CreateCard(2, appID0) }; HashSet itemsToSend = GetItemsForFullBadge(items, 2, appID0); - Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> { + Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult = new() { { (appID0, Steam.Asset.SteamCommunityContextID, 1), 1 }, { (appID0, Steam.Asset.SteamCommunityContextID, 2), 1 } }; @@ -385,7 +385,7 @@ namespace ArchiSteamFarm.Tests { const uint appID1 = 43; const uint appID2 = 44; - HashSet items = new HashSet { + HashSet items = new() { CreateCard(1, appID0), CreateCard(2, appID0), CreateCard(3, appID0), @@ -404,8 +404,12 @@ namespace ArchiSteamFarm.Tests { } private static void AssertResultMatchesExpectation(IReadOnlyDictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult, IReadOnlyCollection itemsToSend) { - if ((expectedResult == null) || (itemsToSend == null)) { - throw new ArgumentNullException(nameof(expectedResult) + " || " + nameof(itemsToSend)); + if (expectedResult == null) { + throw new ArgumentNullException(nameof(expectedResult)); + } + + if (itemsToSend == null) { + throw new ArgumentNullException(nameof(itemsToSend)); } Dictionary<(uint RealAppID, ulong ContextID, ulong ClassID), long> realResult = itemsToSend.GroupBy(asset => (asset.RealAppID, asset.ContextID, asset.ClassID)).ToDictionary(group => group.Key, group => group.Sum(asset => asset.Amount)); @@ -413,7 +417,7 @@ namespace ArchiSteamFarm.Tests { Assert.IsTrue(expectedResult.All(expectation => realResult.TryGetValue(expectation.Key, out long reality) && (expectation.Value == reality))); } - private static Steam.Asset CreateCard(ulong classID, uint realAppID, uint amount = 1, Steam.Asset.EType type = Steam.Asset.EType.TradingCard, Steam.Asset.ERarity rarity = Steam.Asset.ERarity.Common) => new Steam.Asset(Steam.Asset.SteamAppID, Steam.Asset.SteamCommunityContextID, classID, amount, realAppID: realAppID, type: type, rarity: rarity); + private static Steam.Asset CreateCard(ulong classID, uint realAppID, uint amount = 1, Steam.Asset.EType type = Steam.Asset.EType.TradingCard, Steam.Asset.ERarity rarity = Steam.Asset.ERarity.Common) => new(Steam.Asset.SteamAppID, Steam.Asset.SteamCommunityContextID, classID, amount, realAppID: realAppID, type: type, rarity: rarity); private static HashSet GetItemsForFullBadge(IReadOnlyCollection inventory, byte cardsPerSet, uint appID) => GetItemsForFullBadge(inventory, new Dictionary { { appID, cardsPerSet } }); diff --git a/ArchiSteamFarm.Tests/Trading.cs b/ArchiSteamFarm.Tests/Trading.cs index ba6070ce9..f18d2b9c5 100644 --- a/ArchiSteamFarm.Tests/Trading.cs +++ b/ArchiSteamFarm.Tests/Trading.cs @@ -29,42 +29,42 @@ namespace ArchiSteamFarm.Tests { public sealed class Trading { [TestMethod] public void MismatchRarityIsNotFair() { - HashSet itemsToGive = new HashSet { CreateItem(1, rarity: Steam.Asset.ERarity.Rare) }; - HashSet itemsToReceive = new HashSet { CreateItem(2) }; + HashSet itemsToGive = new() { CreateItem(1, rarity: Steam.Asset.ERarity.Rare) }; + HashSet itemsToReceive = new() { CreateItem(2) }; Assert.IsFalse(IsFairExchange(itemsToGive, itemsToReceive)); } [TestMethod] public void MismatchRealAppIDsIsNotFair() { - HashSet itemsToGive = new HashSet { CreateItem(1, realAppID: 570) }; - HashSet itemsToReceive = new HashSet { CreateItem(2) }; + HashSet itemsToGive = new() { CreateItem(1, realAppID: 570) }; + HashSet itemsToReceive = new() { CreateItem(2) }; Assert.IsFalse(IsFairExchange(itemsToGive, itemsToReceive)); } [TestMethod] public void MismatchTypesIsNotFair() { - HashSet itemsToGive = new HashSet { CreateItem(1, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToReceive = new HashSet { CreateItem(2) }; + HashSet itemsToGive = new() { CreateItem(1, type: Steam.Asset.EType.Emoticon) }; + HashSet itemsToReceive = new() { CreateItem(2) }; Assert.IsFalse(IsFairExchange(itemsToGive, itemsToReceive)); } [TestMethod] public void MultiGameMultiTypeBadReject() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 9), CreateItem(3, 9, 730, Steam.Asset.EType.Emoticon), CreateItem(4, realAppID: 730, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(4, realAppID: 730, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(2), CreateItem(3, realAppID: 730, type: Steam.Asset.EType.Emoticon) }; @@ -75,17 +75,17 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void MultiGameMultiTypeNeutralAccept() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 9), CreateItem(3, realAppID: 730, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(3, realAppID: 730, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(2), CreateItem(4, realAppID: 730, type: Steam.Asset.EType.Emoticon) }; @@ -96,18 +96,18 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void MultiGameSingleTypeBadReject() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 9), CreateItem(3, realAppID: 730), CreateItem(4, realAppID: 730) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(3, realAppID: 730) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(2), CreateItem(4, realAppID: 730) }; @@ -118,17 +118,17 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void MultiGameSingleTypeNeutralAccept() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 2), CreateItem(3, realAppID: 730) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(3, realAppID: 730) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(2), CreateItem(4, realAppID: 730) }; @@ -139,7 +139,7 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameAbrynosWasWrongNeutralAccept() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1), CreateItem(2, 2), CreateItem(3), @@ -147,11 +147,11 @@ namespace ArchiSteamFarm.Tests { CreateItem(5) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(2) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(3) }; @@ -161,15 +161,15 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameDonationAccept() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(2), CreateItem(3, type: Steam.Asset.EType.SteamGems) }; @@ -180,18 +180,18 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameMultiTypeBadReject() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 9), CreateItem(3, 9, type: Steam.Asset.EType.Emoticon), CreateItem(4, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(4, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(2), CreateItem(3, type: Steam.Asset.EType.Emoticon) }; @@ -202,17 +202,17 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameMultiTypeNeutralAccept() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 9), CreateItem(3, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(3, type: Steam.Asset.EType.Emoticon) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(2), CreateItem(4, type: Steam.Asset.EType.Emoticon) }; @@ -223,19 +223,19 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameQuantityBadReject() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1), CreateItem(2), CreateItem(3) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(2), CreateItem(3) }; - HashSet itemsToReceive = new HashSet { CreateItem(4, 3) }; + HashSet itemsToReceive = new() { CreateItem(4, 3) }; Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive)); Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive)); @@ -243,17 +243,17 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameQuantityBadReject2() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1), CreateItem(2, 2) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(2, 2) }; - HashSet itemsToReceive = new HashSet { CreateItem(3, 3) }; + HashSet itemsToReceive = new() { CreateItem(3, 3) }; Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive)); Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive)); @@ -261,17 +261,17 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameQuantityNeutralAccept() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 2), CreateItem(2) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(1), CreateItem(2) }; - HashSet itemsToReceive = new HashSet { CreateItem(3, 2) }; + HashSet itemsToReceive = new() { CreateItem(3, 2) }; Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive)); Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive)); @@ -279,13 +279,13 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameSingleTypeBadReject() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1), CreateItem(2) }; - HashSet itemsToGive = new HashSet { CreateItem(1) }; - HashSet itemsToReceive = new HashSet { CreateItem(2) }; + HashSet itemsToGive = new() { CreateItem(1) }; + HashSet itemsToReceive = new() { CreateItem(2) }; Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive)); Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive)); @@ -293,15 +293,15 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameSingleTypeBadWithOverpayingReject() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 2), CreateItem(2, 2), CreateItem(3, 2) }; - HashSet itemsToGive = new HashSet { CreateItem(2) }; + HashSet itemsToGive = new() { CreateItem(2) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(1), CreateItem(3) }; @@ -312,14 +312,14 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameSingleTypeBigDifferenceAccept() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1), CreateItem(2, 5), CreateItem(3) }; - HashSet itemsToGive = new HashSet { CreateItem(2) }; - HashSet itemsToReceive = new HashSet { CreateItem(3) }; + HashSet itemsToGive = new() { CreateItem(2) }; + HashSet itemsToReceive = new() { CreateItem(3) }; Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive)); Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive)); @@ -327,7 +327,7 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameSingleTypeBigDifferenceReject() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1), CreateItem(2, 2), CreateItem(3, 2), @@ -335,12 +335,12 @@ namespace ArchiSteamFarm.Tests { CreateItem(5, 10) }; - HashSet itemsToGive = new HashSet { + HashSet itemsToGive = new() { CreateItem(2), CreateItem(5) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(3), CreateItem(4) }; @@ -351,9 +351,9 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameSingleTypeGoodAccept() { - HashSet inventory = new HashSet { CreateItem(1, 2) }; - HashSet itemsToGive = new HashSet { CreateItem(1) }; - HashSet itemsToReceive = new HashSet { CreateItem(2) }; + HashSet inventory = new() { CreateItem(1, 2) }; + HashSet itemsToGive = new() { CreateItem(1) }; + HashSet itemsToReceive = new() { CreateItem(2) }; Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive)); Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive)); @@ -361,9 +361,9 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameSingleTypeNeutralAccept() { - HashSet inventory = new HashSet { CreateItem(1) }; - HashSet itemsToGive = new HashSet { CreateItem(1) }; - HashSet itemsToReceive = new HashSet { CreateItem(2) }; + HashSet inventory = new() { CreateItem(1) }; + HashSet itemsToGive = new() { CreateItem(1) }; + HashSet itemsToReceive = new() { CreateItem(2) }; Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive)); Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive)); @@ -371,14 +371,14 @@ namespace ArchiSteamFarm.Tests { [TestMethod] public void SingleGameSingleTypeNeutralWithOverpayingAccept() { - HashSet inventory = new HashSet { + HashSet inventory = new() { CreateItem(1, 2), CreateItem(2, 2) }; - HashSet itemsToGive = new HashSet { CreateItem(2) }; + HashSet itemsToGive = new() { CreateItem(2) }; - HashSet itemsToReceive = new HashSet { + HashSet itemsToReceive = new() { CreateItem(1), CreateItem(3) }; @@ -387,6 +387,6 @@ namespace ArchiSteamFarm.Tests { Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive)); } - private static Steam.Asset CreateItem(ulong classID, uint amount = 1, uint realAppID = Steam.Asset.SteamAppID, Steam.Asset.EType type = Steam.Asset.EType.TradingCard, Steam.Asset.ERarity rarity = Steam.Asset.ERarity.Common) => new Steam.Asset(Steam.Asset.SteamAppID, Steam.Asset.SteamCommunityContextID, classID, amount, realAppID: realAppID, type: type, rarity: rarity); + private static Steam.Asset CreateItem(ulong classID, uint amount = 1, uint realAppID = Steam.Asset.SteamAppID, Steam.Asset.EType type = Steam.Asset.EType.TradingCard, Steam.Asset.ERarity rarity = Steam.Asset.ERarity.Common) => new(Steam.Asset.SteamAppID, Steam.Asset.SteamCommunityContextID, classID, amount, realAppID: realAppID, type: type, rarity: rarity); } } diff --git a/ArchiSteamFarm.sln.DotSettings b/ArchiSteamFarm.sln.DotSettings index 156309ad8..e352044d7 100644 --- a/ArchiSteamFarm.sln.DotSettings +++ b/ArchiSteamFarm.sln.DotSettings @@ -28,9 +28,11 @@ SUGGESTION SUGGESTION SUGGESTION + SUGGESTION SUGGESTION SUGGESTION SUGGESTION + SUGGESTION SUGGESTION SUGGESTION SUGGESTION @@ -289,9 +291,11 @@ Required Required ExpressionBody + DefaultExpression ExpressionBody ExpressionBody public protected internal private static extern new virtual abstract sealed override readonly unsafe volatile async + Arithmetic, Shift, Bitwise, Conditional END_OF_LINE END_OF_LINE diff --git a/ArchiSteamFarm/ASF.cs b/ArchiSteamFarm/ASF.cs index 8f852f221..bec33a5bb 100644 --- a/ArchiSteamFarm/ASF.cs +++ b/ArchiSteamFarm/ASF.cs @@ -49,7 +49,7 @@ namespace ArchiSteamFarm { private const byte MaximumRecommendedBotsCount = 10; [PublicAPI] - public static readonly ArchiLogger ArchiLogger = new ArchiLogger(SharedInfo.ASF); + public static readonly ArchiLogger ArchiLogger = new(SharedInfo.ASF); [PublicAPI] public static byte LoadBalancingDelay => Math.Max(GlobalConfig?.LoginLimiterDelay ?? 0, GlobalConfig.DefaultLoginLimiterDelay); @@ -70,7 +70,7 @@ namespace ArchiSteamFarm { internal static ICrossProcessSemaphore? LoginSemaphore { get; private set; } internal static ImmutableDictionary? WebLimitingSemaphores { get; private set; } - private static readonly SemaphoreSlim UpdateSemaphore = new SemaphoreSlim(1, 1); + private static readonly SemaphoreSlim UpdateSemaphore = new(1, 1); private static Timer? AutoUpdatesTimer; private static FileSystemWatcher? FileSystemWatcher; @@ -150,11 +150,11 @@ namespace ArchiSteamFarm { string networkGroupText = ""; if (!string.IsNullOrEmpty(Program.NetworkGroup)) { - using SHA256CryptoServiceProvider hashingAlgorithm = new SHA256CryptoServiceProvider(); + using SHA256CryptoServiceProvider hashingAlgorithm = new(); networkGroupText = "-" + BitConverter.ToString(hashingAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(Program.NetworkGroup!))).Replace("-", ""); } else if (!string.IsNullOrEmpty(globalConfig.WebProxyText)) { - using SHA256CryptoServiceProvider hashingAlgorithm = new SHA256CryptoServiceProvider(); + using SHA256CryptoServiceProvider hashingAlgorithm = new(); networkGroupText = "-" + BitConverter.ToString(hashingAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(globalConfig.WebProxyText!))).Replace("-", ""); } @@ -266,7 +266,7 @@ namespace ArchiSteamFarm { return null; } - Version newVersion = new Version(releaseResponse.Tag!); + Version newVersion = new(releaseResponse.Tag!); ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.UpdateVersionInfo, SharedInfo.Version, newVersion)); @@ -314,7 +314,7 @@ namespace ArchiSteamFarm { ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.UpdateDownloadingNewVersion, newVersion, binaryAsset.Size / 1024 / 1024)); - Progress progressReporter = new Progress(); + Progress progressReporter = new(); progressReporter.ProgressChanged += OnProgressChanged; @@ -340,12 +340,12 @@ namespace ArchiSteamFarm { try { #if NETFRAMEWORK - using MemoryStream memoryStream = new MemoryStream(response.Content); + using MemoryStream memoryStream = new(response.Content); #else - await using MemoryStream memoryStream = new MemoryStream(response.Content); + await using MemoryStream memoryStream = new(response.Content); #endif - using ZipArchive zipArchive = new ZipArchive(memoryStream); + using ZipArchive zipArchive = new(memoryStream); if (!UpdateFromArchive(zipArchive, SharedInfo.HomeDirectory)) { ArchiLogger.LogGenericError(Strings.WarningFailed); @@ -382,7 +382,7 @@ namespace ArchiSteamFarm { } // Save our event in dictionary - object currentWriteEvent = new object(); + object currentWriteEvent = new(); LastWriteEvents[filePath] = currentWriteEvent; // Wait a second for eventual other events to arrive @@ -857,15 +857,16 @@ namespace ArchiSteamFarm { return; } - if (botNames.Count == 0) { - ArchiLogger.LogGenericWarning(Strings.ErrorNoBotsDefined); + switch (botNames.Count) { + case 0: + ArchiLogger.LogGenericWarning(Strings.ErrorNoBotsDefined); - return; - } + return; + case > MaximumRecommendedBotsCount: + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningExcessiveBotsCount, MaximumRecommendedBotsCount)); + await Task.Delay(10000).ConfigureAwait(false); - if (botNames.Count > MaximumRecommendedBotsCount) { - ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningExcessiveBotsCount, MaximumRecommendedBotsCount)); - await Task.Delay(10000).ConfigureAwait(false); + break; } await Utilities.InParallel(botNames.OrderBy(botName => botName, Bot.BotsComparer).Select(Bot.RegisterBot)).ConfigureAwait(false); @@ -884,7 +885,7 @@ namespace ArchiSteamFarm { TimeSpan autoUpdatePeriod = TimeSpan.FromHours(GlobalConfig.UpdatePeriod); AutoUpdatesTimer = new Timer( - async e => await UpdateAndRestart().ConfigureAwait(false), + async _ => await UpdateAndRestart().ConfigureAwait(false), null, autoUpdatePeriod, // Delay autoUpdatePeriod // Period diff --git a/ArchiSteamFarm/Actions.cs b/ArchiSteamFarm/Actions.cs index 3504b143b..14625f752 100644 --- a/ArchiSteamFarm/Actions.cs +++ b/ArchiSteamFarm/Actions.cs @@ -36,11 +36,11 @@ using SteamKit2; namespace ArchiSteamFarm { public sealed class Actions : IAsyncDisposable { - private static readonly SemaphoreSlim GiftCardsSemaphore = new SemaphoreSlim(1, 1); + private static readonly SemaphoreSlim GiftCardsSemaphore = new(1, 1); private readonly Bot Bot; - private readonly ConcurrentHashSet HandledGifts = new ConcurrentHashSet(); - private readonly SemaphoreSlim TradingSemaphore = new SemaphoreSlim(1, 1); + private readonly ConcurrentHashSet HandledGifts = new(); + private readonly SemaphoreSlim TradingSemaphore = new(1, 1); private Timer? CardsFarmerResumeTimer; private bool ProcessingGiftsScheduled; @@ -199,7 +199,7 @@ namespace ArchiSteamFarm { if (resumeInSeconds > 0) { if (CardsFarmerResumeTimer == null) { CardsFarmerResumeTimer = new Timer( - e => Resume(), + _ => Resume(), null, TimeSpan.FromSeconds(resumeInSeconds), // Delay Timeout.InfiniteTimeSpan // Period @@ -333,7 +333,7 @@ namespace ArchiSteamFarm { return (false, Strings.BotNotConnected); } - filterFunction ??= item => true; + filterFunction ??= _ => true; HashSet inventory; @@ -480,7 +480,7 @@ namespace ArchiSteamFarm { internal void OnDisconnected() => HandledGifts.Clear(); private ulong GetFirstSteamMasterID() { - ulong steamMasterID = Bot.BotConfig.SteamUserPermissions.Where(kv => (kv.Key > 0) && (kv.Key != Bot.SteamID) && new SteamID(kv.Key).IsIndividualAccount && (kv.Value == BotConfig.EPermission.Master)).Select(kv => kv.Key).OrderBy(steamID => steamID).FirstOrDefault()!; + ulong steamMasterID = Bot.BotConfig.SteamUserPermissions.Where(kv => (kv.Key > 0) && (kv.Key != Bot.SteamID) && new SteamID(kv.Key).IsIndividualAccount && (kv.Value == BotConfig.EAccess.Master)).Select(kv => kv.Key).OrderBy(steamID => steamID).FirstOrDefault()!; if (steamMasterID > 0) { return steamMasterID; diff --git a/ArchiSteamFarm/ArchiCryptoHelper.cs b/ArchiSteamFarm/ArchiCryptoHelper.cs index a741699d1..714d1a5cc 100644 --- a/ArchiSteamFarm/ArchiCryptoHelper.cs +++ b/ArchiSteamFarm/ArchiCryptoHelper.cs @@ -124,7 +124,7 @@ namespace ArchiSteamFarm { case EHashingMethod.SCrypt: return SCrypt.ComputeDerivedKey(password, salt, SteamParentalSCryptIterations, SteamParentalSCryptBlocksCount, 1, null, hashLength); case EHashingMethod.Pbkdf2: - using (HMACSHA256 hmacAlgorithm = new HMACSHA256(password)) { + using (HMACSHA256 hmacAlgorithm = new(password)) { return Pbkdf2.ComputeDerivedKey(hmacAlgorithm, salt, SteamParentalPbkdf2Iterations, hashLength); } default: diff --git a/ArchiSteamFarm/ArchiHandler.cs b/ArchiSteamFarm/ArchiHandler.cs index 4c6e5ea7b..c1f6de732 100644 --- a/ArchiSteamFarm/ArchiHandler.cs +++ b/ArchiSteamFarm/ArchiHandler.cs @@ -76,42 +76,42 @@ namespace ArchiSteamFarm { switch (packetMsg.MsgType) { case EMsg.ClientCommentNotifications: - ClientMsgProtobuf commentNotifications = new ClientMsgProtobuf(packetMsg); + ClientMsgProtobuf commentNotifications = new(packetMsg); Client.PostCallback(new UserNotificationsCallback(packetMsg.TargetJobID, commentNotifications.Body)); break; case EMsg.ClientItemAnnouncements: - ClientMsgProtobuf itemAnnouncements = new ClientMsgProtobuf(packetMsg); + ClientMsgProtobuf itemAnnouncements = new(packetMsg); Client.PostCallback(new UserNotificationsCallback(packetMsg.TargetJobID, itemAnnouncements.Body)); break; case EMsg.ClientPlayingSessionState: - ClientMsgProtobuf playingSessionState = new ClientMsgProtobuf(packetMsg); + ClientMsgProtobuf playingSessionState = new(packetMsg); Client.PostCallback(new PlayingSessionStateCallback(packetMsg.TargetJobID, playingSessionState.Body)); break; case EMsg.ClientPurchaseResponse: - ClientMsgProtobuf purchaseResponse = new ClientMsgProtobuf(packetMsg); + ClientMsgProtobuf purchaseResponse = new(packetMsg); Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, purchaseResponse.Body)); break; case EMsg.ClientRedeemGuestPassResponse: - ClientMsgProtobuf redeemGuestPassResponse = new ClientMsgProtobuf(packetMsg); + ClientMsgProtobuf redeemGuestPassResponse = new(packetMsg); Client.PostCallback(new RedeemGuestPassResponseCallback(packetMsg.TargetJobID, redeemGuestPassResponse.Body)); break; case EMsg.ClientSharedLibraryLockStatus: - ClientMsgProtobuf sharedLibraryLockStatus = new ClientMsgProtobuf(packetMsg); + ClientMsgProtobuf sharedLibraryLockStatus = new(packetMsg); Client.PostCallback(new SharedLibraryLockStatusCallback(packetMsg.TargetJobID, sharedLibraryLockStatus.Body)); break; case EMsg.ClientUserNotifications: - ClientMsgProtobuf userNotifications = new ClientMsgProtobuf(packetMsg); + ClientMsgProtobuf userNotifications = new(packetMsg); Client.PostCallback(new UserNotificationsCallback(packetMsg.TargetJobID, userNotifications.Body)); break; case EMsg.ClientVanityURLChangedNotification: - ClientMsgProtobuf vanityURLChangedNotification = new ClientMsgProtobuf(packetMsg); + ClientMsgProtobuf vanityURLChangedNotification = new(packetMsg); Client.PostCallback(new VanityURLChangedCallback(packetMsg.TargetJobID, vanityURLChangedNotification.Body)); break; @@ -139,7 +139,7 @@ namespace ArchiSteamFarm { return; } - CChatRoom_AckChatMessage_Notification request = new CChatRoom_AckChatMessage_Notification { + CChatRoom_AckChatMessage_Notification request = new() { chat_group_id = chatGroupID, chat_id = chatID, timestamp = timestamp @@ -165,7 +165,7 @@ namespace ArchiSteamFarm { return; } - CFriendMessages_AckMessage_Notification request = new CFriendMessages_AckMessage_Notification { + CFriendMessages_AckMessage_Notification request = new() { steamid_partner = steamID, timestamp = timestamp }; @@ -186,7 +186,7 @@ namespace ArchiSteamFarm { return; } - ClientMsg request = new ClientMsg { + ClientMsg request = new() { Body = { ClanID = steamID, AcceptInvite = acceptInvite @@ -209,7 +209,7 @@ namespace ArchiSteamFarm { return false; } - CPlayer_AddFriend_Request request = new CPlayer_AddFriend_Request { steamid = steamID }; + CPlayer_AddFriend_Request request = new() { steamid = steamID }; SteamUnifiedMessages.ServiceMethodResponse response; @@ -237,7 +237,7 @@ namespace ArchiSteamFarm { return 0; } - CClanChatRooms_GetClanChatRoomInfo_Request request = new CClanChatRooms_GetClanChatRoomInfo_Request { + CClanChatRooms_GetClanChatRoomInfo_Request request = new() { autocreate = true, steamid = steamID }; @@ -270,7 +270,7 @@ namespace ArchiSteamFarm { return null; } - CPlayer_GetGameBadgeLevels_Request request = new CPlayer_GetGameBadgeLevels_Request(); + CPlayer_GetGameBadgeLevels_Request request = new(); SteamUnifiedMessages.ServiceMethodResponse response; try { @@ -299,7 +299,7 @@ namespace ArchiSteamFarm { return null; } - CChatRoom_GetMyChatRoomGroups_Request request = new CChatRoom_GetMyChatRoomGroups_Request(); + CChatRoom_GetMyChatRoomGroups_Request request = new(); SteamUnifiedMessages.ServiceMethodResponse response; @@ -329,7 +329,7 @@ namespace ArchiSteamFarm { return null; } - CPlayer_GetPrivacySettings_Request request = new CPlayer_GetPrivacySettings_Request(); + CPlayer_GetPrivacySettings_Request request = new(); SteamUnifiedMessages.ServiceMethodResponse response; @@ -359,7 +359,7 @@ namespace ArchiSteamFarm { return null; } - CEcon_GetTradeOfferAccessToken_Request request = new CEcon_GetTradeOfferAccessToken_Request(); + CEcon_GetTradeOfferAccessToken_Request request = new(); SteamUnifiedMessages.ServiceMethodResponse response; @@ -393,7 +393,7 @@ namespace ArchiSteamFarm { return null; } - CTwoFactor_Status_Request request = new CTwoFactor_Status_Request { + CTwoFactor_Status_Request request = new() { steamid = steamID }; @@ -429,7 +429,7 @@ namespace ArchiSteamFarm { return false; } - CChatRoom_JoinChatRoomGroup_Request request = new CChatRoom_JoinChatRoomGroup_Request { chat_group_id = chatGroupID }; + CChatRoom_JoinChatRoomGroup_Request request = new() { chat_group_id = chatGroupID }; SteamUnifiedMessages.ServiceMethodResponse response; @@ -457,7 +457,7 @@ namespace ArchiSteamFarm { return; } - ClientMsgProtobuf request = new ClientMsgProtobuf(EMsg.ClientGamesPlayedWithDataBlob) { + ClientMsgProtobuf request = new(EMsg.ClientGamesPlayedWithDataBlob) { Body = { client_os_type = (uint) Bot.OSType } @@ -509,7 +509,7 @@ namespace ArchiSteamFarm { return null; } - ClientMsgProtobuf request = new ClientMsgProtobuf(EMsg.ClientRedeemGuestPass) { + ClientMsgProtobuf request = new(EMsg.ClientRedeemGuestPass) { SourceJobID = Client.GetNextJobID(), Body = { guest_pass_id = guestPassID } }; @@ -538,7 +538,7 @@ namespace ArchiSteamFarm { return null; } - ClientMsgProtobuf request = new ClientMsgProtobuf(EMsg.ClientRegisterKey) { + ClientMsgProtobuf request = new(EMsg.ClientRegisterKey) { SourceJobID = Client.GetNextJobID(), Body = { key = key } }; @@ -567,7 +567,7 @@ namespace ArchiSteamFarm { return false; } - CPlayer_RemoveFriend_Request request = new CPlayer_RemoveFriend_Request { steamid = steamID }; + CPlayer_RemoveFriend_Request request = new() { steamid = steamID }; SteamUnifiedMessages.ServiceMethodResponse response; @@ -591,7 +591,7 @@ namespace ArchiSteamFarm { return; } - ClientMsgProtobuf request = new ClientMsgProtobuf(EMsg.ClientRequestItemAnnouncements); + ClientMsgProtobuf request = new(EMsg.ClientRequestItemAnnouncements); Client.Send(request); } @@ -612,7 +612,7 @@ namespace ArchiSteamFarm { return EResult.NoConnection; } - CFriendMessages_SendMessage_Request request = new CFriendMessages_SendMessage_Request { + CFriendMessages_SendMessage_Request request = new() { chat_entry_type = (int) EChatEntryType.ChatMsg, contains_bbcode = true, message = message, @@ -653,7 +653,7 @@ namespace ArchiSteamFarm { return EResult.NoConnection; } - CChatRoom_SendChatMessage_Request request = new CChatRoom_SendChatMessage_Request { + CChatRoom_SendChatMessage_Request request = new() { chat_group_id = chatGroupID, chat_id = chatID, message = message @@ -685,7 +685,7 @@ namespace ArchiSteamFarm { return EResult.NoConnection; } - CFriendMessages_SendMessage_Request request = new CFriendMessages_SendMessage_Request { + CFriendMessages_SendMessage_Request request = new() { chat_entry_type = (int) EChatEntryType.Typing, steamid = steamID }; @@ -716,7 +716,7 @@ namespace ArchiSteamFarm { return; } - ClientMsgProtobuf request = new ClientMsgProtobuf(EMsg.ClientCurrentUIMode) { Body = { chat_mode = chatMode } }; + ClientMsgProtobuf request = new(EMsg.ClientCurrentUIMode) { Body = { chat_mode = chatMode } }; Client.Send(request); } @@ -759,9 +759,9 @@ namespace ArchiSteamFarm { return; } - KeyValue receiptInfo = new KeyValue(); + KeyValue receiptInfo = new(); - using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) { + using (MemoryStream ms = new(msg.purchase_receipt_info)) { if (!receiptInfo.TryReadAsBinary(ms)) { ASF.ArchiLogger.LogNullError(nameof(ms)); diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs index 00fe47706..98974a563 100644 --- a/ArchiSteamFarm/ArchiWebHandler.cs +++ b/ArchiSteamFarm/ArchiWebHandler.cs @@ -64,7 +64,7 @@ namespace ArchiSteamFarm { private const string SteamHelpHost = "help.steampowered.com"; private const string SteamStoreHost = "store.steampowered.com"; - private static readonly ConcurrentDictionary CachedCardCountsForGame = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary CachedCardCountsForGame = new(); [PublicAPI] public ArchiCacheable CachedApiKey { get; } @@ -73,7 +73,7 @@ namespace ArchiSteamFarm { public WebBrowser WebBrowser { get; } private readonly Bot Bot; - private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim SessionSemaphore = new(1, 1); private bool Initialized; private DateTime LastSessionCheck; @@ -149,7 +149,7 @@ namespace ArchiSteamFarm { ulong startAssetID = 0; // We need to store asset IDs to make sure we won't get duplicate items - HashSet assetIDs = new HashSet(); + HashSet assetIDs = new(); while (true) { await ASF.InventorySemaphore.WaitAsync().ConfigureAwait(false); @@ -174,7 +174,7 @@ namespace ArchiSteamFarm { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Strings.ErrorObjectIsNull, nameof(response.Content.Assets) + " || " + nameof(response.Content.Descriptions))); } - Dictionary<(ulong ClassID, ulong InstanceID), Steam.InventoryResponse.Description> descriptions = new Dictionary<(ulong ClassID, ulong InstanceID), Steam.InventoryResponse.Description>(); + Dictionary<(ulong ClassID, ulong InstanceID), Steam.InventoryResponse.Description> descriptions = new(); foreach (Steam.InventoryResponse.Description description in response.Content.Descriptions) { if (description.ClassID == 0) { @@ -249,7 +249,7 @@ namespace ArchiSteamFarm { return null; } - Dictionary result = new Dictionary(xmlNodeList.Count); + Dictionary result = new(xmlNodeList.Count); foreach (XmlNode? xmlNode in xmlNodeList) { if (xmlNode == null) { @@ -333,7 +333,7 @@ namespace ArchiSteamFarm { List games = response["games"].Children; - Dictionary result = new Dictionary(games.Count); + Dictionary result = new(games.Count); foreach (KeyValue game in games) { uint appID = game["appid"].AsUnsignedInteger(); @@ -379,8 +379,8 @@ namespace ArchiSteamFarm { throw new ArgumentOutOfRangeException(nameof(itemsPerTrade)); } - Steam.TradeOfferSendRequest singleTrade = new Steam.TradeOfferSendRequest(); - HashSet trades = new HashSet { singleTrade }; + Steam.TradeOfferSendRequest singleTrade = new(); + HashSet trades = new() { singleTrade }; if (itemsToGive != null) { foreach (Steam.Asset itemToGive in itemsToGive) { @@ -416,14 +416,14 @@ namespace ArchiSteamFarm { const string referer = SteamCommunityURL + "/tradeoffer/new"; // Extra entry for sessionID - Dictionary data = new Dictionary(6, StringComparer.Ordinal) { + Dictionary data = new(6, StringComparer.Ordinal) { { "partner", steamID.ToString(CultureInfo.InvariantCulture) }, { "serverid", "1" }, { "trade_offer_create_params", !string.IsNullOrEmpty(token) ? new JObject { { "trade_offer_access_token", token } }.ToString(Formatting.None) : "" }, { "tradeoffermessage", "Sent by " + SharedInfo.PublicIdentifier + "/" + SharedInfo.Version } }; - HashSet mobileTradeOfferIDs = new HashSet(); + HashSet mobileTradeOfferIDs = new(); foreach (Steam.TradeOfferSendRequest trade in trades) { data["json_tradeoffer"] = JsonConvert.SerializeObject(trade); @@ -535,7 +535,7 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, host + request)); - return default; + return default(WebBrowser.ObjectResponse?); } if (checkSessionPreemptively) { @@ -569,14 +569,14 @@ namespace ArchiSteamFarm { Bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed); Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, host + request)); - return default; + return default(WebBrowser.ObjectResponse?); } } WebBrowser.ObjectResponse? response = await WebLimitRequest(host, async () => await WebBrowser.UrlGetToJsonObject(host + request, headers, referer, requestOptions).ConfigureAwait(false)).ConfigureAwait(false); if (response == null) { - return default; + return default(WebBrowser.ObjectResponse?); } if (IsSessionExpiredUri(response.FinalUri)) { @@ -1042,7 +1042,7 @@ namespace ArchiSteamFarm { _ => throw new ArgumentOutOfRangeException(nameof(session)) }; - KeyValuePair sessionValue = new KeyValuePair(sessionName, sessionID!); + KeyValuePair sessionValue = new(sessionName, sessionID!); if (data != null) { data.Remove(sessionValue); @@ -1245,7 +1245,7 @@ namespace ArchiSteamFarm { const string request = "/gifts/0/resolvegiftcard"; // Extra entry for sessionID - Dictionary data = new Dictionary(3, StringComparer.Ordinal) { + Dictionary data = new(3, StringComparer.Ordinal) { { "accept", "1" }, { "giftcardid", giftCardID.ToString(CultureInfo.InvariantCulture) } }; @@ -1282,7 +1282,7 @@ namespace ArchiSteamFarm { string referer = SteamCommunityURL + "/tradeoffer/" + tradeID; // Extra entry for sessionID - Dictionary data = new Dictionary(3, StringComparer.Ordinal) { + Dictionary data = new(3, StringComparer.Ordinal) { { "serverid", "1" }, { "tradeofferid", tradeID.ToString(CultureInfo.InvariantCulture) } }; @@ -1316,7 +1316,7 @@ namespace ArchiSteamFarm { const string request = "/checkout/addfreelicense"; // Extra entry for sessionID - Dictionary data = new Dictionary(3, StringComparer.Ordinal) { + Dictionary data = new(3, StringComparer.Ordinal) { { "action", "add_to_cart" }, { "subid", subID.ToString(CultureInfo.InvariantCulture) } }; @@ -1342,7 +1342,7 @@ namespace ArchiSteamFarm { string request = profileURL + "/ajaxsetprivacy"; // Extra entry for sessionID - Dictionary data = new Dictionary(3, StringComparer.Ordinal) { + Dictionary data = new(3, StringComparer.Ordinal) { { "eCommentPermission", ((byte) userPrivacy.CommentPermission).ToString(CultureInfo.InvariantCulture) }, { "Privacy", JsonConvert.SerializeObject(userPrivacy.Settings) } }; @@ -1370,7 +1370,7 @@ namespace ArchiSteamFarm { string request = "/app/" + appID; // Extra entry for sessionID - Dictionary data = new Dictionary(2, StringComparer.Ordinal) { { "appid_to_clear_from_queue", appID.ToString(CultureInfo.InvariantCulture) } }; + Dictionary data = new(2, StringComparer.Ordinal) { { "appid_to_clear_from_queue", appID.ToString(CultureInfo.InvariantCulture) } }; return await UrlPostWithSession(SteamStoreURL, request, data: data).ConfigureAwait(false); } @@ -1427,7 +1427,7 @@ namespace ArchiSteamFarm { const string request = "/explore/generatenewdiscoveryqueue"; // Extra entry for sessionID - Dictionary data = new Dictionary(2, StringComparer.Ordinal) { { "queuetype", "0" } }; + Dictionary data = new(2, StringComparer.Ordinal) { { "queuetype", "0" } }; WebBrowser.ObjectResponse? response = await UrlPostToJsonObjectWithSession(SteamStoreURL, request, data: data).ConfigureAwait(false); @@ -1476,7 +1476,7 @@ namespace ArchiSteamFarm { return null; } - Dictionary<(uint AppID, ulong ClassID, ulong InstanceID), Steam.InventoryResponse.Description> descriptions = new Dictionary<(uint AppID, ulong ClassID, ulong InstanceID), Steam.InventoryResponse.Description>(); + Dictionary<(uint AppID, ulong ClassID, ulong InstanceID), Steam.InventoryResponse.Description> descriptions = new(); foreach (KeyValue description in response["descriptions"].Children) { uint appID = description["appid"].AsUnsignedInteger(); @@ -1503,7 +1503,7 @@ namespace ArchiSteamFarm { continue; } - Steam.InventoryResponse.Description parsedDescription = new Steam.InventoryResponse.Description { + Steam.InventoryResponse.Description parsedDescription = new() { AppID = appID, ClassID = classID, InstanceID = instanceID, @@ -1514,7 +1514,7 @@ namespace ArchiSteamFarm { List tags = description["tags"].Children; if (tags.Count > 0) { - HashSet parsedTags = new HashSet(tags.Count); + HashSet parsedTags = new(tags.Count); foreach (KeyValue tag in tags) { string? identifier = tag["category"].AsString(); @@ -1542,7 +1542,7 @@ namespace ArchiSteamFarm { descriptions[key] = parsedDescription; } - HashSet result = new HashSet(); + HashSet result = new(); foreach (KeyValue trade in response["trade_offers_received"].Children) { ETradeOfferState state = trade["trade_offer_state"].AsEnum(); @@ -1573,7 +1573,7 @@ namespace ArchiSteamFarm { return null; } - Steam.TradeOffer tradeOffer = new Steam.TradeOffer(tradeOfferID, otherSteamID3, state); + Steam.TradeOffer tradeOffer = new(tradeOfferID, otherSteamID3, state); List itemsToGive = trade["items_to_give"].Children; @@ -1637,7 +1637,7 @@ namespace ArchiSteamFarm { return null; } - HashSet result = new HashSet(apps.Count); + HashSet result = new(apps.Count); foreach (uint appID in apps.Select(app => app["appid"].AsUnsignedInteger())) { if (appID == 0) { @@ -1744,7 +1744,7 @@ namespace ArchiSteamFarm { return new HashSet(0); } - HashSet results = new HashSet(htmlNodes.Count); + HashSet results = new(htmlNodes.Count); foreach (string? giftCardIDText in htmlNodes.Select(node => node.GetAttribute("id"))) { if (string.IsNullOrEmpty(giftCardIDText)) { @@ -1795,7 +1795,7 @@ namespace ArchiSteamFarm { return new HashSet(0); } - HashSet result = new HashSet(htmlNodes.Count); + HashSet result = new(htmlNodes.Count); foreach (string? miniProfile in htmlNodes.Select(htmlNode => htmlNode.GetAttribute("data-miniprofile"))) { if (string.IsNullOrEmpty(miniProfile)) { @@ -1934,7 +1934,7 @@ namespace ArchiSteamFarm { return null; } - Dictionary arguments = new Dictionary(!string.IsNullOrEmpty(tradeToken) ? 3 : 2, StringComparer.Ordinal) { + Dictionary arguments = new(!string.IsNullOrEmpty(tradeToken) ? 3 : 2, StringComparer.Ordinal) { { "key", steamApiKey! }, { "steamid_target", steamID } }; @@ -2057,7 +2057,7 @@ namespace ArchiSteamFarm { const string request = "/mobileconf/multiajaxop"; // Extra entry for sessionID - List> data = new List>(8 + (confirmations.Count * 2)) { + List> data = new(8 + (confirmations.Count * 2)) { new KeyValuePair("a", Bot.SteamID.ToString(CultureInfo.InvariantCulture)), new KeyValuePair("k", confirmationHash), new KeyValuePair("m", "android"), @@ -2104,7 +2104,7 @@ namespace ArchiSteamFarm { // RSA encrypt our session key with the public key for the universe we're on byte[] encryptedSessionKey; - using (RSACrypto rsa = new RSACrypto(publicKey)) { + using (RSACrypto rsa = new(publicKey)) { encryptedSessionKey = rsa.Encrypt(sessionKey); } @@ -2213,7 +2213,7 @@ namespace ArchiSteamFarm { string request = "/gid/" + groupID; // Extra entry for sessionID - Dictionary data = new Dictionary(2, StringComparer.Ordinal) { { "action", "join" } }; + Dictionary data = new(2, StringComparer.Ordinal) { { "action", "join" } }; return await UrlPostWithSession(SteamCommunityURL, request, data: data, session: ESession.CamelCase).ConfigureAwait(false); } @@ -2286,7 +2286,7 @@ namespace ArchiSteamFarm { const string requestValidateCode = "/account/ajaxredeemwalletcode"; // Extra entry for sessionID - Dictionary data = new Dictionary(2, StringComparer.Ordinal) { { "wallet_code", key } }; + Dictionary data = new(2, StringComparer.Ordinal) { { "wallet_code", key } }; WebBrowser.ObjectResponse? responseValidateCode = await UrlPostToJsonObjectWithSession(SteamStoreURL, requestValidateCode, data: data).ConfigureAwait(false); @@ -2322,7 +2322,7 @@ namespace ArchiSteamFarm { string request = profileURL + "/ajaxunpackbooster"; // Extra entry for sessionID - Dictionary data = new Dictionary(3, StringComparer.Ordinal) { + Dictionary data = new(3, StringComparer.Ordinal) { { "appid", appID.ToString(CultureInfo.InvariantCulture) }, { "communityitemid", itemID.ToString(CultureInfo.InvariantCulture) } }; @@ -2542,7 +2542,7 @@ namespace ArchiSteamFarm { rarity = description.Rarity; } - Steam.Asset steamAsset = new Steam.Asset(appID, contextID, classID, amount, instanceID, assetID, marketable, tradable, tags, realAppID, type, rarity); + Steam.Asset steamAsset = new(appID, contextID, classID, amount, instanceID, assetID, marketable, tradable, tags, realAppID, type, rarity); output.Add(steamAsset); } @@ -2594,7 +2594,7 @@ namespace ArchiSteamFarm { const string request = "/dev/registerkey"; // Extra entry for sessionID - Dictionary data = new Dictionary(4, StringComparer.Ordinal) { + Dictionary data = new(4, StringComparer.Ordinal) { { "agreeToTerms", "agreed" }, { "domain", "generated.by." + SharedInfo.AssemblyName.ToLowerInvariant() + ".localhost" }, { "Submit", "Register" } @@ -2699,7 +2699,7 @@ namespace ArchiSteamFarm { return false; } - Dictionary data = new Dictionary(2, StringComparer.Ordinal) { + Dictionary data = new(2, StringComparer.Ordinal) { { "pin", parentalCode }, { "sessionid", sessionID! } }; diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index 59ba8004f..22cff2d7c 100755 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -25,6 +25,8 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.Specialized; +using System.ComponentModel; +using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; @@ -66,43 +68,31 @@ namespace ArchiSteamFarm { internal static StringComparer? BotsComparer { get; private set; } internal static EOSType OSType { get; private set; } = EOSType.Unknown; - private static readonly SemaphoreSlim BotsSemaphore = new SemaphoreSlim(1, 1); + private static readonly SemaphoreSlim BotsSemaphore = new(1, 1); [JsonIgnore] [PublicAPI] - public readonly Actions Actions; + public Actions Actions { get; } [JsonIgnore] [PublicAPI] - public readonly ArchiLogger ArchiLogger; + public ArchiLogger ArchiLogger { get; } [JsonIgnore] [PublicAPI] - public readonly ArchiWebHandler ArchiWebHandler; + public ArchiWebHandler ArchiWebHandler { get; } [JsonProperty] [PublicAPI] - public readonly string BotName; + public string BotName { get; } [JsonProperty] [PublicAPI] - public readonly CardsFarmer CardsFarmer; + public CardsFarmer CardsFarmer { get; } [JsonIgnore] [PublicAPI] - public readonly Commands Commands; - - [JsonIgnore] - [PublicAPI] - public readonly SteamApps SteamApps; - - [JsonIgnore] - [PublicAPI] - public readonly SteamConfiguration SteamConfiguration; - - [JsonIgnore] - [PublicAPI] - public readonly SteamFriends SteamFriends; + public Commands Commands { get; } [JsonProperty] [PublicAPI] @@ -124,6 +114,18 @@ namespace ArchiSteamFarm { [PublicAPI] public IReadOnlyCollection OwnedPackageIDsReadOnly => OwnedPackageIDs.Keys.ToHashSet(); + [JsonIgnore] + [PublicAPI] + public SteamApps SteamApps { get; } + + [JsonIgnore] + [PublicAPI] + public SteamConfiguration SteamConfiguration { get; } + + [JsonIgnore] + [PublicAPI] + public SteamFriends SteamFriends { get; } + internal readonly ArchiHandler ArchiHandler; internal readonly BotDatabase BotDatabase; @@ -132,16 +134,16 @@ namespace ArchiSteamFarm { internal bool IsAccountLocked => AccountFlags.HasFlag(EAccountFlags.Lockdown); private readonly CallbackManager CallbackManager; - private readonly SemaphoreSlim CallbackSemaphore = new SemaphoreSlim(1, 1); - private readonly SemaphoreSlim GamesRedeemerInBackgroundSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim CallbackSemaphore = new(1, 1); + private readonly SemaphoreSlim GamesRedeemerInBackgroundSemaphore = new(1, 1); private readonly Timer HeartBeatTimer; - private readonly SemaphoreSlim InitializationSemaphore = new SemaphoreSlim(1, 1); - private readonly SemaphoreSlim MessagingSemaphore = new SemaphoreSlim(1, 1); - private readonly ConcurrentDictionary PastNotifications = new ConcurrentDictionary(); - private readonly SemaphoreSlim SendCompleteTypesSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim InitializationSemaphore = new(1, 1); + private readonly SemaphoreSlim MessagingSemaphore = new(1, 1); + private readonly ConcurrentDictionary PastNotifications = new(); + private readonly SemaphoreSlim SendCompleteTypesSemaphore = new(1, 1); private readonly Statistics? Statistics; private readonly SteamClient SteamClient; - private readonly ConcurrentHashSet SteamFamilySharingIDs = new ConcurrentHashSet(); + private readonly ConcurrentHashSet SteamFamilySharingIDs = new(); private readonly SteamUser SteamUser; private readonly Trading Trading; @@ -166,7 +168,7 @@ namespace ArchiSteamFarm { #pragma warning disable IDE0051 [JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamID))] - private string SSteamID => SteamID.ToString(); + private string SSteamID => SteamID.ToString(CultureInfo.InvariantCulture); #pragma warning restore IDE0051 [JsonProperty] @@ -231,13 +233,17 @@ namespace ArchiSteamFarm { private byte TwoFactorCodeFailures; private Bot(string botName, BotConfig botConfig, BotDatabase botDatabase) { - if (string.IsNullOrEmpty(botName) || (botConfig == null) || (botDatabase == null) || (Bots == null) || (ASF.GlobalConfig == null) || (ASF.GlobalDatabase == null)) { - throw new ArgumentNullException(nameof(botName) + " || " + nameof(botConfig) + " || " + nameof(botDatabase) + " || " + nameof(Bots) + " || " + nameof(ASF.GlobalConfig) + " || " + nameof(ASF.GlobalDatabase)); + BotName = !string.IsNullOrEmpty(botName) ? botName : throw new ArgumentNullException(nameof(botName)); + BotConfig = botConfig ?? throw new ArgumentNullException(nameof(botConfig)); + BotDatabase = botDatabase ?? throw new ArgumentNullException(nameof(botDatabase)); + + if (ASF.GlobalConfig == null) { + throw new InvalidOperationException(nameof(ASF.GlobalConfig)); } - BotName = botName; - BotConfig = botConfig; - BotDatabase = botDatabase; + if (ASF.GlobalDatabase == null) { + throw new InvalidOperationException(nameof(ASF.GlobalDatabase)); + } ArchiLogger = new ArchiLogger(botName); @@ -263,24 +269,24 @@ namespace ArchiSteamFarm { SteamUnifiedMessages? steamUnifiedMessages = SteamClient.GetHandler(); - ArchiHandler = new ArchiHandler(ArchiLogger, steamUnifiedMessages ?? throw new ArgumentNullException(nameof(steamUnifiedMessages))); + ArchiHandler = new ArchiHandler(ArchiLogger, steamUnifiedMessages ?? throw new InvalidOperationException(nameof(steamUnifiedMessages))); SteamClient.AddHandler(ArchiHandler); CallbackManager = new CallbackManager(SteamClient); CallbackManager.Subscribe(OnConnected); CallbackManager.Subscribe(OnDisconnected); - SteamApps = SteamClient.GetHandler() ?? throw new ArgumentNullException(nameof(SteamApps)); + SteamApps = SteamClient.GetHandler() ?? throw new InvalidOperationException(nameof(SteamApps)); CallbackManager.Subscribe(OnGuestPassList); CallbackManager.Subscribe(OnLicenseList); - SteamFriends = SteamClient.GetHandler() ?? throw new ArgumentNullException(nameof(SteamFriends)); + SteamFriends = SteamClient.GetHandler() ?? throw new InvalidOperationException(nameof(SteamFriends)); CallbackManager.Subscribe(OnFriendsList); CallbackManager.Subscribe(OnPersonaState); CallbackManager.Subscribe(OnServiceMethod); - SteamUser = SteamClient.GetHandler() ?? throw new ArgumentNullException(nameof(SteamUser)); + SteamUser = SteamClient.GetHandler() ?? throw new InvalidOperationException(nameof(SteamUser)); CallbackManager.Subscribe(OnLoggedOff); CallbackManager.Subscribe(OnLoggedOn); CallbackManager.Subscribe(OnLoginKey); @@ -302,9 +308,9 @@ namespace ArchiSteamFarm { } HeartBeatTimer = new Timer( - async e => await HeartBeat().ConfigureAwait(false), + HeartBeat, null, - TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bots.Count), // Delay + TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bots?.Count ?? 0), // Delay TimeSpan.FromMinutes(1) // Period ); } @@ -352,8 +358,12 @@ namespace ArchiSteamFarm { [PublicAPI] public static Bot? GetBot(string botName) { - if (string.IsNullOrEmpty(botName) || (Bots == null)) { - throw new ArgumentNullException(nameof(botName) + " || " + nameof(Bots)); + if (string.IsNullOrEmpty(botName)) { + throw new ArgumentNullException(nameof(botName)); + } + + if (Bots == null) { + throw new InvalidOperationException(nameof(Bots)); } if (Bots.TryGetValue(botName, out Bot? targetBot)) { @@ -369,13 +379,17 @@ namespace ArchiSteamFarm { [PublicAPI] public static HashSet? GetBots(string args) { - if (string.IsNullOrEmpty(args) || (Bots == null)) { - throw new ArgumentNullException(nameof(args) + " || " + nameof(Bots)); + if (string.IsNullOrEmpty(args)) { + throw new ArgumentNullException(nameof(args)); + } + + if (Bots == null) { + throw new InvalidOperationException(nameof(Bots)); } string[] botNames = args.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - HashSet result = new HashSet(); + HashSet result = new(); foreach (string botName in botNames) { if (botName.Equals(SharedInfo.ASF, StringComparison.OrdinalIgnoreCase)) { @@ -450,8 +464,16 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task GetTradeHoldDuration(ulong steamID, ulong tradeID) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (tradeID == 0) || (Bots == null)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(tradeID) + " || " + nameof(Bots)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (tradeID == 0) { + throw new ArgumentOutOfRangeException(nameof(tradeID)); + } + + if (Bots == null) { + throw new InvalidOperationException(nameof(Bots)); } if (SteamFriends.GetFriendRelationship(steamID) == EFriendRelationship.Friend) { @@ -480,25 +502,33 @@ namespace ArchiSteamFarm { } [PublicAPI] - public bool HasPermission(ulong steamID, BotConfig.EPermission permission) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (permission == BotConfig.EPermission.None) || !Enum.IsDefined(typeof(BotConfig.EPermission), permission)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(permission)); + public bool HasAccess(ulong steamID, BotConfig.EAccess access) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentNullException(nameof(steamID)); + } + + if ((access == BotConfig.EAccess.None) || !Enum.IsDefined(typeof(BotConfig.EAccess), access)) { + throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(BotConfig.EAccess)); } if (ASF.IsOwner(steamID)) { return true; } - return permission switch { - BotConfig.EPermission.FamilySharing when SteamFamilySharingIDs.Contains(steamID) => true, - _ => BotConfig.SteamUserPermissions.TryGetValue(steamID, out BotConfig.EPermission realPermission) && (realPermission >= permission) + return access switch { + BotConfig.EAccess.FamilySharing when SteamFamilySharingIDs.Contains(steamID) => true, + _ => BotConfig.SteamUserPermissions.TryGetValue(steamID, out BotConfig.EAccess realPermission) && (realPermission >= access) }; } [PublicAPI] public bool SetUserInput(ASF.EUserInputType inputType, string inputValue) { - if ((inputType == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), inputType) || string.IsNullOrEmpty(inputValue)) { - throw new ArgumentNullException(nameof(inputType) + " || " + nameof(inputValue)); + if ((inputType == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), inputType)) { + throw new InvalidEnumArgumentException(nameof(inputType), (int) inputType, typeof(ASF.EUserInputType)); + } + + if (string.IsNullOrEmpty(inputValue)) { + throw new ArgumentNullException(nameof(inputValue)); } // This switch should cover ONLY bot properties @@ -564,7 +594,7 @@ namespace ArchiSteamFarm { BotDatabase.AddGamesToRedeemInBackground(gamesToRedeemInBackground); if ((GamesRedeemerInBackgroundTimer == null) && BotDatabase.HasGamesToRedeemInBackground && IsConnectedAndLoggedOn) { - Utilities.InBackground(RedeemGamesInBackground); + Utilities.InBackground(() => RedeemGamesInBackground(this)); } } @@ -625,18 +655,24 @@ namespace ArchiSteamFarm { } internal static string FormatBotResponse(string response, string botName) { - if (string.IsNullOrEmpty(response) || string.IsNullOrEmpty(botName)) { - throw new ArgumentNullException(nameof(response) + " || " + nameof(botName)); + if (string.IsNullOrEmpty(response)) { + throw new ArgumentNullException(nameof(response)); + } + + if (string.IsNullOrEmpty(botName)) { + throw new ArgumentNullException(nameof(botName)); } return Environment.NewLine + "<" + botName + "> " + response; } internal async Task<(uint PlayableAppID, DateTime IgnoredUntil, bool IgnoredGlobally)> GetAppDataForIdling(uint appID, float hoursPlayed, bool allowRecursiveDiscovery = true, bool optimisticDiscovery = true) { - if ((appID == 0) || (hoursPlayed < 0)) { - ArchiLogger.LogNullError(nameof(appID) + " || " + nameof(hoursPlayed)); + if (appID == 0) { + throw new ArgumentOutOfRangeException(nameof(appID)); + } - return (0, DateTime.MaxValue, true); + if (hoursPlayed < 0) { + throw new ArgumentOutOfRangeException(nameof(hoursPlayed)); } HashSet? packageIDs = ASF.GlobalDatabase?.GetPackageIDs(appID, OwnedPackageIDs.Keys); @@ -681,7 +717,7 @@ namespace ArchiSteamFarm { return (optimisticDiscovery ? appID : 0, DateTime.MinValue, true); } - SteamApps.PICSRequest request = new SteamApps.PICSRequest(appID, tokenCallback.AppTokens.TryGetValue(appID, out ulong accessToken) ? accessToken : 0, false); + SteamApps.PICSRequest request = new(appID, tokenCallback.AppTokens.TryGetValue(appID, out ulong accessToken) ? accessToken : 0, false); AsyncJobMultiple.ResultSet? productInfoResultSet = null; @@ -727,7 +763,7 @@ namespace ArchiSteamFarm { case "PRERELEASE": return (0, DateTime.MaxValue, true); default: - ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(releaseState), releaseState)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(releaseState), releaseState)); break; } @@ -760,7 +796,7 @@ namespace ArchiSteamFarm { // Types that can't be idled break; default: - ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(type), type)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(type), type)); break; } @@ -798,8 +834,12 @@ namespace ArchiSteamFarm { } internal static string GetFilePath(string botName, EFileType fileType) { - if (string.IsNullOrEmpty(botName) || !Enum.IsDefined(typeof(EFileType), fileType)) { - throw new ArgumentNullException(nameof(botName) + " || " + nameof(fileType)); + if (string.IsNullOrEmpty(botName)) { + throw new ArgumentNullException(nameof(botName)); + } + + if (!Enum.IsDefined(typeof(EFileType), fileType)) { + throw new InvalidEnumArgumentException(nameof(fileType), (int) fileType, typeof(EFileType)); } string botPath = Path.Combine(SharedInfo.ConfigDirectory, botName); @@ -819,11 +859,15 @@ namespace ArchiSteamFarm { internal async Task?> GetMarketableAppIDs() => await ArchiWebHandler.GetAppList().ConfigureAwait(false); internal async Task? AppIDs)>?> GetPackagesData(IReadOnlyCollection packageIDs) { - if ((packageIDs == null) || (packageIDs.Count == 0) || (ASF.GlobalDatabase == null)) { - throw new ArgumentNullException(nameof(packageIDs) + " || " + nameof(ASF.GlobalDatabase)); + if ((packageIDs == null) || (packageIDs.Count == 0)) { + throw new ArgumentNullException(nameof(packageIDs)); } - HashSet packageRequests = new HashSet(); + if (ASF.GlobalDatabase == null) { + throw new InvalidOperationException(nameof(ASF.GlobalDatabase)); + } + + HashSet packageRequests = new(); foreach (uint packageID in packageIDs) { if (!ASF.GlobalDatabase.PackageAccessTokensReadOnly.TryGetValue(packageID, out ulong packageAccessToken)) { @@ -851,7 +895,7 @@ namespace ArchiSteamFarm { return null; } - Dictionary? AppIDs)> result = new Dictionary? AppIDs)>(); + Dictionary? AppIDs)> result = new(); foreach (SteamApps.PICSProductInfoCallback.PICSProductInfo productInfo in productInfoResultSet.Results.SelectMany(productInfoResult => productInfoResult.Packages).Where(productInfoPackages => productInfoPackages.Key != 0).Select(productInfoPackages => productInfoPackages.Value)) { if (productInfo.KeyValues == KeyValue.Invalid) { @@ -920,7 +964,7 @@ namespace ArchiSteamFarm { string? gameName = null; if (!string.IsNullOrEmpty(BotConfig.CustomGamePlayedWhileFarming)) { - gameName = string.Format(BotConfig.CustomGamePlayedWhileFarming!, game.AppID, game.GameName); + gameName = string.Format(CultureInfo.CurrentCulture, BotConfig.CustomGamePlayedWhileFarming!, game.AppID, game.GameName); } await ArchiHandler.PlayGames(game.PlayableAppID.ToEnumerable(), gameName).ConfigureAwait(false); @@ -934,7 +978,7 @@ namespace ArchiSteamFarm { string? gameName = null; if (!string.IsNullOrEmpty(BotConfig.CustomGamePlayedWhileFarming)) { - gameName = string.Format(BotConfig.CustomGamePlayedWhileFarming!, string.Join(", ", games.Select(game => game.AppID)), string.Join(", ", games.Select(game => game.GameName))); + gameName = string.Format(CultureInfo.CurrentCulture, BotConfig.CustomGamePlayedWhileFarming!, string.Join(", ", games.Select(game => game.AppID)), string.Join(", ", games.Select(game => game.GameName))); } await ArchiHandler.PlayGames(games.Select(game => game.PlayableAppID), gameName).ConfigureAwait(false); @@ -946,9 +990,9 @@ namespace ArchiSteamFarm { } try { - OrderedDictionary gamesToRedeemInBackground = new OrderedDictionary(); + OrderedDictionary gamesToRedeemInBackground = new(); - using (StreamReader reader = new StreamReader(filePath)) { + using (StreamReader reader = new(filePath)) { string? line; while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) { @@ -963,7 +1007,7 @@ namespace ArchiSteamFarm { string[] parsedArgs = line.Split(DefaultBackgroundKeysRedeemerSeparator, StringSplitOptions.RemoveEmptyEntries); if (parsedArgs.Length < 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, line)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, line)); continue; } @@ -991,9 +1035,7 @@ namespace ArchiSteamFarm { internal static void Init(StringComparer botsComparer) { if (Bots != null) { - ASF.ArchiLogger.LogGenericError(Strings.WarningFailed); - - return; + throw new InvalidOperationException(nameof(Bots)); } BotsComparer = botsComparer ?? throw new ArgumentNullException(nameof(botsComparer)); @@ -1002,7 +1044,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromIdling(uint appID) { if (appID == 0) { - throw new ArgumentNullException(nameof(appID)); + throw new ArgumentOutOfRangeException(nameof(appID)); } return BotDatabase.IsBlacklistedFromIdling(appID); @@ -1010,7 +1052,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromTrades(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } return BotDatabase.IsBlacklistedFromTrades(steamID); @@ -1018,7 +1060,7 @@ namespace ArchiSteamFarm { internal bool IsPriorityIdling(uint appID) { if (appID == 0) { - throw new ArgumentNullException(nameof(appID)); + throw new ArgumentOutOfRangeException(nameof(appID)); } return BotDatabase.IsPriorityIdling(appID); @@ -1121,8 +1163,12 @@ namespace ArchiSteamFarm { } internal static async Task RegisterBot(string botName) { - if (string.IsNullOrEmpty(botName) || (Bots == null)) { - throw new ArgumentNullException(nameof(botName) + " || " + nameof(Bots)); + if (string.IsNullOrEmpty(botName)) { + throw new ArgumentNullException(nameof(botName)); + } + + if (Bots == null) { + throw new InvalidOperationException(nameof(Bots)); } if (Bots.ContainsKey(botName)) { @@ -1140,7 +1186,7 @@ namespace ArchiSteamFarm { BotConfig? botConfig = await BotConfig.Load(configFilePath).ConfigureAwait(false); if (botConfig == null) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorBotConfigInvalid, configFilePath)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorBotConfigInvalid, configFilePath)); return; } @@ -1160,7 +1206,7 @@ namespace ArchiSteamFarm { BotDatabase? botDatabase = await BotDatabase.CreateOrLoad(databaseFilePath).ConfigureAwait(false); if (botDatabase == null) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorDatabaseInvalid, databaseFilePath)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorDatabaseInvalid, databaseFilePath)); return; } @@ -1209,11 +1255,15 @@ namespace ArchiSteamFarm { } internal async Task Rename(string newBotName) { - if (string.IsNullOrEmpty(newBotName) || (Bots == null)) { - throw new ArgumentNullException(nameof(newBotName) + " || " + nameof(Bots)); + if (string.IsNullOrEmpty(newBotName)) { + throw new ArgumentNullException(nameof(newBotName)); } - if (newBotName.Equals(SharedInfo.ASF) || Bots.ContainsKey(newBotName)) { + if (Bots == null) { + throw new InvalidOperationException(nameof(Bots)); + } + + if (!ASF.IsValidBotName(newBotName) || Bots.ContainsKey(newBotName)) { return false; } @@ -1254,8 +1304,12 @@ namespace ArchiSteamFarm { } internal async Task SendMessage(ulong steamID, string message) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(message)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(message)) { + throw new ArgumentNullException(nameof(message)); } if (!IsConnectedAndLoggedOn) { @@ -1327,7 +1381,7 @@ namespace ArchiSteamFarm { break; default: - ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result), result)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(result), result)); return false; } @@ -1349,8 +1403,16 @@ namespace ArchiSteamFarm { } internal async Task SendMessage(ulong chatGroupID, ulong chatID, string message) { - if ((chatGroupID == 0) || (chatID == 0) || string.IsNullOrEmpty(message)) { - throw new ArgumentNullException(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(message)); + if (chatGroupID == 0) { + throw new ArgumentOutOfRangeException(nameof(chatGroupID)); + } + + if (chatID == 0) { + throw new ArgumentOutOfRangeException(nameof(chatID)); + } + + if (string.IsNullOrEmpty(message)) { + throw new ArgumentNullException(nameof(message)); } if (!IsConnectedAndLoggedOn) { @@ -1418,7 +1480,7 @@ namespace ArchiSteamFarm { break; default: - ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result), result)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(result), result)); return false; } @@ -1441,7 +1503,7 @@ namespace ArchiSteamFarm { internal async Task SendTypingMessage(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } if (!IsConnectedAndLoggedOn) { @@ -1513,7 +1575,7 @@ namespace ArchiSteamFarm { throw new ArgumentNullException(nameof(gamesToRedeemInBackground)); } - HashSet invalidKeys = new HashSet(); + HashSet invalidKeys = new(); foreach (DictionaryEntry game in gamesToRedeemInBackground) { bool invalid = false; @@ -1522,17 +1584,17 @@ namespace ArchiSteamFarm { if (string.IsNullOrEmpty(key)) { invalid = true; - ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(key))); + ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(key))); } else if (!Utilities.IsValidCdKey(key!)) { invalid = true; - ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key)); + ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, key)); } string? name = game.Value as string; if (string.IsNullOrEmpty(name)) { invalid = true; - ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(name))); + ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(name))); } if (invalid && (key != null)) { @@ -1589,7 +1651,7 @@ namespace ArchiSteamFarm { private async Task Destroy(bool force = false) { if (Bots == null) { - throw new ArgumentNullException(nameof(Bots)); + throw new InvalidOperationException(nameof(Bots)); } if (KeepRunning) { @@ -1635,10 +1697,10 @@ namespace ArchiSteamFarm { return new Dictionary(0, StringComparer.Ordinal); } - Dictionary keys = new Dictionary(StringComparer.Ordinal); + Dictionary keys = new(StringComparer.Ordinal); try { - using StreamReader reader = new StreamReader(filePath); + using StreamReader reader = new(filePath); string? line; @@ -1650,7 +1712,7 @@ namespace ArchiSteamFarm { string[] parsedArgs = line.Split(DefaultBackgroundKeysRedeemerSeparator, StringSplitOptions.RemoveEmptyEntries); if (parsedArgs.Length < 3) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, line)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, line)); continue; } @@ -1658,7 +1720,7 @@ namespace ArchiSteamFarm { string key = parsedArgs[^1]; if (!Utilities.IsValidCdKey(key)) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, key)); continue; } @@ -1681,7 +1743,7 @@ namespace ArchiSteamFarm { while (KeepRunning || SteamClient.IsConnected) { if (!CallbackSemaphore.Wait(0)) { if (Debugging.IsUserDebugging) { - ArchiLogger.LogGenericDebug(string.Format(Strings.WarningFailedWithError, nameof(CallbackSemaphore))); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(CallbackSemaphore))); } return; @@ -1697,9 +1759,9 @@ namespace ArchiSteamFarm { } } - private async Task HeartBeat() { + private async void HeartBeat(object? state) { if (ASF.GlobalConfig == null) { - throw new ArgumentNullException(nameof(ASF.GlobalConfig)); + throw new InvalidOperationException(nameof(ASF.GlobalConfig)); } if (!KeepRunning || !IsConnectedAndLoggedOn || (HeartBeatFailures == byte.MaxValue)) { @@ -1742,7 +1804,7 @@ namespace ArchiSteamFarm { string json = await RuntimeCompatibility.File.ReadAllTextAsync(maFilePath).ConfigureAwait(false); if (string.IsNullOrEmpty(json)) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsEmpty, nameof(json))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json))); return; } @@ -1777,7 +1839,7 @@ namespace ArchiSteamFarm { byte connectionTimeout = ASF.GlobalConfig?.ConnectionTimeout ?? GlobalConfig.DefaultConnectionTimeout; ConnectionFailureTimer = new Timer( - async e => await InitPermanentConnectionFailure().ConfigureAwait(false), + InitPermanentConnectionFailure, null, TimeSpan.FromMinutes(Math.Ceiling(connectionTimeout / 30.0)), // Delay Timeout.InfiniteTimeSpan // Period @@ -1801,7 +1863,7 @@ namespace ArchiSteamFarm { string? steamLogin = await Logging.GetUserInput(ASF.EUserInputType.Login, BotName).ConfigureAwait(false); if (string.IsNullOrEmpty(steamLogin) || !SetUserInput(ASF.EUserInputType.Login, steamLogin!)) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamLogin))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(steamLogin))); return false; } @@ -1813,7 +1875,7 @@ namespace ArchiSteamFarm { string? steamPassword = await Logging.GetUserInput(ASF.EUserInputType.Password, BotName).ConfigureAwait(false); if (string.IsNullOrEmpty(steamPassword) || !SetUserInput(ASF.EUserInputType.Password, steamPassword!)) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamPassword))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(steamPassword))); return false; } @@ -1824,7 +1886,7 @@ namespace ArchiSteamFarm { private async Task InitModules() { if (Bots == null) { - throw new ArgumentNullException(nameof(Bots)); + throw new InvalidOperationException(nameof(Bots)); } AccountFlags = EAccountFlags.NormalUser; @@ -1842,9 +1904,9 @@ namespace ArchiSteamFarm { SendItemsTimer = null; } - if ((BotConfig.SendTradePeriod > 0) && (BotConfig.LootableTypes.Count > 0) && BotConfig.SteamUserPermissions.Values.Any(permission => permission >= BotConfig.EPermission.Master)) { + if ((BotConfig.SendTradePeriod > 0) && (BotConfig.LootableTypes.Count > 0) && BotConfig.SteamUserPermissions.Values.Any(permission => permission >= BotConfig.EAccess.Master)) { SendItemsTimer = new Timer( - async e => await Actions.SendInventory(filterFunction: item => BotConfig.LootableTypes.Contains(item.Type)).ConfigureAwait(false), + async _ => await Actions.SendInventory(filterFunction: item => BotConfig.LootableTypes.Contains(item.Type)).ConfigureAwait(false), null, TimeSpan.FromHours(BotConfig.SendTradePeriod) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bots.Count), // Delay TimeSpan.FromHours(BotConfig.SendTradePeriod) // Period @@ -1864,7 +1926,7 @@ namespace ArchiSteamFarm { await PluginsCore.OnBotInitModules(this, BotConfig.AdditionalProperties).ConfigureAwait(false); } - private async Task InitPermanentConnectionFailure() { + private async void InitPermanentConnectionFailure(object? state) { if (!KeepRunning) { return; } @@ -1880,7 +1942,7 @@ namespace ArchiSteamFarm { } PlayingWasBlockedTimer = new Timer( - e => ResetPlayingWasBlockedWithTimer(), + ResetPlayingWasBlockedWithTimer, null, TimeSpan.FromSeconds(MinPlayingBlockedTTL), // Delay Timeout.InfiniteTimeSpan // Period @@ -1911,14 +1973,15 @@ namespace ArchiSteamFarm { throw new ArgumentNullException(nameof(paymentMethod)); } - // Complimentary is also a flag +#pragma warning disable CA2248 return paymentMethod switch { EPaymentMethod.ActivationCode => false, EPaymentMethod.Complimentary => false, EPaymentMethod.GuestPass => false, EPaymentMethod.HardwarePromo => false, - _ => !paymentMethod.HasFlag(EPaymentMethod.Complimentary) + _ => !paymentMethod.HasFlag(EPaymentMethod.Complimentary) // Complimentary is also a flag, for fuck sake }; +#pragma warning restore CA2248 } private async Task JoinMasterChatGroupID() { @@ -1943,7 +2006,7 @@ namespace ArchiSteamFarm { } if (!await ArchiHandler.JoinChatRoomGroup(MasterChatGroupID).ConfigureAwait(false)) { - ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiHandler.JoinChatRoomGroup))); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(ArchiHandler.JoinChatRoomGroup))); } } @@ -2045,7 +2108,7 @@ namespace ArchiSteamFarm { } if (string.IsNullOrEmpty(BotConfig.SteamLogin)) { - throw new ArgumentNullException(nameof(BotConfig.SteamLogin)); + throw new InvalidOperationException(nameof(BotConfig.SteamLogin)); } // Steam login and password fields can contain ASCII characters only, including spaces @@ -2067,7 +2130,7 @@ namespace ArchiSteamFarm { InitConnectionFailureTimer(); - SteamUser.LogOnDetails logOnDetails = new SteamUser.LogOnDetails { + SteamUser.LogOnDetails logOnDetails = new() { AuthCode = AuthCode, CellID = ASF.GlobalDatabase?.CellID, LoginID = LoginID, @@ -2087,8 +2150,12 @@ namespace ArchiSteamFarm { } private async void OnDisconnected(SteamClient.DisconnectedCallback callback) { - if ((callback == null) || (ASF.LoginRateLimitingSemaphore == null)) { - throw new ArgumentNullException(nameof(callback) + " || " + nameof(ASF.LoginRateLimitingSemaphore)); + if (callback == null) { + throw new ArgumentNullException(nameof(callback)); + } + + if (ASF.LoginRateLimitingSemaphore == null) { + throw new InvalidOperationException(nameof(ASF.LoginRateLimitingSemaphore)); } EResult lastLogOnResult = LastLogOnResult; @@ -2135,7 +2202,7 @@ namespace ArchiSteamFarm { break; case EResult.RateLimitExceeded: - ArchiLogger.LogGenericInfo(string.Format(Strings.BotRateLimitExceeded, TimeSpan.FromMinutes(LoginCooldownInMinutes).ToHumanReadable())); + ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotRateLimitExceeded, TimeSpan.FromMinutes(LoginCooldownInMinutes).ToHumanReadable())); if (!await ASF.LoginRateLimitingSemaphore.WaitAsync(1000 * WebBrowser.MaxTries).ConfigureAwait(false)) { break; @@ -2164,7 +2231,7 @@ namespace ArchiSteamFarm { } if (callback.FriendList == null) { - throw new ArgumentNullException(nameof(callback.FriendList)); + throw new ArgumentNullException(nameof(callback)); } foreach (SteamFriends.FriendsListCallback.Friend friend in callback.FriendList.Where(friend => friend.Relationship == EFriendRelationship.RequestRecipient)) { @@ -2192,9 +2259,9 @@ namespace ArchiSteamFarm { break; default: - if (HasPermission(friend.SteamID, BotConfig.EPermission.FamilySharing)) { + if (HasAccess(friend.SteamID, BotConfig.EAccess.FamilySharing)) { if (!await ArchiHandler.AddFriend(friend.SteamID).ConfigureAwait(false)) { - ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiHandler.AddFriend))); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(ArchiHandler.AddFriend))); } break; @@ -2204,7 +2271,7 @@ namespace ArchiSteamFarm { if (acceptFriendRequest) { if (!await ArchiHandler.AddFriend(friend.SteamID).ConfigureAwait(false)) { - ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiHandler.AddFriend))); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(ArchiHandler.AddFriend))); } break; @@ -2212,7 +2279,7 @@ namespace ArchiSteamFarm { if (BotConfig.BotBehaviour.HasFlag(BotConfig.EBotBehaviour.RejectInvalidFriendInvites)) { if (!await ArchiHandler.RemoveFriend(friend.SteamID).ConfigureAwait(false)) { - ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiHandler.RemoveFriend))); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(ArchiHandler.RemoveFriend))); } } @@ -2227,7 +2294,7 @@ namespace ArchiSteamFarm { } if (callback.GuestPasses == null) { - throw new ArgumentNullException(nameof(callback.GuestPasses)); + throw new ArgumentNullException(nameof(callback)); } if ((callback.CountGuestPassesToRedeem == 0) || (callback.GuestPasses.Count == 0) || !BotConfig.AcceptGifts) { @@ -2337,27 +2404,27 @@ namespace ArchiSteamFarm { } if (callback.LicenseList == null) { - throw new ArgumentNullException(nameof(callback.LicenseList)); + throw new ArgumentNullException(nameof(callback)); } if (ASF.GlobalDatabase == null) { - throw new ArgumentNullException(nameof(ASF.GlobalDatabase)); + throw new InvalidOperationException(nameof(ASF.GlobalDatabase)); } if (callback.LicenseList.Count == 0) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsEmpty, nameof(callback.LicenseList))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(callback.LicenseList))); return; } Commands.OnNewLicenseList(); - Dictionary ownedPackageIDs = new Dictionary(); + Dictionary ownedPackageIDs = new(); - Dictionary packageAccessTokens = new Dictionary(); - Dictionary packagesToRefresh = new Dictionary(); + Dictionary packageAccessTokens = new(); + Dictionary packagesToRefresh = new(); - foreach (SteamApps.LicenseListCallback.License license in callback.LicenseList.GroupBy(license => license.PackageID, (packageID, licenses) => licenses.OrderByDescending(license => license.TimeCreated).First())) { + foreach (SteamApps.LicenseListCallback.License license in callback.LicenseList.GroupBy(license => license.PackageID, (_, licenses) => licenses.OrderByDescending(license => license.TimeCreated).First())) { ownedPackageIDs[license.PackageID] = (license.PaymentMethod, license.TimeCreated); if (!ASF.GlobalDatabase.PackageAccessTokensReadOnly.TryGetValue(license.PackageID, out ulong packageAccessToken) || (packageAccessToken != license.AccessToken)) { @@ -2392,7 +2459,7 @@ namespace ArchiSteamFarm { LastLogOnResult = callback.Result; - ArchiLogger.LogGenericInfo(string.Format(Strings.BotLoggedOff, callback.Result)); + ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotLoggedOff, callback.Result)); switch (callback.Result) { case EResult.LoggedInElsewhere: @@ -2436,7 +2503,7 @@ namespace ArchiSteamFarm { switch (callback.Result) { case EResult.AccountDisabled: // Those failures are permanent, we should Stop() the bot if any of those happen - ArchiLogger.LogGenericWarning(string.Format(Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult)); Stop(); break; @@ -2446,7 +2513,7 @@ namespace ArchiSteamFarm { string? authCode = await Logging.GetUserInput(ASF.EUserInputType.SteamGuard, BotName).ConfigureAwait(false); if (string.IsNullOrEmpty(authCode) || !SetUserInput(ASF.EUserInputType.SteamGuard, authCode!)) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(authCode))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(authCode))); Stop(); } @@ -2459,7 +2526,7 @@ namespace ArchiSteamFarm { string? twoFactorCode = await Logging.GetUserInput(ASF.EUserInputType.TwoFactorAuthentication, BotName).ConfigureAwait(false); if (string.IsNullOrEmpty(twoFactorCode) || !SetUserInput(ASF.EUserInputType.TwoFactorAuthentication, twoFactorCode!)) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(twoFactorCode))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(twoFactorCode))); Stop(); } @@ -2468,9 +2535,9 @@ namespace ArchiSteamFarm { break; case EResult.OK: AccountFlags = callback.AccountFlags; - SteamID = callback.ClientSteamID ?? throw new ArgumentNullException(nameof(callback.ClientSteamID)); + SteamID = callback.ClientSteamID ?? throw new InvalidOperationException(nameof(callback.ClientSteamID)); - ArchiLogger.LogGenericInfo(string.Format(Strings.BotLoggedOn, SteamID + (!string.IsNullOrEmpty(callback.VanityURL) ? "/" + callback.VanityURL : ""))); + ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotLoggedOn, SteamID + (!string.IsNullOrEmpty(callback.VanityURL) ? "/" + callback.VanityURL : ""))); // Old status for these doesn't matter, we'll update them if needed InvalidPasswordFailures = TwoFactorCodeFailures = 0; @@ -2510,7 +2577,7 @@ namespace ArchiSteamFarm { if (!string.IsNullOrEmpty(steamParentalCode)) { if (BotConfig.SteamParentalCode != steamParentalCode) { if (!SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode!)) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamParentalCode))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(steamParentalCode))); Stop(); @@ -2523,7 +2590,7 @@ namespace ArchiSteamFarm { steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); if (string.IsNullOrEmpty(steamParentalCode) || !SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode!)) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamParentalCode))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(steamParentalCode))); Stop(); @@ -2539,7 +2606,7 @@ namespace ArchiSteamFarm { string? steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false); if (string.IsNullOrEmpty(steamParentalCode) || !SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode!)) { - ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(steamParentalCode))); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(steamParentalCode))); Stop(); @@ -2549,7 +2616,7 @@ namespace ArchiSteamFarm { ArchiWebHandler.OnVanityURLChanged(callback.VanityURL); - if (!await ArchiWebHandler.Init(SteamID, SteamClient.Universe, callback.WebAPIUserNonce ?? throw new ArgumentNullException(nameof(callback.WebAPIUserNonce)), SteamParentalActive ? BotConfig.SteamParentalCode : null).ConfigureAwait(false)) { + if (!await ArchiWebHandler.Init(SteamID, SteamClient.Universe, callback.WebAPIUserNonce ?? throw new InvalidOperationException(nameof(callback.WebAPIUserNonce)), SteamParentalActive ? BotConfig.SteamParentalCode : null).ConfigureAwait(false)) { if (!await RefreshSession().ConfigureAwait(false)) { break; } @@ -2559,7 +2626,7 @@ namespace ArchiSteamFarm { Utilities.InBackground(ArchiWebHandler.HasValidApiKey); if ((GamesRedeemerInBackgroundTimer == null) && BotDatabase.HasGamesToRedeemInBackground) { - Utilities.InBackground(RedeemGamesInBackground); + Utilities.InBackground(() => RedeemGamesInBackground(this)); } ArchiHandler.SetCurrentMode(2); @@ -2582,7 +2649,7 @@ namespace ArchiSteamFarm { Utilities.InBackground( async () => { if (!await ArchiWebHandler.JoinGroup(BotConfig.SteamMasterClanID).ConfigureAwait(false)) { - ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiWebHandler.JoinGroup))); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(ArchiWebHandler.JoinGroup))); } await JoinMasterChatGroupID().ConfigureAwait(false); @@ -2608,18 +2675,18 @@ namespace ArchiSteamFarm { case EResult.Timeout: case EResult.TryAnotherCM: case EResult.TwoFactorCodeMismatch: - ArchiLogger.LogGenericWarning(string.Format(Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult)); switch (callback.Result) { case EResult.InvalidPassword when string.IsNullOrEmpty(BotDatabase.LoginKey) && (++InvalidPasswordFailures >= MaxInvalidPasswordFailures): InvalidPasswordFailures = 0; - ArchiLogger.LogGenericError(string.Format(Strings.BotInvalidPasswordDuringLogin, MaxInvalidPasswordFailures)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.BotInvalidPasswordDuringLogin, MaxInvalidPasswordFailures)); Stop(); break; case EResult.TwoFactorCodeMismatch when HasMobileAuthenticator && (++TwoFactorCodeFailures >= MaxTwoFactorCodeFailures): TwoFactorCodeFailures = 0; - ArchiLogger.LogGenericError(string.Format(Strings.BotInvalidAuthenticatorDuringLogin, MaxTwoFactorCodeFailures)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.BotInvalidAuthenticatorDuringLogin, MaxTwoFactorCodeFailures)); Stop(); break; @@ -2628,8 +2695,8 @@ namespace ArchiSteamFarm { break; default: // Unexpected result, shutdown immediately - ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(callback.Result), callback.Result)); - ArchiLogger.LogGenericError(string.Format(Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(callback.Result), callback.Result)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.BotUnableToLogin, callback.Result, callback.ExtendedResult)); Stop(); break; @@ -2642,7 +2709,7 @@ namespace ArchiSteamFarm { } if (string.IsNullOrEmpty(callback.LoginKey)) { - throw new ArgumentNullException(nameof(callback.LoginKey)); + throw new ArgumentNullException(nameof(callback)); } if (!BotConfig.UseLoginKeys) { @@ -2684,14 +2751,20 @@ namespace ArchiSteamFarm { fileStream.Seek(callback.Offset, SeekOrigin.Begin); +#if NETFRAMEWORK await fileStream.WriteAsync(callback.Data, 0, callback.BytesToWrite).ConfigureAwait(false); +#else + await fileStream.WriteAsync(callback.Data.AsMemory(0, callback.BytesToWrite)).ConfigureAwait(false); +#endif fileSize = fileStream.Length; fileStream.Seek(0, SeekOrigin.Begin); - using SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider(); +#pragma warning disable CA5350 + using SHA1CryptoServiceProvider sha = new(); sentryHash = await sha.ComputeHashAsync(fileStream).ConfigureAwait(false); +#pragma warning restore CA5350 } catch (Exception e) { ArchiLogger.LogGenericException(e); @@ -2813,7 +2886,7 @@ namespace ArchiSteamFarm { return; } - HashSet newPluginNotifications = new HashSet(); + HashSet newPluginNotifications = new(); foreach ((ArchiHandler.UserNotificationsCallback.EUserNotification notification, uint count) in callback.Notifications) { bool newNotification; @@ -2918,7 +2991,7 @@ namespace ArchiSteamFarm { return firstPageResult; default: - HashSet?>> tasks = new HashSet?>>(maxPages - 1); + HashSet?>> tasks = new(maxPages - 1); for (byte page = 2; page <= maxPages; page++) { // We need a copy of variable being passed when in for loops, as loop will proceed before our task is launched @@ -2942,7 +3015,7 @@ namespace ArchiSteamFarm { private async Task?> GetPossiblyCompletedBadgeAppIDs(byte page) { if (page == 0) { - throw new ArgumentNullException(nameof(page)); + throw new ArgumentOutOfRangeException(nameof(page)); } using IDocument? badgePage = await ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false); @@ -2971,7 +3044,7 @@ namespace ArchiSteamFarm { return new HashSet(0); } - HashSet result = new HashSet(linkElements.Count); + HashSet result = new(linkElements.Count); foreach (string? badgeUri in linkElements.Select(htmlNode => htmlNode.GetAttribute("href"))) { if (string.IsNullOrEmpty(badgeUri)) { @@ -3036,7 +3109,7 @@ namespace ArchiSteamFarm { } if (inventory.Count == 0) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty), nameof(inventory)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty), nameof(inventory)); return; } @@ -3067,16 +3140,18 @@ namespace ArchiSteamFarm { } [PublicAPI] - public async Task?> LoadCardsPerSet(ISet appIDs) { + public async Task?> LoadCardsPerSet(IReadOnlyCollection appIDs) { if ((appIDs == null) || (appIDs.Count == 0)) { throw new ArgumentNullException(nameof(appIDs)); } + ISet uniqueAppIDs = appIDs as ISet ?? appIDs.ToHashSet(); + switch (ASF.GlobalConfig?.OptimizationMode) { case GlobalConfig.EOptimizationMode.MinMemoryUsage: - Dictionary result = new Dictionary(appIDs.Count); + Dictionary result = new(uniqueAppIDs.Count); - foreach (uint appID in appIDs) { + foreach (uint appID in uniqueAppIDs) { byte cardCount = await ArchiWebHandler.GetCardCountForGame(appID).ConfigureAwait(false); if (cardCount == 0) { @@ -3088,7 +3163,7 @@ namespace ArchiSteamFarm { return result; default: - IEnumerable> tasks = appIDs.Select(async appID => (AppID: appID, Cards: await ArchiWebHandler.GetCardCountForGame(appID).ConfigureAwait(false))); + IEnumerable> tasks = uniqueAppIDs.Select(async appID => (AppID: appID, Cards: await ArchiWebHandler.GetCardCountForGame(appID).ConfigureAwait(false))); IList<(uint AppID, byte Cards)> results = await Utilities.InParallel(tasks).ConfigureAwait(false); return results.All(tuple => tuple.Cards > 0) ? results.ToDictionary(res => res.AppID, res => res.Cards) : null; @@ -3097,11 +3172,15 @@ namespace ArchiSteamFarm { [PublicAPI] public static HashSet GetItemsForFullSets(IReadOnlyCollection inventory, IReadOnlyDictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), (uint SetsToExtract, byte ItemsPerSet)> amountsToExtract) { - if ((inventory == null) || (inventory.Count == 0) || (amountsToExtract == null) || (amountsToExtract.Count == 0)) { - throw new ArgumentNullException(nameof(inventory) + " || " + nameof(amountsToExtract)); + if ((inventory == null) || (inventory.Count == 0)) { + throw new ArgumentNullException(nameof(inventory)); } - HashSet result = new HashSet(); + if ((amountsToExtract == null) || (amountsToExtract.Count == 0)) { + throw new ArgumentNullException(nameof(amountsToExtract)); + } + + HashSet result = new(); Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary>> itemsPerClassIDPerSet = inventory.GroupBy(item => (item.RealAppID, item.Type, item.Rarity)).ToDictionary(grouping => grouping.Key, grouping => grouping.GroupBy(item => item.ClassID).ToDictionary(group => group.Key, group => group.ToHashSet())); foreach (((uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) set, (uint setsToExtract, byte itemsPerSet)) in amountsToExtract) { @@ -3110,7 +3189,7 @@ namespace ArchiSteamFarm { } if (itemsPerSet < itemsPerClassID.Count) { - throw new ArgumentOutOfRangeException(nameof(inventory) + " && " + nameof(amountsToExtract)); + throw new InvalidOperationException(nameof(inventory) + " && " + nameof(amountsToExtract)); } if (itemsPerSet > itemsPerClassID.Count) { @@ -3120,7 +3199,7 @@ namespace ArchiSteamFarm { foreach (HashSet itemsOfClass in itemsPerClassID.Values) { uint classRemaining = setsToExtract; - foreach (Steam.Asset item in itemsOfClass.TakeWhile(item => classRemaining > 0)) { + foreach (Steam.Asset item in itemsOfClass.TakeWhile(_ => classRemaining > 0)) { if (item.Amount > classRemaining) { Steam.Asset itemToSend = item.CreateShallowCopy(); itemToSend.Amount = classRemaining; @@ -3156,7 +3235,7 @@ namespace ArchiSteamFarm { WalletCurrency = callback.Currency; } - private async Task RedeemGamesInBackground() { + private async void RedeemGamesInBackground(object? state) { if (!await GamesRedeemerInBackgroundSemaphore.WaitAsync(0).ConfigureAwait(false)) { return; } @@ -3200,7 +3279,7 @@ namespace ArchiSteamFarm { } } - ArchiLogger.LogGenericDebug(string.Format(Strings.BotRedeem, key, result.Result + "/" + result.PurchaseResultDetail)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotRedeem, key, result.Result + "/" + result.PurchaseResultDetail)); bool rateLimited = false; bool redeemed = false; @@ -3224,7 +3303,7 @@ namespace ArchiSteamFarm { break; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.PurchaseResultDetail), result.PurchaseResultDetail)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(result.PurchaseResultDetail), result.PurchaseResultDetail)); break; } @@ -3236,7 +3315,7 @@ namespace ArchiSteamFarm { BotDatabase.RemoveGameToRedeemInBackground(key!); // If user omitted the name or intentionally provided the same name as key, replace it with the Steam result - if (name!.Equals(key) && (result.Items?.Count > 0)) { + if (name!.Equals(key, StringComparison.OrdinalIgnoreCase) && (result.Items?.Count > 0)) { name = string.Join(", ", result.Items.Values); } @@ -3254,17 +3333,17 @@ namespace ArchiSteamFarm { await RuntimeCompatibility.File.AppendAllTextAsync(filePath, logEntry + Environment.NewLine).ConfigureAwait(false); } catch (Exception e) { ArchiLogger.LogGenericException(e); - ArchiLogger.LogGenericError(string.Format(Strings.Content, logEntry)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.Content, logEntry)); break; } } if (IsConnectedAndLoggedOn && BotDatabase.HasGamesToRedeemInBackground) { - ArchiLogger.LogGenericInfo(string.Format(Strings.BotRateLimitExceeded, TimeSpan.FromHours(RedeemCooldownInHours).ToHumanReadable())); + ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotRateLimitExceeded, TimeSpan.FromHours(RedeemCooldownInHours).ToHumanReadable())); GamesRedeemerInBackgroundTimer = new Timer( - async e => await RedeemGamesInBackground().ConfigureAwait(false), + RedeemGamesInBackground, null, TimeSpan.FromHours(RedeemCooldownInHours), // Delay Timeout.InfiniteTimeSpan // Period @@ -3298,20 +3377,18 @@ namespace ArchiSteamFarm { await ArchiHandler.PlayGames(BotConfig.GamesPlayedWhileIdle, BotConfig.CustomGamePlayedWhileIdle).ConfigureAwait(false); } - private void ResetPlayingWasBlockedWithTimer() { + private void ResetPlayingWasBlockedWithTimer(object? state) { PlayingWasBlocked = false; StopPlayingWasBlockedTimer(); } private bool ShouldAckChatMessage(ulong steamID) { - if (Bots == null) { - throw new ArgumentNullException(nameof(Bots)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - ArchiLogger.LogNullError(nameof(steamID)); - - return false; + if (Bots == null) { + throw new InvalidOperationException(nameof(Bots)); } if (BotConfig.BotBehaviour.HasFlag(BotConfig.EBotBehaviour.MarkReceivedMessagesAsRead)) { @@ -3368,7 +3445,7 @@ namespace ArchiSteamFarm { break; default: - ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(settings.passwordhashtype), settings.passwordhashtype)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(settings.passwordhashtype), settings.passwordhashtype)); return (true, null); } diff --git a/ArchiSteamFarm/BotConfig.cs b/ArchiSteamFarm/BotConfig.cs index b2bda5af9..f0c8b807b 100644 --- a/ArchiSteamFarm/BotConfig.cs +++ b/ArchiSteamFarm/BotConfig.cs @@ -23,6 +23,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -129,90 +130,90 @@ namespace ArchiSteamFarm { public static readonly ImmutableHashSet DefaultMatchableTypes = ImmutableHashSet.Create(Steam.Asset.EType.TradingCard); [PublicAPI] - public static readonly ImmutableDictionary DefaultSteamUserPermissions = ImmutableDictionary.Empty; + public static readonly ImmutableDictionary DefaultSteamUserPermissions = ImmutableDictionary.Empty; [PublicAPI] public static readonly ImmutableHashSet DefaultTransferableTypes = ImmutableHashSet.Create(Steam.Asset.EType.BoosterPack, Steam.Asset.EType.FoilTradingCard, Steam.Asset.EType.TradingCard); - private static readonly SemaphoreSlim WriteSemaphore = new SemaphoreSlim(1, 1); + private static readonly SemaphoreSlim WriteSemaphore = new(1, 1); [JsonProperty(Required = Required.DisallowNull)] - public readonly bool AcceptGifts = DefaultAcceptGifts; + public bool AcceptGifts { get; } = DefaultAcceptGifts; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool AutoSteamSaleEvent = DefaultAutoSteamSaleEvent; + public bool AutoSteamSaleEvent { get; } = DefaultAutoSteamSaleEvent; [JsonProperty(Required = Required.DisallowNull)] - public readonly EBotBehaviour BotBehaviour = DefaultBotBehaviour; + public EBotBehaviour BotBehaviour { get; } = DefaultBotBehaviour; [JsonProperty(Required = Required.DisallowNull)] - public readonly ImmutableHashSet CompleteTypesToSend = DefaultCompleteTypesToSend; + public ImmutableHashSet CompleteTypesToSend { get; } = DefaultCompleteTypesToSend; [JsonProperty] - public readonly string? CustomGamePlayedWhileFarming = DefaultCustomGamePlayedWhileFarming; + public string? CustomGamePlayedWhileFarming { get; } = DefaultCustomGamePlayedWhileFarming; [JsonProperty] - public readonly string? CustomGamePlayedWhileIdle = DefaultCustomGamePlayedWhileIdle; + public string? CustomGamePlayedWhileIdle { get; } = DefaultCustomGamePlayedWhileIdle; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool Enabled = DefaultEnabled; + public bool Enabled { get; } = DefaultEnabled; [JsonProperty(Required = Required.DisallowNull)] - public readonly ImmutableList FarmingOrders = DefaultFarmingOrders; + public ImmutableList FarmingOrders { get; } = DefaultFarmingOrders; [JsonProperty(Required = Required.DisallowNull)] - public readonly ImmutableHashSet GamesPlayedWhileIdle = DefaultGamesPlayedWhileIdle; + public ImmutableHashSet GamesPlayedWhileIdle { get; } = DefaultGamesPlayedWhileIdle; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte HoursUntilCardDrops = DefaultHoursUntilCardDrops; + public byte HoursUntilCardDrops { get; } = DefaultHoursUntilCardDrops; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool IdlePriorityQueueOnly = DefaultIdlePriorityQueueOnly; + public bool IdlePriorityQueueOnly { get; } = DefaultIdlePriorityQueueOnly; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool IdleRefundableGames = DefaultIdleRefundableGames; + public bool IdleRefundableGames { get; } = DefaultIdleRefundableGames; [JsonProperty(Required = Required.DisallowNull)] - public readonly ImmutableHashSet LootableTypes = DefaultLootableTypes; + public ImmutableHashSet LootableTypes { get; } = DefaultLootableTypes; [JsonProperty(Required = Required.DisallowNull)] - public readonly ImmutableHashSet MatchableTypes = DefaultMatchableTypes; + public ImmutableHashSet MatchableTypes { get; } = DefaultMatchableTypes; [JsonProperty(Required = Required.DisallowNull)] - public readonly EPersonaState OnlineStatus = DefaultOnlineStatus; + public EPersonaState OnlineStatus { get; } = DefaultOnlineStatus; [JsonProperty(Required = Required.DisallowNull)] - public readonly ArchiCryptoHelper.ECryptoMethod PasswordFormat = DefaultPasswordFormat; + public ArchiCryptoHelper.ECryptoMethod PasswordFormat { get; } = DefaultPasswordFormat; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool Paused = DefaultPaused; + public bool Paused { get; } = DefaultPaused; [JsonProperty(Required = Required.DisallowNull)] - public readonly ERedeemingPreferences RedeemingPreferences = DefaultRedeemingPreferences; + public ERedeemingPreferences RedeemingPreferences { get; } = DefaultRedeemingPreferences; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool SendOnFarmingFinished = DefaultSendOnFarmingFinished; + public bool SendOnFarmingFinished { get; } = DefaultSendOnFarmingFinished; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte SendTradePeriod = DefaultSendTradePeriod; + public byte SendTradePeriod { get; } = DefaultSendTradePeriod; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool ShutdownOnFarmingFinished = DefaultShutdownOnFarmingFinished; + public bool ShutdownOnFarmingFinished { get; } = DefaultShutdownOnFarmingFinished; [JsonProperty] - public readonly string? SteamTradeToken = DefaultSteamTradeToken; + public string? SteamTradeToken { get; } = DefaultSteamTradeToken; [JsonProperty(Required = Required.DisallowNull)] - public readonly ImmutableDictionary SteamUserPermissions = DefaultSteamUserPermissions; + public ImmutableDictionary SteamUserPermissions { get; } = DefaultSteamUserPermissions; [JsonProperty(Required = Required.DisallowNull)] - public readonly ETradingPreferences TradingPreferences = DefaultTradingPreferences; + public ETradingPreferences TradingPreferences { get; } = DefaultTradingPreferences; [JsonProperty(Required = Required.DisallowNull)] - public readonly ImmutableHashSet TransferableTypes = DefaultTransferableTypes; + public ImmutableHashSet TransferableTypes { get; } = DefaultTransferableTypes; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool UseLoginKeys = DefaultUseLoginKeys; + public bool UseLoginKeys { get; } = DefaultUseLoginKeys; [JsonProperty(Required = Required.DisallowNull)] public ulong SteamMasterClanID { get; private set; } = DefaultSteamMasterClanID; @@ -237,7 +238,7 @@ namespace ArchiSteamFarm { string? result = ArchiCryptoHelper.Decrypt(PasswordFormat, SteamPassword!); if (string.IsNullOrEmpty(result)) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SteamPassword))); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(SteamPassword))); return null; } @@ -298,11 +299,11 @@ namespace ArchiSteamFarm { [JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamMasterClanID), Required = Required.DisallowNull)] private string SSteamMasterClanID { - get => SteamMasterClanID.ToString(); + get => SteamMasterClanID.ToString(CultureInfo.InvariantCulture); set { if (string.IsNullOrEmpty(value) || !ulong.TryParse(value, out ulong result)) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SSteamMasterClanID))); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(SSteamMasterClanID))); return; } @@ -316,64 +317,64 @@ namespace ArchiSteamFarm { internal (bool Valid, string? ErrorMessage) CheckValidation() { if (BotBehaviour > EBotBehaviour.All) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(BotBehaviour), BotBehaviour)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(BotBehaviour), BotBehaviour)); } foreach (EFarmingOrder farmingOrder in FarmingOrders.Where(farmingOrder => !Enum.IsDefined(typeof(EFarmingOrder), farmingOrder))) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(FarmingOrders), farmingOrder)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(FarmingOrders), farmingOrder)); } if (GamesPlayedWhileIdle.Count > ArchiHandler.MaxGamesPlayedConcurrently) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(GamesPlayedWhileIdle), GamesPlayedWhileIdle.Count + " > " + ArchiHandler.MaxGamesPlayedConcurrently)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(GamesPlayedWhileIdle), GamesPlayedWhileIdle.Count + " > " + ArchiHandler.MaxGamesPlayedConcurrently)); } foreach (Steam.Asset.EType lootableType in LootableTypes.Where(lootableType => !Enum.IsDefined(typeof(Steam.Asset.EType), lootableType))) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(LootableTypes), lootableType)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(LootableTypes), lootableType)); } foreach (Steam.Asset.EType completableType in CompleteTypesToSend.Where(completableType => !Enum.IsDefined(typeof(Steam.Asset.EType), completableType) || !AllowedCompleteTypesToSend.Contains(completableType))) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(CompleteTypesToSend), completableType)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(CompleteTypesToSend), completableType)); } foreach (Steam.Asset.EType matchableType in MatchableTypes.Where(matchableType => !Enum.IsDefined(typeof(Steam.Asset.EType), matchableType))) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(MatchableTypes), matchableType)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(MatchableTypes), matchableType)); } if (!Enum.IsDefined(typeof(EPersonaState), OnlineStatus)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(OnlineStatus), OnlineStatus)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(OnlineStatus), OnlineStatus)); } if (!Enum.IsDefined(typeof(ArchiCryptoHelper.ECryptoMethod), PasswordFormat)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(PasswordFormat), PasswordFormat)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(PasswordFormat), PasswordFormat)); } if (RedeemingPreferences > ERedeemingPreferences.All) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(RedeemingPreferences), RedeemingPreferences)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(RedeemingPreferences), RedeemingPreferences)); } if ((SteamMasterClanID != 0) && !new SteamID(SteamMasterClanID).IsClanAccount) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamMasterClanID), SteamMasterClanID)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamMasterClanID), SteamMasterClanID)); } if (!string.IsNullOrEmpty(SteamParentalCode) && (SteamParentalCode != "0") && (SteamParentalCode!.Length != SteamParentalCodeLength)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamParentalCode), SteamParentalCode)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamParentalCode), SteamParentalCode)); } if (!string.IsNullOrEmpty(SteamTradeToken) && (SteamTradeToken!.Length != SteamTradeTokenLength)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamTradeToken), SteamTradeToken)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamTradeToken), SteamTradeToken)); } - foreach ((ulong steamID, EPermission permission) in SteamUserPermissions) { + foreach ((ulong steamID, EAccess permission) in SteamUserPermissions) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamUserPermissions), steamID)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamUserPermissions), steamID)); } - if (!Enum.IsDefined(typeof(EPermission), permission)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamUserPermissions), permission)); + if (!Enum.IsDefined(typeof(EAccess), permission)) { + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamUserPermissions), permission)); } } - return TradingPreferences <= ETradingPreferences.All ? (true, (string?) null) : (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(TradingPreferences), TradingPreferences)); + return TradingPreferences <= ETradingPreferences.All ? (true, (string?) null) : (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(TradingPreferences), TradingPreferences)); } internal static async Task Load(string filePath) { @@ -391,7 +392,7 @@ namespace ArchiSteamFarm { string json = await RuntimeCompatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false); if (string.IsNullOrEmpty(json)) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsEmpty, nameof(json))); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json))); return null; } @@ -424,8 +425,12 @@ namespace ArchiSteamFarm { } internal static async Task Write(string filePath, BotConfig botConfig) { - if (string.IsNullOrEmpty(filePath) || (botConfig == null)) { - throw new ArgumentNullException(nameof(filePath) + " || " + nameof(botConfig)); + if (string.IsNullOrEmpty(filePath)) { + throw new ArgumentNullException(nameof(filePath)); + } + + if (botConfig == null) { + throw new ArgumentNullException(nameof(botConfig)); } string json = JsonConvert.SerializeObject(botConfig, Formatting.Indented); @@ -452,6 +457,13 @@ namespace ArchiSteamFarm { return true; } + public enum EAccess : byte { + None, + FamilySharing, + Operator, + Master + } + [Flags] public enum EBotBehaviour : byte { None = 0, @@ -483,13 +495,6 @@ namespace ArchiSteamFarm { MarketableDescending } - public enum EPermission : byte { - None, - FamilySharing, - Operator, - Master - } - [Flags] public enum ERedeemingPreferences : byte { None = 0, diff --git a/ArchiSteamFarm/BotDatabase.cs b/ArchiSteamFarm/BotDatabase.cs index 956bd31d1..736c03281 100644 --- a/ArchiSteamFarm/BotDatabase.cs +++ b/ArchiSteamFarm/BotDatabase.cs @@ -23,6 +23,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -46,16 +47,16 @@ namespace ArchiSteamFarm { internal bool HasIdlingPriorityAppIDs => IdlingPriorityAppIDs.Count > 0; [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentHashSet BlacklistedFromTradesSteamIDs = new ConcurrentHashSet(); + private readonly ConcurrentHashSet BlacklistedFromTradesSteamIDs = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly OrderedDictionary GamesToRedeemInBackground = new OrderedDictionary(); + private readonly OrderedDictionary GamesToRedeemInBackground = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentHashSet IdlingBlacklistedAppIDs = new ConcurrentHashSet(); + private readonly ConcurrentHashSet IdlingBlacklistedAppIDs = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentHashSet IdlingPriorityAppIDs = new ConcurrentHashSet(); + private readonly ConcurrentHashSet IdlingPriorityAppIDs = new(); internal string? LoginKey { get => BackingLoginKey; @@ -170,7 +171,7 @@ namespace ArchiSteamFarm { string json = await RuntimeCompatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false); if (string.IsNullOrEmpty(json)) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsEmpty, nameof(json))); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json))); return null; } @@ -213,7 +214,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromIdling(uint appID) { if (appID == 0) { - throw new ArgumentNullException(nameof(appID)); + throw new ArgumentOutOfRangeException(nameof(appID)); } return IdlingBlacklistedAppIDs.Contains(appID); @@ -221,7 +222,7 @@ namespace ArchiSteamFarm { internal bool IsBlacklistedFromTrades(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } return BlacklistedFromTradesSteamIDs.Contains(steamID); @@ -229,7 +230,7 @@ namespace ArchiSteamFarm { internal bool IsPriorityIdling(uint appID) { if (appID == 0) { - throw new ArgumentNullException(nameof(appID)); + throw new ArgumentOutOfRangeException(nameof(appID)); } return IdlingPriorityAppIDs.Contains(appID); diff --git a/ArchiSteamFarm/CMsgs/CMsgClientAcknowledgeClanInvite.cs b/ArchiSteamFarm/CMsgs/CMsgClientAcknowledgeClanInvite.cs index a36d361ce..ddbf1fb28 100644 --- a/ArchiSteamFarm/CMsgs/CMsgClientAcknowledgeClanInvite.cs +++ b/ArchiSteamFarm/CMsgs/CMsgClientAcknowledgeClanInvite.cs @@ -35,7 +35,7 @@ namespace ArchiSteamFarm.CMsgs { throw new ArgumentNullException(nameof(stream)); } - using BinaryReader binaryReader = new BinaryReader(stream, Encoding.UTF8, true); + using BinaryReader binaryReader = new(stream, Encoding.UTF8, true); ClanID = binaryReader.ReadUInt64(); AcceptInvite = binaryReader.ReadBoolean(); @@ -48,7 +48,7 @@ namespace ArchiSteamFarm.CMsgs { throw new ArgumentNullException(nameof(stream)); } - using BinaryWriter binaryWriter = new BinaryWriter(stream, Encoding.UTF8, true); + using BinaryWriter binaryWriter = new(stream, Encoding.UTF8, true); binaryWriter.Write(ClanID); binaryWriter.Write(AcceptInvite); diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index 07df28d9c..e88bf193d 100755 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -48,7 +48,7 @@ namespace ArchiSteamFarm { [PublicAPI] public static readonly ImmutableHashSet SalesBlacklist = ImmutableHashSet.Create(267420, 303700, 335590, 368020, 425280, 480730, 566020, 639900, 762800, 876740, 991980, 1195670, 1343890); - private static readonly ConcurrentDictionary GloballyIgnoredAppIDs = new ConcurrentDictionary(); // Reserved for unreleased games + private static readonly ConcurrentDictionary GloballyIgnoredAppIDs = new(); // Reserved for unreleased games // Games that were confirmed to show false status on general badges page private static readonly ImmutableHashSet UntrustedAppIDs = ImmutableHashSet.Create(440, 570, 730); @@ -64,20 +64,20 @@ namespace ArchiSteamFarm { [JsonProperty] [PublicAPI] public TimeSpan TimeRemaining => - new TimeSpan( + new( Bot.BotConfig.HoursUntilCardDrops > 0 ? (ushort) Math.Ceiling(GamesToFarm.Count / (float) ArchiHandler.MaxGamesPlayedConcurrently) * Bot.BotConfig.HoursUntilCardDrops : 0, 30 * GamesToFarm.Sum(game => game.CardsRemaining), 0 ); private readonly Bot Bot; - private readonly ConcurrentHashSet CurrentGamesFarming = new ConcurrentHashSet(); - private readonly SemaphoreSlim EventSemaphore = new SemaphoreSlim(1, 1); - private readonly SemaphoreSlim FarmingInitializationSemaphore = new SemaphoreSlim(1, 1); - private readonly SemaphoreSlim FarmingResetSemaphore = new SemaphoreSlim(0, 1); - private readonly ConcurrentList GamesToFarm = new ConcurrentList(); + private readonly ConcurrentHashSet CurrentGamesFarming = new(); + private readonly SemaphoreSlim EventSemaphore = new(1, 1); + private readonly SemaphoreSlim FarmingInitializationSemaphore = new(1, 1); + private readonly SemaphoreSlim FarmingResetSemaphore = new(0, 1); + private readonly ConcurrentList GamesToFarm = new(); private readonly Timer? IdleFarmingTimer; - private readonly ConcurrentDictionary LocallyIgnoredAppIDs = new ConcurrentDictionary(); + private readonly ConcurrentDictionary LocallyIgnoredAppIDs = new(); private IEnumerable> SourcesOfIgnoredAppIDs { get { @@ -104,7 +104,7 @@ namespace ArchiSteamFarm { if (idleFarmingPeriod > 0) { IdleFarmingTimer = new Timer( - async e => await CheckGamesForFarming().ConfigureAwait(false), + CheckGamesForFarming, null, TimeSpan.FromHours(idleFarmingPeriod) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots?.Count ?? 0), // Delay TimeSpan.FromHours(idleFarmingPeriod) // Period @@ -289,7 +289,7 @@ namespace ArchiSteamFarm { } if (Bot.PlayingWasBlocked) { - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.BotExtraIdlingCooldown, TimeSpan.FromSeconds(Bot.MinPlayingBlockedTTL).ToHumanReadable())); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotExtraIdlingCooldown, TimeSpan.FromSeconds(Bot.MinPlayingBlockedTTL).ToHumanReadable())); for (byte i = 0; (i < Bot.MinPlayingBlockedTTL) && Bot.IsPlayingPossible && Bot.PlayingWasBlocked; i++) { await Task.Delay(1000).ConfigureAwait(false); @@ -345,26 +345,35 @@ namespace ArchiSteamFarm { } private async Task CheckGame(uint appID, string name, float hours, byte badgeLevel) { - if ((appID == 0) || string.IsNullOrEmpty(name) || (hours < 0)) { - throw new ArgumentNullException(nameof(appID) + " || " + nameof(name) + " || " + nameof(hours)); + if (appID == 0) { + throw new ArgumentOutOfRangeException(nameof(appID)); + } + + if (string.IsNullOrEmpty(name)) { + throw new ArgumentNullException(nameof(name)); + } + + if (hours < 0) { + throw new ArgumentOutOfRangeException(nameof(hours)); } ushort? cardsRemaining = await GetCardsRemaining(appID).ConfigureAwait(false); - if (!cardsRemaining.HasValue) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningCouldNotCheckCardsStatus, appID, name)); + switch (cardsRemaining) { + case null: + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningCouldNotCheckCardsStatus, appID, name)); - return; + return; + case 0: + return; + default: + GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining.Value, badgeLevel)); + + break; } - - if (cardsRemaining.Value == 0) { - return; - } - - GamesToFarm.Add(new Game(appID, name, hours, cardsRemaining.Value, badgeLevel)); } - private async Task CheckGamesForFarming() { + private async void CheckGamesForFarming(object? state) { if (NowFarming || Paused || !Bot.IsConnectedAndLoggedOn) { return; } @@ -372,9 +381,13 @@ namespace ArchiSteamFarm { await StartFarming().ConfigureAwait(false); } - private async Task CheckPage(IDocument htmlDocument, ISet parsedAppIDs) { - if ((htmlDocument == null) || (parsedAppIDs == null)) { - throw new ArgumentNullException(nameof(htmlDocument) + " || " + nameof(parsedAppIDs)); + private async Task CheckPage(IDocument htmlDocument, ConcurrentHashSet parsedAppIDs) { + if (htmlDocument == null) { + throw new ArgumentNullException(nameof(htmlDocument)); + } + + if (parsedAppIDs == null) { + throw new ArgumentNullException(nameof(parsedAppIDs)); } List htmlNodes = htmlDocument.SelectNodes("//div[@class='badge_row_inner']"); @@ -678,14 +691,18 @@ namespace ArchiSteamFarm { } // If we have any background tasks, wait for them - if ((backgroundTasks != null) && (backgroundTasks.Count > 0)) { + if (backgroundTasks?.Count > 0) { await Task.WhenAll(backgroundTasks).ConfigureAwait(false); } } - private async Task CheckPage(byte page, ISet parsedAppIDs) { - if ((page == 0) || (parsedAppIDs == null)) { - throw new ArgumentNullException(nameof(page) + " || " + nameof(parsedAppIDs)); + private async Task CheckPage(byte page, ConcurrentHashSet parsedAppIDs) { + if (page == 0) { + throw new ArgumentOutOfRangeException(nameof(page)); + } + + if (parsedAppIDs == null) { + throw new ArgumentNullException(nameof(parsedAppIDs)); } using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false); @@ -699,12 +716,12 @@ namespace ArchiSteamFarm { private async Task Farm() { do { - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.GamesToIdle, GamesToFarm.Count, GamesToFarm.Sum(game => game.CardsRemaining), TimeRemaining.ToHumanReadable())); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.GamesToIdle, GamesToFarm.Count, GamesToFarm.Sum(game => game.CardsRemaining), TimeRemaining.ToHumanReadable())); // Now the algorithm used for farming depends on whether account is restricted or not if (Bot.BotConfig.HoursUntilCardDrops > 0) { // If we have restricted card drops, we use complex algorithm - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ChosenFarmingAlgorithm, "Complex")); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.ChosenFarmingAlgorithm, "Complex")); while (GamesToFarm.Count > 0) { // Initially we're going to farm games that passed our HoursUntilCardDrops @@ -758,7 +775,7 @@ namespace ArchiSteamFarm { // Otherwise, we farm our innerGamesToFarm batch until any game hits HoursUntilCardDrops if (await FarmMultiple(innerGamesToFarm).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingFinishedForGames, string.Join(", ", innerGamesToFarm.Select(game => game.AppID)))); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.IdlingFinishedForGames, string.Join(", ", innerGamesToFarm.Select(game => game.AppID)))); } else { NowFarming = false; @@ -767,7 +784,7 @@ namespace ArchiSteamFarm { } } else { // If we have unrestricted card drops, we use simple algorithm - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ChosenFarmingAlgorithm, "Simple")); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.ChosenFarmingAlgorithm, "Simple")); while (GamesToFarm.Count > 0) { // In simple algorithm we're going to farm anything that is playable, regardless of hours @@ -802,7 +819,7 @@ namespace ArchiSteamFarm { } if (game.AppID != game.PlayableAppID) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningIdlingGameMismatch, game.AppID, game.GameName, game.PlayableAppID)); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningIdlingGameMismatch, game.AppID, game.GameName, game.PlayableAppID)); } await Bot.IdleGame(game).ConfigureAwait(false); @@ -811,7 +828,7 @@ namespace ArchiSteamFarm { DateTime endFarmingDate = DateTime.UtcNow.AddHours(ASF.GlobalConfig?.MaxFarmingTime ?? GlobalConfig.DefaultMaxFarmingTime); while ((DateTime.UtcNow < endFarmingDate) && (await ShouldFarm(game).ConfigureAwait(false)).GetValueOrDefault(true)) { - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdling, game.AppID, game.GameName)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.StillIdling, game.AppID, game.GameName)); DateTime startFarmingPeriod = DateTime.UtcNow; @@ -827,7 +844,7 @@ namespace ArchiSteamFarm { } } - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StoppedIdling, game.AppID, game.GameName)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.StoppedIdling, game.AppID, game.GameName)); return success; } @@ -846,7 +863,7 @@ namespace ArchiSteamFarm { } if (maxHour >= Bot.BotConfig.HoursUntilCardDrops) { - Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(maxHour))); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(maxHour))); return true; } @@ -856,7 +873,7 @@ namespace ArchiSteamFarm { bool success = true; while (maxHour < Bot.BotConfig.HoursUntilCardDrops) { - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdlingList, string.Join(", ", games.Select(game => game.AppID)))); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.StillIdlingList, string.Join(", ", games.Select(game => game.AppID)))); DateTime startFarmingPeriod = DateTime.UtcNow; @@ -878,7 +895,7 @@ namespace ArchiSteamFarm { maxHour += timePlayed; } - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StoppedIdlingList, string.Join(", ", games.Select(game => game.AppID)))); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.StoppedIdlingList, string.Join(", ", games.Select(game => game.AppID)))); return success; } @@ -890,7 +907,7 @@ namespace ArchiSteamFarm { CurrentGamesFarming.ReplaceWith(games); - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.NowIdlingList, string.Join(", ", games.Select(game => game.AppID)))); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.NowIdlingList, string.Join(", ", games.Select(game => game.AppID)))); bool result = await FarmHours(games).ConfigureAwait(false); CurrentGamesFarming.Clear(); @@ -905,7 +922,7 @@ namespace ArchiSteamFarm { CurrentGamesFarming.Add(game); - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.NowIdling, game.AppID, game.GameName)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.NowIdling, game.AppID, game.GameName)); bool result = await FarmCards(game).ConfigureAwait(false); CurrentGamesFarming.Clear(); @@ -916,14 +933,14 @@ namespace ArchiSteamFarm { GamesToFarm.Remove(game); - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingFinishedForGame, game.AppID, game.GameName, TimeSpan.FromHours(game.HoursPlayed).ToHumanReadable())); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.IdlingFinishedForGame, game.AppID, game.GameName, TimeSpan.FromHours(game.HoursPlayed).ToHumanReadable())); return true; } private async Task GetCardsRemaining(uint appID) { if (appID == 0) { - throw new ArgumentNullException(nameof(appID)); + throw new ArgumentOutOfRangeException(nameof(appID)); } using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false); @@ -991,7 +1008,7 @@ namespace ArchiSteamFarm { GamesToFarm.Clear(); - ConcurrentHashSet parsedAppIDs = new ConcurrentHashSet(); + ConcurrentHashSet parsedAppIDs = new(); Task mainTask = CheckPage(htmlDocument, parsedAppIDs); @@ -1009,7 +1026,7 @@ namespace ArchiSteamFarm { break; default: - HashSet tasks = new HashSet(maxPages) { mainTask }; + HashSet tasks = new(maxPages) { mainTask }; if (maxPages > 1) { Bot.ArchiLogger.LogGenericInfo(Strings.CheckingOtherBadgePages); @@ -1049,7 +1066,7 @@ namespace ArchiSteamFarm { ConcurrentDictionary ignoredAppIDs = ignoredGlobally ? GloballyIgnoredAppIDs : LocallyIgnoredAppIDs; ignoredAppIDs[game.AppID] = (ignoredUntil > DateTime.MinValue) && (ignoredUntil < DateTime.MaxValue) ? ignoredUntil : DateTime.UtcNow.AddHours(HoursToIgnore); - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingGameNotPossible, game.AppID, game.GameName)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.IdlingGameNotPossible, game.AppID, game.GameName)); return false; } @@ -1067,14 +1084,14 @@ namespace ArchiSteamFarm { ushort? cardsRemaining = await GetCardsRemaining(game.AppID).ConfigureAwait(false); if (!cardsRemaining.HasValue) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningCouldNotCheckCardsStatus, game.AppID, game.GameName)); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningCouldNotCheckCardsStatus, game.AppID, game.GameName)); return null; } game.CardsRemaining = cardsRemaining.Value; - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingStatusForGame, game.AppID, game.GameName, game.CardsRemaining)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.IdlingStatusForGame, game.AppID, game.GameName, game.CardsRemaining)); return game.CardsRemaining > 0; } @@ -1115,7 +1132,7 @@ namespace ArchiSteamFarm { case BotConfig.EFarmingOrder.MarketableDescending: HashSet? marketableAppIDs = await Bot.GetMarketableAppIDs().ConfigureAwait(false); - if ((marketableAppIDs != null) && (marketableAppIDs.Count > 0)) { + if (marketableAppIDs?.Count > 0) { ImmutableHashSet immutableMarketableAppIDs = marketableAppIDs.ToImmutableHashSet(); switch (farmingOrder) { @@ -1128,7 +1145,7 @@ namespace ArchiSteamFarm { break; default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); return; } @@ -1152,12 +1169,12 @@ namespace ArchiSteamFarm { break; case BotConfig.EFarmingOrder.Random: - orderedGamesToFarm = orderedGamesToFarm.ThenBy(game => Utilities.RandomNext()); + orderedGamesToFarm = orderedGamesToFarm.ThenBy(_ => Utilities.RandomNext()); break; case BotConfig.EFarmingOrder.RedeemDateTimesAscending: case BotConfig.EFarmingOrder.RedeemDateTimesDescending: - Dictionary redeemDates = new Dictionary(GamesToFarm.Count); + Dictionary redeemDates = new(GamesToFarm.Count); foreach (Game game in GamesToFarm) { DateTime redeemDate = DateTime.MinValue; @@ -1194,14 +1211,14 @@ namespace ArchiSteamFarm { break; default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); return; } break; default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(farmingOrder), farmingOrder)); return; } @@ -1214,10 +1231,10 @@ namespace ArchiSteamFarm { public sealed class Game : IEquatable { [JsonProperty] - public readonly uint AppID; + public uint AppID { get; } [JsonProperty] - public readonly string GameName; + public string GameName { get; } internal readonly byte BadgeLevel; @@ -1230,14 +1247,10 @@ namespace ArchiSteamFarm { internal uint PlayableAppID { get; set; } internal Game(uint appID, string gameName, float hoursPlayed, ushort cardsRemaining, byte badgeLevel) { - if ((appID == 0) || string.IsNullOrEmpty(gameName) || (hoursPlayed < 0) || (cardsRemaining == 0)) { - throw new ArgumentOutOfRangeException(nameof(appID) + " || " + nameof(gameName) + " || " + nameof(hoursPlayed) + " || " + nameof(cardsRemaining)); - } - - AppID = appID; - GameName = gameName; - HoursPlayed = hoursPlayed; - CardsRemaining = cardsRemaining; + AppID = appID > 0 ? appID : throw new ArgumentOutOfRangeException(nameof(appID)); + GameName = !string.IsNullOrEmpty(gameName) ? gameName : throw new ArgumentNullException(nameof(gameName)); + HoursPlayed = hoursPlayed >= 0 ? hoursPlayed : throw new ArgumentOutOfRangeException(nameof(hoursPlayed)); + CardsRemaining = cardsRemaining > 0 ? cardsRemaining : throw new ArgumentOutOfRangeException(nameof(cardsRemaining)); BadgeLevel = badgeLevel; PlayableAppID = appID; diff --git a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs index b2970f4f8..75ee77c4d 100644 --- a/ArchiSteamFarm/Collections/ConcurrentHashSet.cs +++ b/ArchiSteamFarm/Collections/ConcurrentHashSet.cs @@ -106,7 +106,7 @@ namespace ArchiSteamFarm.Collections { public void SymmetricExceptWith(IEnumerable other) { ISet otherSet = other as ISet ?? other.ToHashSet(); - HashSet removed = new HashSet(); + HashSet removed = new(); foreach (T item in otherSet.Where(Contains)) { removed.Add(item); diff --git a/ArchiSteamFarm/Collections/ConcurrentList.cs b/ArchiSteamFarm/Collections/ConcurrentList.cs index d4e2eb29e..fc7ba57fb 100644 --- a/ArchiSteamFarm/Collections/ConcurrentList.cs +++ b/ArchiSteamFarm/Collections/ConcurrentList.cs @@ -35,8 +35,8 @@ namespace ArchiSteamFarm.Collections { } } - private readonly List BackingCollection = new List(); - private readonly AsyncReaderWriterLock Lock = new AsyncReaderWriterLock(); + private readonly List BackingCollection = new(); + private readonly AsyncReaderWriterLock Lock = new(); int ICollection.Count => Count; int IReadOnlyCollection.Count => Count; diff --git a/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs b/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs index a8b33d26b..5b234aff2 100644 --- a/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs +++ b/ArchiSteamFarm/Collections/FixedSizeConcurrentQueue.cs @@ -26,7 +26,7 @@ using System.Collections.Generic; namespace ArchiSteamFarm.Collections { internal sealed class FixedSizeConcurrentQueue : IEnumerable { - private readonly ConcurrentQueue BackingQueue = new ConcurrentQueue(); + private readonly ConcurrentQueue BackingQueue = new(); internal byte MaxCount { get => BackingMaxCount; diff --git a/ArchiSteamFarm/Commands.cs b/ArchiSteamFarm/Commands.cs index 22714f0af..bf54b7604 100644 --- a/ArchiSteamFarm/Commands.cs +++ b/ArchiSteamFarm/Commands.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Text; @@ -37,14 +38,18 @@ namespace ArchiSteamFarm { private const ushort SteamTypingStatusDelay = 10 * 1000; // Steam client broadcasts typing status each 10 seconds private readonly Bot Bot; - private readonly Dictionary CachedGamesOwned = new Dictionary(); + private readonly Dictionary CachedGamesOwned = new(); internal Commands(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); [PublicAPI] public static string FormatBotResponse(string response, string botName) { - if (string.IsNullOrEmpty(response) || string.IsNullOrEmpty(botName)) { - throw new ArgumentNullException(nameof(response) + " || " + nameof(botName)); + if (string.IsNullOrEmpty(response)) { + throw new ArgumentNullException(nameof(response)); + } + + if (string.IsNullOrEmpty(botName)) { + throw new ArgumentNullException(nameof(botName)); } return Environment.NewLine + "<" + botName + "> " + response; @@ -70,15 +75,19 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task Response(ulong steamID, string message) { - if ((steamID == 0) || string.IsNullOrEmpty(message)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(message)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - string[] args = message.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); + if (string.IsNullOrEmpty(message)) { + throw new ArgumentNullException(nameof(message)); + } + + string[] args = message.Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries); switch (args.Length) { case 0: - throw new ArgumentOutOfRangeException(nameof(args.Length)); + throw new InvalidOperationException(nameof(args.Length)); case 1: switch (args[0].ToUpperInvariant()) { case "2FA": @@ -289,8 +298,12 @@ namespace ArchiSteamFarm { } internal async Task HandleMessage(ulong steamID, string message) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(message)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(message)) { + throw new ArgumentNullException(nameof(message)); } string? commandPrefix = ASF.GlobalConfig != null ? ASF.GlobalConfig.CommandPrefix : GlobalConfig.DefaultCommandPrefix; @@ -301,8 +314,8 @@ namespace ArchiSteamFarm { if (!string.IsNullOrEmpty(pluginsResponse)) { if (!await Bot.SendMessage(steamID, pluginsResponse!).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage))); - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.Content, pluginsResponse)); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.SendMessage))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.Content, pluginsResponse)); } } @@ -319,16 +332,16 @@ namespace ArchiSteamFarm { Task responseTask = Response(steamID, message); - bool feedback = Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing); + bool feedback = Bot.HasAccess(steamID, BotConfig.EAccess.FamilySharing); if (feedback && !responseTask.IsCompleted) { if (!await Bot.SendTypingMessage(steamID).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendTypingMessage))); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.SendTypingMessage))); } while (!responseTask.IsCompleted && (await Task.WhenAny(responseTask, Task.Delay(SteamTypingStatusDelay)).ConfigureAwait(false) != responseTask)) { if (!await Bot.SendTypingMessage(steamID).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendTypingMessage))); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.SendTypingMessage))); } } } @@ -345,14 +358,26 @@ namespace ArchiSteamFarm { } if (!await Bot.SendMessage(steamID, response!).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage))); - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.Content, response)); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.SendMessage))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.Content, response)); } } internal async Task HandleMessage(ulong chatGroupID, ulong chatID, ulong steamID, string message) { - if ((chatGroupID == 0) || (chatID == 0) || (steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message)) { - throw new ArgumentNullException(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(steamID) + " || " + nameof(message)); + if (chatGroupID == 0) { + throw new ArgumentOutOfRangeException(nameof(chatGroupID)); + } + + if (chatID == 0) { + throw new ArgumentOutOfRangeException(nameof(chatID)); + } + + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(message)) { + throw new ArgumentNullException(nameof(message)); } string? commandPrefix = ASF.GlobalConfig != null ? ASF.GlobalConfig.CommandPrefix : GlobalConfig.DefaultCommandPrefix; @@ -363,8 +388,8 @@ namespace ArchiSteamFarm { if (!string.IsNullOrEmpty(pluginsResponse)) { if (!await Bot.SendMessage(chatGroupID, chatID, pluginsResponse!).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage))); - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.Content, pluginsResponse)); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.SendMessage))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.Content, pluginsResponse)); } } @@ -381,18 +406,18 @@ namespace ArchiSteamFarm { Task responseTask = Response(steamID, message); - bool feedback = Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing); + bool feedback = Bot.HasAccess(steamID, BotConfig.EAccess.FamilySharing); if (feedback && !responseTask.IsCompleted) { string pleaseWaitMessage = FormatBotResponse(Strings.PleaseWait); if (!await Bot.SendMessage(chatGroupID, chatID, pleaseWaitMessage).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage))); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.SendMessage))); } while (!responseTask.IsCompleted && (await Task.WhenAny(responseTask, Task.Delay(SteamTypingStatusDelay)).ConfigureAwait(false) != responseTask)) { if (!await Bot.SendMessage(chatGroupID, chatID, pleaseWaitMessage).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage))); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.SendMessage))); } } } @@ -409,8 +434,8 @@ namespace ArchiSteamFarm { } if (!await Bot.SendMessage(chatGroupID, chatID, response!).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(Bot.SendMessage))); - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.Content, response)); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.SendMessage))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.Content, response)); } } @@ -436,7 +461,7 @@ namespace ArchiSteamFarm { Dictionary? gamesOwned = hasValidApiKey.GetValueOrDefault() ? await Bot.ArchiWebHandler.GetOwnedGames(Bot.SteamID).ConfigureAwait(false) : await Bot.ArchiWebHandler.GetMyOwnedGames().ConfigureAwait(false); - if ((gamesOwned != null) && (gamesOwned.Count > 0)) { + if (gamesOwned?.Count > 0) { lock (CachedGamesOwned) { if (CachedGamesOwned.Count == 0) { foreach ((uint appID, string gameName) in gamesOwned) { @@ -453,42 +478,46 @@ namespace ArchiSteamFarm { private async Task Response2FA(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } (bool success, string? token, string message) = await Bot.Actions.GenerateTwoFactorAuthenticationToken().ConfigureAwait(false); - return FormatBotResponse(success && !string.IsNullOrEmpty(token) ? string.Format(Strings.BotAuthenticatorToken, token) : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success && !string.IsNullOrEmpty(token) ? string.Format(CultureInfo.CurrentCulture, Strings.BotAuthenticatorToken, token) : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task Response2FA(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FA(steamID))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task Response2FAConfirm(ulong steamID, bool confirm) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -502,33 +531,41 @@ namespace ArchiSteamFarm { (bool success, string message) = await Bot.Actions.HandleTwoFactorAuthenticationConfirmations(confirm).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task Response2FAConfirm(ulong steamID, string botNames, bool confirm) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FAConfirm(steamID, confirm))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseAddLicense(ulong steamID, string query) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(query)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(query)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { + if (string.IsNullOrEmpty(query)) { + throw new ArgumentNullException(nameof(query)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Operator)) { return null; } @@ -536,7 +573,7 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.BotNotConnected); } - StringBuilder response = new StringBuilder(); + StringBuilder response = new(); string[] entries = query.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); @@ -548,7 +585,7 @@ namespace ArchiSteamFarm { if ((index > 0) && (entry.Length > index + 1)) { if (!uint.TryParse(entry.Substring(index + 1), out gameID) || (gameID == 0)) { - response.AppendLine(FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(gameID)))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(gameID)))); continue; } @@ -557,7 +594,7 @@ namespace ArchiSteamFarm { } else if (uint.TryParse(entry, out gameID) && (gameID > 0)) { type = "SUB"; } else { - response.AppendLine(FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(gameID)))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(gameID)))); continue; } @@ -571,22 +608,22 @@ namespace ArchiSteamFarm { callback = await Bot.SteamApps.RequestFreeLicense(gameID).ToLongRunningTask().ConfigureAwait(false); } catch (Exception e) { Bot.ArchiLogger.LogGenericWarningException(e); - response.AppendLine(FormatBotResponse(string.Format(Strings.BotAddLicense, "app/" + gameID, EResult.Timeout))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotAddLicense, "app/" + gameID, EResult.Timeout))); break; } - response.AppendLine(FormatBotResponse((callback.GrantedApps.Count > 0) || (callback.GrantedPackages.Count > 0) ? string.Format(Strings.BotAddLicenseWithItems, "app/" + gameID, callback.Result, string.Join(", ", callback.GrantedApps.Select(appID => "app/" + appID).Union(callback.GrantedPackages.Select(subID => "sub/" + subID)))) : string.Format(Strings.BotAddLicense, "app/" + gameID, callback.Result))); + response.AppendLine(FormatBotResponse((callback.GrantedApps.Count > 0) || (callback.GrantedPackages.Count > 0) ? string.Format(CultureInfo.CurrentCulture, Strings.BotAddLicenseWithItems, "app/" + gameID, callback.Result, string.Join(", ", callback.GrantedApps.Select(appID => "app/" + appID).Union(callback.GrantedPackages.Select(subID => "sub/" + subID)))) : string.Format(CultureInfo.CurrentCulture, Strings.BotAddLicense, "app/" + gameID, callback.Result))); break; default: if (!await Bot.ArchiWebHandler.AddFreeLicense(gameID).ConfigureAwait(false)) { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotAddLicense, "sub/" + gameID, EResult.Fail))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotAddLicense, "sub/" + gameID, EResult.Fail))); continue; } - response.AppendLine(FormatBotResponse(string.Format(Strings.BotAddLicenseWithItems, gameID, EResult.OK, "sub/" + gameID))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotAddLicenseWithItems, gameID, EResult.OK, "sub/" + gameID))); break; } @@ -596,29 +633,45 @@ namespace ArchiSteamFarm { } private static async Task ResponseAddLicense(ulong steamID, string botNames, string query) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(query)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(query)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(query)) { + throw new ArgumentNullException(nameof(query)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAddLicense(steamID, query))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseAdvancedLoot(ulong steamID, string targetAppID, string targetContextID) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetAppID) || string.IsNullOrEmpty(targetContextID)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetAppID) + " || " + nameof(targetContextID)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(targetAppID)) { + throw new ArgumentNullException(nameof(targetAppID)); + } + + if (string.IsNullOrEmpty(targetContextID)) { + throw new ArgumentNullException(nameof(targetContextID)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -627,49 +680,69 @@ namespace ArchiSteamFarm { } if (!uint.TryParse(targetAppID, out uint appID) || (appID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(appID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(appID))); } if (!ulong.TryParse(targetContextID, out ulong contextID) || (contextID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(contextID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(contextID))); } (bool success, string message) = await Bot.Actions.SendInventory(appID, contextID).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponseAdvancedLoot(ulong steamID, string botNames, string appID, string contextID) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(appID) || string.IsNullOrEmpty(contextID)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(appID) + " || " + nameof(contextID)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(appID)) { + throw new ArgumentNullException(nameof(appID)); + } + + if (string.IsNullOrEmpty(contextID)) { + throw new ArgumentNullException(nameof(contextID)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedLoot(steamID, appID, contextID))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseAdvancedRedeem(ulong steamID, string options, string keys) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(options) || string.IsNullOrEmpty(keys)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(options) + " || " + nameof(keys)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { + if (string.IsNullOrEmpty(options)) { + throw new ArgumentNullException(nameof(options)); + } + + if (string.IsNullOrEmpty(keys)) { + throw new ArgumentNullException(nameof(keys)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Operator)) { return null; } string[] flags = options.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (flags.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(flags))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(flags))); } ERedeemFlags redeemFlags = ERedeemFlags.None; @@ -727,7 +800,7 @@ namespace ArchiSteamFarm { break; default: - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, flag)); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, flag)); } } @@ -735,29 +808,53 @@ namespace ArchiSteamFarm { } private static async Task ResponseAdvancedRedeem(ulong steamID, string botNames, string options, string keys) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(options) || string.IsNullOrEmpty(keys)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(options) + " || " + nameof(keys)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(options)) { + throw new ArgumentNullException(nameof(options)); + } + + if (string.IsNullOrEmpty(keys)) { + throw new ArgumentNullException(nameof(keys)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedRedeem(steamID, options, keys))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseAdvancedTransfer(ulong steamID, uint appID, ulong contextID, Bot targetBot) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (appID == 0) || (contextID == 0) || (targetBot == null)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(appID) + " || " + nameof(contextID) + " || " + nameof(targetBot)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (appID == 0) { + throw new ArgumentOutOfRangeException(nameof(appID)); + } + + if (contextID == 0) { + throw new ArgumentOutOfRangeException(nameof(contextID)); + } + + if (targetBot == null) { + throw new ArgumentNullException(nameof(targetBot)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -771,147 +868,187 @@ namespace ArchiSteamFarm { (bool success, string message) = await Bot.Actions.SendInventory(appID, contextID, targetBot.SteamID).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private async Task ResponseAdvancedTransfer(ulong steamID, string targetAppID, string targetContextID, string botNameTo) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetAppID) || string.IsNullOrEmpty(targetContextID) || string.IsNullOrEmpty(botNameTo)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetAppID) + " || " + nameof(targetContextID) + " || " + nameof(botNameTo)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(targetAppID)) { + throw new ArgumentNullException(nameof(targetAppID)); + } + + if (string.IsNullOrEmpty(targetContextID)) { + throw new ArgumentNullException(nameof(targetContextID)); + } + + if (string.IsNullOrEmpty(botNameTo)) { + throw new ArgumentNullException(nameof(botNameTo)); } Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { - return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; + return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNameTo)) : null; } if (!uint.TryParse(targetAppID, out uint appID) || (appID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(appID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(appID))); } if (!ulong.TryParse(targetContextID, out ulong contextID) || (contextID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(contextID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(contextID))); } return await ResponseAdvancedTransfer(steamID, appID, contextID, targetBot).ConfigureAwait(false); } private static async Task ResponseAdvancedTransfer(ulong steamID, string botNames, string targetAppID, string targetContextID, string botNameTo) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppID) || string.IsNullOrEmpty(targetContextID) || string.IsNullOrEmpty(botNameTo)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppID) + " || " + nameof(targetContextID) + " || " + nameof(botNameTo)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(targetAppID)) { + throw new ArgumentNullException(nameof(targetAppID)); + } + + if (string.IsNullOrEmpty(targetContextID)) { + throw new ArgumentNullException(nameof(targetContextID)); + } + + if (string.IsNullOrEmpty(botNameTo)) { + throw new ArgumentNullException(nameof(botNameTo)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } if (!uint.TryParse(targetAppID, out uint appID) || (appID == 0)) { - return FormatStaticResponse(string.Format(Strings.ErrorIsInvalid, nameof(appID))); + return FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(appID))); } if (!ulong.TryParse(targetContextID, out ulong contextID) || (contextID == 0)) { - return FormatStaticResponse(string.Format(Strings.ErrorIsInvalid, nameof(contextID))); + return FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(contextID))); } Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNameTo)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedTransfer(steamID, appID, contextID, targetBot))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseBackgroundGamesRedeemer(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } uint count = Bot.GamesToRedeemInBackgroundCount; - return FormatBotResponse(string.Format(Strings.BotGamesToRedeemInBackgroundCount, count)); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotGamesToRedeemInBackgroundCount, count)); } private static async Task ResponseBackgroundGamesRedeemer(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBackgroundGamesRedeemer(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseBlacklist(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } IReadOnlyCollection blacklist = Bot.BotDatabase.GetBlacklistedFromTradesSteamIDs(); - return FormatBotResponse(blacklist.Count > 0 ? string.Join(", ", blacklist) : string.Format(Strings.ErrorIsEmpty, nameof(blacklist))); + return FormatBotResponse(blacklist.Count > 0 ? string.Join(", ", blacklist) : string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(blacklist))); } private static async Task ResponseBlacklist(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBlacklist(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseBlacklistAdd(ulong steamID, string targetSteamIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetSteamIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetSteamIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(targetSteamIDs)) { + throw new ArgumentNullException(nameof(targetSteamIDs)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } string[] targets = targetSteamIDs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (targets.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targets))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(targets))); } - HashSet targetIDs = new HashSet(); + HashSet targetIDs = new(); foreach (string target in targets) { if (!ulong.TryParse(target, out ulong targetID) || (targetID == 0) || !new SteamID(targetID).IsIndividualAccount) { - return FormatBotResponse(string.Format(Strings.ErrorParsingObject, nameof(targetID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorParsingObject, nameof(targetID))); } targetIDs.Add(targetID); @@ -923,43 +1060,55 @@ namespace ArchiSteamFarm { } private static async Task ResponseBlacklistAdd(ulong steamID, string botNames, string targetSteamIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetSteamIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetSteamIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(targetSteamIDs)) { + throw new ArgumentNullException(nameof(targetSteamIDs)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBlacklistAdd(steamID, targetSteamIDs)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseBlacklistRemove(ulong steamID, string targetSteamIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetSteamIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetSteamIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(targetSteamIDs)) { + throw new ArgumentNullException(nameof(targetSteamIDs)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } string[] targets = targetSteamIDs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (targets.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targets))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(targets))); } - HashSet targetIDs = new HashSet(); + HashSet targetIDs = new(); foreach (string target in targets) { if (!ulong.TryParse(target, out ulong targetID) || (targetID == 0) || !new SteamID(targetID).IsIndividualAccount) { - return FormatBotResponse(string.Format(Strings.ErrorParsingObject, nameof(targetID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorParsingObject, nameof(targetID))); } targetIDs.Add(targetID); @@ -971,26 +1120,42 @@ namespace ArchiSteamFarm { } private static async Task ResponseBlacklistRemove(ulong steamID, string botNames, string targetSteamIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetSteamIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetSteamIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(targetSteamIDs)) { + throw new ArgumentNullException(nameof(targetSteamIDs)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBlacklistRemove(steamID, targetSteamIDs)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private static string? ResponseEncrypt(ulong steamID, string cryptoMethodText, string stringToEncrypt) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(cryptoMethodText) || string.IsNullOrEmpty(stringToEncrypt)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(cryptoMethodText) + " || " + nameof(stringToEncrypt)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(cryptoMethodText)) { + throw new ArgumentNullException(nameof(cryptoMethodText)); + } + + if (string.IsNullOrEmpty(stringToEncrypt)) { + throw new ArgumentNullException(nameof(stringToEncrypt)); } if (!ASF.IsOwner(steamID)) { @@ -998,17 +1163,17 @@ namespace ArchiSteamFarm { } if (!Enum.TryParse(cryptoMethodText, true, out ArchiCryptoHelper.ECryptoMethod cryptoMethod)) { - return FormatStaticResponse(string.Format(Strings.ErrorIsInvalid, nameof(cryptoMethod))); + return FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(cryptoMethod))); } string? encryptedString = Actions.Encrypt(cryptoMethod, stringToEncrypt); - return FormatStaticResponse(!string.IsNullOrEmpty(encryptedString) ? string.Format(Strings.Result, encryptedString) : Strings.WarningFailed); + return FormatStaticResponse(!string.IsNullOrEmpty(encryptedString) ? string.Format(CultureInfo.CurrentCulture, Strings.Result, encryptedString) : Strings.WarningFailed); } private static string? ResponseExit(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } if (!ASF.IsOwner(steamID)) { @@ -1017,15 +1182,15 @@ namespace ArchiSteamFarm { (bool success, string message) = Actions.Exit(); - return FormatStaticResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatStaticResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private async Task ResponseFarm(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1043,26 +1208,38 @@ namespace ArchiSteamFarm { } private static async Task ResponseFarm(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseFarm(steamID))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private static string? ResponseHash(ulong steamID, string hashingMethodText, string stringToHash) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(hashingMethodText) || string.IsNullOrEmpty(stringToHash)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(hashingMethodText) + " || " + nameof(stringToHash)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(hashingMethodText)) { + throw new ArgumentNullException(nameof(hashingMethodText)); + } + + if (string.IsNullOrEmpty(stringToHash)) { + throw new ArgumentNullException(nameof(stringToHash)); } if (!ASF.IsOwner(steamID)) { @@ -1070,74 +1247,82 @@ namespace ArchiSteamFarm { } if (!Enum.TryParse(hashingMethodText, true, out ArchiCryptoHelper.EHashingMethod hashingMethod)) { - return FormatStaticResponse(string.Format(Strings.ErrorIsInvalid, nameof(hashingMethod))); + return FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(hashingMethod))); } string hash = Actions.Hash(hashingMethod, stringToHash); - return FormatStaticResponse(!string.IsNullOrEmpty(hash) ? string.Format(Strings.Result, hash) : Strings.WarningFailed); + return FormatStaticResponse(!string.IsNullOrEmpty(hash) ? string.Format(CultureInfo.CurrentCulture, Strings.Result, hash) : Strings.WarningFailed); } private string? ResponseHelp(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - return Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing) ? FormatBotResponse(SharedInfo.ProjectURL + "/wiki/Commands") : null; + return Bot.HasAccess(steamID, BotConfig.EAccess.FamilySharing) ? FormatBotResponse(SharedInfo.ProjectURL + "/wiki/Commands") : null; } private string? ResponseIdleBlacklist(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } IReadOnlyCollection idleBlacklist = Bot.BotDatabase.GetIdlingBlacklistedAppIDs(); - return FormatBotResponse(idleBlacklist.Count > 0 ? string.Join(", ", idleBlacklist) : string.Format(Strings.ErrorIsEmpty, nameof(idleBlacklist))); + return FormatBotResponse(idleBlacklist.Count > 0 ? string.Join(", ", idleBlacklist) : string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(idleBlacklist))); } private static async Task ResponseIdleBlacklist(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleBlacklist(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseIdleBlacklistAdd(ulong steamID, string targetAppIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetAppIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetAppIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(targetAppIDs)) { + throw new ArgumentNullException(nameof(targetAppIDs)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } string[] targets = targetAppIDs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (targets.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targets))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(targets))); } - HashSet appIDs = new HashSet(); + HashSet appIDs = new(); foreach (string target in targets) { if (!uint.TryParse(target, out uint appID) || (appID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorParsingObject, nameof(appID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorParsingObject, nameof(appID))); } appIDs.Add(appID); @@ -1158,43 +1343,55 @@ namespace ArchiSteamFarm { } private static async Task ResponseIdleBlacklistAdd(ulong steamID, string botNames, string targetAppIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(targetAppIDs)) { + throw new ArgumentNullException(nameof(targetAppIDs)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleBlacklistAdd(steamID, targetAppIDs)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseIdleBlacklistRemove(ulong steamID, string targetAppIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetAppIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetAppIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(targetAppIDs)) { + throw new ArgumentNullException(nameof(targetAppIDs)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } string[] targets = targetAppIDs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (targets.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targets))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(targets))); } - HashSet appIDs = new HashSet(); + HashSet appIDs = new(); foreach (string target in targets) { if (!uint.TryParse(target, out uint appID) || (appID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorParsingObject, nameof(appID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorParsingObject, nameof(appID))); } appIDs.Add(appID); @@ -1206,75 +1403,91 @@ namespace ArchiSteamFarm { } private static async Task ResponseIdleBlacklistRemove(ulong steamID, string botNames, string targetAppIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(targetAppIDs)) { + throw new ArgumentNullException(nameof(targetAppIDs)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleBlacklistRemove(steamID, targetAppIDs)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseIdleQueue(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } IReadOnlyCollection idleQueue = Bot.BotDatabase.GetIdlingPriorityAppIDs(); - return FormatBotResponse(idleQueue.Count > 0 ? string.Join(", ", idleQueue) : string.Format(Strings.ErrorIsEmpty, nameof(idleQueue))); + return FormatBotResponse(idleQueue.Count > 0 ? string.Join(", ", idleQueue) : string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(idleQueue))); } private static async Task ResponseIdleQueue(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleQueue(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseIdleQueueAdd(ulong steamID, string targetAppIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetAppIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetAppIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(targetAppIDs)) { + throw new ArgumentNullException(nameof(targetAppIDs)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } string[] targets = targetAppIDs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (targets.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targets))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(targets))); } - HashSet appIDs = new HashSet(); + HashSet appIDs = new(); foreach (string target in targets) { if (!uint.TryParse(target, out uint appID) || (appID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorParsingObject, nameof(appID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorParsingObject, nameof(appID))); } appIDs.Add(appID); @@ -1286,43 +1499,55 @@ namespace ArchiSteamFarm { } private static async Task ResponseIdleQueueAdd(ulong steamID, string botNames, string targetAppIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(targetAppIDs)) { + throw new ArgumentNullException(nameof(targetAppIDs)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleQueueAdd(steamID, targetAppIDs)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseIdleQueueRemove(ulong steamID, string targetAppIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetAppIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetAppIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(targetAppIDs)) { + throw new ArgumentNullException(nameof(targetAppIDs)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } string[] targets = targetAppIDs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (targets.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targets))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(targets))); } - HashSet appIDs = new HashSet(); + HashSet appIDs = new(); foreach (string target in targets) { if (!uint.TryParse(target, out uint appID) || (appID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorParsingObject, nameof(appID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorParsingObject, nameof(appID))); } appIDs.Add(appID); @@ -1334,29 +1559,45 @@ namespace ArchiSteamFarm { } private static async Task ResponseIdleQueueRemove(ulong steamID, string botNames, string targetAppIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetAppIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetAppIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(targetAppIDs)) { + throw new ArgumentNullException(nameof(targetAppIDs)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseIdleQueueRemove(steamID, targetAppIDs)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseInput(ulong steamID, string propertyName, string inputValue) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(propertyName) || string.IsNullOrEmpty(inputValue)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(propertyName) + " || " + nameof(inputValue)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(propertyName)) { + throw new ArgumentNullException(nameof(propertyName)); + } + + if (string.IsNullOrEmpty(inputValue)) { + throw new ArgumentNullException(nameof(inputValue)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1367,7 +1608,7 @@ namespace ArchiSteamFarm { } if (!Enum.TryParse(propertyName, true, out ASF.EUserInputType inputType) || (inputType == ASF.EUserInputType.None) || !Enum.IsDefined(typeof(ASF.EUserInputType), inputType)) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(inputType))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(inputType))); } bool result = Bot.SetUserInput(inputType, inputValue); @@ -1376,29 +1617,41 @@ namespace ArchiSteamFarm { } private static async Task ResponseInput(ulong steamID, string botNames, string propertyName, string inputValue) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(propertyName) || string.IsNullOrEmpty(inputValue)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(propertyName) + " || " + nameof(inputValue)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(propertyName)) { + throw new ArgumentNullException(nameof(propertyName)); + } + + if (string.IsNullOrEmpty(inputValue)) { + throw new ArgumentNullException(nameof(inputValue)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseInput(steamID, propertyName, inputValue)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseLevel(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1408,33 +1661,37 @@ namespace ArchiSteamFarm { uint? level = await Bot.ArchiHandler.GetLevel().ConfigureAwait(false); - return FormatBotResponse(level.HasValue ? string.Format(Strings.BotLevel, level.Value) : Strings.WarningFailed); + return FormatBotResponse(level.HasValue ? string.Format(CultureInfo.CurrentCulture, Strings.BotLevel, level.Value) : Strings.WarningFailed); } private static async Task ResponseLevel(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLevel(steamID))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseLoot(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1443,38 +1700,46 @@ namespace ArchiSteamFarm { } if (Bot.BotConfig.LootableTypes.Count == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(Bot.BotConfig.LootableTypes))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(Bot.BotConfig.LootableTypes))); } (bool success, string message) = await Bot.Actions.SendInventory(filterFunction: item => Bot.BotConfig.LootableTypes.Contains(item.Type)).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponseLoot(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLoot(steamID))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseLootByRealAppIDs(ulong steamID, string realAppIDsText, bool exclude = false) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(realAppIDsText)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(realAppIDsText)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(realAppIDsText)) { + throw new ArgumentNullException(nameof(realAppIDsText)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1483,20 +1748,20 @@ namespace ArchiSteamFarm { } if (Bot.BotConfig.LootableTypes.Count == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(Bot.BotConfig.LootableTypes))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(Bot.BotConfig.LootableTypes))); } string[] appIDTexts = realAppIDsText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (appIDTexts.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDTexts))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(appIDTexts))); } - HashSet realAppIDs = new HashSet(); + HashSet realAppIDs = new(); foreach (string appIDText in appIDTexts) { if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(appID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(appID))); } realAppIDs.Add(appID); @@ -1504,33 +1769,45 @@ namespace ArchiSteamFarm { (bool success, string message) = await Bot.Actions.SendInventory(filterFunction: item => Bot.BotConfig.LootableTypes.Contains(item.Type) && (exclude ^ realAppIDs.Contains(item.RealAppID))).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponseLootByRealAppIDs(ulong steamID, string botNames, string realAppIDsText, bool exclude = false) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(realAppIDsText)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(realAppIDsText)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(realAppIDsText)) { + throw new ArgumentNullException(nameof(realAppIDsText)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLootByRealAppIDs(steamID, realAppIDsText, exclude))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseNickname(ulong steamID, string nickname) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(nickname)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(nickname)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(nickname)) { + throw new ArgumentNullException(nameof(nickname)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1544,29 +1821,41 @@ namespace ArchiSteamFarm { } private static async Task ResponseNickname(ulong steamID, string botNames, string nickname) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(nickname)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(nickname)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(nickname)) { + throw new ArgumentNullException(nameof(nickname)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseNickname(steamID, nickname)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task<(string? Response, Dictionary? OwnedGames)> ResponseOwns(ulong steamID, string query) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(query)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(query)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { + if (string.IsNullOrEmpty(query)) { + throw new ArgumentNullException(nameof(query)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Operator)) { return (null, null); } @@ -1576,8 +1865,8 @@ namespace ArchiSteamFarm { Dictionary? gamesOwned = await FetchGamesOwned(true).ConfigureAwait(false); - StringBuilder response = new StringBuilder(); - Dictionary result = new Dictionary(); + StringBuilder response = new(); + Dictionary result = new(); string[] entries = query.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); @@ -1603,20 +1892,20 @@ namespace ArchiSteamFarm { case "APP" when uint.TryParse(game, out appID) && (appID > 0): HashSet? packageIDs = ASF.GlobalDatabase?.GetPackageIDs(appID, Bot.OwnedPackageIDs.Keys); - if ((packageIDs != null) && (packageIDs.Count > 0)) { + if (packageIDs?.Count > 0) { if ((gamesOwned != null) && gamesOwned.TryGetValue(appID, out string? cachedGameName)) { result["app/" + appID] = cachedGameName; - response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, "app/" + appID, cachedGameName))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnedAlreadyWithName, "app/" + appID, cachedGameName))); } else { - result["app/" + appID] = appID.ToString(); - response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlready, "app/" + appID))); + result["app/" + appID] = appID.ToString(CultureInfo.InvariantCulture); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnedAlready, "app/" + appID))); } } else { if (gamesOwned == null) { gamesOwned = await FetchGamesOwned().ConfigureAwait(false); if (gamesOwned == null) { - response.AppendLine(FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(gamesOwned)))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(gamesOwned)))); break; } @@ -1624,9 +1913,9 @@ namespace ArchiSteamFarm { if (gamesOwned.TryGetValue(appID, out string? gameName)) { result["app/" + appID] = gameName; - response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, "app/" + appID, gameName))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnedAlreadyWithName, "app/" + appID, gameName))); } else { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotNotOwnedYet, "app/" + appID))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotOwnedYet, "app/" + appID))); } } @@ -1639,7 +1928,7 @@ namespace ArchiSteamFarm { regex = new Regex(game); } catch (ArgumentException e) { Bot.ArchiLogger.LogGenericWarningException(e); - response.AppendLine(FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(regex)))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(regex)))); break; } @@ -1648,7 +1937,7 @@ namespace ArchiSteamFarm { gamesOwned = await FetchGamesOwned().ConfigureAwait(false); if (gamesOwned == null) { - response.AppendLine(FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(gamesOwned)))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(gamesOwned)))); break; } @@ -1660,21 +1949,21 @@ namespace ArchiSteamFarm { foundWithRegex = true; result["app/" + appID] = gameName; - response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, "app/" + appID, gameName))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnedAlreadyWithName, "app/" + appID, gameName))); } if (!foundWithRegex) { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotNotOwnedYet, entry))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotOwnedYet, entry))); } continue; case "S" when uint.TryParse(game, out uint packageID) && (packageID > 0): case "SUB" when uint.TryParse(game, out packageID) && (packageID > 0): if (Bot.OwnedPackageIDs.ContainsKey(packageID)) { - result["sub/" + packageID] = packageID.ToString(); - response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlready, "sub/" + packageID))); + result["sub/" + packageID] = packageID.ToString(CultureInfo.InvariantCulture); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnedAlready, "sub/" + packageID))); } else { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotNotOwnedYet, "sub/" + packageID))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotOwnedYet, "sub/" + packageID))); } break; @@ -1683,7 +1972,7 @@ namespace ArchiSteamFarm { gamesOwned = await FetchGamesOwned().ConfigureAwait(false); if (gamesOwned == null) { - response.AppendLine(FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(gamesOwned)))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(gamesOwned)))); break; } @@ -1691,44 +1980,52 @@ namespace ArchiSteamFarm { bool foundWithName = false; - foreach ((uint appID, string gameName) in gamesOwned.Where(gameOwned => gameOwned.Value.IndexOf(game, StringComparison.OrdinalIgnoreCase) >= 0)) { + foreach ((uint appID, string gameName) in gamesOwned.Where(gameOwned => gameOwned.Value.Contains(game, StringComparison.OrdinalIgnoreCase))) { foundWithName = true; result["app/" + appID] = gameName; - response.AppendLine(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, "app/" + appID, gameName))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnedAlreadyWithName, "app/" + appID, gameName))); } if (!foundWithName) { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotNotOwnedYet, entry))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotOwnedYet, entry))); } break; } } - return (response.Length > 0 ? response.ToString() : FormatBotResponse(string.Format(Strings.BotNotOwnedYet, query)), result); + return (response.Length > 0 ? response.ToString() : FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotOwnedYet, query)), result); } private static async Task ResponseOwns(ulong steamID, string botNames, string query) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(query)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(query)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(query)) { + throw new ArgumentNullException(nameof(query)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList<(string? Response, Dictionary? OwnedGames)> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseOwns(steamID, query))).ConfigureAwait(false); - List<(string Response, Dictionary OwnedGames)> validResults = new List<(string Response, Dictionary OwnedGames)>(results.Where(result => !string.IsNullOrEmpty(result.Response) && (result.OwnedGames != null))!); + List<(string Response, Dictionary OwnedGames)> validResults = new(results.Where(result => !string.IsNullOrEmpty(result.Response) && (result.OwnedGames != null))!); if (validResults.Count == 0) { return null; } - Dictionary ownedGamesStats = new Dictionary(); + Dictionary ownedGamesStats = new(); foreach ((string gameID, string gameName) in validResults.Where(validResult => validResult.OwnedGames.Count > 0).SelectMany(validResult => validResult.OwnedGames)) { if (ownedGamesStats.TryGetValue(gameID, out (ushort Count, string GameName) ownedGameStats)) { @@ -1744,98 +2041,114 @@ namespace ArchiSteamFarm { ownedGamesStats[gameID] = ownedGameStats; } - IEnumerable extraResponses = ownedGamesStats.Select(kv => FormatStaticResponse(string.Format(Strings.BotOwnsOverviewPerGame, kv.Value.Count, validResults.Count, kv.Key + (!string.IsNullOrEmpty(kv.Value.GameName) ? " | " + kv.Value.GameName : "")))); + IEnumerable extraResponses = ownedGamesStats.Select(kv => FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotOwnsOverviewPerGame, kv.Value.Count, validResults.Count, kv.Key + (!string.IsNullOrEmpty(kv.Value.GameName) ? " | " + kv.Value.GameName : "")))); return string.Join(Environment.NewLine, validResults.Select(result => result.Response).Concat(extraResponses)); } private string? ResponsePassword(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } if (string.IsNullOrEmpty(Bot.BotConfig.DecryptedSteamPassword)) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(BotConfig.DecryptedSteamPassword))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(BotConfig.DecryptedSteamPassword))); } - Dictionary encryptedPasswords = new Dictionary(2) { + Dictionary encryptedPasswords = new(2) { { ArchiCryptoHelper.ECryptoMethod.AES, ArchiCryptoHelper.Encrypt(ArchiCryptoHelper.ECryptoMethod.AES, Bot.BotConfig.DecryptedSteamPassword!) ?? "" }, { ArchiCryptoHelper.ECryptoMethod.ProtectedDataForCurrentUser, ArchiCryptoHelper.Encrypt(ArchiCryptoHelper.ECryptoMethod.ProtectedDataForCurrentUser, Bot.BotConfig.DecryptedSteamPassword!) ?? "" } }; - return FormatBotResponse(string.Join(", ", encryptedPasswords.Where(kv => !string.IsNullOrEmpty(kv.Value)).Select(kv => string.Format(Strings.BotEncryptedPassword, kv.Key, kv.Value)))); + return FormatBotResponse(string.Join(", ", encryptedPasswords.Where(kv => !string.IsNullOrEmpty(kv.Value)).Select(kv => string.Format(CultureInfo.CurrentCulture, Strings.BotEncryptedPassword, kv.Key, kv.Value)))); } private static async Task ResponsePassword(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponsePassword(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponsePause(ulong steamID, bool permanent, string? resumeInSecondsText = null) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.FamilySharing)) { return null; } - if (permanent && !Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { + if (permanent && !Bot.HasAccess(steamID, BotConfig.EAccess.Operator)) { return FormatBotResponse(Strings.ErrorAccessDenied); } ushort resumeInSeconds = 0; if (!string.IsNullOrEmpty(resumeInSecondsText) && (!ushort.TryParse(resumeInSecondsText, out resumeInSeconds) || (resumeInSeconds == 0))) { - return string.Format(Strings.ErrorIsInvalid, nameof(resumeInSecondsText)); + return string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(resumeInSecondsText)); } (bool success, string message) = await Bot.Actions.Pause(permanent, resumeInSeconds).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponsePause(ulong steamID, string botNames, bool permanent, string? resumeInSecondsText = null) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePause(steamID, permanent, resumeInSecondsText))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponsePlay(ulong steamID, IReadOnlyCollection gameIDs, string? gameName = null) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (gameIDs == null) || (gameIDs.Count > ArchiHandler.MaxGamesPlayedConcurrently)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(gameIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (gameIDs == null) { + throw new ArgumentNullException(nameof(gameIDs)); + } + + if (gameIDs.Count > ArchiHandler.MaxGamesPlayedConcurrently) { + throw new ArgumentOutOfRangeException(nameof(gameIDs)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1845,15 +2158,19 @@ namespace ArchiSteamFarm { (bool success, string message) = await Bot.Actions.Play(gameIDs, gameName).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private async Task ResponsePlay(ulong steamID, string targetGameIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(targetGameIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(targetGameIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(targetGameIDs)) { + throw new ArgumentNullException(nameof(targetGameIDs)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1864,11 +2181,11 @@ namespace ArchiSteamFarm { string[] games = targetGameIDs.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (games.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(games))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(games))); } - HashSet gamesToPlay = new HashSet(); - StringBuilder gameName = new StringBuilder(); + HashSet gamesToPlay = new(); + StringBuilder gameName = new(); foreach (string game in games) { if (!uint.TryParse(game, out uint gameID) || (gameID == 0)) { @@ -1878,7 +2195,7 @@ namespace ArchiSteamFarm { } if (gamesToPlay.Count >= ArchiHandler.MaxGamesPlayedConcurrently) { - return FormatBotResponse(string.Format(Strings.WarningFailedWithError, nameof(gamesToPlay) + " > " + ArchiHandler.MaxGamesPlayedConcurrently)); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(gamesToPlay) + " > " + ArchiHandler.MaxGamesPlayedConcurrently)); } gamesToPlay.Add(gameID); @@ -1888,29 +2205,41 @@ namespace ArchiSteamFarm { } private static async Task ResponsePlay(ulong steamID, string botNames, string targetGameIDs) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(targetGameIDs)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(targetGameIDs)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(targetGameIDs)) { + throw new ArgumentNullException(nameof(targetGameIDs)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePlay(steamID, targetGameIDs))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponsePrivacy(ulong steamID, string privacySettingsText) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(privacySettingsText)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(privacySettingsText)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(privacySettingsText)) { + throw new ArgumentNullException(nameof(privacySettingsText)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -1918,15 +2247,16 @@ namespace ArchiSteamFarm { return FormatBotResponse(Strings.BotNotConnected); } + // There are only 7 privacy settings + const byte privacySettings = 7; + string[] privacySettingsArgs = privacySettingsText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - if (privacySettingsArgs.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(privacySettingsArgs))); - } - - // There are only 7 privacy settings - if (privacySettingsArgs.Length > 7) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(privacySettingsArgs))); + switch (privacySettingsArgs.Length) { + case 0: + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(privacySettingsArgs))); + case > privacySettings: + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(privacySettingsArgs))); } ArchiHandler.EPrivacySetting profile = ArchiHandler.EPrivacySetting.Private; @@ -1940,7 +2270,7 @@ namespace ArchiSteamFarm { // Converting digits to enum for (byte index = 0; index < privacySettingsArgs.Length; index++) { if (!Enum.TryParse(privacySettingsArgs[index], true, out ArchiHandler.EPrivacySetting privacySetting) || (privacySetting == ArchiHandler.EPrivacySetting.Unknown) || !Enum.IsDefined(typeof(ArchiHandler.EPrivacySetting), privacySetting)) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(privacySettingsArgs))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(privacySettingsArgs))); } // Child setting can't be less restrictive than its parent @@ -1953,7 +2283,7 @@ namespace ArchiSteamFarm { case 1: // OwnedGames, child of Profile if (profile < privacySetting) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(ownedGames))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(ownedGames))); } ownedGames = privacySetting; @@ -1962,7 +2292,7 @@ namespace ArchiSteamFarm { case 2: // Playtime, child of OwnedGames if (ownedGames < privacySetting) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(playtime))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(playtime))); } playtime = privacySetting; @@ -1971,7 +2301,7 @@ namespace ArchiSteamFarm { case 3: // FriendsList, child of Profile if (profile < privacySetting) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(ownedGames))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(ownedGames))); } friendsList = privacySetting; @@ -1980,7 +2310,7 @@ namespace ArchiSteamFarm { case 4: // Inventory, child of Profile if (profile < privacySetting) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(inventory))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(inventory))); } inventory = privacySetting; @@ -1989,7 +2319,7 @@ namespace ArchiSteamFarm { case 5: // InventoryGifts, child of Inventory if (inventory < privacySetting) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(inventoryGifts))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(inventoryGifts))); } inventoryGifts = privacySetting; @@ -1998,7 +2328,7 @@ namespace ArchiSteamFarm { case 6: // Comments, child of Profile if (profile < privacySetting) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(comments))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(comments))); } // Comments use different numbers than everything else, but we want to have this command consistent for end-user, so we'll map them @@ -2016,48 +2346,64 @@ namespace ArchiSteamFarm { break; default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(privacySetting), privacySetting)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(privacySetting), privacySetting)); - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(privacySetting))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(privacySetting))); } break; default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(index), index)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(index), index)); - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(index))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(index))); } } - Steam.UserPrivacy userPrivacy = new Steam.UserPrivacy(new Steam.UserPrivacy.PrivacySettings(profile, ownedGames, playtime, friendsList, inventory, inventoryGifts), comments); + Steam.UserPrivacy userPrivacy = new(new Steam.UserPrivacy.PrivacySettings(profile, ownedGames, playtime, friendsList, inventory, inventoryGifts), comments); return FormatBotResponse(await Bot.ArchiWebHandler.ChangePrivacySettings(userPrivacy).ConfigureAwait(false) ? Strings.Success : Strings.WarningFailed); } private static async Task ResponsePrivacy(ulong steamID, string botNames, string privacySettingsText) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(privacySettingsText)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(privacySettingsText)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(privacySettingsText)) { + throw new ArgumentNullException(nameof(privacySettingsText)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePrivacy(steamID, privacySettingsText))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseRedeem(ulong steamID, string keysText, ERedeemFlags redeemFlags = ERedeemFlags.None) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(keysText) || (Bot.Bots == null)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(keysText) + " || " + nameof(Bot.Bots)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Operator)) { + if (string.IsNullOrEmpty(keysText)) { + throw new ArgumentNullException(nameof(keysText)); + } + + if (Bot.Bots == null) { + throw new InvalidOperationException(nameof(Bot.Bots)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Operator)) { return null; } @@ -2068,7 +2414,7 @@ namespace ArchiSteamFarm { string[] keys = keysText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (keys.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(keys))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(keys))); } bool forward = !redeemFlags.HasFlag(ERedeemFlags.SkipForwarding) && (redeemFlags.HasFlag(ERedeemFlags.ForceForwarding) || Bot.BotConfig.RedeemingPreferences.HasFlag(BotConfig.ERedeemingPreferences.Forwarding)); @@ -2079,10 +2425,10 @@ namespace ArchiSteamFarm { HashSet pendingKeys = keys.ToHashSet(StringComparer.Ordinal); HashSet unusedKeys = pendingKeys.ToHashSet(StringComparer.Ordinal); - HashSet rateLimitedBots = new HashSet(); - HashSet triedBots = new HashSet(); + HashSet rateLimitedBots = new(); + HashSet triedBots = new(); - StringBuilder response = new StringBuilder(); + StringBuilder response = new(); using (HashSet.Enumerator keysEnumerator = pendingKeys.GetEnumerator()) { // Initial key @@ -2092,7 +2438,7 @@ namespace ArchiSteamFarm { while (!string.IsNullOrEmpty(key)) { string startingKey = key!; - using (IEnumerator botsEnumerator = Bot.Bots.Where(bot => (bot.Value != Bot) && bot.Value.IsConnectedAndLoggedOn && bot.Value.Commands.Bot.HasPermission(steamID, BotConfig.EPermission.Operator)).OrderByDescending(bot => Bot.BotsComparer?.Compare(bot.Key, Bot.BotName) > 0).ThenBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value).GetEnumerator()) { + using (IEnumerator botsEnumerator = Bot.Bots.Where(bot => (bot.Value != Bot) && bot.Value.IsConnectedAndLoggedOn && bot.Value.Commands.Bot.HasAccess(steamID, BotConfig.EAccess.Operator)).OrderByDescending(bot => Bot.BotsComparer?.Compare(bot.Key, Bot.BotName) > 0).ThenBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value).GetEnumerator()) { Bot? currentBot = Bot; while (!string.IsNullOrEmpty(key) && (currentBot != null)) { @@ -2118,7 +2464,7 @@ namespace ArchiSteamFarm { ArchiHandler.PurchaseResponseCallback? result = skipRequest ? new ArchiHandler.PurchaseResponseCallback(EResult.Fail, EPurchaseResultDetail.CancelledByUser) : await currentBot.Actions.RedeemKey(key!).ConfigureAwait(false); if (result == null) { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeem, key, EPurchaseResultDetail.Timeout), currentBot.BotName)); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotRedeem, key, EPurchaseResultDetail.Timeout), currentBot.BotName)); // Either bot will be changed, or loop aborted currentBot = null; @@ -2140,10 +2486,10 @@ namespace ArchiSteamFarm { } } - if ((result.Items != null) && (result.Items.Count > 0)) { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeemWithItems, key, result.Result + "/" + result.PurchaseResultDetail, string.Join(", ", result.Items)), currentBot.BotName)); + if (result.Items?.Count > 0) { + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotRedeemWithItems, key, result.Result + "/" + result.PurchaseResultDetail, string.Join(", ", result.Items)), currentBot.BotName)); } else if (!skipRequest) { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeem, key, result.Result + "/" + result.PurchaseResultDetail), currentBot.BotName)); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotRedeem, key, result.Result + "/" + result.PurchaseResultDetail), currentBot.BotName)); } switch (result.PurchaseResultDetail) { @@ -2188,11 +2534,11 @@ namespace ArchiSteamFarm { bool alreadyHandled = false; - foreach (Bot innerBot in Bot.Bots.Where(bot => (bot.Value != currentBot) && (!redeemFlags.HasFlag(ERedeemFlags.SkipInitial) || (bot.Value != Bot)) && !triedBots.Contains(bot.Value) && !rateLimitedBots.Contains(bot.Value) && bot.Value.IsConnectedAndLoggedOn && bot.Value.Commands.Bot.HasPermission(steamID, BotConfig.EPermission.Operator) && ((items.Count == 0) || items.Keys.Any(packageID => !bot.Value.OwnedPackageIDs.ContainsKey(packageID)))).OrderBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value)) { + foreach (Bot innerBot in Bot.Bots.Where(bot => (bot.Value != currentBot) && (!redeemFlags.HasFlag(ERedeemFlags.SkipInitial) || (bot.Value != Bot)) && !triedBots.Contains(bot.Value) && !rateLimitedBots.Contains(bot.Value) && bot.Value.IsConnectedAndLoggedOn && bot.Value.Commands.Bot.HasAccess(steamID, BotConfig.EAccess.Operator) && ((items.Count == 0) || items.Keys.Any(packageID => !bot.Value.OwnedPackageIDs.ContainsKey(packageID)))).OrderBy(bot => bot.Key, Bot.BotsComparer).Select(bot => bot.Value)) { ArchiHandler.PurchaseResponseCallback? otherResult = await innerBot.Actions.RedeemKey(key!).ConfigureAwait(false); if (otherResult == null) { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeem, key, EResult.Timeout + "/" + EPurchaseResultDetail.Timeout), innerBot.BotName)); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotRedeem, key, EResult.Timeout + "/" + EPurchaseResultDetail.Timeout), innerBot.BotName)); continue; } @@ -2214,11 +2560,7 @@ namespace ArchiSteamFarm { break; } - if ((otherResult.Items != null) && (otherResult.Items.Count > 0)) { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeemWithItems, key, otherResult.Result + "/" + otherResult.PurchaseResultDetail, string.Join(", ", otherResult.Items)), innerBot.BotName)); - } else { - response.AppendLine(FormatBotResponse(string.Format(Strings.BotRedeem, key, otherResult.Result + "/" + otherResult.PurchaseResultDetail), innerBot.BotName)); - } + response.AppendLine(FormatBotResponse(otherResult.Items?.Count > 0 ? string.Format(CultureInfo.CurrentCulture, Strings.BotRedeemWithItems, key, otherResult.Result + "/" + otherResult.PurchaseResultDetail, string.Join(", ", otherResult.Items)) : string.Format(CultureInfo.CurrentCulture, Strings.BotRedeem, key, otherResult.Result + "/" + otherResult.PurchaseResultDetail), innerBot.BotName)); if (alreadyHandled) { break; @@ -2243,7 +2585,7 @@ namespace ArchiSteamFarm { goto case EPurchaseResultDetail.CancelledByUser; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result.PurchaseResultDetail), result.PurchaseResultDetail)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(result.PurchaseResultDetail), result.PurchaseResultDetail)); unusedKeys.Remove(key!); @@ -2273,36 +2615,44 @@ namespace ArchiSteamFarm { } if (unusedKeys.Count > 0) { - response.AppendLine(FormatBotResponse(string.Format(Strings.UnusedKeys, string.Join(", ", unusedKeys)))); + response.AppendLine(FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.UnusedKeys, string.Join(", ", unusedKeys)))); } return response.Length > 0 ? response.ToString() : null; } - private static async Task ResponseRedeem(ulong steamID, string botNames, string keys, ERedeemFlags redeemFlags = ERedeemFlags.None) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(keys)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(keys)); + private static async Task ResponseRedeem(ulong steamID, string botNames, string keysText, ERedeemFlags redeemFlags = ERedeemFlags.None) { + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(keysText)) { + throw new ArgumentNullException(nameof(keysText)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } - IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseRedeem(steamID, keys, redeemFlags))).ConfigureAwait(false); + IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseRedeem(steamID, keysText, redeemFlags))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseReset(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -2312,30 +2662,34 @@ namespace ArchiSteamFarm { (bool success, string message) = await Bot.Actions.Play(Enumerable.Empty(), Bot.BotConfig.CustomGamePlayedWhileIdle).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponseReset(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseReset(steamID))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private static string? ResponseRestart(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } if (!ASF.IsOwner(steamID)) { @@ -2344,76 +2698,84 @@ namespace ArchiSteamFarm { (bool success, string message) = Actions.Restart(); - return FormatStaticResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatStaticResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private string? ResponseResume(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.FamilySharing)) { return null; } (bool success, string message) = Bot.Actions.Resume(); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponseResume(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseResume(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseStart(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } (bool success, string message) = Bot.Actions.Start(); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponseStart(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStart(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseStats(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } if (!ASF.IsOwner(steamID)) { @@ -2423,15 +2785,15 @@ namespace ArchiSteamFarm { ushort memoryInMegabytes = (ushort) (GC.GetTotalMemory(false) / 1024 / 1024); TimeSpan uptime = DateTime.UtcNow.Subtract(RuntimeCompatibility.ProcessStartTime.ToUniversalTime()); - return FormatBotResponse(string.Format(Strings.BotStats, memoryInMegabytes, uptime.ToHumanReadable())); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotStats, memoryInMegabytes, uptime.ToHumanReadable())); } private (string? Response, Bot Bot) ResponseStatus(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.FamilySharing)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.FamilySharing)) { return (null, Bot); } @@ -2460,28 +2822,32 @@ namespace ArchiSteamFarm { } if (Bot.CardsFarmer.CurrentGamesFarmingReadOnly.Count > 1) { - return (FormatBotResponse(string.Format(Strings.BotStatusIdlingList, string.Join(", ", Bot.CardsFarmer.CurrentGamesFarmingReadOnly.Select(game => game.AppID + " (" + game.GameName + ")")), Bot.CardsFarmer.GamesToFarmReadOnly.Count, Bot.CardsFarmer.GamesToFarmReadOnly.Sum(game => game.CardsRemaining), Bot.CardsFarmer.TimeRemaining.ToHumanReadable())), Bot); + return (FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotStatusIdlingList, string.Join(", ", Bot.CardsFarmer.CurrentGamesFarmingReadOnly.Select(game => game.AppID + " (" + game.GameName + ")")), Bot.CardsFarmer.GamesToFarmReadOnly.Count, Bot.CardsFarmer.GamesToFarmReadOnly.Sum(game => game.CardsRemaining), Bot.CardsFarmer.TimeRemaining.ToHumanReadable())), Bot); } CardsFarmer.Game soloGame = Bot.CardsFarmer.CurrentGamesFarmingReadOnly.First(); - return (FormatBotResponse(string.Format(Strings.BotStatusIdling, soloGame.AppID, soloGame.GameName, soloGame.CardsRemaining, Bot.CardsFarmer.GamesToFarmReadOnly.Count, Bot.CardsFarmer.GamesToFarmReadOnly.Sum(game => game.CardsRemaining), Bot.CardsFarmer.TimeRemaining.ToHumanReadable())), Bot); + return (FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotStatusIdling, soloGame.AppID, soloGame.GameName, soloGame.CardsRemaining, Bot.CardsFarmer.GamesToFarmReadOnly.Count, Bot.CardsFarmer.GamesToFarmReadOnly.Sum(game => game.CardsRemaining), Bot.CardsFarmer.TimeRemaining.ToHumanReadable())), Bot); } private static async Task ResponseStatus(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList<(string? Response, Bot Bot)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStatus(steamID)))).ConfigureAwait(false); - List<(string Response, Bot Bot)> validResults = new List<(string Response, Bot Bot)>(results.Where(result => !string.IsNullOrEmpty(result.Response))!); + List<(string Response, Bot Bot)> validResults = new(results.Where(result => !string.IsNullOrEmpty(result.Response))!); if (validResults.Count == 0) { return null; @@ -2489,49 +2855,57 @@ namespace ArchiSteamFarm { HashSet botsRunning = validResults.Where(result => result.Bot.KeepRunning).Select(result => result.Bot).ToHashSet(); - string extraResponse = string.Format(Strings.BotStatusOverview, botsRunning.Count, validResults.Count, botsRunning.Sum(bot => bot.CardsFarmer.GamesToFarmReadOnly.Count), botsRunning.Sum(bot => bot.CardsFarmer.GamesToFarmReadOnly.Sum(game => game.CardsRemaining))); + string extraResponse = string.Format(CultureInfo.CurrentCulture, Strings.BotStatusOverview, botsRunning.Count, validResults.Count, botsRunning.Sum(bot => bot.CardsFarmer.GamesToFarmReadOnly.Count), botsRunning.Sum(bot => bot.CardsFarmer.GamesToFarmReadOnly.Sum(game => game.CardsRemaining))); return string.Join(Environment.NewLine, validResults.Select(result => result.Response).Union(extraResponse.ToEnumerable())); } private string? ResponseStop(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } (bool success, string message) = Bot.Actions.Stop(); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponseStop(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStop(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseTransfer(ulong steamID, string botNameTo) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNameTo)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNameTo)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(botNameTo)) { + throw new ArgumentNullException(nameof(botNameTo)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -2540,13 +2914,13 @@ namespace ArchiSteamFarm { } if (Bot.BotConfig.TransferableTypes.Count == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(Bot.BotConfig.TransferableTypes))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(Bot.BotConfig.TransferableTypes))); } Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { - return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; + return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNameTo)) : null; } if (!targetBot.IsConnectedAndLoggedOn) { @@ -2559,33 +2933,49 @@ namespace ArchiSteamFarm { (bool success, string message) = await Bot.Actions.SendInventory(targetSteamID: targetBot.SteamID, filterFunction: item => Bot.BotConfig.TransferableTypes.Contains(item.Type)).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private static async Task ResponseTransfer(ulong steamID, string botNames, string botNameTo) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(botNameTo)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(botNameTo)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(botNameTo)) { + throw new ArgumentNullException(nameof(botNameTo)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransfer(steamID, botNameTo))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private async Task ResponseTransferByRealAppIDs(ulong steamID, IReadOnlyCollection realAppIDs, Bot targetBot, bool exclude = false) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (realAppIDs == null) || (realAppIDs.Count == 0) || (targetBot == null)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(realAppIDs) + " || " + nameof(targetBot)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if ((realAppIDs == null) || (realAppIDs.Count == 0)) { + throw new ArgumentNullException(nameof(realAppIDs)); + } + + if (targetBot == null) { + throw new ArgumentNullException(nameof(targetBot)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -2594,7 +2984,7 @@ namespace ArchiSteamFarm { } if (Bot.BotConfig.TransferableTypes.Count == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(Bot.BotConfig.TransferableTypes))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(Bot.BotConfig.TransferableTypes))); } if (!targetBot.IsConnectedAndLoggedOn) { @@ -2607,35 +2997,43 @@ namespace ArchiSteamFarm { (bool success, string message) = await Bot.Actions.SendInventory(targetSteamID: targetBot.SteamID, filterFunction: item => Bot.BotConfig.TransferableTypes.Contains(item.Type) && (exclude ^ realAppIDs.Contains(item.RealAppID))).ConfigureAwait(false); - return FormatBotResponse(success ? message : string.Format(Strings.WarningFailedWithError, message)); + return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message)); } private async Task ResponseTransferByRealAppIDs(ulong steamID, string realAppIDsText, string botNameTo, bool exclude = false) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(realAppIDsText) || string.IsNullOrEmpty(botNameTo)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(realAppIDsText) + " || " + nameof(botNameTo)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (string.IsNullOrEmpty(realAppIDsText)) { + throw new ArgumentNullException(nameof(realAppIDsText)); + } + + if (string.IsNullOrEmpty(botNameTo)) { + throw new ArgumentNullException(nameof(botNameTo)); + } + + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { - return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; + return ASF.IsOwner(steamID) ? FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNameTo)) : null; } string[] appIDTexts = realAppIDsText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (appIDTexts.Length == 0) { - return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDTexts))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(appIDTexts))); } - HashSet realAppIDs = new HashSet(); + HashSet realAppIDs = new(); foreach (string appIDText in appIDTexts) { if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { - return FormatBotResponse(string.Format(Strings.ErrorIsInvalid, nameof(appID))); + return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(appID))); } realAppIDs.Add(appID); @@ -2645,27 +3043,39 @@ namespace ArchiSteamFarm { } private static async Task ResponseTransferByRealAppIDs(ulong steamID, string botNames, string realAppIDsText, string botNameTo, bool exclude = false) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames) || string.IsNullOrEmpty(realAppIDsText) || string.IsNullOrEmpty(botNameTo)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames) + " || " + nameof(realAppIDsText) + " || " + nameof(botNameTo)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); + } + + if (string.IsNullOrEmpty(realAppIDsText)) { + throw new ArgumentNullException(nameof(realAppIDsText)); + } + + if (string.IsNullOrEmpty(botNameTo)) { + throw new ArgumentNullException(nameof(botNameTo)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } string[] appIDTexts = realAppIDsText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (appIDTexts.Length == 0) { - return FormatStaticResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDTexts))); + return FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(appIDTexts))); } - HashSet realAppIDs = new HashSet(); + HashSet realAppIDs = new(); foreach (string appIDText in appIDTexts) { if (!uint.TryParse(appIDText, out uint appID) || (appID == 0)) { - return FormatStaticResponse(string.Format(Strings.ErrorIsInvalid, nameof(appID))); + return FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(appID))); } realAppIDs.Add(appID); @@ -2674,30 +3084,30 @@ namespace ArchiSteamFarm { Bot? targetBot = Bot.GetBot(botNameTo); if (targetBot == null) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNameTo)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNameTo)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransferByRealAppIDs(steamID, realAppIDs, targetBot, exclude))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private string? ResponseUnknown(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - return Bot.HasPermission(steamID, BotConfig.EPermission.Operator) ? FormatBotResponse(Strings.UnknownCommand) : null; + return Bot.HasAccess(steamID, BotConfig.EAccess.Operator) ? FormatBotResponse(Strings.UnknownCommand) : null; } private async Task ResponseUnpackBoosters(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } @@ -2729,26 +3139,30 @@ namespace ArchiSteamFarm { } private static async Task ResponseUnpackBoosters(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseUnpackBoosters(steamID))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } private static async Task ResponseUpdate(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } if (!ASF.IsOwner(steamID)) { @@ -2762,38 +3176,42 @@ namespace ArchiSteamFarm { private string? ResponseVersion(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - return Bot.HasPermission(steamID, BotConfig.EPermission.Operator) ? FormatBotResponse(string.Format(Strings.BotVersion, SharedInfo.ASF, SharedInfo.Version)) : null; + return Bot.HasAccess(steamID, BotConfig.EAccess.Operator) ? FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotVersion, SharedInfo.ASF, SharedInfo.Version)) : null; } private string? ResponseWalletBalance(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { - throw new ArgumentNullException(nameof(steamID)); + throw new ArgumentOutOfRangeException(nameof(steamID)); } - if (!Bot.HasPermission(steamID, BotConfig.EPermission.Master)) { + if (!Bot.HasAccess(steamID, BotConfig.EAccess.Master)) { return null; } - return !Bot.IsConnectedAndLoggedOn ? FormatBotResponse(Strings.BotNotConnected) : FormatBotResponse(Bot.WalletCurrency != ECurrencyCode.Invalid ? string.Format(Strings.BotWalletBalance, Bot.WalletBalance / 100.0, Bot.WalletCurrency.ToString()) : Strings.BotHasNoWallet); + return !Bot.IsConnectedAndLoggedOn ? FormatBotResponse(Strings.BotNotConnected) : FormatBotResponse(Bot.WalletCurrency != ECurrencyCode.Invalid ? string.Format(CultureInfo.CurrentCulture, Strings.BotWalletBalance, Bot.WalletBalance / 100.0, Bot.WalletCurrency.ToString()) : Strings.BotHasNoWallet); } private static async Task ResponseWalletBalance(ulong steamID, string botNames) { - if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(botNames)) { - throw new ArgumentNullException(nameof(steamID) + " || " + nameof(botNames)); + if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { + throw new ArgumentOutOfRangeException(nameof(steamID)); + } + + if (string.IsNullOrEmpty(botNames)) { + throw new ArgumentNullException(nameof(botNames)); } HashSet? bots = Bot.GetBots(botNames); if ((bots == null) || (bots.Count == 0)) { - return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; + return ASF.IsOwner(steamID) ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null; } IList results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseWalletBalance(steamID)))).ConfigureAwait(false); - List responses = new List(results.Where(result => !string.IsNullOrEmpty(result))!); + List responses = new(results.Where(result => !string.IsNullOrEmpty(result))!); return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null; } diff --git a/ArchiSteamFarm/Debugging.cs b/ArchiSteamFarm/Debugging.cs index fa3578f11..10453f9e1 100644 --- a/ArchiSteamFarm/Debugging.cs +++ b/ArchiSteamFarm/Debugging.cs @@ -37,7 +37,7 @@ namespace ArchiSteamFarm { internal sealed class DebugListener : IDebugListener { public void WriteLine(string category, string msg) { if (string.IsNullOrEmpty(category) && string.IsNullOrEmpty(msg)) { - throw new ArgumentNullException(nameof(category) + " && " + nameof(msg)); + throw new InvalidOperationException(nameof(category) + " && " + nameof(msg)); } ASF.ArchiLogger.LogGenericDebug(category + " | " + msg); diff --git a/ArchiSteamFarm/GitHub.cs b/ArchiSteamFarm/GitHub.cs index a94a89b54..3f7d79526 100644 --- a/ArchiSteamFarm/GitHub.cs +++ b/ArchiSteamFarm/GitHub.cs @@ -59,9 +59,9 @@ namespace ArchiSteamFarm { } MarkdownDocument markdownDocument = Markdown.Parse(markdownText); - MarkdownDocument result = new MarkdownDocument(); + MarkdownDocument result = new(); - foreach (Block block in markdownDocument.SkipWhile(block => !(block is HeadingBlock headingBlock) || (headingBlock.Inline.FirstChild == null) || !(headingBlock.Inline.FirstChild is LiteralInline literalInline) || (literalInline.Content.ToString() != "Changelog")).Skip(1).TakeWhile(block => !(block is ThematicBreakBlock)).ToList()) { + foreach (Block block in markdownDocument.SkipWhile(block => block is not HeadingBlock headingBlock || headingBlock.Inline.FirstChild is not LiteralInline literalInline || (literalInline.Content.ToString() != "Changelog")).Skip(1).TakeWhile(block => block is not ThematicBreakBlock).ToList()) { // All blocks that we're interested in must be removed from original markdownDocument firstly markdownDocument.Remove(block); result.Add(block); @@ -71,8 +71,12 @@ namespace ArchiSteamFarm { } private static async Task GetReleaseFromURL(string releaseURL) { - if ((ASF.WebBrowser == null) || string.IsNullOrEmpty(releaseURL)) { - throw new ArgumentNullException(nameof(ASF.WebBrowser) + " || " + nameof(releaseURL)); + if (string.IsNullOrEmpty(releaseURL)) { + throw new ArgumentNullException(nameof(releaseURL)); + } + + if (ASF.WebBrowser == null) { + throw new InvalidOperationException(nameof(ASF.WebBrowser)); } WebBrowser.ObjectResponse? objectResponse = await ASF.WebBrowser.UrlGetToJsonObject(releaseURL).ConfigureAwait(false); @@ -81,8 +85,12 @@ namespace ArchiSteamFarm { } private static async Task?> GetReleasesFromURL(string releaseURL) { - if ((ASF.WebBrowser == null) || string.IsNullOrEmpty(releaseURL)) { - throw new ArgumentNullException(nameof(ASF.WebBrowser) + " || " + nameof(releaseURL)); + if (string.IsNullOrEmpty(releaseURL)) { + throw new ArgumentNullException(nameof(releaseURL)); + } + + if (ASF.WebBrowser == null) { + throw new InvalidOperationException(nameof(ASF.WebBrowser)); } WebBrowser.ObjectResponse>? objectResponse = await ASF.WebBrowser.UrlGetToJsonObject>(releaseURL).ConfigureAwait(false); @@ -116,9 +124,9 @@ namespace ArchiSteamFarm { return null; } - using StringWriter writer = new StringWriter(); + using StringWriter writer = new(); - HtmlRenderer renderer = new HtmlRenderer(writer); + HtmlRenderer renderer = new(writer); renderer.Render(Changelog); writer.Flush(); @@ -138,9 +146,9 @@ namespace ArchiSteamFarm { return null; } - using StringWriter writer = new StringWriter(); + using StringWriter writer = new(); - HtmlRenderer renderer = new HtmlRenderer(writer) { + HtmlRenderer renderer = new(writer) { EnableHtmlForBlock = false, EnableHtmlForInline = false }; diff --git a/ArchiSteamFarm/GlobalConfig.cs b/ArchiSteamFarm/GlobalConfig.cs index 712222df0..63746804b 100644 --- a/ArchiSteamFarm/GlobalConfig.cs +++ b/ArchiSteamFarm/GlobalConfig.cs @@ -23,6 +23,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; using System.Net; using System.Threading; @@ -123,82 +124,76 @@ namespace ArchiSteamFarm { [PublicAPI] public static readonly ImmutableHashSet DefaultBlacklist = ImmutableHashSet.Empty; - private static readonly SemaphoreSlim WriteSemaphore = new SemaphoreSlim(1, 1); + private static readonly SemaphoreSlim WriteSemaphore = new(1, 1); [JsonProperty(Required = Required.DisallowNull)] - public readonly bool AutoRestart = DefaultAutoRestart; + public bool AutoRestart { get; } = DefaultAutoRestart; [JsonProperty(Required = Required.DisallowNull)] - public readonly ImmutableHashSet Blacklist = DefaultBlacklist; + public ImmutableHashSet Blacklist { get; } = DefaultBlacklist; [JsonProperty] - public readonly string? CommandPrefix = DefaultCommandPrefix; + public string? CommandPrefix { get; } = DefaultCommandPrefix; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte ConfirmationsLimiterDelay = DefaultConfirmationsLimiterDelay; + public byte ConfirmationsLimiterDelay { get; } = DefaultConfirmationsLimiterDelay; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte ConnectionTimeout = DefaultConnectionTimeout; + public byte ConnectionTimeout { get; } = DefaultConnectionTimeout; [JsonProperty] - public readonly string? CurrentCulture = DefaultCurrentCulture; + public string? CurrentCulture { get; } = DefaultCurrentCulture; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool Debug = DefaultDebug; + public bool Debug { get; } = DefaultDebug; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte FarmingDelay = DefaultFarmingDelay; + public byte FarmingDelay { get; } = DefaultFarmingDelay; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte GiftsLimiterDelay = DefaultGiftsLimiterDelay; + public byte GiftsLimiterDelay { get; } = DefaultGiftsLimiterDelay; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool Headless = DefaultHeadless; + public bool Headless { get; } = DefaultHeadless; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte IdleFarmingPeriod = DefaultIdleFarmingPeriod; + public byte IdleFarmingPeriod { get; } = DefaultIdleFarmingPeriod; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte InventoryLimiterDelay = DefaultInventoryLimiterDelay; + public byte InventoryLimiterDelay { get; } = DefaultInventoryLimiterDelay; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool IPC = DefaultIPC; + public bool IPC { get; } = DefaultIPC; [JsonProperty(Required = Required.DisallowNull)] - public readonly ArchiCryptoHelper.EHashingMethod IPCPasswordFormat = DefaultIPCPasswordFormat; + public ArchiCryptoHelper.EHashingMethod IPCPasswordFormat { get; } = DefaultIPCPasswordFormat; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte LoginLimiterDelay = DefaultLoginLimiterDelay; + public byte LoginLimiterDelay { get; } = DefaultLoginLimiterDelay; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte MaxFarmingTime = DefaultMaxFarmingTime; + public byte MaxFarmingTime { get; } = DefaultMaxFarmingTime; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte MaxTradeHoldDuration = DefaultMaxTradeHoldDuration; + public byte MaxTradeHoldDuration { get; } = DefaultMaxTradeHoldDuration; [JsonProperty(Required = Required.DisallowNull)] - public readonly EOptimizationMode OptimizationMode = DefaultOptimizationMode; + public EOptimizationMode OptimizationMode { get; } = DefaultOptimizationMode; [JsonProperty(Required = Required.DisallowNull)] - public readonly bool Statistics = DefaultStatistics; + public bool Statistics { get; } = DefaultStatistics; [JsonProperty] - public readonly string? SteamMessagePrefix = DefaultSteamMessagePrefix; + public string? SteamMessagePrefix { get; } = DefaultSteamMessagePrefix; [JsonProperty(Required = Required.DisallowNull)] - public readonly EUpdateChannel UpdateChannel = DefaultUpdateChannel; + public EUpdateChannel UpdateChannel { get; } = DefaultUpdateChannel; [JsonProperty(Required = Required.DisallowNull)] - public readonly byte UpdatePeriod = DefaultUpdatePeriod; + public byte UpdatePeriod { get; } = DefaultUpdatePeriod; [JsonProperty(Required = Required.DisallowNull)] - public readonly ushort WebLimiterDelay = DefaultWebLimiterDelay; - - [JsonProperty(PropertyName = nameof(WebProxy))] - public readonly string? WebProxyText = DefaultWebProxyText; - - [JsonProperty] - public readonly string? WebProxyUsername = DefaultWebProxyUsername; + public ushort WebLimiterDelay { get; } = DefaultWebLimiterDelay; [JsonIgnore] [PublicAPI] @@ -222,13 +217,13 @@ namespace ArchiSteamFarm { return null; } - WebProxy proxy = new WebProxy { + WebProxy proxy = new() { Address = uri, BypassProxyOnLocal = true }; if (!string.IsNullOrEmpty(WebProxyUsername) || !string.IsNullOrEmpty(WebProxyPassword)) { - NetworkCredential credentials = new NetworkCredential(); + NetworkCredential credentials = new(); if (!string.IsNullOrEmpty(WebProxyUsername)) { credentials.UserName = WebProxyUsername; @@ -247,6 +242,12 @@ namespace ArchiSteamFarm { } } + [JsonProperty(PropertyName = nameof(WebProxy))] + public string? WebProxyText { get; } = DefaultWebProxyText; + + [JsonProperty] + public string? WebProxyUsername { get; } = DefaultWebProxyUsername; + [JsonProperty] internal readonly string? IPCPassword = DefaultIPCPassword; @@ -284,11 +285,11 @@ namespace ArchiSteamFarm { [JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(SteamOwnerID), Required = Required.DisallowNull)] private string SSteamOwnerID { - get => SteamOwnerID.ToString(); + get => SteamOwnerID.ToString(CultureInfo.InvariantCulture); set { if (string.IsNullOrEmpty(value) || !ulong.TryParse(value, out ulong result)) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SSteamOwnerID))); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(SSteamOwnerID))); return; } @@ -302,38 +303,38 @@ namespace ArchiSteamFarm { internal (bool Valid, string? ErrorMessage) CheckValidation() { if (ConnectionTimeout == 0) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(ConnectionTimeout), ConnectionTimeout)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(ConnectionTimeout), ConnectionTimeout)); } if (FarmingDelay == 0) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(FarmingDelay), FarmingDelay)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(FarmingDelay), FarmingDelay)); } if (!Enum.IsDefined(typeof(ArchiCryptoHelper.EHashingMethod), IPCPasswordFormat)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(IPCPasswordFormat), IPCPasswordFormat)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(IPCPasswordFormat), IPCPasswordFormat)); } if (MaxFarmingTime == 0) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(MaxFarmingTime), MaxFarmingTime)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(MaxFarmingTime), MaxFarmingTime)); } if (!Enum.IsDefined(typeof(EOptimizationMode), OptimizationMode)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(OptimizationMode), OptimizationMode)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(OptimizationMode), OptimizationMode)); } if (!string.IsNullOrEmpty(SteamMessagePrefix) && (SteamMessagePrefix!.Length > Bot.MaxMessagePrefixLength)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamMessagePrefix), SteamMessagePrefix)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamMessagePrefix), SteamMessagePrefix)); } if ((SteamOwnerID != 0) && !new SteamID(SteamOwnerID).IsIndividualAccount) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamOwnerID), SteamOwnerID)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamOwnerID), SteamOwnerID)); } if ((SteamProtocols <= 0) || (SteamProtocols > ProtocolTypes.All)) { - return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamProtocols), SteamProtocols)); + return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(SteamProtocols), SteamProtocols)); } - return Enum.IsDefined(typeof(EUpdateChannel), UpdateChannel) ? (true, (string?) null) : (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(UpdateChannel), UpdateChannel)); + return Enum.IsDefined(typeof(EUpdateChannel), UpdateChannel) ? (true, (string?) null) : (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(UpdateChannel), UpdateChannel)); } internal static async Task Load(string filePath) { @@ -351,7 +352,7 @@ namespace ArchiSteamFarm { string json = await RuntimeCompatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false); if (string.IsNullOrEmpty(json)) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsEmpty, nameof(json))); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json))); return null; } @@ -384,8 +385,12 @@ namespace ArchiSteamFarm { } internal static async Task Write(string filePath, GlobalConfig globalConfig) { - if (string.IsNullOrEmpty(filePath) || (globalConfig == null)) { - throw new ArgumentNullException(nameof(filePath) + " || " + nameof(globalConfig)); + if (string.IsNullOrEmpty(filePath)) { + throw new ArgumentNullException(nameof(filePath)); + } + + if (globalConfig == null) { + throw new ArgumentNullException(nameof(globalConfig)); } string json = JsonConvert.SerializeObject(globalConfig, Formatting.Indented); diff --git a/ArchiSteamFarm/GlobalDatabase.cs b/ArchiSteamFarm/GlobalDatabase.cs index 5633f3d76..ca9a0c419 100644 --- a/ArchiSteamFarm/GlobalDatabase.cs +++ b/ArchiSteamFarm/GlobalDatabase.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -36,7 +37,7 @@ namespace ArchiSteamFarm { public sealed class GlobalDatabase : SerializableFile { [JsonProperty(Required = Required.DisallowNull)] [PublicAPI] - public readonly Guid Guid = Guid.NewGuid(); + public Guid Identifier { get; } = Guid.NewGuid(); [JsonIgnore] [PublicAPI] @@ -47,15 +48,15 @@ namespace ArchiSteamFarm { public IReadOnlyDictionary? AppIDs)> PackagesDataReadOnly => PackagesData; [JsonProperty(Required = Required.DisallowNull)] - internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider(); + internal readonly InMemoryServerListProvider ServerListProvider = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentDictionary PackagesAccessTokens = new ConcurrentDictionary(); + private readonly ConcurrentDictionary PackagesAccessTokens = new(); [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentDictionary? AppIDs)> PackagesData = new ConcurrentDictionary? AppIDs)>(); + private readonly ConcurrentDictionary? AppIDs)> PackagesData = new(); - private readonly SemaphoreSlim PackagesRefreshSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim PackagesRefreshSemaphore = new(1, 1); internal uint CellID { get => BackingCellID; @@ -110,7 +111,7 @@ namespace ArchiSteamFarm { string json = await RuntimeCompatibility.File.ReadAllTextAsync(filePath).ConfigureAwait(false); if (string.IsNullOrEmpty(json)) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsEmpty, nameof(json))); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json))); return null; } @@ -135,11 +136,15 @@ namespace ArchiSteamFarm { } internal HashSet GetPackageIDs(uint appID, IEnumerable packageIDs) { - if ((appID == 0) || (packageIDs == null)) { - throw new ArgumentNullException(nameof(appID) + " || " + nameof(packageIDs)); + if (appID == 0) { + throw new ArgumentOutOfRangeException(nameof(appID)); } - HashSet result = new HashSet(); + if (packageIDs == null) { + throw new ArgumentNullException(nameof(packageIDs)); + } + + HashSet result = new(); foreach (uint packageID in packageIDs.Where(packageID => packageID != 0)) { if (!PackagesData.TryGetValue(packageID, out (uint ChangeNumber, HashSet? AppIDs) packagesData) || (packagesData.AppIDs?.Contains(appID) != true)) { @@ -172,8 +177,12 @@ namespace ArchiSteamFarm { } internal async Task RefreshPackages(Bot bot, IReadOnlyDictionary packages) { - if ((bot == null) || (packages == null) || (packages.Count == 0)) { - throw new ArgumentNullException(nameof(bot) + " || " + nameof(packages)); + if (bot == null) { + throw new ArgumentNullException(nameof(bot)); + } + + if ((packages == null) || (packages.Count == 0)) { + throw new ArgumentNullException(nameof(packages)); } await PackagesRefreshSemaphore.WaitAsync().ConfigureAwait(false); @@ -216,7 +225,7 @@ namespace ArchiSteamFarm { // ReSharper disable UnusedMember.Global public bool ShouldSerializeCellID() => CellID != 0; - public bool ShouldSerializePackagesData() => PackagesData.Count > 0; + public bool ShouldSerializePackagesData() => !PackagesData.IsEmpty; public bool ShouldSerializeServerListProvider() => ServerListProvider.ShouldSerializeServerRecords(); // ReSharper restore UnusedMember.Global diff --git a/ArchiSteamFarm/Helpers/ArchiCacheable.cs b/ArchiSteamFarm/Helpers/ArchiCacheable.cs index f15bb38ff..146af0702 100644 --- a/ArchiSteamFarm/Helpers/ArchiCacheable.cs +++ b/ArchiSteamFarm/Helpers/ArchiCacheable.cs @@ -29,7 +29,7 @@ using JetBrains.Annotations; namespace ArchiSteamFarm.Helpers { public sealed class ArchiCacheable : IDisposable where T : class { private readonly TimeSpan CacheLifetime; - private readonly SemaphoreSlim InitSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim InitSemaphore = new(1, 1); private readonly Func> ResolveFunction; private bool IsInitialized => InitializedAt > DateTime.MinValue; @@ -68,7 +68,7 @@ namespace ArchiSteamFarm.Helpers { if (!success) { switch (fallback) { case EFallback.DefaultForType: - return (false, default); + return (false, default(T?)); case EFallback.FailedNow: return (false, result); case EFallback.SuccessPreviously: @@ -103,7 +103,7 @@ namespace ArchiSteamFarm.Helpers { } InitializedAt = DateTime.MinValue; - InitializedValue = default; + InitializedValue = default(T?); } finally { InitSemaphore.Release(); } diff --git a/ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs b/ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs index 303f1a2b4..7cabda784 100644 --- a/ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs +++ b/ArchiSteamFarm/Helpers/CrossProcessFileBasedSemaphore.cs @@ -32,7 +32,7 @@ namespace ArchiSteamFarm.Helpers { private const ushort SpinLockDelay = 1000; // In milliseconds private readonly string FilePath; - private readonly SemaphoreSlim LocalSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim LocalSemaphore = new(1, 1); private FileStream? FileLock; @@ -163,10 +163,10 @@ namespace ArchiSteamFarm.Helpers { Directory.CreateDirectory(directoryPath!); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - DirectoryInfo directoryInfo = new DirectoryInfo(directoryPath!); + DirectoryInfo directoryInfo = new(directoryPath!); try { - DirectorySecurity directorySecurity = new DirectorySecurity(directoryPath!, AccessControlSections.All); + DirectorySecurity directorySecurity = new(directoryPath!, AccessControlSections.All); directoryInfo.SetAccessControl(directorySecurity); } catch (PrivilegeNotHeldException e) { @@ -182,10 +182,10 @@ namespace ArchiSteamFarm.Helpers { using (new FileStream(FilePath, FileMode.CreateNew)) { } if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - FileInfo fileInfo = new FileInfo(FilePath); + FileInfo fileInfo = new(FilePath); try { - FileSecurity fileSecurity = new FileSecurity(FilePath, AccessControlSections.All); + FileSecurity fileSecurity = new(FilePath, AccessControlSections.All); fileInfo.SetAccessControl(fileSecurity); } catch (PrivilegeNotHeldException e) { diff --git a/ArchiSteamFarm/Helpers/SerializableFile.cs b/ArchiSteamFarm/Helpers/SerializableFile.cs index d1ce4d41e..eb922cbfc 100644 --- a/ArchiSteamFarm/Helpers/SerializableFile.cs +++ b/ArchiSteamFarm/Helpers/SerializableFile.cs @@ -27,7 +27,7 @@ using Newtonsoft.Json; namespace ArchiSteamFarm.Helpers { public abstract class SerializableFile : IDisposable { - private readonly SemaphoreSlim FileSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim FileSemaphore = new(1, 1); protected string? FilePath { private get; set; } diff --git a/ArchiSteamFarm/IPC/ArchiKestrel.cs b/ArchiSteamFarm/IPC/ArchiKestrel.cs index 3376abba2..ddc39df96 100644 --- a/ArchiSteamFarm/IPC/ArchiKestrel.cs +++ b/ArchiSteamFarm/IPC/ArchiKestrel.cs @@ -68,9 +68,9 @@ namespace ArchiSteamFarm.IPC { // The order of dependency injection matters, pay attention to it #if NETFRAMEWORK - WebHostBuilder builder = new WebHostBuilder(); + WebHostBuilder builder = new(); #else - HostBuilder builder = new HostBuilder(); + HostBuilder builder = new(); #endif string customDirectory = Path.Combine(Directory.GetCurrentDirectory(), SharedInfo.WebsiteDirectory); diff --git a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs index ab5409442..a79755599 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/ASFController.cs @@ -67,7 +67,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { uint memoryUsage = (uint) GC.GetTotalMemory(false) / 1024; - ASFResponse result = new ASFResponse(SharedInfo.BuildInfo.Variant, SharedInfo.BuildInfo.CanUpdate, ASF.GlobalConfig, memoryUsage, RuntimeCompatibility.ProcessStartTime, SharedInfo.Version); + ASFResponse result = new(SharedInfo.BuildInfo.Variant, SharedInfo.BuildInfo.CanUpdate, ASF.GlobalConfig, memoryUsage, RuntimeCompatibility.ProcessStartTime, SharedInfo.Version); return Ok(new GenericResponse(result)); } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs index f1e07507c..93382f2da 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/BotController.cs @@ -117,7 +117,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { return BadRequest(new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(botNames)))); } - Dictionary result = new Dictionary(bots.Count, Bot.BotsComparer); + Dictionary result = new(bots.Count, Bot.BotsComparer); foreach (string botName in bots) { if (Bot.Bots.TryGetValue(botName, out Bot? bot)) { @@ -199,7 +199,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { IList<(Dictionary? UnusedKeys, Dictionary? UsedKeys)> results = await Utilities.InParallel(bots.Select(bot => bot.GetUsedAndUnusedKeys())).ConfigureAwait(false); - Dictionary result = new Dictionary(bots.Count, Bot.BotsComparer); + Dictionary result = new(bots.Count, Bot.BotsComparer); foreach (Bot bot in bots) { (Dictionary? unusedKeys, Dictionary? usedKeys) = results[result.Count]; @@ -243,7 +243,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.AddGamesToRedeemInBackground(validGamesToRedeemInBackground)))).ConfigureAwait(false); - Dictionary result = new Dictionary(bots.Count, Bot.BotsComparer); + Dictionary result = new(bots.Count, Bot.BotsComparer); foreach (Bot bot in bots) { result[bot.BotName] = validGamesToRedeemInBackground; @@ -342,12 +342,12 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { IList results = await Utilities.InParallel(bots.Select(bot => request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).SelectMany(task => task)).ConfigureAwait(false); - Dictionary> result = new Dictionary>(bots.Count, Bot.BotsComparer); + Dictionary> result = new(bots.Count, Bot.BotsComparer); int count = 0; foreach (Bot bot in bots) { - Dictionary responses = new Dictionary(request.KeysToRedeem.Count, StringComparer.Ordinal); + Dictionary responses = new(request.KeysToRedeem.Count, StringComparer.Ordinal); result[bot.BotName] = responses; foreach (string key in request.KeysToRedeem) { @@ -484,7 +484,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { IList<(bool Success, string Message)> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.HandleTwoFactorAuthenticationConfirmations(request.Accept, request.AcceptedType, request.AcceptedCreatorIDs?.Count > 0 ? request.AcceptedCreatorIDs : null, request.WaitIfNeeded))).ConfigureAwait(false); - Dictionary result = new Dictionary(bots.Count, Bot.BotsComparer); + Dictionary result = new(bots.Count, Bot.BotsComparer); foreach (Bot bot in bots) { (bool success, string message) = results[result.Count]; @@ -513,7 +513,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { IList<(bool Success, string? Token, string Message)> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.GenerateTwoFactorAuthenticationToken())).ConfigureAwait(false); - Dictionary> result = new Dictionary>(bots.Count, Bot.BotsComparer); + Dictionary> result = new(bots.Count, Bot.BotsComparer); foreach (Bot bot in bots) { (bool success, string? token, string message) = results[result.Count]; diff --git a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs index d9dfcd462..8791435db 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/NLogController.cs @@ -38,7 +38,7 @@ using Newtonsoft.Json; namespace ArchiSteamFarm.IPC.Controllers.Api { [Route("Api/NLog")] public sealed class NLogController : ArchiController { - private static readonly ConcurrentDictionary ActiveLogWebSockets = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary ActiveLogWebSockets = new(); /// /// Fetches ASF log in realtime. @@ -63,7 +63,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { try { using WebSocket webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); - SemaphoreSlim sendSemaphore = new SemaphoreSlim(1, 1); + SemaphoreSlim sendSemaphore = new(1, 1); if (!ActiveLogWebSockets.TryAdd(webSocket, sendSemaphore)) { sendSemaphore.Dispose(); diff --git a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs index 4f4e9b1a6..8764e5704 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/TypeController.cs @@ -57,7 +57,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { HashSet customAttributes = targetType.CustomAttributes.Select(attribute => attribute.AttributeType.GetUnifiedName()).Where(customAttribute => !string.IsNullOrEmpty(customAttribute)).ToHashSet(StringComparer.Ordinal)!; string? underlyingType = null; - Dictionary body = new Dictionary(StringComparer.Ordinal); + Dictionary body = new(StringComparer.Ordinal); if (targetType.IsClass) { foreach (FieldInfo field in targetType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).Where(field => !field.IsPrivate)) { @@ -106,9 +106,9 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { } } - TypeResponse.TypeProperties properties = new TypeResponse.TypeProperties(baseType, customAttributes.Count > 0 ? customAttributes : null, underlyingType); + TypeResponse.TypeProperties properties = new(baseType, customAttributes.Count > 0 ? customAttributes : null, underlyingType); - TypeResponse response = new TypeResponse(body, properties); + TypeResponse response = new(body, properties); return Ok(new GenericResponse(response)); } diff --git a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs index 1a0913205..0078c8629 100644 --- a/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs +++ b/ArchiSteamFarm/IPC/Controllers/Api/WWWController.cs @@ -130,7 +130,7 @@ namespace ArchiSteamFarm.IPC.Controllers.Api { [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)] [ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)] public async Task> SendPost([FromBody] WWWSendRequest request) { - if ((request == null)) { + if (request == null) { throw new ArgumentNullException(nameof(request)); } diff --git a/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs b/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs index 76d5fc0e0..0f0a9957a 100644 --- a/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs +++ b/ArchiSteamFarm/IPC/Integration/ApiAuthenticationMiddleware.cs @@ -38,8 +38,8 @@ namespace ArchiSteamFarm.IPC.Integration { private const byte FailedAuthorizationsCooldownInHours = 1; private const byte MaxFailedAuthorizationAttempts = 5; - private static readonly SemaphoreSlim AuthorizationSemaphore = new SemaphoreSlim(1, 1); - private static readonly ConcurrentDictionary FailedAuthorizations = new ConcurrentDictionary(); + private static readonly SemaphoreSlim AuthorizationSemaphore = new(1, 1); + private static readonly ConcurrentDictionary FailedAuthorizations = new(); private static Timer? ClearFailedAuthorizationsTimer; @@ -50,7 +50,7 @@ namespace ArchiSteamFarm.IPC.Integration { lock (FailedAuthorizations) { ClearFailedAuthorizationsTimer ??= new Timer( - e => FailedAuthorizations.Clear(), + _ => FailedAuthorizations.Clear(), null, TimeSpan.FromHours(FailedAuthorizationsCooldownInHours), // Delay TimeSpan.FromHours(FailedAuthorizationsCooldownInHours) // Period @@ -76,7 +76,7 @@ namespace ArchiSteamFarm.IPC.Integration { } private static async Task GetAuthenticationStatus(HttpContext context) { - if ((context == null)) { + if (context == null) { throw new ArgumentNullException(nameof(context)); } diff --git a/ArchiSteamFarm/IPC/Integration/EnumSchemaFilter.cs b/ArchiSteamFarm/IPC/Integration/EnumSchemaFilter.cs index a869ab082..7ef41b0a0 100644 --- a/ArchiSteamFarm/IPC/Integration/EnumSchemaFilter.cs +++ b/ArchiSteamFarm/IPC/Integration/EnumSchemaFilter.cs @@ -47,7 +47,7 @@ namespace ArchiSteamFarm.IPC.Integration { schema.Format = "flags"; } - OpenApiObject definition = new OpenApiObject(); + OpenApiObject definition = new(); foreach (object? enumValue in context.Type.GetEnumValues()) { if (enumValue == null) { @@ -93,7 +93,7 @@ namespace ArchiSteamFarm.IPC.Integration { return true; } catch (InvalidCastException) { - typedValue = default; + typedValue = default(T); return false; } diff --git a/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs b/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs index 9f367e9d0..d54f97290 100644 --- a/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs +++ b/ArchiSteamFarm/IPC/Requests/TwoFactorAuthenticationConfirmationsRequest.cs @@ -64,7 +64,7 @@ namespace ArchiSteamFarm.IPC.Requests { throw new ArgumentNullException(nameof(value)); } - HashSet acceptedCreatorIDs = new HashSet(value.Count); + HashSet acceptedCreatorIDs = new(); foreach (string creatorIDText in value) { if (!ulong.TryParse(creatorIDText, out ulong creatorID) || (creatorID == 0)) { diff --git a/ArchiSteamFarm/Json/Steam.cs b/ArchiSteamFarm/Json/Steam.cs index 0ff958327..84e4c26f6 100644 --- a/ArchiSteamFarm/Json/Steam.cs +++ b/ArchiSteamFarm/Json/Steam.cs @@ -322,8 +322,8 @@ namespace ArchiSteamFarm.Json { [PublicAPI] public IReadOnlyCollection ItemsToReceiveReadOnly => ItemsToReceive; - internal readonly HashSet ItemsToGive = new HashSet(); - internal readonly HashSet ItemsToReceive = new HashSet(); + internal readonly HashSet ItemsToGive = new(); + internal readonly HashSet ItemsToReceive = new(); [PublicAPI] public ulong OtherSteamID64 { get; private set; } @@ -649,14 +649,14 @@ namespace ArchiSteamFarm.Json { internal sealed class TradeOfferSendRequest { [JsonProperty(PropertyName = "me", Required = Required.Always)] - internal readonly ItemList ItemsToGive = new ItemList(); + internal readonly ItemList ItemsToGive = new(); [JsonProperty(PropertyName = "them", Required = Required.Always)] - internal readonly ItemList ItemsToReceive = new ItemList(); + internal readonly ItemList ItemsToReceive = new(); internal sealed class ItemList { [JsonProperty(PropertyName = "assets", Required = Required.Always)] - internal readonly HashSet Assets = new HashSet(); + internal readonly HashSet Assets = new(); } } diff --git a/ArchiSteamFarm/MobileAuthenticator.cs b/ArchiSteamFarm/MobileAuthenticator.cs index 7f1b4cccb..2ae1dfbb7 100644 --- a/ArchiSteamFarm/MobileAuthenticator.cs +++ b/ArchiSteamFarm/MobileAuthenticator.cs @@ -22,7 +22,9 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Security.Cryptography; using System.Text; using System.Threading; @@ -40,11 +42,13 @@ namespace ArchiSteamFarm { internal const byte CodeDigits = 5; private const byte CodeInterval = 30; - private const byte SteamTimeTTL = 24; // For how many hours we can assume that SteamTimeDifference is correct + + // For how many hours we can assume that SteamTimeDifference is correct + private const byte SteamTimeTTL = 24; internal static readonly ImmutableSortedSet CodeCharacters = ImmutableSortedSet.Create('2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'T', 'V', 'W', 'X', 'Y'); - private static readonly SemaphoreSlim TimeSemaphore = new SemaphoreSlim(1, 1); + private static readonly SemaphoreSlim TimeSemaphore = new(1, 1); private static DateTime LastSteamTimeCheck; private static int? SteamTimeDifference; @@ -70,13 +74,13 @@ namespace ArchiSteamFarm { internal async Task GenerateToken() { if (Bot == null) { - throw new ArgumentNullException(nameof(Bot)); + throw new InvalidOperationException(nameof(Bot)); } uint time = await GetSteamTime().ConfigureAwait(false); if (time == 0) { - throw new ArgumentNullException(nameof(time)); + throw new InvalidOperationException(nameof(time)); } return GenerateTokenForTime(time); @@ -84,13 +88,13 @@ namespace ArchiSteamFarm { internal async Task?> GetConfirmations() { if (Bot == null) { - throw new ArgumentNullException(nameof(Bot)); + throw new InvalidOperationException(nameof(Bot)); } (bool success, string? deviceID) = await CachedDeviceID.GetValue().ConfigureAwait(false); if (!success || string.IsNullOrEmpty(deviceID)) { - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, nameof(deviceID))); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(deviceID))); return null; } @@ -98,7 +102,7 @@ namespace ArchiSteamFarm { uint time = await GetSteamTime().ConfigureAwait(false); if (time == 0) { - throw new ArgumentNullException(nameof(time)); + throw new InvalidOperationException(nameof(time)); } string? confirmationHash = GenerateConfirmationHash(time, "conf"); @@ -117,7 +121,7 @@ namespace ArchiSteamFarm { return null; } - HashSet result = new HashSet(); + HashSet result = new(); List confirmationNodes = htmlDocument.SelectNodes("//div[@class='mobileconf_list_entry']"); @@ -183,7 +187,7 @@ namespace ArchiSteamFarm { } if (!Enum.IsDefined(typeof(Confirmation.EType), type)) { - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(type), type)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(type), type)); return null; } @@ -195,14 +199,18 @@ namespace ArchiSteamFarm { } internal async Task HandleConfirmations(IReadOnlyCollection confirmations, bool accept) { - if ((confirmations == null) || (confirmations.Count == 0) || (Bot == null)) { - throw new ArgumentNullException(nameof(confirmations) + " || " + nameof(Bot)); + if ((confirmations == null) || (confirmations.Count == 0)) { + throw new ArgumentNullException(nameof(confirmations)); + } + + if (Bot == null) { + throw new InvalidOperationException(nameof(Bot)); } (bool success, string? deviceID) = await CachedDeviceID.GetValue().ConfigureAwait(false); if (!success || string.IsNullOrEmpty(deviceID)) { - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, nameof(deviceID))); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(deviceID))); return false; } @@ -210,7 +218,7 @@ namespace ArchiSteamFarm { uint time = await GetSteamTime().ConfigureAwait(false); if (time == 0) { - throw new ArgumentNullException(nameof(time)); + throw new InvalidOperationException(nameof(time)); } string? confirmationHash = GenerateConfirmationHash(time, "conf"); @@ -250,8 +258,16 @@ namespace ArchiSteamFarm { internal void Init(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); private string? GenerateConfirmationHash(uint time, string? tag = null) { - if ((time == 0) || (Bot == null) || string.IsNullOrEmpty(IdentitySecret)) { - throw new ArgumentNullException(nameof(time) + " || " + nameof(Bot) + " || " + nameof(IdentitySecret)); + if (time == 0) { + throw new ArgumentOutOfRangeException(nameof(time)); + } + + if (Bot == null) { + throw new InvalidOperationException(nameof(Bot)); + } + + if (string.IsNullOrEmpty(IdentitySecret)) { + throw new InvalidOperationException(nameof(IdentitySecret)); } byte[] identitySecret; @@ -260,7 +276,7 @@ namespace ArchiSteamFarm { identitySecret = Convert.FromBase64String(IdentitySecret!); } catch (FormatException e) { Bot.ArchiLogger.LogGenericException(e); - Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(IdentitySecret))); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(IdentitySecret))); return null; } @@ -285,16 +301,28 @@ namespace ArchiSteamFarm { Array.Copy(Encoding.UTF8.GetBytes(tag!), 0, buffer, 8, bufferSize - 8); } - using HMACSHA1 hmac = new HMACSHA1(identitySecret); + byte[] hash; - byte[] hash = hmac.ComputeHash(buffer); +#pragma warning disable CA5350 + using (HMACSHA1 hmac = new(identitySecret)) { + hash = hmac.ComputeHash(buffer); + } +#pragma warning restore CA5350 return Convert.ToBase64String(hash); } private string? GenerateTokenForTime(uint time) { - if ((time == 0) || (Bot == null) || string.IsNullOrEmpty(SharedSecret)) { - throw new ArgumentNullException(nameof(time) + " || " + nameof(Bot) + " || " + nameof(SharedSecret)); + if (time == 0) { + throw new ArgumentOutOfRangeException(nameof(time)); + } + + if (Bot == null) { + throw new InvalidOperationException(nameof(Bot)); + } + + if (string.IsNullOrEmpty(SharedSecret)) { + throw new InvalidOperationException(nameof(SharedSecret)); } byte[] sharedSecret; @@ -303,7 +331,7 @@ namespace ArchiSteamFarm { sharedSecret = Convert.FromBase64String(SharedSecret!); } catch (FormatException e) { Bot.ArchiLogger.LogGenericException(e); - Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SharedSecret))); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(SharedSecret))); return null; } @@ -316,9 +344,11 @@ namespace ArchiSteamFarm { byte[] hash; - using (HMACSHA1 hmac = new HMACSHA1(sharedSecret)) { +#pragma warning disable CA5350 + using (HMACSHA1 hmac = new(sharedSecret)) { hash = hmac.ComputeHash(timeArray); } +#pragma warning restore CA5350 // The last 4 bits of the mac say where the code starts int start = hash[^1] & 0x0f; @@ -347,7 +377,7 @@ namespace ArchiSteamFarm { private async Task GetSteamTime() { if (Bot == null) { - throw new ArgumentNullException(nameof(Bot)); + throw new InvalidOperationException(nameof(Bot)); } if (SteamTimeDifference.HasValue && (DateTime.UtcNow.Subtract(LastSteamTimeCheck).TotalHours < SteamTimeTTL)) { @@ -378,7 +408,7 @@ namespace ArchiSteamFarm { private static async Task LimitConfirmationsRequestsAsync() { if (ASF.ConfirmationsSemaphore == null) { - throw new ArgumentNullException(nameof(ASF.ConfirmationsSemaphore)); + throw new InvalidOperationException(nameof(ASF.ConfirmationsSemaphore)); } byte confirmationsLimiterDelay = ASF.GlobalConfig?.ConfirmationsLimiterDelay ?? GlobalConfig.DefaultConfirmationsLimiterDelay; @@ -420,14 +450,10 @@ namespace ArchiSteamFarm { internal readonly EType Type; internal Confirmation(ulong id, ulong key, ulong creator, EType type) { - if ((id == 0) || (key == 0) || (creator == 0) || !Enum.IsDefined(typeof(EType), type)) { - throw new ArgumentNullException(nameof(id) + " || " + nameof(key) + " || " + nameof(creator) + " || " + nameof(type)); - } - - ID = id; - Key = key; - Creator = creator; - Type = type; + ID = id > 0 ? id : throw new ArgumentOutOfRangeException(nameof(id)); + Key = key > 0 ? key : throw new ArgumentOutOfRangeException(nameof(key)); + Creator = creator > 0 ? creator : throw new ArgumentOutOfRangeException(nameof(creator)); + Type = Enum.IsDefined(typeof(EType), type) ? type : throw new InvalidEnumArgumentException(nameof(type), (int) type, typeof(EType)); } // REF: Internal documentation diff --git a/ArchiSteamFarm/NLog/ArchiLogger.cs b/ArchiSteamFarm/NLog/ArchiLogger.cs index e351e5fc2..756ac5fa6 100644 --- a/ArchiSteamFarm/NLog/ArchiLogger.cs +++ b/ArchiSteamFarm/NLog/ArchiLogger.cs @@ -135,7 +135,7 @@ namespace ArchiSteamFarm.NLog { throw new InvalidOperationException("((" + nameof(chatGroupID) + " || " + nameof(chatID) + ") && " + nameof(steamID) + ")"); } - StringBuilder loggedMessage = new StringBuilder(previousMethodName + "() " + message + " " + (echo ? "->" : "<-") + " "); + StringBuilder loggedMessage = new(previousMethodName + "() " + message + " " + (echo ? "->" : "<-") + " "); if ((chatGroupID != 0) && (chatID != 0)) { loggedMessage.Append(chatGroupID + "-" + chatID); @@ -147,7 +147,7 @@ namespace ArchiSteamFarm.NLog { loggedMessage.Append(steamID); } - LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Trace, Logger.Name, loggedMessage.ToString()); + LogEventInfo logEventInfo = new(LogLevel.Trace, Logger.Name, loggedMessage.ToString()); logEventInfo.Properties["Echo"] = echo; logEventInfo.Properties["Message"] = message; logEventInfo.Properties["ChatGroupID"] = chatGroupID; @@ -218,7 +218,7 @@ namespace ArchiSteamFarm.NLog { string loggedMessage = previousMethodName + "() " + steamID.AccountType + " " + steamID64; - LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Trace, Logger.Name, loggedMessage); + LogEventInfo logEventInfo = new(LogLevel.Trace, Logger.Name, loggedMessage); logEventInfo.Properties["AccountType"] = steamID.AccountType; logEventInfo.Properties["SteamID"] = steamID64; diff --git a/ArchiSteamFarm/NLog/HistoryTarget.cs b/ArchiSteamFarm/NLog/HistoryTarget.cs index a26a80094..cc5ae1f8b 100644 --- a/ArchiSteamFarm/NLog/HistoryTarget.cs +++ b/ArchiSteamFarm/NLog/HistoryTarget.cs @@ -35,7 +35,7 @@ namespace ArchiSteamFarm.NLog { internal IEnumerable ArchivedMessages => HistoryQueue; - private readonly FixedSizeConcurrentQueue HistoryQueue = new FixedSizeConcurrentQueue(DefaultMaxCount); + private readonly FixedSizeConcurrentQueue HistoryQueue = new(DefaultMaxCount); // This is NLog config property, it must have public get() and set() capabilities [PublicAPI] diff --git a/ArchiSteamFarm/NLog/Logging.cs b/ArchiSteamFarm/NLog/Logging.cs index 1e57ecb33..7f13c833d 100644 --- a/ArchiSteamFarm/NLog/Logging.cs +++ b/ArchiSteamFarm/NLog/Logging.cs @@ -42,8 +42,8 @@ namespace ArchiSteamFarm.NLog { private const string GeneralLayout = @"${date:format=yyyy-MM-dd HH\:mm\:ss}|${processname}-${processid}|${level:uppercase=true}|" + LayoutMessage; private const string LayoutMessage = @"${logger}|${message}${onexception:inner= ${exception:format=toString,Data}}"; - private static readonly ConcurrentHashSet ConsoleLoggingRules = new ConcurrentHashSet(); - private static readonly SemaphoreSlim ConsoleSemaphore = new SemaphoreSlim(1, 1); + private static readonly ConcurrentHashSet ConsoleLoggingRules = new(); + private static readonly SemaphoreSlim ConsoleSemaphore = new(1, 1); private static bool IsUsingCustomConfiguration; private static bool IsWaitingForUserInput; @@ -156,9 +156,9 @@ namespace ArchiSteamFarm.NLog { } ConfigurationItemFactory.Default.ParseMessageTemplates = false; - LoggingConfiguration config = new LoggingConfiguration(); + LoggingConfiguration config = new(); - ColoredConsoleTarget coloredConsoleTarget = new ColoredConsoleTarget("ColoredConsole") { Layout = GeneralLayout }; + ColoredConsoleTarget coloredConsoleTarget = new("ColoredConsole") { Layout = GeneralLayout }; config.AddTarget(coloredConsoleTarget); config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, coloredConsoleTarget)); @@ -172,7 +172,7 @@ namespace ArchiSteamFarm.NLog { ASF.ArchiLogger.LogGenericException(e); } - FileTarget fileTarget = new FileTarget("File") { + FileTarget fileTarget = new("File") { ArchiveFileName = Path.Combine("${currentdir}", SharedInfo.ArchivalLogsDirectory, SharedInfo.ArchivalLogFile), ArchiveNumbering = ArchiveNumberingMode.Rolling, ArchiveOldFileOnStartup = true, @@ -242,7 +242,7 @@ namespace ArchiSteamFarm.NLog { } private static string? ConsoleReadLine() { - using CancellationTokenSource cts = new CancellationTokenSource(); + using CancellationTokenSource cts = new(); try { CancellationToken token = cts.Token; @@ -256,14 +256,14 @@ namespace ArchiSteamFarm.NLog { } private static string ConsoleReadLineMasked(char mask = '*') { - using CancellationTokenSource cts = new CancellationTokenSource(); + using CancellationTokenSource cts = new(); try { CancellationToken token = cts.Token; Utilities.InBackground(() => BeepUntilCanceled(token)); - StringBuilder result = new StringBuilder(); + StringBuilder result = new(); ConsoleKeyInfo keyInfo; diff --git a/ArchiSteamFarm/NLog/SteamTarget.cs b/ArchiSteamFarm/NLog/SteamTarget.cs index 7852a23ba..0cd5152ae 100644 --- a/ArchiSteamFarm/NLog/SteamTarget.cs +++ b/ArchiSteamFarm/NLog/SteamTarget.cs @@ -62,7 +62,7 @@ namespace ArchiSteamFarm.NLog { base.Write(logEvent); - if ((SteamID == 0) || (Bot.Bots == null) || (Bot.Bots.IsEmpty)) { + if ((SteamID == 0) || (Bot.Bots == null) || Bot.Bots.IsEmpty) { return; } diff --git a/ArchiSteamFarm/OS.cs b/ArchiSteamFarm/OS.cs index 52f1b76d0..cc3465449 100644 --- a/ArchiSteamFarm/OS.cs +++ b/ArchiSteamFarm/OS.cs @@ -21,6 +21,7 @@ using System; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -100,12 +101,12 @@ namespace ArchiSteamFarm { // The only purpose of using hashingAlgorithm here is to cut on a potential size of the resource name - paths can be really long, and we almost certainly have some upper limit on the resource name we can allocate // At the same time it'd be the best if we avoided all special characters, such as '/' found e.g. in base64, as we can't be sure that it's not a prohibited character in regards to native OS implementation - // Because of that, MD5 is sufficient for our case, as it generates alphanumeric characters only, and is barely 128-bit long. We don't need any kind of complex cryptography or collision detection here, any hashing algorithm will do, and the shorter the better - using (MD5 hashingAlgorithm = MD5.Create()) { + // Because of that, SHA256 is sufficient for our case, as it generates alphanumeric characters only, and is barely 256-bit long. We don't need any kind of complex cryptography or collision detection here, any hashing algorithm will do, and the shorter the better + using (SHA256CryptoServiceProvider hashingAlgorithm = new()) { uniqueName = "Global\\" + GetOsResourceName(nameof(SingleInstance)) + "-" + BitConverter.ToString(hashingAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(Directory.GetCurrentDirectory()))).Replace("-", ""); } - Mutex singleInstance = new Mutex(true, uniqueName, out bool result); + Mutex singleInstance = new(true, uniqueName, out bool result); if (!result) { singleInstance.Dispose(); @@ -128,14 +129,14 @@ namespace ArchiSteamFarm { } if (!File.Exists(path) && !Directory.Exists(path)) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, "!" + nameof(path))); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, "!" + nameof(path))); return; } // Chmod() returns 0 on success, -1 on failure if (NativeMethods.Chmod(path, (int) permission) != 0) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, Marshal.GetLastWin32Error())); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, Marshal.GetLastWin32Error())); } } @@ -190,7 +191,7 @@ namespace ArchiSteamFarm { // SetThreadExecutionState() returns NULL on failure, which is mapped to 0 (EExecutionState.None) in our case if (result == NativeMethods.EExecutionState.None) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, result)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, result)); } } @@ -214,7 +215,7 @@ namespace ArchiSteamFarm { internal const uint EnableQuickEditMode = 0x0040; internal const sbyte StandardInputHandle = -10; - [DllImport("libc", EntryPoint = "chmod", SetLastError = true)] + [DllImport("libc", CharSet = CharSet.Unicode, EntryPoint = "chmod", SetLastError = true)] internal static extern int Chmod(string path, int mode); [DllImport("kernel32.dll")] diff --git a/ArchiSteamFarm/Plugins/PluginsCore.cs b/ArchiSteamFarm/Plugins/PluginsCore.cs index b36a9af94..7c17a51aa 100644 --- a/ArchiSteamFarm/Plugins/PluginsCore.cs +++ b/ArchiSteamFarm/Plugins/PluginsCore.cs @@ -101,7 +101,7 @@ namespace ArchiSteamFarm.Plugins { ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.Initializing, nameof(Plugins))); - ConventionBuilder conventions = new ConventionBuilder(); + ConventionBuilder conventions = new(); conventions.ForTypesDerivedFrom().Export(); ContainerConfiguration configuration = new ContainerConfiguration().WithAssemblies(assemblies, conventions); @@ -122,7 +122,7 @@ namespace ArchiSteamFarm.Plugins { return true; } - HashSet invalidPlugins = new HashSet(); + HashSet invalidPlugins = new(); foreach (IPlugin plugin in activePlugins) { try { @@ -460,7 +460,7 @@ namespace ArchiSteamFarm.Plugins { } internal static async Task OnBotTradeOffer(Bot bot, Steam.TradeOffer tradeOffer) { - if ((bot == null)) { + if (bot == null) { throw new ArgumentNullException(nameof(bot)); } @@ -506,7 +506,7 @@ namespace ArchiSteamFarm.Plugins { } internal static async Task OnBotUserNotifications(Bot bot, IReadOnlyCollection newNotifications) { - if ((bot == null)) { + if (bot == null) { throw new ArgumentNullException(nameof(bot)); } @@ -574,7 +574,7 @@ namespace ArchiSteamFarm.Plugins { return null; } - HashSet assemblies = new HashSet(); + HashSet assemblies = new(); try { foreach (string assemblyPath in Directory.EnumerateFiles(path, "*.dll", SearchOption.AllDirectories)) { diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index 4f2570ff6..f4aa8de37 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -43,7 +43,7 @@ namespace ArchiSteamFarm { internal static bool RestartAllowed { get; private set; } = true; internal static bool ShutdownSequenceInitialized { get; private set; } - private static readonly TaskCompletionSource ShutdownResetEvent = new TaskCompletionSource(); + private static readonly TaskCompletionSource ShutdownResetEvent = new(); private static bool SystemRequired; @@ -67,7 +67,7 @@ namespace ArchiSteamFarm { throw new ArgumentNullException(nameof(executableName)); } - IEnumerable arguments = Environment.GetCommandLineArgs().Skip(executableName.Equals(SharedInfo.AssemblyName) ? 1 : 0); + IEnumerable arguments = Environment.GetCommandLineArgs().Skip(executableName.Equals(SharedInfo.AssemblyName, StringComparison.Ordinal) ? 1 : 0); try { Process.Start(OS.ProcessFileName, string.Join(" ", arguments)); @@ -139,7 +139,7 @@ namespace ArchiSteamFarm { } if (ASF.GlobalConfig == null) { - throw new ArgumentNullException(nameof(ASF.GlobalConfig)); + throw new InvalidOperationException(nameof(ASF.GlobalConfig)); } // Parse post-init args @@ -204,7 +204,7 @@ namespace ArchiSteamFarm { globalConfig = await GlobalConfig.Load(globalConfigFile).ConfigureAwait(false); if (globalConfig == null) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorGlobalConfigNotLoaded, globalConfigFile)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorGlobalConfigNotLoaded, globalConfigFile)); await Task.Delay(5 * 1000).ConfigureAwait(false); await Exit(1).ConfigureAwait(false); @@ -281,7 +281,7 @@ namespace ArchiSteamFarm { if (currentStringObjects.Count < defaultStringObjects.Count) { float translationCompleteness = currentStringObjects.Count / (float) defaultStringObjects.Count; - ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.TranslationIncomplete, CultureInfo.CurrentUICulture.Name, translationCompleteness.ToString("P1"))); + ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.TranslationIncomplete, CultureInfo.CurrentUICulture.Name, translationCompleteness.ToString("P1", CultureInfo.CurrentCulture))); } return true; @@ -304,7 +304,7 @@ namespace ArchiSteamFarm { GlobalDatabase? globalDatabase = await GlobalDatabase.CreateOrLoad(globalDatabaseFile).ConfigureAwait(false); if (globalDatabase == null) { - ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorDatabaseInvalid, globalDatabaseFile)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorDatabaseInvalid, globalDatabaseFile)); await Task.Delay(5 * 1000).ConfigureAwait(false); await Exit(1).ConfigureAwait(false); @@ -390,7 +390,7 @@ namespace ArchiSteamFarm { } if (e.ExceptionObject == null) { - throw new ArgumentNullException(nameof(e.ExceptionObject)); + throw new ArgumentNullException(nameof(e)); } await ASF.ArchiLogger.LogFatalException((Exception) e.ExceptionObject).ConfigureAwait(false); @@ -403,7 +403,7 @@ namespace ArchiSteamFarm { } if (e.Exception == null) { - throw new ArgumentNullException(nameof(e.Exception)); + throw new ArgumentNullException(nameof(e)); } await ASF.ArchiLogger.LogFatalException(e.Exception).ConfigureAwait(false); @@ -487,11 +487,11 @@ namespace ArchiSteamFarm { foreach (string arg in args) { switch (arg) { - case "--network-group" when !networkGroupNext: + case "--network-group" when !networkGroupNext && !pathNext: networkGroupNext = true; break; - case "--path" when !pathNext: + case "--path" when !networkGroupNext && !pathNext: pathNext = true; break; @@ -502,10 +502,17 @@ namespace ArchiSteamFarm { } else if (pathNext) { pathNext = false; HandlePathArgument(arg); - } else if ((arg.Length > 16) && arg.StartsWith("--network-group=", StringComparison.Ordinal)) { - HandleNetworkGroupArgument(arg.Substring(16)); - } else if ((arg.Length > 7) && arg.StartsWith("--path=", StringComparison.Ordinal)) { - HandlePathArgument(arg.Substring(7)); + } else { + switch (arg.Length) { + case > 16 when arg.StartsWith("--network-group=", StringComparison.Ordinal): + HandleNetworkGroupArgument(arg.Substring(16)); + + break; + case > 7 when arg.StartsWith("--path=", StringComparison.Ordinal): + HandlePathArgument(arg.Substring(7)); + + break; + } } break; diff --git a/ArchiSteamFarm/RuntimeCompatibility.cs b/ArchiSteamFarm/RuntimeCompatibility.cs index 7bb480a20..fffdd15bd 100644 --- a/ArchiSteamFarm/RuntimeCompatibility.cs +++ b/ArchiSteamFarm/RuntimeCompatibility.cs @@ -137,14 +137,16 @@ namespace ArchiSteamFarm { } #if NETFRAMEWORK - internal static Task ComputeHashAsync(this HashAlgorithm hashAlgorithm, Stream inputStream) => Task.FromResult(hashAlgorithm.ComputeHash(inputStream)); + public static Task ComputeHashAsync(this HashAlgorithm hashAlgorithm, Stream inputStream) => Task.FromResult(hashAlgorithm.ComputeHash(inputStream)); - internal static IWebHostBuilder ConfigureWebHostDefaults(this IWebHostBuilder builder, Action configure) { + public static IWebHostBuilder ConfigureWebHostDefaults(this IWebHostBuilder builder, Action configure) { configure(builder); return builder; } + public static bool Contains(this string input, string value, StringComparison comparisonType) => input.IndexOf(value, comparisonType) >= 0; + // ReSharper disable once UseDeconstructionOnParameter - we actually implement deconstruction here public static void Deconstruct(this KeyValuePair kv, out TKey key, out TValue value) { key = kv.Key; @@ -154,7 +156,7 @@ namespace ArchiSteamFarm { public static ValueTask DisposeAsync(this IDisposable disposable) { disposable.Dispose(); - return default; + return default(ValueTask); } public static async Task ReceiveAsync(this WebSocket webSocket, byte[] buffer, CancellationToken cancellationToken) => await webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationToken).ConfigureAwait(false); diff --git a/ArchiSteamFarm/Statistics.cs b/ArchiSteamFarm/Statistics.cs index e79779cb6..92d9f1775 100644 --- a/ArchiSteamFarm/Statistics.cs +++ b/ArchiSteamFarm/Statistics.cs @@ -23,6 +23,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -50,9 +51,9 @@ namespace ArchiSteamFarm { ); private readonly Bot Bot; - private readonly SemaphoreSlim MatchActivelySemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim MatchActivelySemaphore = new(1, 1); private readonly Timer MatchActivelyTimer; - private readonly SemaphoreSlim RequestsSemaphore = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim RequestsSemaphore = new(1, 1); private DateTime LastAnnouncementCheck; private DateTime LastHeartBeat; @@ -63,7 +64,7 @@ namespace ArchiSteamFarm { Bot = bot ?? throw new ArgumentNullException(nameof(bot)); MatchActivelyTimer = new Timer( - async e => await MatchActively().ConfigureAwait(false), + MatchActively, null, TimeSpan.FromHours(1) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots?.Count ?? 0), // Delay TimeSpan.FromHours(8) // Period @@ -97,9 +98,9 @@ namespace ArchiSteamFarm { const string request = URL + "/Api/HeartBeat"; - Dictionary data = new Dictionary(2, StringComparer.Ordinal) { - { "Guid", (ASF.GlobalDatabase?.Guid ?? Guid.NewGuid()).ToString("N") }, - { "SteamID", Bot.SteamID.ToString() } + Dictionary data = new(2, StringComparer.Ordinal) { + { "Guid", (ASF.GlobalDatabase?.Identifier ?? Guid.NewGuid()).ToString("N") }, + { "SteamID", Bot.SteamID.ToString(CultureInfo.InvariantCulture) } }; WebBrowser.BasicResponse? response = await Bot.ArchiWebHandler.WebBrowser.UrlPost(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false); @@ -123,7 +124,7 @@ namespace ArchiSteamFarm { internal async Task OnLoggedOn() { if (!await Bot.ArchiWebHandler.JoinGroup(SharedInfo.ASFGroupSteamID).ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(ArchiWebHandler.JoinGroup))); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(ArchiWebHandler.JoinGroup))); } } @@ -206,15 +207,15 @@ namespace ArchiSteamFarm { const string request = URL + "/Api/Announce"; - Dictionary data = new Dictionary(9, StringComparer.Ordinal) { + Dictionary data = new(9, StringComparer.Ordinal) { { "AvatarHash", avatarHash ?? "" }, - { "GamesCount", inventory.Select(item => item.RealAppID).Distinct().Count().ToString() }, - { "Guid", (ASF.GlobalDatabase?.Guid ?? Guid.NewGuid()).ToString("N") }, - { "ItemsCount", inventory.Count.ToString() }, + { "GamesCount", inventory.Select(item => item.RealAppID).Distinct().Count().ToString(CultureInfo.InvariantCulture) }, + { "Guid", (ASF.GlobalDatabase?.Identifier ?? Guid.NewGuid()).ToString("N") }, + { "ItemsCount", inventory.Count.ToString(CultureInfo.InvariantCulture) }, { "MatchableTypes", JsonConvert.SerializeObject(acceptedMatchableTypes) }, { "MatchEverything", Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything) ? "1" : "0" }, { "Nickname", nickname ?? "" }, - { "SteamID", Bot.SteamID.ToString() }, + { "SteamID", Bot.SteamID.ToString(CultureInfo.InvariantCulture) }, { "TradeToken", tradeToken! } }; @@ -257,7 +258,7 @@ namespace ArchiSteamFarm { bool? hasPublicInventory = await Bot.HasPublicInventory().ConfigureAwait(false); if (hasPublicInventory != true) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.HasPublicInventory) + ": " + (hasPublicInventory?.ToString() ?? "null"))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.HasPublicInventory) + ": " + (hasPublicInventory?.ToString() ?? "null"))); return hasPublicInventory; } @@ -268,21 +269,21 @@ namespace ArchiSteamFarm { private async Task IsEligibleForMatching() { // Bot must have ASF 2FA if (!Bot.HasMobileAuthenticator) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.HasMobileAuthenticator) + ": " + Bot.HasMobileAuthenticator)); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.HasMobileAuthenticator) + ": " + Bot.HasMobileAuthenticator)); return false; } // Bot must have STM enable in TradingPreferences if (!Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher)) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.BotConfig.TradingPreferences) + ": " + Bot.BotConfig.TradingPreferences)); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.BotConfig.TradingPreferences) + ": " + Bot.BotConfig.TradingPreferences)); return false; } // Bot must have at least one accepted matchable type set if ((Bot.BotConfig.MatchableTypes.Count == 0) || Bot.BotConfig.MatchableTypes.All(type => !AcceptedMatchableTypes.Contains(type))) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.BotConfig.MatchableTypes) + ": " + string.Join(", ", Bot.BotConfig.MatchableTypes))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.BotConfig.MatchableTypes) + ": " + string.Join(", ", Bot.BotConfig.MatchableTypes))); return false; } @@ -291,7 +292,7 @@ namespace ArchiSteamFarm { bool? hasValidApiKey = await Bot.ArchiWebHandler.HasValidApiKey().ConfigureAwait(false); if (hasValidApiKey != true) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.ArchiWebHandler.HasValidApiKey) + ": " + (hasValidApiKey?.ToString() ?? "null"))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(Bot.ArchiWebHandler.HasValidApiKey) + ": " + (hasValidApiKey?.ToString() ?? "null"))); return hasValidApiKey; } @@ -299,7 +300,7 @@ namespace ArchiSteamFarm { return true; } - private async Task MatchActively() { + private async void MatchActively(object? state) { if (!Bot.IsConnectedAndLoggedOn || Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything) || !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchActively) || (await IsEligibleForMatching().ConfigureAwait(false) != true)) { Bot.ArchiLogger.LogGenericTrace(Strings.ErrorAborted); @@ -323,7 +324,7 @@ namespace ArchiSteamFarm { try { Bot.ArchiLogger.LogGenericTrace(Strings.Starting); - Dictionary? GivenAssetIDs, ISet? ReceivedAssetIDs)> triedSteamIDs = new Dictionary? GivenAssetIDs, ISet? ReceivedAssetIDs)>(); + Dictionary? GivenAssetIDs, ISet? ReceivedAssetIDs)> triedSteamIDs = new(); bool shouldContinueMatching = true; bool tradedSomething = false; @@ -341,9 +342,9 @@ namespace ArchiSteamFarm { } using (await Bot.Actions.GetTradingLock().ConfigureAwait(false)) { - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ActivelyMatchingItems, i)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.ActivelyMatchingItems, i)); (shouldContinueMatching, tradedSomething) = await MatchActivelyRound(acceptedMatchableTypes, triedSteamIDs).ConfigureAwait(false); - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.DoneActivelyMatchingItems, i)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.DoneActivelyMatchingItems, i)); } } @@ -354,8 +355,12 @@ namespace ArchiSteamFarm { } private async Task<(bool ShouldContinueMatching, bool TradedSomething)> MatchActivelyRound(IReadOnlyCollection acceptedMatchableTypes, IDictionary? GivenAssetIDs, ISet? ReceivedAssetIDs)> triedSteamIDs) { - if ((acceptedMatchableTypes == null) || (acceptedMatchableTypes.Count == 0) || (triedSteamIDs == null)) { - throw new ArgumentNullException(nameof(acceptedMatchableTypes) + " || " + nameof(triedSteamIDs)); + if ((acceptedMatchableTypes == null) || (acceptedMatchableTypes.Count == 0)) { + throw new ArgumentNullException(nameof(acceptedMatchableTypes)); + } + + if (triedSteamIDs == null) { + throw new ArgumentNullException(nameof(triedSteamIDs)); } HashSet ourInventory; @@ -373,7 +378,7 @@ namespace ArchiSteamFarm { } if (ourInventory.Count == 0) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(ourInventory))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(ourInventory))); return (false, false); } @@ -382,7 +387,7 @@ namespace ArchiSteamFarm { if (Trading.IsEmptyForMatching(ourFullState, ourTradableState)) { // User doesn't have any more dupes in the inventory - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(ourFullState) + " || " + nameof(ourTradableState))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(ourFullState) + " || " + nameof(ourTradableState))); return (false, false); } @@ -390,7 +395,7 @@ namespace ArchiSteamFarm { ImmutableHashSet? listedUsers = await GetListedUsers().ConfigureAwait(false); if ((listedUsers == null) || (listedUsers.Count == 0)) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(listedUsers))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(listedUsers))); return (false, false); } @@ -398,7 +403,7 @@ namespace ArchiSteamFarm { byte maxTradeHoldDuration = ASF.GlobalConfig?.MaxTradeHoldDuration ?? GlobalConfig.DefaultMaxTradeHoldDuration; byte totalMatches = 0; - HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> skippedSetsThisRound = new HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)>(); + HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> skippedSetsThisRound = new(); foreach (ListedUser listedUser in listedUsers.Where(listedUser => (listedUser.SteamID != Bot.SteamID) && acceptedMatchableTypes.Any(listedUser.MatchableTypes.Contains) && (!triedSteamIDs.TryGetValue(listedUser.SteamID, out (byte Tries, ISet? GivenAssetIDs, ISet? ReceivedAssetIDs) attempt) || (attempt.Tries < byte.MaxValue)) && !Bot.IsBlacklistedFromTrades(listedUser.SteamID)).OrderBy(listedUser => triedSteamIDs.TryGetValue(listedUser.SteamID, out (byte Tries, ISet? GivenAssetIDs, ISet? ReceivedAssetIDs) attempt) ? attempt.Tries : 0).ThenByDescending(listedUser => listedUser.MatchEverything).ThenByDescending(listedUser => listedUser.MatchEverything || (listedUser.ItemsCount < MaxItemsForFairBots)).ThenByDescending(listedUser => listedUser.Score)) { HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> wantedSets = ourTradableState.Keys.Where(set => !skippedSetsThisRound.Contains(set) && listedUser.MatchableTypes.Contains(set.Type)).ToHashSet(); @@ -415,18 +420,15 @@ namespace ArchiSteamFarm { byte? holdDuration = await Bot.ArchiWebHandler.GetTradeHoldDurationForUser(listedUser.SteamID, listedUser.TradeToken).ConfigureAwait(false); - if (!holdDuration.HasValue) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(holdDuration))); + switch (holdDuration) { + case null: + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(holdDuration))); - continue; - } - - if (holdDuration.Value > 0) { - if (holdDuration.Value > maxTradeHoldDuration) { + continue; + case > 0 when holdDuration.Value > maxTradeHoldDuration: Bot.ArchiLogger.LogGenericTrace(holdDuration.Value + " > " + maxTradeHoldDuration); continue; - } } HashSet theirInventory; @@ -444,24 +446,24 @@ namespace ArchiSteamFarm { } if (theirInventory.Count == 0) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(theirInventory))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(theirInventory))); continue; } - HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> skippedSetsThisUser = new HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)>(); + HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> skippedSetsThisUser = new(); Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> theirTradableState = Trading.GetTradableInventoryState(theirInventory); - Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> inventoryStateChanges = new Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary>(); + Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> inventoryStateChanges = new(); for (byte i = 0; i < Trading.MaxTradesPerAccount; i++) { byte itemsInTrade = 0; - HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> skippedSetsThisTrade = new HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)>(); + HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> skippedSetsThisTrade = new(); - Dictionary classIDsToGive = new Dictionary(); - Dictionary classIDsToReceive = new Dictionary(); - Dictionary fairClassIDsToGive = new Dictionary(); - Dictionary fairClassIDsToReceive = new Dictionary(); + Dictionary classIDsToGive = new(); + Dictionary classIDsToReceive = new(); + Dictionary fairClassIDsToGive = new(); + Dictionary fairClassIDsToReceive = new(); foreach (((uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) set, Dictionary ourFullItems) in ourFullState.Where(set => !skippedSetsThisUser.Contains(set.Key) && listedUser.MatchableTypes.Contains(set.Key.Type) && set.Value.Values.Any(count => count > 1))) { if (!ourTradableState.TryGetValue(set, out Dictionary? ourTradableItems) || (ourTradableItems.Count == 0)) { @@ -473,8 +475,8 @@ namespace ArchiSteamFarm { } // Those 2 collections are on user-basis since we can't be sure that the trade passes through (and therefore we need to keep original state in case of failure) - Dictionary ourFullSet = new Dictionary(ourFullItems); - Dictionary ourTradableSet = new Dictionary(ourTradableItems); + Dictionary ourFullSet = new(ourFullItems); + Dictionary ourTradableSet = new(ourTradableItems); // We also have to take into account changes that happened in previous trades with this user, so this block will adapt to that if (inventoryStateChanges.TryGetValue(set, out Dictionary? pastChanges) && (pastChanges.Count > 0)) { @@ -607,7 +609,7 @@ namespace ArchiSteamFarm { } if (skippedSetsThisTrade.Count == 0) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(skippedSetsThisTrade))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(skippedSetsThisTrade))); break; } @@ -618,7 +620,7 @@ namespace ArchiSteamFarm { if ((itemsToGive.Count != itemsToReceive.Count) || !Trading.IsFairExchange(itemsToGive, itemsToReceive)) { // Failsafe - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, Strings.ErrorAborted)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, Strings.ErrorAborted)); return (false, skippedSetsThisRound.Count > 0); } @@ -644,7 +646,7 @@ namespace ArchiSteamFarm { (bool success, HashSet? mobileTradeOfferIDs) = await Bot.ArchiWebHandler.SendTradeOffer(listedUser.SteamID, itemsToGive, itemsToReceive, listedUser.TradeToken, true).ConfigureAwait(false); - if ((mobileTradeOfferIDs != null) && (mobileTradeOfferIDs.Count > 0) && Bot.HasMobileAuthenticator) { + if ((mobileTradeOfferIDs?.Count > 0) && Bot.HasMobileAuthenticator) { (bool twoFactorSuccess, _) = await Bot.Actions.HandleTwoFactorAuthenticationConfirmations(true, MobileAuthenticator.Confirmation.EType.Trade, mobileTradeOfferIDs, true).ConfigureAwait(false); if (!twoFactorSuccess) { @@ -692,7 +694,7 @@ namespace ArchiSteamFarm { ourTradableState.TrimExcess(); } - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ActivelyMatchingItemsRound, skippedSetsThisRound.Count)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.ActivelyMatchingItemsRound, skippedSetsThisRound.Count)); // Keep matching when we either traded something this round (so it makes sense for a refresh) or if we didn't try all available bots yet (so it makes sense to keep going) return ((totalMatches > 0) && ((skippedSetsThisRound.Count > 0) || triedSteamIDs.Values.All(data => data.Tries < 2)), skippedSetsThisRound.Count > 0); @@ -705,7 +707,7 @@ namespace ArchiSteamFarm { internal readonly ushort ItemsCount; #pragma warning restore 649 - internal readonly HashSet MatchableTypes = new HashSet(); + internal readonly HashSet MatchableTypes = new(); #pragma warning disable 649 [JsonProperty(PropertyName = "steam_id", Required = Required.Always)] @@ -740,7 +742,7 @@ namespace ArchiSteamFarm { break; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(value), value)); return; } @@ -762,7 +764,7 @@ namespace ArchiSteamFarm { break; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(value), value)); return; } @@ -784,7 +786,7 @@ namespace ArchiSteamFarm { break; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(value), value)); return; } @@ -806,7 +808,7 @@ namespace ArchiSteamFarm { break; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(value), value)); return; } @@ -828,7 +830,7 @@ namespace ArchiSteamFarm { break; default: - ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(value), value)); + ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(value), value)); return; } diff --git a/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs b/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs index b6ed34570..16ddbe335 100644 --- a/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs +++ b/ArchiSteamFarm/SteamKit2/InMemoryServerListProvider.cs @@ -30,7 +30,7 @@ using SteamKit2.Discovery; namespace ArchiSteamFarm.SteamKit2 { internal sealed class InMemoryServerListProvider : IServerListProvider { [JsonProperty(Required = Required.DisallowNull)] - private readonly ConcurrentHashSet ServerRecords = new ConcurrentHashSet(); + private readonly ConcurrentHashSet ServerRecords = new(); public Task> FetchServerListAsync() => Task.FromResult(ServerRecords.Where(server => !string.IsNullOrEmpty(server.Host) && (server.Port > 0) && (server.ProtocolTypes > 0)).Select(server => ServerRecord.CreateServer(server.Host!, server.Port, server.ProtocolTypes))); diff --git a/ArchiSteamFarm/SteamPICSChanges.cs b/ArchiSteamFarm/SteamPICSChanges.cs index d8a80bad8..f3c86b5eb 100644 --- a/ArchiSteamFarm/SteamPICSChanges.cs +++ b/ArchiSteamFarm/SteamPICSChanges.cs @@ -22,7 +22,6 @@ using System; using System.Linq; using System.Threading; -using System.Threading.Tasks; using ArchiSteamFarm.Localization; using ArchiSteamFarm.Plugins; using SteamKit2; @@ -31,8 +30,8 @@ namespace ArchiSteamFarm { internal static class SteamPICSChanges { private const byte RefreshTimerInMinutes = 5; - private static readonly SemaphoreSlim RefreshSemaphore = new SemaphoreSlim(1, 1); - private static readonly Timer RefreshTimer = new Timer(async e => await RefreshChanges().ConfigureAwait(false)); + private static readonly SemaphoreSlim RefreshSemaphore = new(1, 1); + private static readonly Timer RefreshTimer = new(RefreshChanges); private static uint LastChangeNumber; private static bool TimerAlreadySet; @@ -54,7 +53,7 @@ namespace ArchiSteamFarm { } } - private static async Task RefreshChanges() { + private static async void RefreshChanges(object? state) { if (!await RefreshSemaphore.WaitAsync(0).ConfigureAwait(false)) { return; } diff --git a/ArchiSteamFarm/SteamSaleEvent.cs b/ArchiSteamFarm/SteamSaleEvent.cs index 575f71eca..98382aad1 100644 --- a/ArchiSteamFarm/SteamSaleEvent.cs +++ b/ArchiSteamFarm/SteamSaleEvent.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Immutable; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using AngleSharp.Dom; @@ -37,7 +38,7 @@ namespace ArchiSteamFarm { Bot = bot ?? throw new ArgumentNullException(nameof(bot)); SaleEventTimer = new Timer( - async e => await ExploreDiscoveryQueue().ConfigureAwait(false), + ExploreDiscoveryQueue, null, TimeSpan.FromHours(1.1) + TimeSpan.FromSeconds(ASF.LoadBalancingDelay * Bot.Bots?.Count ?? 0), // Delay TimeSpan.FromHours(8.1) // Period @@ -46,23 +47,23 @@ namespace ArchiSteamFarm { public async ValueTask DisposeAsync() => await SaleEventTimer.DisposeAsync().ConfigureAwait(false); - private async Task ExploreDiscoveryQueue() { + private async void ExploreDiscoveryQueue(object? state) { if (!Bot.IsConnectedAndLoggedOn) { return; } Bot.ArchiLogger.LogGenericTrace(Strings.Starting); - for (byte i = 0; (i < MaxSingleQueuesDaily) && (await IsDiscoveryQueueAvailable().ConfigureAwait(false)).GetValueOrDefault(); i++) { + for (byte i = 0; (i < MaxSingleQueuesDaily) && Bot.IsConnectedAndLoggedOn && (await IsDiscoveryQueueAvailable().ConfigureAwait(false)).GetValueOrDefault(); i++) { ImmutableHashSet? queue = await Bot.ArchiWebHandler.GenerateNewDiscoveryQueue().ConfigureAwait(false); if ((queue == null) || (queue.Count == 0)) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(queue))); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(queue))); break; } - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ClearingDiscoveryQueue, i)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.ClearingDiscoveryQueue, i)); // We could in theory do this in parallel, but who knows what would happen... foreach (uint queuedAppID in queue) { @@ -75,7 +76,7 @@ namespace ArchiSteamFarm { return; } - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.DoneClearingDiscoveryQueue, i)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.DoneClearingDiscoveryQueue, i)); } Bot.ArchiLogger.LogGenericTrace(Strings.Done); diff --git a/ArchiSteamFarm/Trading.cs b/ArchiSteamFarm/Trading.cs index 7e136c0e4..69c99edc5 100644 --- a/ArchiSteamFarm/Trading.cs +++ b/ArchiSteamFarm/Trading.cs @@ -22,6 +22,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -39,8 +41,8 @@ namespace ArchiSteamFarm { internal const byte MaxTradesPerAccount = 5; // This is limit introduced by Valve private readonly Bot Bot; - private readonly ConcurrentHashSet HandledTradeOfferIDs = new ConcurrentHashSet(); - private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1, 1); + private readonly ConcurrentHashSet HandledTradeOfferIDs = new(); + private readonly SemaphoreSlim TradesSemaphore = new(1, 1); private bool ParsingScheduled; @@ -61,18 +63,22 @@ namespace ArchiSteamFarm { [PublicAPI] public static bool IsFairExchange(IReadOnlyCollection itemsToGive, IReadOnlyCollection itemsToReceive) { - if ((itemsToGive == null) || (itemsToGive.Count == 0) || (itemsToReceive == null) || (itemsToReceive.Count == 0)) { - throw new ArgumentNullException(nameof(itemsToGive) + " || " + nameof(itemsToReceive)); + if ((itemsToGive == null) || (itemsToGive.Count == 0)) { + throw new ArgumentNullException(nameof(itemsToGive)); } - Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), uint> itemsToGiveAmounts = new Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), uint>(); + if ((itemsToReceive == null) || (itemsToReceive.Count == 0)) { + throw new ArgumentNullException(nameof(itemsToReceive)); + } + + Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), uint> itemsToGiveAmounts = new(); foreach (Steam.Asset item in itemsToGive) { (uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); itemsToGiveAmounts[key] = itemsToGiveAmounts.TryGetValue(key, out uint amount) ? amount + item.Amount : item.Amount; } - Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), uint> itemsToReceiveAmounts = new Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), uint>(); + Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), uint> itemsToReceiveAmounts = new(); foreach (Steam.Asset item in itemsToReceive) { (uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); @@ -90,9 +96,17 @@ namespace ArchiSteamFarm { } [PublicAPI] - public static bool IsTradeNeutralOrBetter(HashSet inventory, ISet itemsToGive, ISet itemsToReceive) { - if ((inventory == null) || (inventory.Count == 0) || (itemsToGive == null) || (itemsToGive.Count == 0) || (itemsToReceive == null) || (itemsToReceive.Count == 0)) { - throw new ArgumentNullException(nameof(inventory) + " || " + nameof(itemsToGive) + " || " + nameof(itemsToReceive)); + public static bool IsTradeNeutralOrBetter(HashSet inventory, IReadOnlyCollection itemsToGive, IReadOnlyCollection itemsToReceive) { + if ((inventory == null) || (inventory.Count == 0)) { + throw new ArgumentNullException(nameof(inventory)); + } + + if ((itemsToGive == null) || (itemsToGive.Count == 0)) { + throw new ArgumentNullException(nameof(itemsToGive)); + } + + if ((itemsToReceive == null) || (itemsToReceive.Count == 0)) { + throw new ArgumentNullException(nameof(itemsToReceive)); } // Input of this function is items we're expected to give/receive and our inventory (limited to realAppIDs of itemsToGive/itemsToReceive) @@ -107,7 +121,7 @@ namespace ArchiSteamFarm { // This loop is a bit more complex due to the fact that we might have a mix of the same item splitted into different amounts foreach (Steam.Asset itemToGive in itemsToGive) { uint amountToGive = itemToGive.Amount; - HashSet itemsToRemove = new HashSet(); + HashSet itemsToRemove = new(); // Keep in mind that ClassID is unique only within appID scope - we can do it like this because we're not dealing with non-Steam items here (otherwise we'd need to check appID too) foreach (Steam.Asset item in inventory.Where(item => item.ClassID == itemToGive.ClassID)) { @@ -125,7 +139,7 @@ namespace ArchiSteamFarm { } if (amountToGive > 0) { - throw new ArgumentNullException(nameof(amountToGive)); + throw new InvalidOperationException(nameof(amountToGive)); } if (itemsToRemove.Count > 0) { @@ -196,8 +210,8 @@ namespace ArchiSteamFarm { throw new ArgumentNullException(nameof(inventory)); } - Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> fullState = new Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary>(); - Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> tradableState = new Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary>(); + Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> fullState = new(); + Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> tradableState = new(); foreach (Steam.Asset item in inventory) { (uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); @@ -227,7 +241,7 @@ namespace ArchiSteamFarm { throw new ArgumentNullException(nameof(inventory)); } - Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> tradableState = new Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary>(); + Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> tradableState = new(); foreach (Steam.Asset item in inventory.Where(item => item.Tradable)) { (uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); @@ -242,12 +256,16 @@ namespace ArchiSteamFarm { return tradableState; } - internal static HashSet GetTradableItemsFromInventory(ISet inventory, IDictionary classIDs) { - if ((inventory == null) || (inventory.Count == 0) || (classIDs == null) || (classIDs.Count == 0)) { - throw new ArgumentNullException(nameof(inventory) + " || " + nameof(classIDs)); + internal static HashSet GetTradableItemsFromInventory(IReadOnlyCollection inventory, IDictionary classIDs) { + if ((inventory == null) || (inventory.Count == 0)) { + throw new ArgumentNullException(nameof(inventory)); } - HashSet result = new HashSet(); + if ((classIDs == null) || (classIDs.Count == 0)) { + throw new ArgumentNullException(nameof(classIDs)); + } + + HashSet result = new(); foreach (Steam.Asset item in inventory.Where(item => item.Tradable)) { if (!classIDs.TryGetValue(item.ClassID, out uint amount)) { @@ -271,13 +289,17 @@ namespace ArchiSteamFarm { } internal static bool IsEmptyForMatching(IReadOnlyDictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> fullState, IReadOnlyDictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> tradableState) { - if ((fullState == null) || (tradableState == null)) { - throw new ArgumentNullException(nameof(fullState) + " || " + nameof(tradableState)); + if (fullState == null) { + throw new ArgumentNullException(nameof(fullState)); } - foreach (((uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) set, Dictionary state) in tradableState) { + if (tradableState == null) { + throw new ArgumentNullException(nameof(tradableState)); + } + + foreach (((uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) set, IReadOnlyDictionary state) in tradableState) { if (!fullState.TryGetValue(set, out Dictionary? fullSet) || (fullSet.Count == 0)) { - throw new ArgumentNullException(nameof(fullSet)); + throw new InvalidOperationException(nameof(fullSet)); } if (!IsEmptyForMatching(fullSet, state)) { @@ -290,19 +312,23 @@ namespace ArchiSteamFarm { } internal static bool IsEmptyForMatching(IReadOnlyDictionary fullSet, IReadOnlyDictionary tradableSet) { - if ((fullSet == null) || (tradableSet == null)) { - throw new ArgumentNullException(nameof(fullSet) + " || " + nameof(tradableSet)); + if (fullSet == null) { + throw new ArgumentNullException(nameof(fullSet)); + } + + if (tradableSet == null) { + throw new ArgumentNullException(nameof(tradableSet)); } foreach ((ulong classID, uint amount) in tradableSet) { switch (amount) { case 0: // No tradable items, this should never happen, dictionary should not have this key to begin with - throw new ArgumentOutOfRangeException(nameof(amount)); + throw new InvalidOperationException(nameof(amount)); case 1: // Single tradable item, can be matchable or not depending on the rest of the inventory if (!fullSet.TryGetValue(classID, out uint fullAmount) || (fullAmount == 0) || (fullAmount < amount)) { - throw new ArgumentNullException(nameof(fullAmount)); + throw new InvalidOperationException(nameof(fullAmount)); } if (fullAmount > 1) { @@ -361,7 +387,7 @@ namespace ArchiSteamFarm { throw new ArgumentNullException(nameof(inventory)); } - Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> state = new Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary>(); + Dictionary<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity), Dictionary> state = new(); foreach (Steam.Asset item in inventory) { (uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity) key = (item.RealAppID, item.Type, item.Rarity); @@ -391,7 +417,7 @@ namespace ArchiSteamFarm { IList<(ParseTradeResult? TradeResult, bool RequiresMobileConfirmation)> results = await Utilities.InParallel(tasks).ConfigureAwait(false); if (Bot.HasMobileAuthenticator) { - HashSet mobileTradeOfferIDs = results.Where(result => (result.TradeResult != null) && (result.TradeResult.Result == ParseTradeResult.EResult.Accepted) && result.RequiresMobileConfirmation).Select(result => result.TradeResult!.TradeOfferID).ToHashSet(); + HashSet mobileTradeOfferIDs = results.Where(result => (result.TradeResult?.Result == ParseTradeResult.EResult.Accepted) && result.RequiresMobileConfirmation).Select(result => result.TradeResult!.TradeOfferID).ToHashSet(); if (mobileTradeOfferIDs.Count > 0) { (bool twoFactorSuccess, _) = await Bot.Actions.HandleTwoFactorAuthenticationConfirmations(true, MobileAuthenticator.Confirmation.EType.Trade, mobileTradeOfferIDs, true).ConfigureAwait(false); @@ -410,7 +436,7 @@ namespace ArchiSteamFarm { await PluginsCore.OnBotTradeOfferResults(Bot, validTradeResults).ConfigureAwait(false); } - return results.Any(result => (result.TradeResult != null) && (result.TradeResult.Result == ParseTradeResult.EResult.Accepted) && (!result.RequiresMobileConfirmation || Bot.HasMobileAuthenticator) && (result.TradeResult.ReceivedItemTypes?.Any(receivedItemType => Bot.BotConfig.LootableTypes.Contains(receivedItemType)) == true)); + return results.Any(result => (result.TradeResult?.Result == ParseTradeResult.EResult.Accepted) && (!result.RequiresMobileConfirmation || Bot.HasMobileAuthenticator) && (result.TradeResult.ReceivedItemTypes?.Any(receivedItemType => Bot.BotConfig.LootableTypes.Contains(receivedItemType)) == true)); } private async Task<(ParseTradeResult? TradeResult, bool RequiresMobileConfirmation)> ParseTrade(Steam.TradeOffer tradeOffer) { @@ -419,14 +445,14 @@ namespace ArchiSteamFarm { } if (tradeOffer.State != ETradeOfferState.Active) { - Bot.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, tradeOffer.State)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, tradeOffer.State)); return (null, false); } if (!HandledTradeOfferIDs.Add(tradeOffer.TradeOfferID)) { // We've already seen this trade, this should not happen - Bot.ArchiLogger.LogGenericError(string.Format(Strings.IgnoringTrade, tradeOffer.TradeOfferID)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.IgnoringTrade, tradeOffer.TradeOfferID)); return (new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.Ignored, tradeOffer.ItemsToReceive), false); } @@ -448,7 +474,7 @@ namespace ArchiSteamFarm { switch (result) { case ParseTradeResult.EResult.Accepted: - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.AcceptingTrade, tradeOffer.TradeOfferID)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.AcceptingTrade, tradeOffer.TradeOfferID)); (bool success, bool requiresMobileConfirmation) = await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false); @@ -459,7 +485,7 @@ namespace ArchiSteamFarm { } if (tradeOffer.ItemsToReceive.Sum(item => item.Amount) > tradeOffer.ItemsToGive.Sum(item => item.Amount)) { - Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.BotAcceptedDonationTrade, tradeOffer.TradeOfferID)); + Bot.ArchiLogger.LogGenericTrace(string.Format(CultureInfo.CurrentCulture, Strings.BotAcceptedDonationTrade, tradeOffer.TradeOfferID)); } tradeRequiresMobileConfirmation = requiresMobileConfirmation; @@ -467,7 +493,7 @@ namespace ArchiSteamFarm { break; case ParseTradeResult.EResult.Blacklisted: case ParseTradeResult.EResult.Rejected when Bot.BotConfig.BotBehaviour.HasFlag(BotConfig.EBotBehaviour.RejectInvalidTrades): - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.RejectingTrade, tradeOffer.TradeOfferID)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.RejectingTrade, tradeOffer.TradeOfferID)); if (!await Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false)) { result = ParseTradeResult.EResult.TryAgain; @@ -478,7 +504,7 @@ namespace ArchiSteamFarm { break; case ParseTradeResult.EResult.Ignored: case ParseTradeResult.EResult.Rejected: - Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IgnoringTrade, tradeOffer.TradeOfferID)); + Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.IgnoringTrade, tradeOffer.TradeOfferID)); break; case ParseTradeResult.EResult.TryAgain: @@ -486,7 +512,7 @@ namespace ArchiSteamFarm { goto case ParseTradeResult.EResult.Ignored; default: - Bot.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(result), result)); + Bot.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(result), result)); return (null, false); } @@ -495,25 +521,29 @@ namespace ArchiSteamFarm { } private async Task ShouldAcceptTrade(Steam.TradeOffer tradeOffer) { - if (Bot.Bots == null) { - throw new ArgumentNullException(nameof(Bot.Bots)); + if (tradeOffer == null) { + throw new ArgumentNullException(nameof(tradeOffer)); } - if ((tradeOffer == null) || (ASF.GlobalConfig == null)) { - throw new ArgumentNullException(nameof(tradeOffer) + " || " + nameof(ASF.GlobalConfig)); + if (ASF.GlobalConfig == null) { + throw new InvalidOperationException(nameof(ASF.GlobalConfig)); + } + + if (Bot.Bots == null) { + throw new InvalidOperationException(nameof(Bot.Bots)); } if (tradeOffer.OtherSteamID64 != 0) { // Always accept trades from SteamMasterID - if (Bot.HasPermission(tradeOffer.OtherSteamID64, BotConfig.EPermission.Master)) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, nameof(tradeOffer.OtherSteamID64) + " " + tradeOffer.OtherSteamID64 + ": " + BotConfig.EPermission.Master)); + if (Bot.HasAccess(tradeOffer.OtherSteamID64, BotConfig.EAccess.Master)) { + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, nameof(tradeOffer.OtherSteamID64) + " " + tradeOffer.OtherSteamID64 + ": " + BotConfig.EAccess.Master)); return ParseTradeResult.EResult.Accepted; } // Always deny trades from blacklisted steamIDs if (Bot.IsBlacklistedFromTrades(tradeOffer.OtherSteamID64)) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Blacklisted, nameof(tradeOffer.OtherSteamID64) + " " + tradeOffer.OtherSteamID64)); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Blacklisted, nameof(tradeOffer.OtherSteamID64) + " " + tradeOffer.OtherSteamID64)); return ParseTradeResult.EResult.Blacklisted; } @@ -523,7 +553,7 @@ namespace ArchiSteamFarm { switch (tradeOffer.ItemsToGive.Count) { case 0 when tradeOffer.ItemsToReceive.Count == 0: // If it's steam issue, try again later - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(tradeOffer.ItemsToReceive.Count) + " = 0")); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(tradeOffer.ItemsToReceive.Count) + " = 0")); return ParseTradeResult.EResult.TryAgain; case 0: @@ -531,18 +561,18 @@ namespace ArchiSteamFarm { bool acceptDonations = Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.AcceptDonations); bool acceptBotTrades = !Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.DontAcceptBotTrades); - // If we accept donations and bot trades, accept it right away - if (acceptDonations && acceptBotTrades) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, nameof(acceptDonations) + " = " + true + " && " + nameof(acceptBotTrades) + " = " + true)); + switch (acceptDonations) { + case true when acceptBotTrades: + // If we accept donations and bot trades, accept it right away + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, nameof(acceptDonations) + " = " + true + " && " + nameof(acceptBotTrades) + " = " + true)); - return ParseTradeResult.EResult.Accepted; - } + return ParseTradeResult.EResult.Accepted; - // If we don't accept donations, neither bot trades, deny it right away - if (!acceptDonations && !acceptBotTrades) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(acceptDonations) + " = " + false + " && " + nameof(acceptBotTrades) + " = " + false)); + case false when !acceptBotTrades: + // If we don't accept donations, neither bot trades, deny it right away + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(acceptDonations) + " = " + false + " && " + nameof(acceptBotTrades) + " = " + false)); - return ParseTradeResult.EResult.Rejected; + return ParseTradeResult.EResult.Rejected; } // Otherwise we either accept donations but not bot trades, or we accept bot trades but not donations @@ -550,28 +580,28 @@ namespace ArchiSteamFarm { ParseTradeResult.EResult result = (acceptDonations && !isBotTrade) || (acceptBotTrades && isBotTrade) ? ParseTradeResult.EResult.Accepted : ParseTradeResult.EResult.Rejected; - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, result, nameof(acceptDonations) + " = " + acceptDonations + " && " + nameof(acceptBotTrades) + " = " + acceptBotTrades + " && " + nameof(isBotTrade) + " = " + isBotTrade)); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, result, nameof(acceptDonations) + " = " + acceptDonations + " && " + nameof(acceptBotTrades) + " = " + acceptBotTrades + " && " + nameof(isBotTrade) + " = " + isBotTrade)); return result; } // If we don't have SteamTradeMatcher enabled, this is the end for us if (!Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher)) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(BotConfig.ETradingPreferences.SteamTradeMatcher) + " = " + false)); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(BotConfig.ETradingPreferences.SteamTradeMatcher) + " = " + false)); return ParseTradeResult.EResult.Rejected; } // Decline trade if we're giving more count-wise, this is a very naive pre-check, it'll be strengthened in more detailed fair types exchange next if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(tradeOffer.ItemsToGive.Count) + ": " + tradeOffer.ItemsToGive.Count + " > " + tradeOffer.ItemsToReceive.Count)); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(tradeOffer.ItemsToGive.Count) + ": " + tradeOffer.ItemsToGive.Count + " > " + tradeOffer.ItemsToReceive.Count)); return ParseTradeResult.EResult.Rejected; } // Decline trade if we're requested to handle any not-accepted item type or if it's not fair games/types exchange if (!tradeOffer.IsValidSteamItemsRequest(Bot.BotConfig.MatchableTypes) || !IsFairExchange(tradeOffer.ItemsToGive, tradeOffer.ItemsToReceive)) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(tradeOffer.IsValidSteamItemsRequest) + " || " + nameof(IsFairExchange))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(tradeOffer.IsValidSteamItemsRequest) + " || " + nameof(IsFairExchange))); return ParseTradeResult.EResult.Rejected; } @@ -581,32 +611,30 @@ namespace ArchiSteamFarm { // Fetch trade hold duration byte? holdDuration = await Bot.GetTradeHoldDuration(tradeOffer.OtherSteamID64, tradeOffer.TradeOfferID).ConfigureAwait(false); - if (!holdDuration.HasValue) { - // If we can't get trade hold duration, try again later - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(holdDuration))); + switch (holdDuration) { + case null: + // If we can't get trade hold duration, try again later + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(holdDuration))); - return ParseTradeResult.EResult.TryAgain; - } + return ParseTradeResult.EResult.TryAgain; - // If user has a trade hold, we add extra logic - if (holdDuration.Value > 0) { + // If user has a trade hold, we add extra logic // If trade hold duration exceeds our max, or user asks for cards with short lifespan, reject the trade - if ((holdDuration.Value > ASF.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGive.Any(item => ((item.Type == Steam.Asset.EType.FoilTradingCard) || (item.Type == Steam.Asset.EType.TradingCard)) && CardsFarmer.SalesBlacklist.Contains(item.RealAppID))) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(holdDuration) + " > 0: " + holdDuration.Value)); + case > 0 when (holdDuration.Value > ASF.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGive.Any(item => ((item.Type == Steam.Asset.EType.FoilTradingCard) || (item.Type == Steam.Asset.EType.TradingCard)) && CardsFarmer.SalesBlacklist.Contains(item.RealAppID)): + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(holdDuration) + " > 0: " + holdDuration.Value)); return ParseTradeResult.EResult.Rejected; - } } // If we're matching everything, this is enough for us if (Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything)) { - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, BotConfig.ETradingPreferences.MatchEverything)); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, BotConfig.ETradingPreferences.MatchEverything)); return ParseTradeResult.EResult.Accepted; } // Get sets we're interested in - HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> wantedSets = new HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)>(); + HashSet<(uint RealAppID, Steam.Asset.EType Type, Steam.Asset.ERarity Rarity)> wantedSets = new(); foreach (Steam.Asset item in tradeOffer.ItemsToGive) { wantedSets.Add((item.RealAppID, item.Type, item.Rarity)); @@ -620,21 +648,21 @@ namespace ArchiSteamFarm { } catch (HttpRequestException e) { // If we can't check our inventory when not using MatchEverything, this is a temporary failure, try again later Bot.ArchiLogger.LogGenericWarningException(e); - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory))); return ParseTradeResult.EResult.TryAgain; } catch (Exception e) { // If we can't check our inventory when not using MatchEverything, this is a temporary failure, try again later Bot.ArchiLogger.LogGenericException(e); - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory))); return ParseTradeResult.EResult.TryAgain; } if (inventory.Count == 0) { // If we can't check our inventory when not using MatchEverything, this is a temporary failure, try again later - Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(inventory))); - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory))); + Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(inventory))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory))); return ParseTradeResult.EResult.TryAgain; } @@ -644,29 +672,33 @@ namespace ArchiSteamFarm { // We're now sure whether the trade is neutral+ for us or not ParseTradeResult.EResult acceptResult = accept ? ParseTradeResult.EResult.Accepted : ParseTradeResult.EResult.Rejected; - Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, acceptResult, nameof(IsTradeNeutralOrBetter))); + Bot.ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, acceptResult, nameof(IsTradeNeutralOrBetter))); return acceptResult; } public sealed class ParseTradeResult { [PublicAPI] - public readonly EResult Result; + public EResult Result { get; } [PublicAPI] - public readonly ulong TradeOfferID; + public ulong TradeOfferID { get; } internal readonly ImmutableHashSet? ReceivedItemTypes; internal ParseTradeResult(ulong tradeOfferID, EResult result, IReadOnlyCollection? itemsToReceive = null) { - if ((tradeOfferID == 0) || (result == EResult.Unknown)) { - throw new ArgumentNullException(nameof(tradeOfferID) + " || " + nameof(result)); + if (tradeOfferID == 0) { + throw new ArgumentOutOfRangeException(nameof(tradeOfferID)); + } + + if ((result == EResult.Unknown) || !Enum.IsDefined(typeof(EResult), result)) { + throw new InvalidEnumArgumentException(nameof(result), (int) result, typeof(EResult)); } TradeOfferID = tradeOfferID; Result = result; - if ((itemsToReceive != null) && (itemsToReceive.Count > 0)) { + if (itemsToReceive?.Count > 0) { ReceivedItemTypes = itemsToReceive.Select(item => item.Type).ToImmutableHashSet(); } } diff --git a/ArchiSteamFarm/Utilities.cs b/ArchiSteamFarm/Utilities.cs index bb1fa5bf2..b1f01107a 100644 --- a/ArchiSteamFarm/Utilities.cs +++ b/ArchiSteamFarm/Utilities.cs @@ -39,12 +39,20 @@ namespace ArchiSteamFarm { private const byte TimeoutForLongRunningTasksInSeconds = 60; // Normally we wouldn't need to use this singleton, but we want to ensure decent randomness across entire program's lifetime - private static readonly Random Random = new Random(); + private static readonly Random Random = new(); [PublicAPI] public static string GetArgsAsText(string[] args, byte argsToSkip, string delimiter) { - if ((args == null) || (args.Length <= argsToSkip) || string.IsNullOrEmpty(delimiter)) { - throw new ArgumentNullException(nameof(args) + " || " + nameof(argsToSkip) + " || " + nameof(delimiter)); + if (args == null) { + throw new ArgumentNullException(nameof(args)); + } + + if (args.Length <= argsToSkip) { + throw new InvalidOperationException(nameof(args.Length) + " && " + nameof(argsToSkip)); + } + + if (string.IsNullOrEmpty(delimiter)) { + throw new ArgumentNullException(nameof(delimiter)); } return string.Join(delimiter, args.Skip(argsToSkip)); @@ -56,15 +64,23 @@ namespace ArchiSteamFarm { throw new ArgumentNullException(nameof(text)); } - string[] args = text.Split(new char[0], argsToSkip + 1, StringSplitOptions.RemoveEmptyEntries); + string[] args = text.Split(Array.Empty(), argsToSkip + 1, StringSplitOptions.RemoveEmptyEntries); return args[^1]; } [PublicAPI] public static string? GetCookieValue(this CookieContainer cookieContainer, string url, string name) { - if ((cookieContainer == null) || string.IsNullOrEmpty(url) || string.IsNullOrEmpty(name)) { - throw new ArgumentNullException(nameof(cookieContainer) + " || " + nameof(url) + " || " + nameof(name)); + if (cookieContainer == null) { + throw new ArgumentNullException(nameof(cookieContainer)); + } + + if (string.IsNullOrEmpty(url)) { + throw new ArgumentNullException(nameof(url)); + } + + if (string.IsNullOrEmpty(name)) { + throw new ArgumentNullException(nameof(name)); } CookieCollection cookies = cookieContainer.GetCookies(new Uri(url)); @@ -188,23 +204,22 @@ namespace ArchiSteamFarm { [PublicAPI] public static int RandomNext(int maxValue) { - if (maxValue < 0) { - throw new ArgumentOutOfRangeException(nameof(maxValue)); - } - - if (maxValue <= 1) { - return maxValue; - } - - lock (Random) { - return Random.Next(maxValue); + switch (maxValue) { + case < 0: + throw new ArgumentOutOfRangeException(nameof(maxValue)); + case <= 1: + return maxValue; + default: + lock (Random) { + return Random.Next(maxValue); + } } } [PublicAPI] public static int RandomNext(int minValue, int maxValue) { if (minValue > maxValue) { - throw new ArgumentOutOfRangeException(nameof(minValue) + " && " + nameof(maxValue)); + throw new InvalidOperationException(nameof(minValue) + " && " + nameof(maxValue)); } if (minValue >= maxValue - 1) { @@ -281,8 +296,12 @@ namespace ArchiSteamFarm { } internal static bool RelativeDirectoryStartsWith(string directory, params string[] prefixes) { - if (string.IsNullOrEmpty(directory) || (prefixes == null) || (prefixes.Length == 0)) { - throw new ArgumentNullException(nameof(directory) + " || " + nameof(prefixes)); + if (string.IsNullOrEmpty(directory)) { + throw new ArgumentNullException(nameof(directory)); + } + + if ((prefixes == null) || (prefixes.Length == 0)) { + throw new ArgumentNullException(nameof(prefixes)); } return (from prefix in prefixes where directory.Length > prefix.Length let pathSeparator = directory[prefix.Length] where (pathSeparator == Path.DirectorySeparatorChar) || (pathSeparator == Path.AltDirectorySeparatorChar) select prefix).Any(prefix => directory.StartsWith(prefix, StringComparison.Ordinal)); diff --git a/ArchiSteamFarm/WebBrowser.cs b/ArchiSteamFarm/WebBrowser.cs index 74913e89a..874f520c0 100644 --- a/ArchiSteamFarm/WebBrowser.cs +++ b/ArchiSteamFarm/WebBrowser.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -46,7 +47,7 @@ namespace ArchiSteamFarm { private const byte MaxIdleTime = 15; // Defines in seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it [PublicAPI] - public readonly CookieContainer CookieContainer = new CookieContainer(); + public CookieContainer CookieContainer { get; } = new(); [PublicAPI] public TimeSpan Timeout => HttpClient.Timeout; @@ -90,10 +91,10 @@ namespace ArchiSteamFarm { [PublicAPI] public HttpClient GenerateDisposableHttpClient(bool extendedTimeout = false) { if (ASF.GlobalConfig == null) { - throw new ArgumentNullException(nameof(ASF.GlobalConfig)); + throw new InvalidOperationException(nameof(ASF.GlobalConfig)); } - HttpClient result = new HttpClient(HttpClientHandler, false) { + HttpClient result = new(HttpClientHandler, false) { #if !NETFRAMEWORK DefaultRequestVersion = HttpVersion.Version20, #endif @@ -109,8 +110,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlGetToBinary(string request, IReadOnlyCollection>? headers = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries, IProgress? progressReporter = null) { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } BinaryResponse? result = null; @@ -154,9 +159,9 @@ namespace ArchiSteamFarm { progressReporter?.Report(0); #if NETFRAMEWORK - using MemoryStream ms = new MemoryStream((int) response.Length); + using MemoryStream ms = new((int) response.Length); #else - await using MemoryStream ms = new MemoryStream((int) response.Length); + await using MemoryStream ms = new((int) response.Length); #endif try { @@ -167,13 +172,21 @@ namespace ArchiSteamFarm { byte[] buffer = new byte[8192]; // This is HttpClient's buffer, using more doesn't make sense while (response.Content.CanRead) { +#if NETFRAMEWORK int read = await response.Content.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); +#else + int read = await response.Content.ReadAsync(buffer.AsMemory(0, buffer.Length)).ConfigureAwait(false); +#endif if (read == 0) { break; } +#if NETFRAMEWORK await ms.WriteAsync(buffer, 0, read).ConfigureAwait(false); +#else + await ms.WriteAsync(buffer.AsMemory(0, read)).ConfigureAwait(false); +#endif if ((batchIncreaseSize == 0) || (batch >= 99)) { continue; @@ -200,8 +213,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -209,8 +222,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlGetToHtmlDocument(string request, IReadOnlyCollection>? headers = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } HtmlDocumentResponse? result = null; @@ -263,8 +280,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -272,8 +289,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task?> UrlGetToJsonObject(string request, IReadOnlyCollection>? headers = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } ObjectResponse? result = null; @@ -317,14 +338,15 @@ namespace ArchiSteamFarm { T? obj; try { - using StreamReader streamReader = new StreamReader(response.Content); - using JsonReader jsonReader = new JsonTextReader(streamReader); - JsonSerializer serializer = new JsonSerializer(); + using StreamReader streamReader = new(response.Content); + using JsonTextReader jsonReader = new(streamReader); + + JsonSerializer serializer = new(); obj = serializer.Deserialize(jsonReader); if (obj == null) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(obj))); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(obj))); continue; } @@ -338,8 +360,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -347,8 +369,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlGetToStream(string request, IReadOnlyCollection>? headers = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } StreamResponse? result = null; @@ -385,8 +411,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -394,8 +420,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlGetToString(string request, IReadOnlyCollection>? headers = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } StringResponse? result = null; @@ -432,8 +462,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -441,8 +471,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlGetToXmlDocument(string request, IReadOnlyCollection>? headers = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } XmlDocumentResponse? result = null; @@ -483,7 +517,7 @@ namespace ArchiSteamFarm { continue; } - XmlDocument xmlDocument = new XmlDocument(); + XmlDocument xmlDocument = new(); try { xmlDocument.Load(response.Content); @@ -497,8 +531,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -506,8 +540,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlHead(string request, IReadOnlyCollection>? headers = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } BasicResponse? result = null; @@ -539,8 +577,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -548,8 +586,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlPost(string request, IReadOnlyCollection>? headers = null, T? data = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } BasicResponse? result = null; @@ -581,8 +623,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -590,8 +632,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlPostToHtmlDocument(string request, IReadOnlyCollection>? headers = null, T? data = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } HtmlDocumentResponse? result = null; @@ -644,8 +690,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -653,8 +699,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task?> UrlPostToJsonObject(string request, IReadOnlyCollection>? headers = null, TData? data = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where TResult : class where TData : class { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } ObjectResponse? result = null; @@ -698,14 +748,14 @@ namespace ArchiSteamFarm { TResult? obj; try { - using StreamReader steamReader = new StreamReader(response.Content); + using StreamReader steamReader = new(response.Content); using JsonReader jsonReader = new JsonTextReader(steamReader); - JsonSerializer serializer = new JsonSerializer(); + JsonSerializer serializer = new(); obj = serializer.Deserialize(jsonReader); if (obj == null) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(obj))); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(obj))); continue; } @@ -719,8 +769,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -728,8 +778,12 @@ namespace ArchiSteamFarm { [PublicAPI] public async Task UrlPostToStream(string request, IReadOnlyCollection>? headers = null, T? data = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, byte maxTries = MaxTries) where T : class { - if (string.IsNullOrEmpty(request) || (maxTries == 0)) { - throw new ArgumentNullException(nameof(request) + " || " + nameof(maxTries)); + if (string.IsNullOrEmpty(request)) { + throw new ArgumentNullException(nameof(request)); + } + + if (maxTries == 0) { + throw new ArgumentOutOfRangeException(nameof(maxTries)); } StreamResponse? result = null; @@ -766,8 +820,8 @@ namespace ArchiSteamFarm { } if (maxTries > 1) { - ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, maxTries)); - ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); + ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, maxTries)); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.ErrorFailingRequest, request)); } return result; @@ -814,13 +868,17 @@ namespace ArchiSteamFarm { } private async Task InternalRequest(Uri requestUri, HttpMethod httpMethod, IReadOnlyCollection>? headers = null, T? data = null, string? referer = null, ERequestOptions requestOptions = ERequestOptions.None, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead, byte maxRedirections = MaxTries) where T : class { - if ((requestUri == null) || (httpMethod == null)) { - throw new ArgumentNullException(nameof(requestUri) + " || " + nameof(httpMethod)); + if (requestUri == null) { + throw new ArgumentNullException(nameof(requestUri)); + } + + if (httpMethod == null) { + throw new ArgumentNullException(nameof(httpMethod)); } HttpResponseMessage response; - using (HttpRequestMessage request = new HttpRequestMessage(httpMethod, requestUri)) { + using (HttpRequestMessage request = new(httpMethod, requestUri)) { #if !NETFRAMEWORK request.Version = HttpClient.DefaultRequestVersion; #endif @@ -914,7 +972,7 @@ namespace ArchiSteamFarm { return response; default: // We have no clue about those, but maybe HttpClient can handle them for us - ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(redirectUri.Scheme), redirectUri.Scheme)); + ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnknownValuePleaseReport, nameof(redirectUri.Scheme), redirectUri.Scheme)); break; } @@ -952,7 +1010,7 @@ namespace ArchiSteamFarm { if (response.StatusCode.IsClientErrorCode()) { if (Debugging.IsUserDebugging) { - ArchiLogger.LogGenericDebug(string.Format(Strings.Content, await response.Content.ReadAsStringAsync().ConfigureAwait(false))); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.Content, await response.Content.ReadAsStringAsync().ConfigureAwait(false))); } // Do not retry on client errors @@ -966,7 +1024,7 @@ namespace ArchiSteamFarm { using (response) { if (Debugging.IsUserDebugging) { - ArchiLogger.LogGenericDebug(string.Format(Strings.Content, await response.Content.ReadAsStringAsync().ConfigureAwait(false))); + ArchiLogger.LogGenericDebug(string.Format(CultureInfo.CurrentCulture, Strings.Content, await response.Content.ReadAsStringAsync().ConfigureAwait(false))); } return null; @@ -975,7 +1033,7 @@ namespace ArchiSteamFarm { public class BasicResponse { [PublicAPI] - public readonly HttpStatusCode StatusCode; + public HttpStatusCode StatusCode { get; } internal readonly Uri FinalUri; @@ -984,7 +1042,7 @@ namespace ArchiSteamFarm { throw new ArgumentNullException(nameof(httpResponseMessage)); } - FinalUri = httpResponseMessage.Headers.Location ?? httpResponseMessage.RequestMessage?.RequestUri ?? throw new ArgumentNullException(nameof(FinalUri)); + FinalUri = httpResponseMessage.Headers.Location ?? httpResponseMessage.RequestMessage?.RequestUri ?? throw new InvalidOperationException(); StatusCode = httpResponseMessage.StatusCode; } @@ -1000,14 +1058,14 @@ namespace ArchiSteamFarm { public sealed class BinaryResponse : BasicResponse { [PublicAPI] - public readonly byte[]? Content; + public byte[]? Content { get; } public BinaryResponse(BasicResponse basicResponse, byte[] content) : this(basicResponse) { - if ((basicResponse == null) || (content == null)) { - throw new ArgumentNullException(nameof(basicResponse) + " || " + nameof(content)); + if (basicResponse == null) { + throw new ArgumentNullException(nameof(basicResponse)); } - Content = content; + Content = content ?? throw new ArgumentNullException(nameof(content)); } public BinaryResponse(BasicResponse basicResponse) : base(basicResponse) { @@ -1019,7 +1077,7 @@ namespace ArchiSteamFarm { public sealed class HtmlDocumentResponse : BasicResponse, IDisposable { [PublicAPI] - public readonly IDocument? Content; + public IDocument? Content { get; } public HtmlDocumentResponse(BasicResponse basicResponse) : base(basicResponse) { if (basicResponse == null) { @@ -1028,11 +1086,11 @@ namespace ArchiSteamFarm { } private HtmlDocumentResponse(BasicResponse basicResponse, IDocument content) : this(basicResponse) { - if ((basicResponse == null) || (content == null)) { - throw new ArgumentNullException(nameof(basicResponse) + " || " + nameof(content)); + if (basicResponse == null) { + throw new ArgumentNullException(nameof(basicResponse)); } - Content = content; + Content = content ?? throw new ArgumentNullException(nameof(content)); } public void Dispose() => Content?.Dispose(); @@ -1059,7 +1117,7 @@ namespace ArchiSteamFarm { public sealed class ObjectResponse : BasicResponse where T : class { [PublicAPI] - public readonly T? Content; + public T? Content { get; } public ObjectResponse(BasicResponse basicResponse, T content) : this(basicResponse) { if (basicResponse == null) { @@ -1078,19 +1136,19 @@ namespace ArchiSteamFarm { public sealed class StreamResponse : BasicResponse, IAsyncDisposable { [PublicAPI] - public readonly Stream? Content; + public Stream? Content { get; } [PublicAPI] - public readonly long Length; + public long Length { get; } private readonly HttpResponseMessage ResponseMessage; internal StreamResponse(HttpResponseMessage httpResponseMessage, Stream content) : this(httpResponseMessage) { - if ((httpResponseMessage == null) || (content == null)) { - throw new ArgumentNullException(nameof(httpResponseMessage) + " || " + nameof(content)); + if (httpResponseMessage == null) { + throw new ArgumentNullException(nameof(httpResponseMessage)); } - Content = content; + Content = content ?? throw new ArgumentNullException(nameof(content)); } internal StreamResponse(HttpResponseMessage httpResponseMessage) : base(httpResponseMessage) { @@ -1113,14 +1171,14 @@ namespace ArchiSteamFarm { public sealed class StringResponse : BasicResponse { [PublicAPI] - public readonly string? Content; + public string? Content { get; } internal StringResponse(HttpResponseMessage httpResponseMessage, string content) : this(httpResponseMessage) { - if ((httpResponseMessage == null) || (content == null)) { - throw new ArgumentNullException(nameof(httpResponseMessage) + " || " + nameof(content)); + if (httpResponseMessage == null) { + throw new ArgumentNullException(nameof(httpResponseMessage)); } - Content = content; + Content = content ?? throw new ArgumentNullException(nameof(content)); } internal StringResponse(HttpResponseMessage httpResponseMessage) : base(httpResponseMessage) { @@ -1132,14 +1190,14 @@ namespace ArchiSteamFarm { public sealed class XmlDocumentResponse : BasicResponse { [PublicAPI] - public readonly XmlDocument? Content; + public XmlDocument? Content { get; } public XmlDocumentResponse(BasicResponse basicResponse, XmlDocument content) : this(basicResponse) { - if ((basicResponse == null) || (content == null)) { - throw new ArgumentNullException(nameof(basicResponse) + " || " + nameof(content)); + if (basicResponse == null) { + throw new ArgumentNullException(nameof(basicResponse)); } - Content = content; + Content = content ?? throw new ArgumentNullException(nameof(content)); } public XmlDocumentResponse(BasicResponse basicResponse) : base(basicResponse) {